diff --git a/decodedtext.cpp b/decodedtext.cpp index 1527d4354..4cd085f27 100644 --- a/decodedtext.cpp +++ b/decodedtext.cpp @@ -7,6 +7,11 @@ 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 @@ -14,28 +19,39 @@ DecodedText::DecodedText (QString const& the_string) , message_ {string_.mid (column_qsoText + padding_).trimmed ()} , is_standard_ {false} { + string_ = string_.left (column_qsoText + padding_ + 25); if (message_.length() >= 1) { - message_ = message_.left (22).remove (QRegularExpression {"[<>]"}); + 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); } }; -void DecodedText::removeAddedInfo () +QStringList DecodedText::messageWords () const { - if (string_.indexOf (" CQ ") > 0) { - // TODO this magic 37 characters is also referenced in DisplayText::_appendDXCCWorkedB4() - auto eom_pos = string_.indexOf (' ', 37); - if (eom_pos < 37) eom_pos = string_.size () - 1; // we always want at least the characters - // to position 37 - string_ = string_.left (eom_pos + 1); // remove DXCC entity and worked B4 status. TODO need a better way to do this - } + 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 @@ -132,35 +148,38 @@ bool DecodedText::report(QString const& myBaseCall, QString const& dxBaseCall, / // get the first text word, usually the call QString DecodedText::call() const { - auto call = string_; - call = call.replace (QRegularExpression {" CQ ([A-Z]{2,2}|[0-9]{3,3}) "}, " CQ_\\1 ").mid (column_qsoText + padding_); - int i = call.indexOf(" "); - return call.mid(0,i); + 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 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 (">", ""); + 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 (">", ""); } -int DecodedText::timeInSeconds() const +unsigned DecodedText::timeInSeconds() const { - return 60*string_.mid(column_time,2).toInt() + string_.mid(2,2).toInt(); + 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); } /* diff --git a/decodedtext.h b/decodedtext.h index 49b054c71..18859026a 100644 --- a/decodedtext.h +++ b/decodedtext.h @@ -14,8 +14,10 @@ /* -0123456789012345678901234567890123456789 +012345678901234567890123456789012345678901 ^ ^ ^ ^ ^ ^ +2343 -11 0.8 1259 # CQ VP2X/GM4WJS GL33 +2343 -11 0.8 1259 # CQ 999 VP2V/GM4WJS 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 @@ -30,7 +32,7 @@ public: explicit DecodedText (QString const&); QString string() const { return string_; }; - void removeAddedInfo (); + QStringList messageWords () const; int indexOf(QString s) const { return string_.indexOf(s); }; int indexOf(QString s, int i) const { return string_.indexOf(s,i); }; QString mid(int f, int t) const { return string_.mid(f,t); }; @@ -58,7 +60,7 @@ public: // get the second word, most likely the de call and the third word, most likely grid void deCallAndGrid(/*out*/QString& call, QString& grid) const; - int timeInSeconds() const; + unsigned timeInSeconds() const; // returns a string of the SNR field with a leading + or - followed by two digits QString report() const; diff --git a/displaytext.cpp b/displaytext.cpp index fa62940d6..096e46cfc 100644 --- a/displaytext.cpp +++ b/displaytext.cpp @@ -85,77 +85,82 @@ QString DisplayText::appendDXCCWorkedB4(QString message, QString const& callsign QColor color_DXCC, QColor color_NewCall) { - QString call = callsign; - QString countryName; - bool callWorkedBefore; - bool countryWorkedBefore; + // allow for seconds + unsigned padding {message.indexOf (" ") > 4 ? 2U : 0U}; + QString call = callsign; + QString countryName; + bool callWorkedBefore; + bool countryWorkedBefore; - if(call.length()==2) { - int i0=message.indexOf("CQ "+call); - call=message.mid(i0+6,-1); - i0=call.indexOf(" "); - call=call.mid(0,i0); - } - if(call.length()<3) return message; - if(!call.contains(QRegExp("[0-9]|[A-Z]"))) return message; + if(call.length()==2) { + int i0=message.indexOf("CQ "+call); + call=message.mid(i0+6,-1); + i0=call.indexOf(" "); + call=call.mid(0,i0); + } + if(call.length()<3) return message; + if(!call.contains(QRegExp("[0-9]|[A-Z]"))) return message; - logBook.match(/*in*/call,/*out*/countryName,callWorkedBefore,countryWorkedBefore); - int charsAvail = 48; + logBook.match(/*in*/call,/*out*/countryName,callWorkedBefore,countryWorkedBefore); + int charsAvail = 52 + padding; - // the decoder (seems) to always generate 41 chars. For a normal CQ call, the last five are spaces - // TODO this magic 37 characters is also referenced in MainWindow::doubleClickOnCall() - int nmin=37; - int i=message.indexOf(" CQ "); - int k=message.mid(i+4,3).toInt(); - if(k>0 and k<999) nmin += 4; - int s3 = message.indexOf(" ",nmin); - if (s3 < nmin) s3 = nmin; // always want at least the characters to position 35 - s3 += 1; // convert the index into a character count - message = message.left(s3); // reduce trailing white space - charsAvail -= s3; - if (charsAvail > 4) + // the decoder (seems) to always generate 41 chars. For a normal CQ + // call, the last five are spaces + // + // A maximum length call is "QRZ VP2X/GM4WJS IO91" "CQ AA ..." or CQ + // nnn ..." don't allow grid squares so are not longer. Here we align + // the added info at least after the longest CQ/QRZ message plus one + // space so that it can be stripped off algorithmically later. + // + int nmin = 46 + padding; + int s3 = message.indexOf (" ", nmin); + if (s3 < nmin) s3 = nmin; // always want at least the characters to position 45 + s3 += 1; // convert the index into a character count + message = message.left(s3); // reduce trailing white space + charsAvail -= s3; + if (charsAvail > 4) { - if (!countryWorkedBefore) // therefore not worked call either + if (!countryWorkedBefore) // therefore not worked call either { - message += "!"; - *bg = color_DXCC; + message += "!"; + *bg = color_DXCC; } + else + if (!callWorkedBefore) // but have worked the country + { + message += "~"; + *bg = color_NewCall; + } else - if (!callWorkedBefore) // but have worked the country - { - message += "~"; - *bg = color_NewCall; - } - else - { - message += " "; // have worked this call before - *bg = color_CQ; - } - charsAvail -= 1; + { + message += " "; // have worked this call before + *bg = color_CQ; + } + charsAvail -= 1; - // do some obvious abbreviations - countryName.replace ("Islands", "Is."); - countryName.replace ("Island", "Is."); - countryName.replace ("North ", "N. "); - countryName.replace ("Northern ", "N. "); - countryName.replace ("South ", "S. "); - countryName.replace ("East ", "E. "); - countryName.replace ("Eastern ", "E. "); - countryName.replace ("West ", "W. "); - countryName.replace ("Western ", "W. "); - countryName.replace ("Central ", "C. "); - countryName.replace (" and ", " & "); - countryName.replace ("Republic", "Rep."); - countryName.replace ("United States", "U.S.A."); - countryName.replace ("Fed. Rep. of ", ""); - countryName.replace ("French ", "Fr."); - countryName.replace ("Asiatic", "AS"); - countryName.replace ("European", "EU"); - countryName.replace ("African", "AF"); + // do some obvious abbreviations + countryName.replace ("Islands", "Is."); + countryName.replace ("Island", "Is."); + countryName.replace ("North ", "N. "); + countryName.replace ("Northern ", "N. "); + countryName.replace ("South ", "S. "); + countryName.replace ("East ", "E. "); + countryName.replace ("Eastern ", "E. "); + countryName.replace ("West ", "W. "); + countryName.replace ("Western ", "W. "); + countryName.replace ("Central ", "C. "); + countryName.replace (" and ", " & "); + countryName.replace ("Republic", "Rep."); + countryName.replace ("United States", "U.S.A."); + countryName.replace ("Fed. Rep. of ", ""); + countryName.replace ("French ", "Fr."); + countryName.replace ("Asiatic", "AS"); + countryName.replace ("European", "EU"); + countryName.replace ("African", "AF"); - message += countryName; + message += countryName; } - return message; + return message; } void DisplayText::displayDecodedText(DecodedText const& decodedText, QString const& myCall, diff --git a/lib/stdmsg.f90 b/lib/stdmsg.f90 index b006fda93..97e842c1d 100644 --- a/lib/stdmsg.f90 +++ b/lib/stdmsg.f90 @@ -6,7 +6,7 @@ logical*1 function stdmsg(msg0) call packmsg(msg0,dat,itype) call unpackmsg(dat,msg) - stdmsg=(msg.eq.msg0) .and. (itype.ge.0) + stdmsg=(msg.eq.msg0) .and. (itype.ge.0) .and. itype.ne.6 return end function stdmsg diff --git a/mainwindow.cpp b/mainwindow.cpp index 85f690964..abb2174ab 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -101,8 +101,6 @@ extern "C" { void geniscat_(char* msg, char* msgsent, int itone[], int len1, int len2); - bool stdmsg_(const char* msg, int len); - void azdist_(char* MyGrid, char* HisGrid, double* utch, int* nAz, int* nEl, int* nDmiles, int* nDkm, int* nHotAz, int* nHotABetter, int len1, int len2); @@ -1431,12 +1429,11 @@ void MainWindow::fastSink(qint64 frames) m_logBook,m_config.color_CQ(),m_config.color_MyCall(),m_config.color_DXCC(), m_config.color_NewCall()); m_bDecoded=true; - auto_sequence (message, ui->sbFtol->value (), std::numeric_limits::max ()); + auto_sequence (decodedtext, ui->sbFtol->value (), std::numeric_limits::max ()); if (m_mode != "ISCAT") postDecode (true, decodedtext.string ()); writeAllTxt(message); bool stdMsg = decodedtext.report(m_baseCall, Radio::base_callsign(ui->dxCallEntry->text()),m_rptRcvd); - decodedtext = DecodedText {message.left (4) + message.mid (6, -1)}; if (stdMsg) pskPost (decodedtext); } @@ -2554,16 +2551,13 @@ void MainWindow::decode() //decode() void::MainWindow::fast_decode_done() { float t,tmax=-99.0; - QString msg0; dec_data.params.nagain=false; dec_data.params.ndiskdat=false; // if(m_msg[0][0]==0) m_bDecoded=false; - for(int i=0; i<100; i++) { - if (tmax >= 0.0) auto_sequence (msg0, ui->sbFtol->value (), ui->sbFtol->value ()); - if(m_msg[i][0]==0) break; + for(int i=0; m_msg[i][0] && i<100; i++) { QString message=QString::fromLatin1(m_msg[i]); m_msg[i][0]=0; - if(message.length()>80) message=message.mid(0,80); + if(message.length()>80) message=message.left (80); if(narg[13]/8==narg[12]) message=message.trimmed().replace("<...>",m_calls); //Left (Band activity) window @@ -2576,7 +2570,6 @@ void::MainWindow::fast_decode_done() t=message.mid(10,5).toFloat(); if(t>tmax) { - msg0=message; tmax=t; m_bDecoded=true; } @@ -2585,14 +2578,13 @@ void::MainWindow::fast_decode_done() if(m_mode=="JT9" or m_mode=="MSK144") { // find and extract any report for myCall - QString msg=message.mid(0,4) + message.mid(6,-1); - decodedtext = DecodedText {msg.replace (QChar::LineFeed, "")}; bool stdMsg = decodedtext.report(m_baseCall, Radio::base_callsign(ui->dxCallEntry->text()), m_rptRcvd); // extract details and send to PSKreporter if (stdMsg) pskPost (decodedtext); } + if (tmax >= 0.0) auto_sequence (decodedtext, ui->sbFtol->value (), ui->sbFtol->value ()); } m_startAnother=m_loopall; m_nPick=0; @@ -2736,7 +2728,7 @@ void MainWindow::readFromStdout() //readFromStdout // int snr=decodedtext.string().mid(6,4).toInt(); m_bDoubleClicked=true; m_bAutoReply = true; - processMessage (decodedtext.string (), decodedtext.string ().size ()); + processMessage (decodedtext); ui->cbFirst->setStyleSheet(""); } else { if (for_us or (abs(audioFreq - m_wideGraph->rxFreq()) <= 10)) bDisplayRight=true; @@ -2757,9 +2749,9 @@ void MainWindow::readFromStdout() //readFromStdout if(b65 and m_modeTx!="JT65") on_pbTxMode_clicked(); if(!b65 and m_modeTx=="JT65") on_pbTxMode_clicked(); } - m_QSOText = decodedtext.string (); + m_QSOText = decodedtext.string ().trimmed (); } - if(m_mode=="FT8" or m_mode=="QRA64") auto_sequence (decodedtext.string(), 25, 50); + if(m_mode=="FT8" or m_mode=="QRA64") auto_sequence (decodedtext, 25, 50); postDecode (true, decodedtext.string ()); @@ -2793,40 +2785,50 @@ void MainWindow::readFromStdout() //readFromStdout // another caller and we are going to transmit within // +/- this value of the reply to another caller // -void MainWindow::auto_sequence (QString const& message, unsigned start_tolerance, unsigned stop_tolerance) +void MainWindow::auto_sequence (DecodedText const& message, unsigned start_tolerance, unsigned stop_tolerance) { - auto const& parts = message.split (' ', QString::SkipEmptyParts); - if (parts.size () > 6) { - bool ok; - auto df = parts[3].toInt (&ok); - auto within_tolerance = ok - && (qAbs (ui->RxFreqSpinBox->value () - df) <= int (start_tolerance) - || qAbs (ui->TxFreqSpinBox->value () - df) <= int (start_tolerance)); + auto const& message_words = message.messageWords (); + auto is_73 = message_words.filter (QRegularExpression {"^(73|RR73)$"}).size (); + if (message_words.size () > 2 && (message.isStandardMessage () || is_73)) { + auto df = message.frequencyOffset (); + auto within_tolerance = + (qAbs (ui->RxFreqSpinBox->value () - df) <= int (start_tolerance) + || qAbs (ui->TxFreqSpinBox->value () - df) <= int (start_tolerance)); + bool acceptable_73 = is_73 + && m_QSOProgress >= ROGER_REPORT + && ((message.isStandardMessage () + && (message_words.contains (m_baseCall) + || message_words.contains (m_config.my_callsign ()) + || message_words.contains (ui->dxCallEntry->text ()) + || message_words.contains (Radio::base_callsign (ui->dxCallEntry->text ())) + || message_words.contains ("DE"))) + || !message.isStandardMessage ()); // free text 73/RR73 if (m_auto && (REPLYING == m_QSOProgress || (!ui->tx1->isEnabled () && REPORT == m_QSOProgress)) && qAbs (ui->TxFreqSpinBox->value () - df) <= int (stop_tolerance) - && !parts[5].contains (QRegularExpression {"(^(CQ|QRZ)$)|" + m_baseCall}) - && parts[6].contains (Radio::base_callsign (ui->dxCallEntry->text ()))) { + && !message_words.at (1).contains (QRegularExpression {"(^(CQ|QRZ))|" + m_baseCall}) + && message_words.at (2).contains (Radio::base_callsign (ui->dxCallEntry->text ()))) { // auto stop to avoid accidental QRM auto_tx_mode (false); } else if (m_auto // transmit allowed && ui->cbAutoSeq->isVisible () && ui->cbAutoSeq->isChecked() // auto-sequencing allowed && ((!m_bCallingCQ // not calling CQ/QRZ - && !m_sentFirst73 // finished QSO - && ((parts[5].contains (m_baseCall) + && !m_sentFirst73 // not finished QSO + && ((message_words.at (1).contains (m_baseCall) // being called and not already in a QSO - && parts[6].contains (Radio::base_callsign (ui->dxCallEntry->text ()))) + && message_words.at (2).contains (Radio::base_callsign (ui->dxCallEntry->text ()))) // type 2 compound replies || (within_tolerance - && ((m_QSOProgress >= ROGER_REPORT && message_is_73 (0, parts)) - || ("DE" == parts[5] && parts[6].contains (Radio::base_callsign (m_hisCall))))))) + && (acceptable_73 + || ("DE" == message_words.at (1) && message_words.at (2).contains (Radio::base_callsign (m_hisCall))))))) || (m_bCallingCQ && m_bAutoReply - && ((within_tolerance && "DE" == parts[5]) // look for type 2 compound call replies on our Tx and Rx offsets - || parts[5].contains (m_baseCall))))) + // look for type 2 compound call replies on our Tx and Rx offsets + && ((within_tolerance && "DE" == message_words.at (1)) + || message_words.at (1).contains (m_baseCall))))) { - processMessage (message, message.size ()); + processMessage (message); } } } @@ -3478,7 +3480,10 @@ void MainWindow::on_txrb1_toggled (bool status) void MainWindow::on_txrb1_doubleClicked () { - ui->tx1->setEnabled (!ui->tx1->isEnabled ()); + // skip Tx1, only allowed if not a type 2 compound callsign + auto const& my_callsign = m_config.my_callsign (); + auto is_compound = my_callsign != m_baseCall; + ui->tx1->setEnabled ((is_compound && shortList (my_callsign)) || !ui->tx1->isEnabled ()); if (!ui->tx1->isEnabled ()) { // leave time for clicks to complete before setting txrb2 QTimer::singleShot (500, ui->txrb2, SLOT (click ())); @@ -3512,7 +3517,10 @@ void MainWindow::on_txrb4_toggled (bool status) void MainWindow::on_txrb4_doubleClicked () { - m_send_RR73 = !m_send_RR73; + // RR73 only allowed if not a type 2 compound callsign + auto const& my_callsign = m_config.my_callsign (); + auto is_compound = my_callsign != m_baseCall; + m_send_RR73 = !((is_compound && !shortList (my_callsign)) || m_send_RR73); genStdMsgs (m_rpt); } @@ -3551,7 +3559,10 @@ void MainWindow::on_txb1_clicked() void MainWindow::on_txb1_doubleClicked() { - ui->tx1->setEnabled (!ui->tx1->isEnabled ()); + // skip Tx1, only allowed if not a type 1 compound callsign + auto const& my_callsign = m_config.my_callsign (); + auto is_compound = my_callsign != m_baseCall; + ui->tx1->setEnabled ((is_compound && shortList (my_callsign)) || !ui->tx1->isEnabled ()); } void MainWindow::on_txb2_clicked() @@ -3580,7 +3591,10 @@ void MainWindow::on_txb4_clicked() void MainWindow::on_txb4_doubleClicked() { - m_send_RR73 = !m_send_RR73; + // RR73 only allowed if not a type 2 compound callsign + auto const& my_callsign = m_config.my_callsign (); + auto is_compound = my_callsign != m_baseCall; + m_send_RR73 = !((is_compound && !shortList (my_callsign)) || m_send_RR73); genStdMsgs (m_rpt); } @@ -3627,28 +3641,18 @@ void MainWindow::doubleClickOnCall(bool alt, bool ctrl) } else { cursor=ui->decodedTextBrowser2->textCursor(); } - - cursor.select(QTextCursor::LineUnderCursor); - int position {cursor.position()}; - - QString messages; - if(!m_decodedText2) messages= ui->decodedTextBrowser2->toPlainText(); - if(m_decodedText2) messages= ui->decodedTextBrowser->toPlainText(); - if(ui->cbCQTx->isEnabled() && ui->cbCQTx->isChecked()) m_bDoubleClickAfterCQnnn=true; - m_bDoubleClicked=true; - processMessage(messages, position, ctrl, alt); + cursor.setPosition (cursor.selectionStart ()); + DecodedText message {cursor.block ().text ()}; + m_bDoubleClicked = true; + processMessage (message, ctrl, alt); } -void MainWindow::processMessage(QString const& messages, int position, bool ctrl, bool alt) +void MainWindow::processMessage(DecodedText const& message, bool ctrl, bool alt) { - QString t1 = messages.left(position); //contents up to \n on selected line - int i1=t1.lastIndexOf(QChar::LineFeed) + 1; //points to first char of line - QString t2 = messages.mid(i1,position-i1); //selected line - // basic mode sanity checks - auto const& parts = t2.split (' ', QString::SkipEmptyParts); + auto const& parts = message.string ().split (' ', QString::SkipEmptyParts); if (parts.size () < 5) return; - auto const& mode = parts[4].mid(0,1); + auto const& mode = parts.at (4).left (1); if (("JT9+JT65" == m_mode && !("@" == mode || "#" == mode)) || ("JT65" == m_mode && mode != "#") || ("JT9" == m_mode && mode != "@") @@ -3657,22 +3661,21 @@ void MainWindow::processMessage(QString const& messages, int position, bool ctrl return; } - QString t2a; - int ntsec=3600*t2.mid(0,2).toInt() + 60*t2.mid(2,2).toInt(); - if(m_bFastMode or m_mode=="FT8") { - ntsec = ntsec + t2.mid(4,2).toInt(); - t2a = t2.left (4) + t2.mid (6); //Change hhmmss to hhmm for the message parser - } - t2a = t2.left (44); // strip any quality info trailing the + // QString t2a; + // int ntsec=3600*t2.mid(0,2).toInt() + 60*t2.mid(2,2).toInt(); + // if(m_bFastMode or m_mode=="FT8") { + // ntsec = ntsec + t2.mid(4,2).toInt(); + // t2a = t2.left (4) + t2.mid (6); //Change hhmmss to hhmm for the message parser + // } + //t2a = t2.left (44); // strip any quality info trailing the // decoded message if(m_bFastMode or m_mode=="FT8") { - i1=t2a.indexOf(" CQ "); + auto i1=message.string ().indexOf(" CQ "); if(i1>10) { bool ok; - Frequency kHz {t2a.mid (i1+4,3).toUInt (&ok)}; + Frequency kHz {message.string ().mid (i1+4,3).toUInt (&ok)}; if(ok && kHz <= 999) { - t2a = t2a.mid (0, i1+4) + t2a.mid (i1+8, -1); if (m_config.is_transceiver_online ()) { //QSY Freq for answering CQ nnn setRig (m_freqNominal / 1000000 * 1000000 + 1000 * kHz); @@ -3681,74 +3684,58 @@ void MainWindow::processMessage(QString const& messages, int position, bool ctrl } } } - DecodedText decodedtext {t2a}; - decodedtext.removeAddedInfo (); - auto t3 = decodedtext.string (); - auto t4 = t3.replace (QRegularExpression {" CQ ([A-Z]{2,2}|[0-9]{3,3}) "}, " CQ_\\1 ").split (" ", QString::SkipEmptyParts); - if(t4.size () < 6) return; //Skip the rest if no decoded text - - int frequency = decodedtext.frequencyOffset(); - if (ui->RxFreqSpinBox->isEnabled () and m_mode != "MSK144") { - ui->RxFreqSpinBox->setValue (frequency); //Set Rx freq - } - - if (decodedtext.isTX()) { + //Skip the rest if no decoded text extracted + int frequency = message.frequencyOffset(); + if (message.isTX()) { if (!m_config.enable_VHF_features() && ctrl && ui->TxFreqSpinBox->isEnabled()) { ui->TxFreqSpinBox->setValue(frequency); //Set Tx freq } return; } - int nmod=ntsec % (2*m_TRperiod); + int nmod = message.timeInSeconds () % (2*m_TRperiod); m_txFirst=(nmod!=0); ui->txFirstCheckBox->setChecked(m_txFirst); + auto const& message_words = message.messageWords (); + if (message_words.size () < 2) return; + QString hiscall; QString hisgrid; - decodedtext.deCallAndGrid(/*out*/hiscall,hisgrid); - bool is_73 = t4.filter (QRegularExpression {"^(73|RR73)$"}).size (); - auto acceptable_73 = - m_QSOProgress >= ROGER_REPORT - && is_73 - && ((decodedtext.isStandardMessage () - && (t4.contains (m_baseCall) - || t4.contains (m_config.my_callsign ()) - || t4.contains (ui->dxCallEntry->text ()) - || t4.contains (Radio::base_callsign (ui->dxCallEntry->text ())) - || t4.contains ("DE"))) - || !decodedtext.isStandardMessage ()); - if ((is_73 && !acceptable_73) - || (!Radio::is_callsign (hiscall) // not interested if not from QSO partner - && !(t4.size () == 7 // unless it is of the form - && (t4.at (5) == m_baseCall // " 73" - || t4.at (5).startsWith (m_baseCall + '/') - || t4.at (5).endsWith ('/' + m_baseCall)) - && t4.at (6) == "73"))) + message.deCallAndGrid(/*out*/hiscall,hisgrid); + auto is_73 = message_words.filter (QRegularExpression {"^(73|RR73)$"}).size (); + if (!is_73 && !message.isStandardMessage ()) + // && (!Radio::is_callsign (hiscall) // not interested if not from QSO partner + // && !(t4.size () == 7 // unless it is of the form + // && (t4.at (5) == m_baseCall // " 73" + // || t4.at (5).startsWith (m_baseCall + '/') + // || t4.at (5).endsWith ('/' + m_baseCall)) + // && t4.at (6) == "73"))) { qDebug () << "Not processing message - hiscall:" << hiscall << "hisgrid:" << hisgrid; return; } // only allow automatic mode changes between JT9 and JT65, and when not transmitting if (!m_transmitting and m_mode == "JT9+JT65") { - if (decodedtext.isJT9()) + if (message.isJT9()) { m_modeTx="JT9"; ui->pbTxMode->setText("Tx JT9 @"); m_wideGraph->setModeTx(m_modeTx); - } else if (decodedtext.isJT65()) { + } else if (message.isJT65()) { m_modeTx="JT65"; ui->pbTxMode->setText("Tx JT65 #"); m_wideGraph->setModeTx(m_modeTx); } - } else if ((decodedtext.isJT9 () and m_modeTx != "JT9" and m_mode != "JT4") or - (decodedtext.isJT65 () and m_modeTx != "JT65" and m_mode != "JT4")) { + } else if ((message.isJT9 () and m_modeTx != "JT9" and m_mode != "JT4") or + (message.isJT65 () and m_modeTx != "JT65" and m_mode != "JT4")) { // if we are not allowing mode change then don't process decode return; } - QString firstcall = decodedtext.call(); + QString firstcall = message.call(); if(!m_bFastMode and (!m_config.enable_VHF_features() or m_mode=="FT8")) { // Don't change Tx freq if in a fast mode, or VHF features enabled; also not if a // station is calling me, unless m_lockTxFreq is true or CTRL is held down. @@ -3768,7 +3755,7 @@ void MainWindow::processMessage(QString const& messages, int position, bool ctrl auto base_call = Radio::base_callsign (hiscall); // Determine appropriate response to received message - auto dtext = " " + decodedtext.string () + " "; + auto dtext = " " + message.string () + " "; int gen_msg {0}; if(dtext.contains (" " + m_baseCall + " ") || dtext.contains ("<" + m_baseCall + " ") @@ -3776,13 +3763,13 @@ void MainWindow::processMessage(QString const& messages, int position, bool ctrl || dtext.contains (" " + m_baseCall + "/") || (firstcall == "DE" /*&& ((t4.size () > 7 && t4.at(7) != "73") || t4.size () <= 7)*/)) { - if (t4.size () > 7 // enough fields for a normal msg - && (t4.at (5).contains (m_baseCall) || "DE" == t4.at (5)) - && t4.at (6).contains (qso_partner_base_call) - && !t4.at (7).contains (grid_regexp)) // but no grid on end of msg + if (message_words.size () > 3 // enough fields for a normal message + && (message_words.at (1).contains (m_baseCall) || "DE" == message_words.at (1)) + && message_words.at (2).contains (qso_partner_base_call) + && !message_words.at (3).contains (grid_regexp)) // but no grid on end of msg { - QString r=t4.at (7); - if(m_QSOProgress >= ROGER_REPORT && (r.mid(0,3)=="RRR" || r.toInt()==73 || "RR73" == r)) { + QString r=message_words.at (3); + if(m_QSOProgress >= ROGER_REPORT && (r=="RRR" || r.toInt()==73 || "RR73" == r)) { if(ui->tabWidget->currentIndex()==1) { gen_msg = 5; if (ui->rbGenMsg->isChecked ()) m_ntx=7; @@ -3819,7 +3806,7 @@ void MainWindow::processMessage(QString const& messages, int position, bool ctrl } } else if (m_QSOProgress >= ROGERS - && t4.size () >= 7 && t4.at (5).contains (m_baseCall) && t4.at (6) == "73") { + && message_words.size () > 2 && message_words.at (1).contains (m_baseCall) && message_words.at (2) == "73") { // 73 back to compound call holder if(ui->tabWidget->currentIndex()==1) { gen_msg = 5; @@ -3833,7 +3820,7 @@ void MainWindow::processMessage(QString const& messages, int position, bool ctrl m_QSOProgress = SIGNOFF; } else if (!(m_bAutoReply && m_QSOProgress > CALLING)) { - if ((t4.size () >= 9 && t4.at (5).contains (m_baseCall) && t4.at (8) == "OOO") + if ((message_words.size () > 4 && message_words.at (1).contains (m_baseCall) && message_words.at (4) == "OOO") || ((m_mode=="MSK144" or m_mode=="FT8") && m_config.contestMode())) { // EME short code report or MSK144/FT8 contest mode reply, send back Tx3 m_ntx = 3; @@ -3865,7 +3852,7 @@ void MainWindow::processMessage(QString const& messages, int position, bool ctrl return; } } - else if (firstcall == "DE" && t4.size () >= 8 && t4.at (7) == "73") { + else if (firstcall == "DE" && message_words.size () > 3 && message_words.at (3) == "73") { if (m_QSOProgress >= ROGERS && base_call == qso_partner_base_call && m_currentMessageType) { // 73 back to compound call holder if(ui->tabWidget->currentIndex()==1) { @@ -3898,7 +3885,7 @@ void MainWindow::processMessage(QString const& messages, int position, bool ctrl } } } - else if (acceptable_73) { + else if (is_73 && !message.isStandardMessage ()) { if(ui->tabWidget->currentIndex()==1) { gen_msg = 5; if (ui->rbGenMsg->isChecked ()) m_ntx=7; @@ -3931,14 +3918,18 @@ void MainWindow::processMessage(QString const& messages, int position, bool ctrl // if we get here then we are reacting to the message if (m_bAutoReply) m_bCallingCQ = CALLING == m_QSOProgress; + + if (ui->RxFreqSpinBox->isEnabled () and m_mode != "MSK144") { + ui->RxFreqSpinBox->setValue (frequency); //Set Rx freq + } + QString s1 = m_QSOText.trimmed (); - QString s2=t2.trimmed(); - if (s1!=s2 and !decodedtext.isTX()) { - decodedtext = DecodedText {t2}; - ui->decodedTextBrowser2->displayDecodedText(decodedtext, m_baseCall, + QString s2 = message.string ().trimmed(); + if (s1!=s2 and !message.isTX()) { + ui->decodedTextBrowser2->displayDecodedText(message, m_baseCall, false, m_logBook,m_config.color_CQ(), m_config.color_MyCall(), m_config.color_DXCC(),m_config.color_NewCall()); - m_QSOText = decodedtext.string (); + m_QSOText = s1; } if (hiscall != "73" @@ -3960,7 +3951,7 @@ void MainWindow::processMessage(QString const& messages, int position, bool ctrl lookup(); m_hisGrid = ui->dxGridEntry->text(); - QString rpt = decodedtext.report(); + QString rpt = message.report(); int n=rpt.toInt(); if(m_mode=="MSK144" and m_bShMsgs) { int n=rpt.toInt(); @@ -6118,7 +6109,9 @@ void MainWindow::replyToCQ (QTime time, qint32 snr, float delta_time, quint32 de // find the linefeed at the end of the line position = ui->decodedTextBrowser->toPlainText().indexOf(QChar::LineFeed,position); m_bDoubleClicked = true; - processMessage (messages, position); + auto start = messages.left (position).lastIndexOf (QChar::LineFeed) + 1; + DecodedText message {messages.mid (start, position - start)}; + processMessage (message); tx_watchdog (false); QApplication::alert (this); } diff --git a/mainwindow.h b/mainwindow.h index b3a87431c..d726c6d9e 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -304,7 +304,7 @@ private: private: void astroUpdate (); void writeAllTxt(QString message); - void auto_sequence (QString const& message, unsigned start_tolerance, unsigned stop_tolerance); + void auto_sequence (DecodedText const& message, unsigned start_tolerance, unsigned stop_tolerance); void hideMenus(bool b); NetworkAccessManager m_network_manager; @@ -591,7 +591,7 @@ private: void pskPost(DecodedText const& decodedtext); void displayDialFrequency (); void transmitDisplay (bool); - void processMessage(QString const& messages, qint32 position, bool ctrl = false, bool alt = false); + void processMessage(DecodedText const&, bool ctrl = false, bool alt = false); void replyToCQ (QTime, qint32 snr, float delta_time, quint32 delta_frequency, QString const& mode, QString const& message_text); void replayDecodes (); void postDecode (bool is_new, QString const& message); diff --git a/mainwindow.ui b/mainwindow.ui index 6ab1010bc..04948d102 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -6,8 +6,8 @@ 0 0 - 876 - 583 + 815 + 548 @@ -1089,7 +1089,7 @@ QLabel[oob="true"] { - <html><head/><body><p>Send this message in next Tx interval</p><p>Double click to toggle the use of the Tx1 message to start a QSO with a station</p></body></html> + <html><head/><body><p>Send this message in next Tx interval</p><p>Double click to toggle the use of the Tx1 message to start a QSO with a station (not allowed for type 1 compound call holders)</p></body></html> @@ -1111,7 +1111,7 @@ QLabel[oob="true"] { - <html><head/><body><p>Switch to this Tx message NOW</p><p>Double click to toggle the use of the Tx1 message to start a QSO with a station</p></body></html> + <html><head/><body><p>Switch to this Tx message NOW</p><p>Double click to toggle the use of the Tx1 message to start a QSO with a station (not allowed for type 1 compund call holders)</p></body></html> Qt::LeftToRight @@ -1227,7 +1227,7 @@ QLabel[oob="true"] { - <html><head/><body><p>Send this message in next Tx interval</p><p>Double-click to toggle between RRR and RR73 messages in Tx4</p><p>RR73 messages should only be used when you are reasonably confident that no message repititions will be required</p></body></html> + <html><head/><body><p>Send this message in next Tx interval</p><p>Double-click to toggle between RRR and RR73 messages in Tx4 (not allowed for type 2 compound call holders)</p><p>RR73 messages should only be used when you are reasonably confident that no message repititions will be required</p></body></html> @@ -1249,7 +1249,7 @@ QLabel[oob="true"] { - <html><head/><body><p>Switch to this Tx message NOW</p><p>Double-click to toggle between RRR and RR73 messages in Tx4</p><p>RR73 messages should only be used when you are reasonably confident that no message repititions will be required</p></body></html> + <html><head/><body><p>Switch to this Tx message NOW</p><p>Double-click to toggle between RRR and RR73 messages in Tx4 (not allowed for type2 compound call holders)</p><p>RR73 messages should only be used when you are reasonably confident that no message repititions will be required</p></body></html> Tx &4 @@ -2335,7 +2335,7 @@ QPushButton[state="ok"] { 0 0 - 876 + 815 21