diff --git a/Radio.cpp b/Radio.cpp index d93df3983..961fc1d46 100644 --- a/Radio.cpp +++ b/Radio.cpp @@ -1,72 +1,107 @@ -#include "Radio.hpp" - -#include - -#include -#include -#include -#include -#include -#include - -namespace Radio -{ - namespace - { - struct init - { - init () - { - qRegisterMetaType ("Frequency"); - - qRegisterMetaType ("Frequencies"); - qRegisterMetaTypeStreamOperators ("Frequencies"); - - qRegisterMetaType ("FrequencyDelta"); - } - } static_initaializer; - - double constexpr MHz_factor {1.e6}; - int constexpr frequency_precsion {6}; - } - - - Frequency frequency (QVariant const& v, int scale) - { - return std::llround (v.toDouble () * std::pow (10., scale)); - } - - FrequencyDelta frequency_delta (QVariant const& v, int scale) - { - return std::llround (v.toDouble () * std::pow (10., scale)); - } - - - QString frequency_MHz_string (Frequency f, QLocale const& locale) - { - return locale.toString (f / MHz_factor, 'f', frequency_precsion); - } - - QString frequency_MHz_string (FrequencyDelta d, QLocale const& locale) - { - return locale.toString (d / MHz_factor, 'f', frequency_precsion); - } - - QString pretty_frequency_MHz_string (Frequency f, QLocale const& locale) - { - auto f_string = locale.toString (f / MHz_factor, 'f', frequency_precsion); - return f_string.insert (f_string.size () - 3, QChar::Nbsp); - } - - QString pretty_frequency_MHz_string (double f, int scale, QLocale const& locale) - { - auto f_string = locale.toString (f / std::pow (10., scale - 6), 'f', frequency_precsion); - return f_string.insert (f_string.size () - 3, QChar::Nbsp); - } - - QString pretty_frequency_MHz_string (FrequencyDelta d, QLocale const& locale) - { - auto d_string = locale.toString (d / MHz_factor, 'f', frequency_precsion); - return d_string.insert (d_string.size () - 3, QChar::Nbsp); - } -} +#include "Radio.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace Radio +{ + namespace + { + struct init + { + init () + { + qRegisterMetaType ("Frequency"); + + qRegisterMetaType ("Frequencies"); + qRegisterMetaTypeStreamOperators ("Frequencies"); + + qRegisterMetaType ("FrequencyDelta"); + } + } static_initaializer; + + double constexpr MHz_factor {1.e6}; + int constexpr frequency_precsion {6}; + + // very loose validation - callsign must contain a letter next to + // a number + QRegularExpression valid_callsign_regexp {R"(\d[[:alpha:]]|[[:alpha:]]\d)"}; + } + + + Frequency frequency (QVariant const& v, int scale) + { + return std::llround (v.toDouble () * std::pow (10., scale)); + } + + FrequencyDelta frequency_delta (QVariant const& v, int scale) + { + return std::llround (v.toDouble () * std::pow (10., scale)); + } + + + QString frequency_MHz_string (Frequency f, QLocale const& locale) + { + return locale.toString (f / MHz_factor, 'f', frequency_precsion); + } + + QString frequency_MHz_string (FrequencyDelta d, QLocale const& locale) + { + return locale.toString (d / MHz_factor, 'f', frequency_precsion); + } + + QString pretty_frequency_MHz_string (Frequency f, QLocale const& locale) + { + auto f_string = locale.toString (f / MHz_factor, 'f', frequency_precsion); + return f_string.insert (f_string.size () - 3, QChar::Nbsp); + } + + QString pretty_frequency_MHz_string (double f, int scale, QLocale const& locale) + { + auto f_string = locale.toString (f / std::pow (10., scale - 6), 'f', frequency_precsion); + return f_string.insert (f_string.size () - 3, QChar::Nbsp); + } + + QString pretty_frequency_MHz_string (FrequencyDelta d, QLocale const& locale) + { + auto d_string = locale.toString (d / MHz_factor, 'f', frequency_precsion); + return d_string.insert (d_string.size () - 3, QChar::Nbsp); + } + + bool is_callsign (QString const& callsign) + { + return callsign.contains (valid_callsign_regexp); + } + + bool is_compound_callsign (QString const& callsign) + { + return callsign.contains ('/'); + } + + // split on first '/' and return the larger portion or the whole if + // there is no '/' + QString base_callsign (QString callsign) + { + auto slash_pos = callsign.indexOf ('/'); + if (slash_pos >= 0) + { + auto right_size = callsign.size () - slash_pos - 1; + if (right_size>= slash_pos) + { + callsign = callsign.mid (slash_pos + 1); + } + else + { + callsign = callsign.left (slash_pos); + } + } + return callsign; + } +} diff --git a/Radio.hpp b/Radio.hpp index c9e279244..3d3211bbd 100644 --- a/Radio.hpp +++ b/Radio.hpp @@ -1,47 +1,54 @@ -#ifndef RADIO_HPP_ -#define RADIO_HPP_ - -#include -#include -#include - -class QVariant; -class QString; - -// -// Declarations common to radio software. -// - -namespace Radio -{ - // - // Frequency types - // - using Frequency = quint64; - using Frequencies = QList; - using FrequencyDelta = qint64; - - // - // Frequency type conversion. - // - // QVariant argument is convertible to double and is assumed to - // be scaled by (10 ** -scale). - // - Frequency frequency (QVariant const&, int scale); - FrequencyDelta frequency_delta (QVariant const&, int scale); - - // - // Frequency type formatting - // - QString frequency_MHz_string (Frequency, QLocale const& = QLocale ()); - QString frequency_MHz_string (FrequencyDelta, QLocale const& = QLocale ()); - QString pretty_frequency_MHz_string (Frequency, QLocale const& = QLocale ()); - QString pretty_frequency_MHz_string (double, int scale, QLocale const& = QLocale ()); - QString pretty_frequency_MHz_string (FrequencyDelta, QLocale const& = QLocale ()); -} - -Q_DECLARE_METATYPE (Radio::Frequency); -Q_DECLARE_METATYPE (Radio::Frequencies); -Q_DECLARE_METATYPE (Radio::FrequencyDelta); - -#endif +#ifndef RADIO_HPP_ +#define RADIO_HPP_ + +#include +#include +#include + +class QVariant; +class QString; + +// +// Declarations common to radio software. +// + +namespace Radio +{ + // + // Frequency types + // + using Frequency = quint64; + using Frequencies = QList; + using FrequencyDelta = qint64; + + // + // Frequency type conversion. + // + // QVariant argument is convertible to double and is assumed to + // be scaled by (10 ** -scale). + // + Frequency frequency (QVariant const&, int scale); + FrequencyDelta frequency_delta (QVariant const&, int scale); + + // + // Frequency type formatting + // + QString frequency_MHz_string (Frequency, QLocale const& = QLocale ()); + QString frequency_MHz_string (FrequencyDelta, QLocale const& = QLocale ()); + QString pretty_frequency_MHz_string (Frequency, QLocale const& = QLocale ()); + QString pretty_frequency_MHz_string (double, int scale, QLocale const& = QLocale ()); + QString pretty_frequency_MHz_string (FrequencyDelta, QLocale const& = QLocale ()); + + // + // Callsigns + // + bool is_callsign (QString const&); + bool is_compound_callsign (QString const&); + QString base_callsign (QString); +} + +Q_DECLARE_METATYPE (Radio::Frequency); +Q_DECLARE_METATYPE (Radio::Frequencies); +Q_DECLARE_METATYPE (Radio::FrequencyDelta); + +#endif diff --git a/decodedtext.cpp b/decodedtext.cpp index 974ae4150..00d662201 100644 --- a/decodedtext.cpp +++ b/decodedtext.cpp @@ -2,15 +2,29 @@ #include #include "decodedtext.h" - - QString DecodedText::CQersCall() { - // extract the CQer's call TODO: does this work with all call formats? What about 'CQ DX'? - int s1 = 4 + _string.indexOf(" CQ "); - int s2 = _string.indexOf(" ",s1); - QString call = _string.mid(s1,s2-s1); - return call; + // extract the CQer's call TODO: does this work with all call formats? + int s1; + int position; + if ((position = _string.indexOf (" CQ DX ")) >= 0) + { + s1 = 7 + position; + } + else if ((position = _string.indexOf (" CQ ")) >= 0) + { + s1 = 4 + position; + } + else if ((position = _string.indexOf (" DE ")) >= 0) + { + s1 = 4 + position; + } + else if ((position = _string.indexOf (" QRZ ")) >= 0) + { + s1 = 5 + position; + } + auto s2 = _string.indexOf (" ", s1); + return _string.mid (s1, s2 - s1); } @@ -95,21 +109,22 @@ bool DecodedText::report(QString const& myBaseCall, QString const& dxBaseCall, / // get the first text word, usually the call QString DecodedText::call() { - QString call = _string.mid(column_qsoText); - int i = call.indexOf(" "); - call = call.mid(0,i); - return call; + auto call = _string; + call = call.replace (" CQ DX ", " CQ_DX ").mid (column_qsoText); + int i = call.indexOf(" "); + return call.mid(0,i); } // get the second word, most likely the de call and the third word, most likely grid void DecodedText::deCallAndGrid(/*out*/QString& call, QString& grid) { - QString msg=_string.mid(column_qsoText); - int i1 = msg.indexOf(" "); - call = msg.mid(i1+1); - int i2 = call.indexOf(" "); - grid = call.mid(i2+1,4); - call = call.mid(0,i2); + auto msg = _string; + msg = msg.replace (" CQ DX ", " CQ_DX ").mid (column_qsoText); + int i1 = msg.indexOf(" "); + call = msg.mid(i1+1); + int i2 = call.indexOf(" "); + grid = call.mid(i2+1,4); + call = call.mid(0,i2); } int DecodedText::timeInSeconds() diff --git a/displaytext.cpp b/displaytext.cpp index 4922edd8f..d9712f369 100644 --- a/displaytext.cpp +++ b/displaytext.cpp @@ -44,10 +44,7 @@ void DisplayText::_appendDXCCWorkedB4(DecodedText& t1, QString& bg, QColor color_DXCC, QColor color_NewCall) { - // extract the CQer's call TODO: does this work with all call formats? What about 'CQ DX'? - int s1 = 4 + t1.indexOf(" CQ "); - int s2 = t1.indexOf(" ",s1); - QString call = t1.mid(s1,s2-s1); + QString call = t1.CQersCall (); QString countryName; bool callWorkedBefore; @@ -114,7 +111,7 @@ void DisplayText::displayDecodedText(DecodedText decodedText, QString myCall, { QString bg="white"; bool CQcall = false; - if (decodedText.indexOf(" CQ ") > 0) + if (decodedText.string ().contains (" CQ ") > 0 || decodedText.string ().contains (" CQ DX ")) { CQcall = true; bg=color_CQ.name(); diff --git a/mainwindow.cpp b/mainwindow.cpp index 9787f3ded..b5d769e1d 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -26,6 +26,7 @@ #include "sleep.h" #include "getfile.h" #include "logqso.h" +#include "Radio.hpp" #include "Bands.hpp" #include "TransceiverFactory.hpp" #include "FrequencyList.hpp" @@ -334,6 +335,8 @@ MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdme m_repeatMsg=0; m_secBandChanged=0; m_lockTxFreq=false; + m_baseCall = Radio::base_callsign (m_config.my_callsign ()); + ui->readFreq->setEnabled(false); m_QSOText.clear(); decodeBusy(false); @@ -665,6 +668,7 @@ void MainWindow::on_actionSettings_triggered() //Setup Dialog { if (m_config.my_callsign () != callsign) { + m_baseCall = Radio::base_callsign (m_config.my_callsign ()); morse_(const_cast (m_config.my_callsign ().toLatin1().constData()) , const_cast (icw) , &m_ncw @@ -765,12 +769,6 @@ void MainWindow::on_actionAbout_triggered() //Display "About" void MainWindow::on_autoButton_clicked (bool checked) { m_auto = checked; - if (!m_auto) - { - m_btxok = false; - monitor (true); - m_repeatMsg = 0; - } } void MainWindow::auto_tx_mode (bool state) @@ -1383,11 +1381,9 @@ void MainWindow::readFromStdout() //readFromStdout DecodedText decodedtext; decodedtext = t.replace("\n",""); //t.replace("\n","").mid(0,t.length()-4); - auto my_base_call = baseCall (m_config.my_callsign ()); - // the left band display ui->decodedTextBrowser->displayDecodedText (decodedtext - , my_base_call + , m_baseCall , m_config.DXCC () , m_logBook , m_config.color_CQ() @@ -1399,7 +1395,7 @@ void MainWindow::readFromStdout() //readFromStdout { // the right QSO window ui->decodedTextBrowser2->displayDecodedText(decodedtext - , my_base_call + , m_baseCall , false , m_logBook , m_config.color_CQ() @@ -1414,8 +1410,8 @@ void MainWindow::readFromStdout() //readFromStdout } // find and extract any report for myCall - bool stdMsg = decodedtext.report(my_base_call - , baseCall (ui->dxCallEntry-> text ().toUpper ().trimmed ()) + bool stdMsg = decodedtext.report(m_baseCall + , Radio::base_callsign (ui->dxCallEntry-> text ().toUpper ().trimmed ()) , /*mod*/m_rptRcvd); // extract details and send to PSKreporter @@ -1524,7 +1520,7 @@ void MainWindow::guiUpdate() double t2p=fmod(tsec,2*m_TRperiod); bool bTxTime = ((t2p >= tx1) and (t2p < tx2)) or m_tune; - if(m_auto or m_tune) { + if(m_transmitting || m_auto || m_tune) { QFile f(m_config.temp_dir ().absoluteFilePath ("txboth")); if(f.exists() and fmod(tsec,m_TRperiod) < (1.0 + 85.0*m_nsps/12000.0)) { @@ -1563,6 +1559,7 @@ void MainWindow::guiUpdate() Q_EMIT m_config.transceiver_ptt (true); ptt1Timer->start(200); //Sequencer delay } + if(!bTxTime) { m_btxok=false; } @@ -1623,15 +1620,27 @@ void MainWindow::guiUpdate() } } - QStringList w=t.split(" ",QString::SkipEmptyParts); - t=""; - if(w.length()==3) t=w[2]; - icw[0]=0; - m_sent73=(t=="73" or itype==6); - if(m_sent73) { - if(m_config.id_after_73 ()) icw[0]=m_ncw; - if(m_config.prompt_to_log () && !m_tune) logQSOTimer->start(200); - } + auto t2 = QDateTime::currentDateTimeUtc ().toString ("hhmm"); + icw[0] = 0; + auto msg_parts = t.split (" ", QString::SkipEmptyParts); + m_sent73 = (itype < 6 && msg_parts.contains ("73")) + || (itype == 6 && t.contains ("73")); + if (m_sent73) + { + m_qsoStop=t2; + if(m_config.id_after_73 ()) + { + icw[0] = m_ncw; + } + if (m_config.prompt_to_log () && !m_tune) + { + logQSOTimer->start (0); + } + if (m_config.disable_TX_on_73 ()) + { + auto_tx_mode (false); + } + } if(m_config.id_interval () >0) { int nmin=(m_sec0-m_secID)/60; @@ -1641,27 +1650,51 @@ void MainWindow::guiUpdate() } } - QString t2=QDateTime::currentDateTimeUtc().toString("hhmm"); - if(itype<6 and w.length()>=3 and w[1]==m_config.my_callsign ()) { - int i1; - bool ok; - i1=t.toInt(&ok); - if(ok and i1>=-50 and i1<50) { - m_rptSent=t; - m_qsoStart=t2; - } else { - if(t.mid(0,1)=="R") { - i1=t.mid(1).toInt(&ok); - if(ok and i1>=-50 and i1<50) { - m_rptSent=t.mid(1); - m_qsoStart=t2; + if (itype < 6 && msg_parts.length() >= 3 + && (msg_parts[1] == m_config.my_callsign () || msg_parts[1] == m_baseCall)) + { + int i1; + bool ok; + i1 = msg_parts[2].toInt(&ok); + if(ok and i1>=-50 and i1<50) + { + m_rptSent = msg_parts[2]; + m_qsoStart = t2; } - } + else + { + if (msg_parts[2].mid (0, 1) == "R") + { + i1 = msg_parts[2].mid (1).toInt (&ok); + if (ok and i1 >= -50 and i1 < 50) + { + m_rptSent = msg_parts[2].mid (1); + m_qsoStart = t2; + } + } + } + qDebug () << "Report sent:" << m_rptSent; } - } - if(itype==6 or (w.length()==3 and w[2]=="73")) m_qsoStop=t2; m_restart=false; } + else + { + if (!m_auto && m_sent73) + { + m_sent73 = false; + if (1 == ui->tabWidget->currentIndex()) + { + ui->genMsg->setText(ui->tx6->text()); + m_ntx=7; + ui->rbGenMsg->setChecked(true); + } + else + { + m_ntx=6; + ui->txrb6->setChecked(true); + } + } + } if (g_iptt == 1 && iptt0 == 0) { @@ -1798,11 +1831,6 @@ void MainWindow::stopTx2() //Lower PTT Q_EMIT m_config.transceiver_ptt (false); - if (m_config.disable_TX_on_73 () && m_sent73) - { - on_stopTxButton_clicked(); - } - if (m_config.watchdog () && m_repeatMsg>=m_watchdogLimit-1) { on_stopTxButton_clicked(); @@ -1904,29 +1932,25 @@ void MainWindow::doubleClickOnCall(bool shift, bool ctrl) if (decodedtext.indexOf(" CQ ") > 0) { // TODO this magic 36 characters is also referenced in DisplayText::_appendDXCCWorkedB4() - int s3 = decodedtext.indexOf(" ",35); - if (s3 < 35) - s3 = 35; // we always want at least the characters to position 35 - s3 += 1; // convert the index into a character count - decodedtext = decodedtext.left(s3); // remove DXCC entity and worked B4 status. TODO need a better way to do this + auto eom_pos = decodedtext.string ().indexOf (' ', 35); + if (eom_pos < 35) eom_pos = decodedtext.string ().size () - 1; // we always want at least the characters + // to position 35 + decodedtext = decodedtext.string ().left (eom_pos + 1); // remove DXCC entity and worked B4 status. TODO need a better way to do this } // if(decodedtext.indexOf("Tx")==6) return; //Ignore Tx line - int i4=t.mid(i1).length(); - if(i4>55) i4=55; - QString t3=t.mid(i1,i4); - int i5=t3.indexOf(" CQ DX "); - if(i5>0) t3=t3.mid(0,i5+3) + "_" + t3.mid(i5+4); //Make it "CQ_DX" (one word) - QStringList t4=t3.split(" ",QString::SkipEmptyParts); - if(t4.length() <5) return; //Skip the rest if no decoded text + // int i4=t.mid(i1).length(); + // if(i4>55) i4=55; + // QString t3=t.mid(i1,i4); + auto t3 = decodedtext.string (); + auto t4 = t3.replace (" CQ DX ", " CQ_DX ").split (" ", QString::SkipEmptyParts); + if(t4.size () <5) return; //Skip the rest if no decoded text QString hiscall; QString hisgrid; decodedtext.deCallAndGrid(/*out*/hiscall,hisgrid); - // basic valid call sign check i.e. contains at least one digit and - // one letter next to each other - if (!hiscall.contains (QRegularExpression {R"(\d[[:upper:]]|[[:upper:]]\d)"})) + if (!Radio::is_callsign (hiscall)) { return; } @@ -1957,7 +1981,7 @@ void MainWindow::doubleClickOnCall(bool shift, bool ctrl) QString firstcall = decodedtext.call(); // Don't change Tx freq if a station is calling me, unless m_lockTxFreq // is true or CTRL is held down - if ((firstcall!=m_config.my_callsign ()) or m_lockTxFreq or ctrl) + if ((firstcall!=m_config.my_callsign () && firstcall != m_baseCall) || m_lockTxFreq or ctrl) { if (ui->TxFreqSpinBox->isEnabled ()) { @@ -1969,13 +1993,11 @@ void MainWindow::doubleClickOnCall(bool shift, bool ctrl) } } - auto my_base_call = baseCall (m_config.my_callsign ()); - int i9=m_QSOText.indexOf(decodedtext.string()); if (i9<0 and !decodedtext.isTX()) { ui->decodedTextBrowser2->displayDecodedText(decodedtext - , my_base_call + , m_baseCall , false , m_logBook , m_config.color_CQ() @@ -1998,8 +2020,8 @@ void MainWindow::doubleClickOnCall(bool shift, bool ctrl) return; } - auto base_call = baseCall (hiscall); - if (base_call != baseCall (ui->dxCallEntry-> text ().toUpper ().trimmed ()) || base_call != hiscall) + auto base_call = Radio::base_callsign (hiscall); + if (base_call != Radio::base_callsign (ui->dxCallEntry-> text ().toUpper ().trimmed ()) || base_call != hiscall) { // his base call different or his call more qualified // i.e. compound version of same base call @@ -2022,14 +2044,15 @@ void MainWindow::doubleClickOnCall(bool shift, bool ctrl) // determine the appropriate response to the received msg auto dtext = " " + decodedtext.string () + " "; - if(dtext.contains (" " + my_base_call + " ") - || dtext.contains ("/" + my_base_call + " ") - || dtext.contains (" " + my_base_call + "/")) + if(dtext.contains (" " + m_baseCall + " ") + || dtext.contains ("/" + m_baseCall + " ") + || dtext.contains (" " + m_baseCall + "/") + || firstcall == "DE") { - if (t4.length()>=7 // enough fields for a normal msg - and !gridOK(t4.at(7))) // but no grid on end of msg + if (t4.size () > 7 // enough fields for a normal msg + and !gridOK (t4.at (7))) // but no grid on end of msg { - QString r=t4.at(7); + QString r=t4.at (7); if(r.mid(0,3)=="RRR") { m_ntx=5; ui->txrb5->setChecked(true); @@ -2116,10 +2139,9 @@ void MainWindow::genStdMsgs(QString rpt) //genStdMsgs() ui->genMsg->setText(""); return; } - QString hisBase=baseCall(hisCall); - QString myBase=baseCall(m_config.my_callsign ()); + QString hisBase = Radio::base_callsign (hisCall); - QString t0=hisBase + " " + myBase + " "; + QString t0=hisBase + " " + m_baseCall + " "; t=t0 + m_config.my_grid ().mid(0,4); msgtype(t, ui->tx1); if(rpt == "") { @@ -2139,9 +2161,9 @@ void MainWindow::genStdMsgs(QString rpt) //genStdMsgs() msgtype(t, ui->tx5->lineEdit ()); } - if(m_config.my_callsign ()!=myBase) { + if(m_config.my_callsign () != m_baseCall) { if(shortList(m_config.my_callsign ())) { - t=hisCall + " " + m_config.my_callsign (); + t=hisBase + " " + m_config.my_callsign (); msgtype(t, ui->tx1); t="CQ " + m_config.my_callsign (); msgtype(t, ui->tx6); @@ -2187,15 +2209,6 @@ void MainWindow::genStdMsgs(QString rpt) //genStdMsgs() m_rpt=rpt; } -QString MainWindow::baseCall(QString t) -{ - int n1=t.indexOf("/"); - if(n1<0) return t; - int n2=t.length()-n1-1; - if(n2>=n1) return t.mid(n1+1); - return t.mid(0,n1); -} - void MainWindow::lookup() //lookup() { QString hisCall=ui->dxCallEntry->text().toUpper().trimmed(); @@ -2780,8 +2793,8 @@ void MainWindow::on_rbGenMsg_toggled(bool checked) { m_freeText=!checked; if(!m_freeText) { + if(m_ntx != 7 && m_transmitting) m_restart=true; m_ntx=7; - if(m_transmitting) m_restart=true; } } diff --git a/mainwindow.h b/mainwindow.h index b53cce38f..73fba27db 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -386,7 +386,6 @@ private: void qsy(Frequency f); bool gridOK(QString g); bool shortList(QString callsign); - QString baseCall(QString t); void transmit (double snr = 99.); void rigFailure (QString const& reason, QString const& detail); void pskSetLocal ();