Merge branch 'support-2.3.1' into release-2.4.0
| @ -16,11 +16,15 @@ namespace | ||||
| 
 | ||||
| DecodedText::DecodedText (QString const& the_string) | ||||
|   : string_ {the_string.left (the_string.indexOf (QChar::Nbsp))} // discard appended info
 | ||||
|   , clean_string_ {string_} | ||||
|   , padding_ {string_.indexOf (" ") > 4 ? 2 : 0} // allow for
 | ||||
|                                                     // seconds
 | ||||
|   , message_ {string_.mid (column_qsoText + padding_).trimmed ()} | ||||
|   , is_standard_ {false} | ||||
| { | ||||
|   // discard appended AP info
 | ||||
|   clean_string_.replace (QRegularExpression {R"(^(.*)(?:(?:\?\s)?a[0-9].*)$)"}, "\\1"); | ||||
| 
 | ||||
| //  qDebug () << "DecodedText: the_string:" << the_string << "Nbsp pos:" << the_string.indexOf (QChar::Nbsp);
 | ||||
|   if (message_.length() >= 1) | ||||
|     { | ||||
|  | ||||
| @ -32,6 +32,7 @@ public: | ||||
|   explicit DecodedText (QString const& message); | ||||
| 
 | ||||
|   QString string() const { return string_; }; | ||||
|   QString clean_string() const { return clean_string_; }; | ||||
|   QStringList messageWords () const; | ||||
|   int indexOf(QString s) const { return string_.indexOf(s); }; | ||||
|   int indexOf(QString s, int i) const { return string_.indexOf(s,i); }; | ||||
| @ -76,6 +77,7 @@ private: | ||||
|       column_qsoText = 22 }; | ||||
| 
 | ||||
|   QString string_; | ||||
|   QString clean_string_; | ||||
|   int padding_; | ||||
|   QString message_; | ||||
|   QString message0_; | ||||
|  | ||||
| @ -576,20 +576,28 @@ void MultiSettings::impl::select_configuration (QString const& target_name) | ||||
| { | ||||
|   if (main_window_ && target_name != current_) | ||||
|     { | ||||
|       bool changed {false}; | ||||
|       { | ||||
|         auto const& current_group = settings_.group (); | ||||
|         if (current_group.size ()) settings_.endGroup (); | ||||
|         // position to the alternative settings
 | ||||
|         SettingsGroup alternatives {&settings_, multi_settings_root_group}; | ||||
|         // save the target settings
 | ||||
|         SettingsGroup target_group {&settings_, target_name}; | ||||
|         new_settings_ = get_settings (); | ||||
|         if (settings_.childGroups ().contains (target_name)) | ||||
|           { | ||||
|             changed = true; | ||||
|             // save the target settings
 | ||||
|             SettingsGroup target_group {&settings_, target_name}; | ||||
|             new_settings_ = get_settings (); | ||||
|           } | ||||
|         if (current_group.size ()) settings_.beginGroup (current_group); | ||||
|       } | ||||
|       // and set up the restart
 | ||||
|       current_ = target_name; | ||||
|       Q_EMIT parent_->configurationNameChanged (unescape_ampersands (current_)); | ||||
|       restart (RepositionType::save_and_replace); | ||||
|       if (changed) | ||||
|         { | ||||
|           // and set up the restart
 | ||||
|           current_ = target_name; | ||||
|           Q_EMIT parent_->configurationNameChanged (unescape_ampersands (current_)); | ||||
|           restart (RepositionType::save_and_replace); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -117,7 +117,7 @@ SampleDownloader::impl::impl (QSettings * settings | ||||
|   main_layout_.addLayout (&left_layout_, 0, 0); | ||||
|   main_layout_.addWidget (&button_box_, 0, 1); | ||||
|   main_layout_.addWidget (&details_widget_, 1, 0, 1, 2); | ||||
|   main_layout_.setRowStretch (1, 2); | ||||
|   main_layout_.setRowStretch (0, 2); | ||||
|   setLayout (&main_layout_); | ||||
| 
 | ||||
|   connect (&button_box_, &QDialogButtonBox::clicked, this, &SampleDownloader::impl::button_clicked); | ||||
|  | ||||
| @ -49,6 +49,8 @@ Directory::Directory (Configuration const * configuration | ||||
| 
 | ||||
|   setColumnCount (2); | ||||
|   setHeaderLabels ({tr ("File"), tr ("Progress")}); | ||||
|   headerItem ()->setTextAlignment (0, Qt::AlignHCenter); | ||||
|   headerItem ()->setTextAlignment (1, Qt::AlignHCenter); | ||||
|   header ()->setSectionResizeMode (QHeaderView::ResizeToContents); | ||||
|   setItemDelegate (&item_delegate_); | ||||
| 
 | ||||
| @ -294,14 +296,15 @@ void Directory::update (QTreeWidgetItem * item) | ||||
|       int counted {0}; | ||||
| 
 | ||||
|       // get the count, progress and size of children
 | ||||
|       int items {recurse_children (item, &counted, &bytes, &max)}; | ||||
|       // int items {recurse_children (item, &counted, &bytes, &max)};
 | ||||
|       recurse_children (item, &counted, &bytes, &max); | ||||
| 
 | ||||
|       // estimate size of items not yet downloaded as average of
 | ||||
|       // those actually present
 | ||||
|       if (counted) | ||||
|         { | ||||
|           max += (items - counted) * max / counted; | ||||
|         } | ||||
|       // if (counted)
 | ||||
|       //   {
 | ||||
|       //     max += (items - counted) * max / counted;
 | ||||
|       //   }
 | ||||
| 
 | ||||
|       // save as our progress
 | ||||
|       item->setData (1, Qt::UserRole, max); | ||||
|  | ||||
| @ -14,28 +14,42 @@ void DirectoryDelegate::paint (QPainter * painter, QStyleOptionViewItem const& o | ||||
| { | ||||
|   if (1 == index.column ()) | ||||
|     { | ||||
|       QStyleOptionProgressBar progress_bar_option; | ||||
|       progress_bar_option.rect = option.rect; | ||||
|       progress_bar_option.state = QStyle::State_Enabled; | ||||
|       progress_bar_option.direction = QApplication::layoutDirection (); | ||||
|       progress_bar_option.fontMetrics = QApplication::fontMetrics (); | ||||
|       progress_bar_option.minimum = 0; | ||||
|       progress_bar_option.maximum = 100; | ||||
|       auto progress = index.data ().toLongLong (); | ||||
|       qint64 percent; | ||||
|       if (progress > 0) | ||||
|         { | ||||
|           auto percent = int (progress * 100 / index.data (Qt::UserRole).toLongLong ()); | ||||
|           progress_bar_option.progress = percent; | ||||
|           progress_bar_option.text = QString::number (percent) + '%'; | ||||
|           progress_bar_option.textVisible = true; | ||||
|           progress_bar_option.textAlignment = Qt::AlignCenter; | ||||
|           percent = int (progress * 100 / index.data (Qt::UserRole).toLongLong ()); | ||||
|         } | ||||
| #if !defined (Q_OS_DARWIN) | ||||
|       QStyleOptionProgressBar progress_option; | ||||
|       auto control_element = QStyle::CE_ProgressBar; | ||||
|       progress_option.minimum = 0; | ||||
|       progress_option.maximum = 100; | ||||
|       progress_option.textAlignment = Qt::AlignCenter; | ||||
|       if (progress > 0) | ||||
|         { | ||||
|           progress_option.progress = percent; | ||||
|           progress_option.textVisible = true; | ||||
|         } | ||||
|       else | ||||
|         { | ||||
|           // not started
 | ||||
|           progress_bar_option.progress = -1; | ||||
|           progress_option.progress = -1; | ||||
|         } | ||||
|       QApplication::style ()->drawControl (QStyle::CE_ProgressBar, &progress_bar_option, painter); | ||||
| #else | ||||
|       // workaround for broken QProgressBar item delegates on macOS
 | ||||
|       QStyleOptionViewItem progress_option; | ||||
|       auto control_element = QStyle::CE_ItemViewItem; | ||||
|       progress_option.displayAlignment = Qt::AlignHCenter; | ||||
|       progress_option.index = index; | ||||
|       progress_option.features = QStyleOptionViewItem::HasDisplay; | ||||
| #endif | ||||
|       progress_option.rect = option.rect; | ||||
|       progress_option.state = QStyle::State_Enabled; | ||||
|       progress_option.direction = QApplication::layoutDirection (); | ||||
|       progress_option.fontMetrics = QApplication::fontMetrics (); | ||||
|       progress_option.text = QString::number (progress > 0 ? percent : 0) + '%'; | ||||
|       QApplication::style ()->drawControl (control_element, &progress_option, painter); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|  | ||||
| @ -27,7 +27,12 @@ FileNode::FileNode (QTreeWidgetItem * parent | ||||
| 
 | ||||
| void FileNode::error (QString const& title, QString const& message) | ||||
| { | ||||
|   MessageBox::warning_message (treeWidget (), title, message); | ||||
|   if (MessageBox::Retry == MessageBox::warning_message (treeWidget (), title, message | ||||
|                                                         , QString {}, MessageBox::Cancel | MessageBox::Retry | ||||
|                                                         , MessageBox::Cancel)) | ||||
|     { | ||||
|       sync (true); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool FileNode::sync (bool local) | ||||
|  | ||||
| @ -436,7 +436,11 @@ HamlibTransceiver::HamlibTransceiver (logger_type * logger, | ||||
|         { | ||||
|           m_->set_conf ("ptt_type", "RTS"); | ||||
|         } | ||||
|       set_conf ("ptt_share", "1"); | ||||
|     } | ||||
| 
 | ||||
|   // do this late to allow any configuration option to be overriden
 | ||||
|   load_user_settings (); | ||||
| } | ||||
| 
 | ||||
| HamlibTransceiver::HamlibTransceiver (logger_type * logger, | ||||
| @ -453,52 +457,7 @@ HamlibTransceiver::HamlibTransceiver (logger_type * logger, | ||||
| 
 | ||||
|   // m_->rig_->state.obj = this;
 | ||||
| 
 | ||||
|   //
 | ||||
|   // user defined Hamlib settings
 | ||||
|   //
 | ||||
|   auto settings_file_name = QStandardPaths::locate (QStandardPaths::AppConfigLocation | ||||
|                                                     , "hamlib_settings.json"); | ||||
|   if (!settings_file_name.isEmpty ()) | ||||
|     { | ||||
|       QFile settings_file {settings_file_name}; | ||||
|       qDebug () << "Using Hamlib settings file:" << settings_file_name; | ||||
|       if (settings_file.open (QFile::ReadOnly)) | ||||
|         { | ||||
|           QJsonParseError status; | ||||
|           auto settings_doc = QJsonDocument::fromJson (settings_file.readAll (), &status); | ||||
|           if (status.error) | ||||
|             { | ||||
|               throw error {tr ("Hamlib settings file error: %1 at character offset %2") | ||||
|                              .arg (status.errorString ()).arg (status.offset)}; | ||||
|             } | ||||
|           qDebug () << "Hamlib settings JSON:" << settings_doc.toJson (); | ||||
|           if (!settings_doc.isObject ()) | ||||
|             { | ||||
|               throw error {tr ("Hamlib settings file error: top level must be a JSON object")}; | ||||
|             } | ||||
|           auto const& settings = settings_doc.object (); | ||||
| 
 | ||||
|           //
 | ||||
|           // configuration settings
 | ||||
|           //
 | ||||
|           auto const& config = settings["config"]; | ||||
|           if (!config.isUndefined ()) | ||||
|             { | ||||
|               if (!config.isObject ()) | ||||
|                 { | ||||
|                   throw error {tr ("Hamlib settings file error: config must be a JSON object")}; | ||||
|                 } | ||||
|               auto const& config_list = config.toObject (); | ||||
|               for (auto item = config_list.constBegin (); item != config_list.constEnd (); ++item) | ||||
|                 { | ||||
|                   m_->set_conf (item.key ().toLocal8Bit ().constData () | ||||
|                                 , (*item).toVariant ().toString ().toLocal8Bit ().constData ()); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|   if (!m_->is_dummy_) | ||||
|   if (!is_dummy_) | ||||
|     { | ||||
|       switch (rig_get_caps_int (m_->model_, RIG_CAPS_PORT_TYPE)) | ||||
|         { | ||||
| @ -592,17 +551,69 @@ HamlibTransceiver::HamlibTransceiver (logger_type * logger, | ||||
|         { | ||||
|           m_->set_conf ("ptt_type", "RTS"); | ||||
|         } | ||||
|       set_conf ("ptt_share", "1"); | ||||
|     } | ||||
| 
 | ||||
|   // Make Icom CAT split commands less glitchy
 | ||||
|   m_->set_conf ("no_xchg", "1"); | ||||
| 
 | ||||
|   // do this late to allow any configuration option to be overriden
 | ||||
|   load_user_settings (); | ||||
| 
 | ||||
|   // would be nice to get events but not supported on Windows and also not on a lot of rigs
 | ||||
|   // rig_set_freq_callback (m_->rig_.data (), &frequency_change_callback, this);
 | ||||
| } | ||||
| 
 | ||||
| HamlibTransceiver::~HamlibTransceiver () = default; | ||||
| 
 | ||||
| void HamlibTransceiver::load_user_settings () | ||||
| { | ||||
|   //
 | ||||
|   // user defined Hamlib settings
 | ||||
|   //
 | ||||
|   auto settings_file_name = QStandardPaths::locate (QStandardPaths::AppConfigLocation | ||||
|                                                     , "hamlib_settings.json"); | ||||
|   if (!settings_file_name.isEmpty ()) | ||||
|     { | ||||
|       QFile settings_file {settings_file_name}; | ||||
|       qDebug () << "Using Hamlib settings file:" << settings_file_name; | ||||
|       if (settings_file.open (QFile::ReadOnly)) | ||||
|         { | ||||
|           QJsonParseError status; | ||||
|           auto settings_doc = QJsonDocument::fromJson (settings_file.readAll (), &status); | ||||
|           if (status.error) | ||||
|             { | ||||
|               throw error {tr ("Hamlib settings file error: %1 at character offset %2") | ||||
|                              .arg (status.errorString ()).arg (status.offset)}; | ||||
|             } | ||||
|           qDebug () << "Hamlib settings JSON:" << settings_doc.toJson (); | ||||
|           if (!settings_doc.isObject ()) | ||||
|             { | ||||
|               throw error {tr ("Hamlib settings file error: top level must be a JSON object")}; | ||||
|             } | ||||
|           auto const& settings = settings_doc.object (); | ||||
| 
 | ||||
|           //
 | ||||
|           // configuration settings
 | ||||
|           //
 | ||||
|           auto const& config = settings["config"]; | ||||
|           if (!config.isUndefined ()) | ||||
|             { | ||||
|               if (!config.isObject ()) | ||||
|                 { | ||||
|                   throw error {tr ("Hamlib settings file error: config must be a JSON object")}; | ||||
|                 } | ||||
|               auto const& config_list = config.toObject (); | ||||
|               for (auto item = config_list.constBegin (); item != config_list.constEnd (); ++item) | ||||
|                 { | ||||
|                   set_conf (item.key ().toLocal8Bit ().constData () | ||||
|                             , (*item).toVariant ().toString ().toLocal8Bit ().constData ()); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| int HamlibTransceiver::do_start () | ||||
| { | ||||
|   CAT_TRACE ("starting: " << rig_get_caps_cptr (m_->model_, RIG_CAPS_MFG_NAME_CPTR) | ||||
|  | ||||
| @ -24,6 +24,7 @@ public: | ||||
|   ~HamlibTransceiver (); | ||||
| 
 | ||||
| private: | ||||
|   void load_user_settings (); | ||||
|   int do_start () override; | ||||
|   void do_stop () override; | ||||
|   void do_frequency (Frequency, MODE, bool no_ignore) override; | ||||
|  | ||||
| @ -192,7 +192,7 @@ WSJTXLogging::WSJTXLogging () | ||||
|         ( | ||||
|          sinks::file::make_collector | ||||
|          ( | ||||
|           keywords::max_size = 40 * 1024 * 1024 | ||||
|           keywords::max_size = 5 * 1024 * 1024 | ||||
|           , keywords::min_free_space = 1024 * 1024 * 1024 | ||||
|           , keywords::max_files = 12 | ||||
|           , keywords::target = app_data.absoluteFilePath ("logs").toStdWString () | ||||
|  | ||||
| @ -166,9 +166,9 @@ function (document) | ||||
|       COMMAND ${ASCIIDOCTOR_EXECUTABLE} ${_args_ASCIIDOCTOR_OPTIONS} | ||||
|       -b html5 | ||||
|       -a nofooter | ||||
|       -a VERSION_MAJOR=${WSJTX_VERSION_MAJOR} | ||||
|       -a VERSION_MINOR=${WSJTX_VERSION_MINOR} | ||||
|       -a VERSION_PATCH=${WSJTX_VERSION_PATCH} | ||||
|       -a VERSION_MAJOR=${PROJECT_VERSION_MAJOR} | ||||
|       -a VERSION_MINOR=${PROJECT_VERSION_MINOR} | ||||
|       -a VERSION_PATCH=${PROJECT_VERSION_PATCH} | ||||
|       -a VERSION=${wsjtx_VERSION} | ||||
|       --out-file=${_html_file} ${_source_name} | ||||
|       DEPENDS ${_args_DEPENDS} | ||||
| @ -195,9 +195,9 @@ function (document) | ||||
|       COMMAND ${ASCIIDOCTOR_EXECUTABLE} ARGS ${_args_ASCIIDOCTOR_OPTIONS} | ||||
|       -b docbook | ||||
|       -a data-uri! | ||||
|       -a VERSION_MAJOR=${WSJTX_VERSION_MAJOR} | ||||
|       -a VERSION_MINOR=${WSJTX_VERSION_MINOR} | ||||
|       -a VERSION_PATCH=${WSJTX_VERSION_PATCH}${BUILD_TYPE_REVISION} | ||||
|       -a VERSION_MAJOR=${wsjtx_VERSION_MAJOR} | ||||
|       -a VERSION_MINOR=${wsjtx_VERSION_MINOR} | ||||
|       -a VERSION_PATCH=${wsjtx_VERSION_PATCH}${BUILD_TYPE_REVISION} | ||||
|       -a VERSION=${wsjtx_VERSION} | ||||
|       -D ${CMAKE_CURRENT_BINARY_DIR} | ||||
|       -o ${_docbook_file} ${_source_name} | ||||
|  | ||||
| @ -94,8 +94,8 @@ d). Edit lines as needed. Keeping them in alphabetic order help see dupes. | ||||
| :sourceforge-jtsdk: https://sourceforge.net/projects/jtsdk[SourceForge JTSDK] | ||||
| :ubuntu_sdk: https://launchpad.net/~ubuntu-sdk-team/+archive/ppa[Ubuntu SDK Notice] | ||||
| :win_openssl_packages: https://slproweb.com/products/Win32OpenSSL.html[Windows OpenSSL Packages] | ||||
| :win32_openssl: https://slproweb.com/download/Win32OpenSSL_Light-1_1_1h.msi[Win32 OpenSSL Light Package] | ||||
| :win64_openssl: https://slproweb.com/download/Win64OpenSSL_Light-1_1_1h.msi[Win64 OpenSSL Light Package] | ||||
| :win32_openssl: https://slproweb.com/download/Win32OpenSSL_Light-1_1_1j.msi[Win32 OpenSSL Light Package] | ||||
| :win64_openssl: https://slproweb.com/download/Win64OpenSSL_Light-1_1_1j.msi[Win64 OpenSSL Light Package] | ||||
| :writelog: https://writelog.com/[Writelog] | ||||
| :wsjtx_group:  https://groups.io/g/WSJTX[WSJTX Group] | ||||
| :wsjtx: https://physics.princeton.edu/pulsar/K1JT/wsjtx.html[WSJT-X] | ||||
|  | ||||
| @ -37,6 +37,12 @@ examples for configurations `FT8` and `Echo`: | ||||
| ==== View Menu | ||||
| image::view-menu.png[align="left",alt="View Menu"] | ||||
| 
 | ||||
| The *SWL Mode* action reduces the _WSJT-X_ main window to a minimum | ||||
| size with just the menus, decodes windows, and status bar visible. You | ||||
| may find this useful when running multiple instances of the | ||||
| application. Both size and location of the main window are saved and | ||||
| recalled independently for this view. | ||||
| 
 | ||||
| [[MODE_MENU]] | ||||
| ==== Mode Menu | ||||
| image::mode-menu.png[align="left",alt="Mode Menu"] | ||||
|  | ||||
| @ -1,11 +1,10 @@ | ||||
| // Status=edited | ||||
| 
 | ||||
| Two arrangements of controls are provided for generating and selecting | ||||
| Tx messages.  Controls familiar to users of program _WSJT_ | ||||
| appear on *Tab 1*, providing six fields for message entry. | ||||
| Pre-formatted messages for the standard minimal QSO are generated when | ||||
| you click *Generate Std Msgs* or double-click on an appropriate line | ||||
| in one of the decoded text windows. | ||||
| Controls familiar to users of program _WSJT_ appear on *Tab 1*, | ||||
| providing six fields for message entry.  Pre-formatted messages for | ||||
| the standard minimal QSO are generated when you click *Generate Std | ||||
| Msgs* or double-click on an appropriate line in one of the decoded | ||||
| text windows. | ||||
| 
 | ||||
| //.Traditional Message Menu | ||||
| image::traditional-msg-box.png[align="center",alt="Traditional Message Menu"] | ||||
| @ -28,31 +27,12 @@ stored messages entered on the *Files -> Settings -> Tx Macros* tab. | ||||
| Pressing *Enter* on a modified message #5 automatically adds that | ||||
| message to the stored macros. | ||||
| 
 | ||||
| * In some circumstances it may be desirable to make your QSOs as | ||||
| short as possible.  To configure the program to start contacts with | ||||
| message #2, disable message #1 by double-clicking its radio-button in the *Next* column or *Tx 1* button in the *Now* column.  Similarly, to send RR73 | ||||
| rather than RRR for message #4, double-click one of its buttons. | ||||
| 
 | ||||
| The second arrangement of controls for generating and selecting | ||||
| Tx messages appears on *Tab 2* of the Message Control Panel: | ||||
| 
 | ||||
| //.New Message Menu | ||||
| image::new-msg-box.png[align="center",alt="New Message Menu"] | ||||
| 
 | ||||
| With this setup you normally follow a top-to-bottom sequence of | ||||
| transmissions from the left column if you are calling CQ, or the right | ||||
| column if answering a CQ.   | ||||
| 
 | ||||
| * Clicking a button puts the appropriate message in the *Gen Msg* box. | ||||
| If you are already transmitting, the Tx message is changed | ||||
| immediately. | ||||
| 
 | ||||
| * You can enter and transmit anything (up to 13 characters, including | ||||
| spaces) in the *Free Msg* box. | ||||
| 
 | ||||
| * Click the pull-down arrow in the *Free Msg* box to select a | ||||
| stored macro.  Pressing *Enter* on a modified message here | ||||
| automatically adds that message to the table of stored macros. | ||||
| * In some circumstances it may be desirable to make your QSOs as short | ||||
| as possible.  To configure the program to start contacts with message | ||||
| #2, disable message #1 by double-clicking its radio-button in the | ||||
| *Next* column or *Tx 1* button in the *Now* column.  Similarly, to | ||||
| send RR73 rather than RRR for message #4, double-click one of its | ||||
| buttons. | ||||
| 
 | ||||
| + | ||||
| 
 | ||||
|  | ||||
| @ -2,12 +2,12 @@ | ||||
| 
 | ||||
| === AP Decoding | ||||
| 
 | ||||
| The _WSJT-X_ decoders for FT4, FT8, JT65, QRA64, include  | ||||
| The _WSJT-X_ decoders for FST4, FT4, FT8, JT65, and QRA64 include  | ||||
| procedures that use naturally accumulating information during a | ||||
| minimal QSO.  This _a priori_ (AP) information increases sensitivity | ||||
| of the decoder by up to 4 dB, at the cost of a slightly higher rate of | ||||
| false decodes.  AP is optional in FT8, JT65, and QRA64, but is always | ||||
| enabled for FT4. | ||||
| enabled for FT4 and FST4 when decode depth is Normal or Deep. | ||||
| 
 | ||||
| For example: when you decide to answer a CQ, you already know your own | ||||
| callsign and that of your potential QSO partner.  The software | ||||
| @ -27,7 +27,7 @@ example, `a2` indicates that the successful decode used MyCall as | ||||
| hypothetically known information. | ||||
| 
 | ||||
| [[FT8_AP_INFO_TABLE]] | ||||
| .FT4 and FT8 AP information types | ||||
| .FST4, FT4, and FT8 AP information types | ||||
| [width="35%",cols="h10,<m20",frame=topbot,options="header"] | ||||
| |=============================================== | ||||
| |aP | Message components | ||||
| @ -47,7 +47,9 @@ forwarded to {pskreporter}. | ||||
| 
 | ||||
| Table 2 lists the six possible QSO states that are tracked by the | ||||
| _WSJT-X_ auto-sequencer, along with the type of AP decoding that would | ||||
| be attempted in each state. | ||||
| be attempted in each state in FT4 or FT8. The FST4 table (not shown)  | ||||
| is the same except that it omits the decoding attempts for AP types  | ||||
| 4 and 5 to save time. | ||||
| 
 | ||||
| [[FT8_AP_DECODING_TYPES_TABLE]] | ||||
| .FT4 and FT8 AP decoding types for each QSO state | ||||
| @ -109,13 +111,15 @@ summarized in the following Table: | ||||
| [width="50%",cols="h,3*^",frame=topbot,options="header"] | ||||
| |=========================================== | ||||
| |Mode    |Mode character|Sync character|End of line information | ||||
| |FST4    | `            |      | ?   aP | ||||
| |FT4     | ~            |      | ?   aP | ||||
| |FT8     | ~            |      | ?   aP | ||||
| |JT4     | $            | *, # | f, fN, dCN | ||||
| |JT9     | @            |      | | ||||
| |JT65    | #            |      | | ||||
| |JT65 VHF| #            | *, # | f, fN, dCN | ||||
| |QRA64   | :            | *    | R | ||||
| |QRA64   | :            | *    | R:w | ||||
| 
 | ||||
| |ISCAT   |              | *    | M  N  C  T | ||||
| |MSK144  | &            |      | | ||||
| |=========================================== | ||||
|  | ||||
| Before Width: | Height: | Size: 230 KiB After Width: | Height: | Size: 134 KiB | 
| Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 5.9 KiB | 
							
								
								
									
										
											BIN
										
									
								
								doc/user_guide/en/images/FST4-1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 477 KiB | 
							
								
								
									
										
											BIN
										
									
								
								doc/user_guide/en/images/FST4W-1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 389 KiB | 
| Before Width: | Height: | Size: 161 KiB After Width: | Height: | Size: 175 KiB | 
| Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 24 KiB | 
| Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 38 KiB | 
| Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 29 KiB | 
| Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 8.7 KiB | 
| Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 47 KiB | 
| Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 16 KiB | 
| Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 98 KiB | 
| Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 5.0 KiB | 
| Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 23 KiB | 
| Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 26 KiB | 
| Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 5.5 KiB | 
| @ -160,7 +160,7 @@ guidelines for contest logging with FT4, FT8, and MSK144: | ||||
| [[COMP-CALL]]  | ||||
| === Nonstandard Callsigns | ||||
| 
 | ||||
| *FT4, FT8, and MSK144* | ||||
| *FT4, FT8, FST4, and MSK144* | ||||
| 
 | ||||
| Compound callsigns like xx/K1ABC or K1ABC/x and special event | ||||
| callsigns like YW18FIFA are supported for normal QSOs but not for  | ||||
|  | ||||
| @ -31,4 +31,9 @@ synchronization symbols. | ||||
| for making 2-way QSOs, and FST4W should replace WSPR for propagation | ||||
| tests*.  Operating conventions on these LF and MF bands will | ||||
| eventually determine the most useful T/R sequence lengths for each | ||||
| type of operation. | ||||
| type of operation. We also expect that the 60 second variant of FST4 | ||||
| (FST4-60) will outperform JT9 for DX QSOs on HF bands due, in part, | ||||
| to the FST4 decoder's ability to use AP decoding for messages received | ||||
| from a QSO partner. In addition, FST4 provides the added benefits  | ||||
| associated with 77-bit messages and auto-sequencing.  | ||||
| 
 | ||||
|  | ||||
| @ -18,6 +18,13 @@ image::colors.png[align="center",alt="Colors Screen"] | ||||
| * Check *Highlight by Mode* if you wish worked before status to be per | ||||
|   <<INTRO,mode>>. | ||||
| 
 | ||||
| * Check *Only grid Fields sought* if you are only interested in the | ||||
|   leading two character grid locator Fields rather than teh four | ||||
|   character grid locator Squares. | ||||
| 
 | ||||
| * Check *Include extra WAE entities* if you are interested in the | ||||
|   extra entities defined for DARC WAE and CQ Marathon awards. | ||||
| 
 | ||||
| * Worked before status is calculated from your _WSJT-X_ ADIF | ||||
|   <<LOGGING, Logging>> file, you may replace ADIF log file with one | ||||
|   exported from your station logging application, *Rescan ADIF Log* | ||||
|  | ||||
| @ -96,5 +96,6 @@ green to indicate that proper communication has been established. | ||||
| Failure of the CAT-control test turns the button red and displays an | ||||
| error message.  After a successful CAT test, toggle the *Test PTT* | ||||
| button to confirm that your selected method of T/R control is working | ||||
| properly. (If you selected *VOX* for _PTT Method_, you can test T/R | ||||
| properly, the button turns red if the rig has been successfully | ||||
| keyed. (If you selected *VOX* for _PTT Method_, you can test T/R | ||||
| switching later by using the *Tune* button on the main window.) | ||||
|  | ||||
| @ -19,6 +19,10 @@ QRZ messages. Full details of the protocol can be found in comments | ||||
| at the top of this file in our source code repository: | ||||
| https://sourceforge.net/p/wsjt/wsjtx/ci/master/tree/Network/NetworkMessage.hpp | ||||
| 
 | ||||
| NOTE: The *Outgoing interfaces* and *Multicast TTL* fields are only | ||||
|       present when a multicast group IP address has been entered in | ||||
|       the *UDP Server* field. | ||||
| 
 | ||||
| Programs like _JTAlert_ use the _UDP Server_ feature to obtain | ||||
| information about running _WSJT-X_ instances.  If you are using | ||||
| _JTAlert_ to control _WSJT-X_, be sure to check the | ||||
|  | ||||
| @ -16,6 +16,5 @@ characters) in the entry field at top, then click *Add*. | ||||
| - You can reorder your macro messages by using drag-and-drop. The | ||||
| new order will be preserved when _WSJT-X_ is restarted. | ||||
| 
 | ||||
| - Messages can also be added from the main window's *Tx5* field on Tab | ||||
| 1 or the *Free msg* field on Tab 2. Simply hit [Enter] after the | ||||
| message has been entered. | ||||
| - Messages can also be added from the main window's *Tx5* | ||||
|   field. Simply hit [Enter] after the message has been entered. | ||||
|  | ||||
| @ -1,5 +1,19 @@ | ||||
| Do not confuse FST4 with FT4, which has a very different purpose! | ||||
| FST4 is is designed for making 2-way QSOs on the LF and MF bands. | ||||
| FST4 is designed primarily for making weak-signal 2-way QSOs on the  | ||||
| LF and MF bands. T/R periods from 15 s up to 1800 s are  | ||||
| available. Longer T/R periods provide better sensitivity only if  | ||||
| Tx and Rx frequency instability and channel Doppler spread | ||||
| are small enough so that received signals  | ||||
| remain phase coherent over periods spanning several transmitted symbols. | ||||
| Generally speaking, Rx and Tx frequency changes  | ||||
| during the transmission and channel Doppler spread should each be small compared | ||||
| to the symbol keying rate shown for each T/R duration in Table 7 within section | ||||
| <<PROTOCOL_OVERVIEW,Protocol Specifications>>. For example, the keying rate for  | ||||
| the 1800 s T/R period is 0.089 Baud, so | ||||
| successful operation using this T/R length requires Tx and Rx frequency | ||||
| stability better than 0.089 Hz over the duration of the 1800 s transmission in  | ||||
| addition to channel Doppler spread smaller than 0.089 Hz.  | ||||
| 
 | ||||
| Operation with FST4 is similar to that with other _WSJT-X_ modes: most | ||||
| on-screen controls, auto-sequencing, and other features behave in | ||||
| familiar ways.  However, operating conventions on the 2200 and 630 m | ||||
| @ -21,3 +35,26 @@ decoding process (and of course will be undecodable).  By checking | ||||
| further limit the decoding range to the setting of *F Tol* on | ||||
| either side of *Rx Freq*. | ||||
| 
 | ||||
| A noise blanker can be enabled by setting the *NB* percentage to a non-zero value.  | ||||
| This setting determines how many of the largest-amplitude samples will be  | ||||
| blanked (zeroed) before the data is submitted to the decoder. Most users find | ||||
| that settings between 0% (no blanking) and 10% work best. If the noise  | ||||
| blanker percentage is set to -1%, then the decoder will try 0, 5, 10, 15, and 20 % | ||||
| in succession. Similarly, a setting of -2% causes the decoder to loop over | ||||
| blanking percentages 0, 2, 4, ... 20 %. To save time, the multiple blanking  | ||||
| percentages triggered by negative *NB* settings are tried only for signal  | ||||
| candidates located near (within +/- 20 Hz) of the *Rx* frequency setting. | ||||
| 
 | ||||
| .Open a sample Wave File: | ||||
| 
 | ||||
| - Select *FST4* on the *Mode* menu. Set *T/R* to 60 s and *Decode | Deep*. | ||||
| - Set *NB* (noise blanker) to 0%. | ||||
| - Set up the Wide Graph display with settings appropriate for the FST4-60 mode.  | ||||
| For example, try *Bins/Pixel* 2 and *N Avg* 4. Set the *Start* frequency and the width of  | ||||
| the Wide Graph to include the frequency range that you want to decode. For this | ||||
| example, make sure that *Start* is less than 1000 Hz and that the Wide Graph extends to above 1400 Hz. | ||||
| - Set *F Low* 1000, *F High* 1400. These settings define the decoder's frequency search range. | ||||
| - Open a sample Wave file using *File | Open* and select the file | ||||
| ...\save\samples\FST4+FST4W\210115_0058.wav. After _WSJT-X_ has processed the file you should see something similar to the following screen shot: | ||||
| 
 | ||||
| image::FST4-1.png[align="left"] | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| FST4W is used in the same way as WSPR, but FST4W has significant | ||||
| advantages for use on the 2200 and 630 m bands.  By default the | ||||
| advantages for use on the 2200 m and 630 m bands.  By default the | ||||
| central *Rx Freq* is 1500 Hz and *F Tol* is 100 Hz, so the active | ||||
| decoding range is 1400 to 1600 Hz.  However, for added flexibility you | ||||
| can select different center frequencies and *F Tol* values.  We expect | ||||
| @ -16,3 +16,19 @@ If three operators agree in advance to select the options *1/3*, | ||||
| a fixed sequence with no two stations transmitting simultaneously. | ||||
| Sequence 1 is the first sequence after 00:00 UTC.  For WSPR-like | ||||
| scheduling behavior, you should select *Random* with this control. | ||||
| 
 | ||||
| .Open a Wave File: | ||||
| 
 | ||||
| - Select *FST4W* on the *Mode* menu. Set *T/R* to 1800 s and *Decode | Deep*. | ||||
| - Set *NB* to 0%. | ||||
| - Select appropriate wide graph settings. For example, try *Bins/Pixel* 1,  | ||||
| *Start* 1200 Hz and *N Avg* 150.  | ||||
| - Open a sample Wave file using *File | Open* and select the file | ||||
| ...\save\samples\FST4+FST4W\201230_0300.wav.  | ||||
| When it is finished you should see a single decode as shown in the  | ||||
| screenshot: | ||||
| 
 | ||||
| image::FST4W-1.png[align="left"] | ||||
| 
 | ||||
| Note that the weak signal associated with the single decode is all but invisible on the  | ||||
| widegraph spectrogram.  | ||||
|  | ||||
| @ -13,9 +13,6 @@ and *Page Up/Down* key presses, with the *Page* keys moving the | ||||
| controls in larger steps.  You can also type numbers directly into | ||||
| the spinner controls or use the mouse wheel. | ||||
| 
 | ||||
| - Select *Tab 2* (below the *Decode* button) to choose the alternative | ||||
| set of controls for generating and selecting Tx messages. | ||||
| 
 | ||||
| [[DOWNLOAD_SAMPLES]] | ||||
| === Download Samples | ||||
| 
 | ||||
|  | ||||
| @ -381,7 +381,7 @@ decode*, *Decode after EME delay* | ||||
| 
 | ||||
| .Wide Graph: | ||||
| 
 | ||||
| - *Bins/Pixel* = 3, *N Avg* = 10 | ||||
| - *Bins/Pixel* = 4, *N Avg* = 10 | ||||
| 
 | ||||
| - Adjust the width of the window so that the frequency range extends | ||||
|   up to at least 2400 Hz. | ||||
|  | ||||
| @ -119,6 +119,15 @@ In macOS, enter the following command from a terminal: | ||||
| 
 | ||||
|  open /Applications/wsjtx.app --args -stylesheet :/qdarkstyle/style.qss | ||||
| 
 | ||||
| In Linux using the Unity or GNOME GUI the following commands will | ||||
| update the _WSJT-X_ start up: | ||||
| 
 | ||||
| .... | ||||
| sed '/Exec=wsjtx/ s/$/ -stylesheet :\/qdarkstyle\/style.qss/' \ | ||||
|   /usr/share/applications/wsjtx.desktop >~/.local/share/applications/wsjtx.desktop | ||||
| update-desktop-database ~/.local/share/applications/ | ||||
| .... | ||||
| 
 | ||||
| Depending on your operating system, the main _WSJT-X_ window will look | ||||
| something like this: | ||||
| 
 | ||||
| @ -193,7 +202,7 @@ include::wspr.adoc[] | ||||
| include::controls-functions-menus.adoc[] | ||||
| 
 | ||||
| [[CONTROLS_MAIN]] | ||||
| === Button Row | ||||
|     === Button Row | ||||
| include::controls-functions-main-window.adoc[] | ||||
| 
 | ||||
| [[CONTROLS_LEFT]] | ||||
|  | ||||
| @ -7,7 +7,7 @@ TargetFileName="${AppLocalDataLocation}/logs/wsjtx_syslog_%Y-%m.log" | ||||
| RotationTimePoint="01 00:00:00" | ||||
| Append=true | ||||
| EnableFinalRotation=false | ||||
| MaxSize=41943040 | ||||
| MaxSize=52428800 | ||||
| MinFreeSpace=1073741824 | ||||
| MaxFiles=12 | ||||
| Target="${AppLocalDataLocation}/logs" | ||||
|  | ||||
| @ -614,7 +614,7 @@ contains | ||||
|                      endif | ||||
|                      call this%callback(nutc,smax1,nsnr,xdt,fsig,msg,    & | ||||
|                         iaptype,qual,ntrperiod,lwspr,fmid,w50) | ||||
|                      if(iwspr.eq.0 .and. nb.lt.0) go to 900 | ||||
| !                     if(iwspr.eq.0 .and. nb.lt.0) go to 900 | ||||
|                      goto 800 | ||||
|                   endif | ||||
|                enddo  ! metrics | ||||
|  | ||||
| @ -65,7 +65,8 @@ program ft4code | ||||
|      if(i3.eq.2) msgtype="EU VHF Contest" | ||||
|      if(i3.eq.3) msgtype="ARRL RTTY Roundup" | ||||
|      if(i3.eq.4) msgtype="Nonstandard calls" | ||||
|      if(i3.ge.5) msgtype="Undefined msg type" | ||||
|      if(i3.eq.5) msgtype="EU VHF Contest" | ||||
|      if(i3.ge.6) msgtype="Undefined msg type" | ||||
|      if(i3.ge.1) n3=-1 | ||||
|      bad=" " | ||||
|      comment='         ' | ||||
|  | ||||
| @ -64,7 +64,8 @@ program ft8code | ||||
|      if(i3.eq.2) msgtype="EU VHF Contest" | ||||
|      if(i3.eq.3) msgtype="ARRL RTTY Roundup" | ||||
|      if(i3.eq.4) msgtype="Nonstandard call" | ||||
|      if(i3.ge.5) msgtype="Undefined type" | ||||
|      if(i3.eq.5) msgtype="EU VHF Contest" | ||||
|      if(i3.ge.6) msgtype="Undefined type" | ||||
|      if(i3.ge.1) n3=-1 | ||||
|      bad=" " | ||||
|      comment='         ' | ||||
|  | ||||
| @ -51,7 +51,8 @@ program msk144code | ||||
|      if(i3.eq.2) msgtype="EU VHF Contest" | ||||
|      if(i3.eq.3) msgtype="ARRL RTTY Roundup" | ||||
|      if(i3.eq.4) msgtype="Nonstandard calls" | ||||
|      if(i3.ge.5) msgtype="Undefined msg type" | ||||
|      if(i3.eq.5) msgtype="EU VHF Contest" | ||||
|      if(i3.ge.6) msgtype="Undefined msg type" | ||||
|      if(i3.ge.1) n3=-1 | ||||
|      if(i4tone(41).lt.0) then | ||||
|         msgtype="Sh msg" | ||||
|  | ||||
| @ -100,7 +100,7 @@ subroutine msk144decodeframe(c,softbits,msgreceived,nsuccess) | ||||
|     nsuccess=1 | ||||
|     write(c77,'(77i1)') decoded77 | ||||
|     read(c77(72:77),'(2b3)') n3,i3 | ||||
|     if( (i3.eq.0.and.(n3.eq.1 .or. n3.eq.3 .or. n3.eq.4 .or. n3.gt.5)) .or. i3.eq.3 .or. i3.gt.4 ) then | ||||
|     if( (i3.eq.0.and.(n3.eq.1 .or. n3.eq.3 .or. n3.eq.4 .or. n3.gt.5)) .or. i3.eq.3 .or. i3.gt.5 ) then | ||||
|         nsuccess=0  | ||||
|     else  | ||||
|         call unpack77(c77,1,msgreceived,unpk77_success) | ||||
|  | ||||
							
								
								
									
										7
									
								
								main.cpp
									
									
									
									
									
								
							
							
						
						| @ -225,8 +225,7 @@ int main(int argc, char *argv[]) | ||||
|       // disallow multiple instances with same instance key
 | ||||
|       QLockFile instance_lock {temp_dir.absoluteFilePath (a.applicationName () + ".lock")}; | ||||
|       instance_lock.setStaleLockTime (0); | ||||
|       bool lock_ok {false}; | ||||
|       while (!(lock_ok = instance_lock.tryLock ())) | ||||
|       while (!instance_lock.tryLock ()) | ||||
|         { | ||||
|           if (QLockFile::LockFailedError == instance_lock.error ()) | ||||
|             { | ||||
| @ -249,6 +248,10 @@ int main(int argc, char *argv[]) | ||||
|                   throw std::runtime_error {"Multiple instances must have unique rig names"}; | ||||
|                 } | ||||
|             } | ||||
|           else | ||||
|             { | ||||
|               throw std::runtime_error {"Failed to access lock file"}; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|       // load UI translations
 | ||||
|  | ||||
| @ -1,6 +1,10 @@ | ||||
| set (SAMPLE_FILES | ||||
|   FT4/000000_000002.wav | ||||
|   FT8/181201_180245.wav | ||||
|   FST4+FST4W/210115_0058.wav | ||||
|   FST4+FST4W/201230_0300.wav | ||||
|   MSK144/181211_120500.wav | ||||
|   MSK144/181211_120800.wav | ||||
|   ISCAT/ISCAT-A/VK7MO_110401_235515.wav | ||||
|   ISCAT/ISCAT-B/K0AWU_100714_115000.wav | ||||
|   JT4/JT4A/DF2ZC_070926_040700.WAV | ||||
| @ -17,6 +21,7 @@ set (SAMPLE_FILES | ||||
|   JT9/130418_1742.wav | ||||
|   MSK144/181211_120500.wav | ||||
|   MSK144/181211_120800.wav | ||||
|   QRA64/QRA64C/161113_0111.wav | ||||
|   WSPR/150426_0918.wav | ||||
|   Q65/30A_Ionoscatter_6m/201203_022700.wav | ||||
|   Q65/30A_Ionoscatter_6m/201203_022800.wav | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								samples/FST4+FST4W/201230_0300.wav
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								samples/FST4+FST4W/210115_0058.wav
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -64,7 +64,10 @@ void AbstractLogWindow::impl::delete_QSOs () | ||||
|                                                        , tr ("Confirm Delete") | ||||
|                                                        , tr ("Are you sure you want to delete the %n " | ||||
|                                                              "selected QSO(s) from the log?", "" | ||||
|                                                              , row_indexes.size ()))) | ||||
|                                                              , row_indexes.size ()) | ||||
|                                                        , QString {} | ||||
|                                                        , MessageBox::Yes | MessageBox::No | ||||
|                                                        , MessageBox::No)) | ||||
|     { | ||||
|       // We must work with source model indexes because we don't want row
 | ||||
|       // removes to invalidate model indexes we haven't yet processed. We
 | ||||
|  | ||||
| @ -3633,7 +3633,7 @@ void MainWindow::auto_sequence (DecodedText const& message, unsigned start_toler | ||||
|   auto const& message_words = message.messageWords (); | ||||
|   auto is_73 = message_words.filter (QRegularExpression {"^(73|RR73)$"}).size(); | ||||
|   bool is_OK=false; | ||||
|   if(m_mode=="MSK144" and message.string().indexOf(ui->dxCallEntry->text()+" R ")>0) is_OK=true; | ||||
|   if(m_mode=="MSK144" and message.clean_string ().indexOf(ui->dxCallEntry->text()+" R ")>0) is_OK=true; | ||||
|   if (message_words.size () > 2 && (message.isStandardMessage() || (is_73 or is_OK))) { | ||||
|     auto df = message.frequencyOffset (); | ||||
|     auto within_tolerance = (qAbs (ui->RxFreqSpinBox->value () - df) <= int (start_tolerance) | ||||
| @ -3648,7 +3648,7 @@ void MainWindow::auto_sequence (DecodedText const& message, unsigned start_toler | ||||
|                || message_words.contains ("DE"))) | ||||
|           || !message.isStandardMessage ()); // free text 73/RR73
 | ||||
| 
 | ||||
|     QStringList w=message.string().mid(22).remove("<").remove(">").split(" ",SkipEmptyParts); | ||||
|     QStringList w=message.clean_string ().mid(22).remove("<").remove(">").split(" ",SkipEmptyParts); | ||||
|     QString w2; | ||||
|     int nrpt=0; | ||||
|     if (w.size () > 2) | ||||
| @ -3660,8 +3660,8 @@ void MainWindow::auto_sequence (DecodedText const& message, unsigned start_toler | ||||
|         } | ||||
|       } | ||||
|     bool bEU_VHF=(nrpt>=520001 and nrpt<=594000); | ||||
|     if(bEU_VHF and message.string().contains("<"+m_config.my_callsign() + "> ")) { | ||||
|       m_xRcvd=message.string().trimmed().right(13); | ||||
|     if(bEU_VHF and message.clean_string ().contains("<"+m_config.my_callsign() + "> ")) { | ||||
|       m_xRcvd=message.clean_string ().trimmed().right(13); | ||||
|     } | ||||
|     if (m_auto | ||||
|         && (m_QSOProgress==REPLYING  or (!ui->tx1->isEnabled () and m_QSOProgress==REPORT)) | ||||
| @ -4180,7 +4180,7 @@ void MainWindow::guiUpdate() | ||||
|           statusUpdate (); | ||||
|         } | ||||
|     } | ||||
|     m_bCallingCQ = CALLING == m_QSOProgress | ||||
|     m_bCallingCQ = 6 == m_ntx | ||||
|       || m_currentMessage.contains (QRegularExpression {"^(CQ|QRZ) "}); | ||||
|     if(m_mode=="FT8" or m_mode=="FT4") { | ||||
|       if(m_bCallingCQ && ui->cbFirst->isVisible () && ui->cbFirst->isChecked ()) { | ||||
| @ -4212,16 +4212,15 @@ void MainWindow::guiUpdate() | ||||
|       msg_parts[1].remove (QChar {'<'}); | ||||
|       msg_parts[1].remove (QChar {'>'}); | ||||
|     } | ||||
|     auto is_73 = m_QSOProgress >= ROGER_REPORT | ||||
|       && message_is_73 (m_currentMessageType, msg_parts); | ||||
|     auto is_73 = message_is_73 (m_currentMessageType, msg_parts); | ||||
|     m_sentFirst73 = is_73 | ||||
|       && !message_is_73 (m_lastMessageType, m_lastMessageSent.split (' ', SkipEmptyParts)); | ||||
|     if (m_sentFirst73) { | ||||
|     if (m_sentFirst73 || (is_73 && CALLING == m_QSOProgress)) { | ||||
|       m_qsoStop=t2; | ||||
|       if(m_config.id_after_73 ()) { | ||||
|         icw[0] = m_ncw; | ||||
|       } | ||||
|       if((m_config.prompt_to_log() or m_config.autoLog()) && !m_tune) logQSOTimer.start(0); | ||||
|       if((m_config.prompt_to_log() or m_config.autoLog()) && !m_tune && CALLING != m_QSOProgress) logQSOTimer.start(0); | ||||
|     } | ||||
| 
 | ||||
|     bool b=(m_mode=="FT8" or m_mode=="FT4") and ui->cbAutoSeq->isChecked(); | ||||
| @ -4778,7 +4777,7 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie | ||||
|   auto ctrl = modifiers.testFlag (Qt::ControlModifier); | ||||
|   // auto alt = modifiers.testFlag (Qt::AltModifier);
 | ||||
|   // basic mode sanity checks
 | ||||
|   auto const& parts = message.string ().split (' ', SkipEmptyParts); | ||||
|   auto const& parts = message.clean_string ().split (' ', SkipEmptyParts); | ||||
|   if (parts.size () < 5) return; | ||||
|   auto const& mode = parts.at (4).left (1); | ||||
|   if (("JT65" == m_mode && mode != "#") | ||||
| @ -4827,16 +4826,16 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie | ||||
|   QString hisgrid; | ||||
|   message.deCallAndGrid(/*out*/hiscall,hisgrid); | ||||
| 
 | ||||
|   if(message.string().contains(hiscall+"/R")) { | ||||
|   if(message.clean_string ().contains(hiscall+"/R")) { | ||||
|     hiscall+="/R"; | ||||
|     ui->dxCallEntry->setText(hiscall); | ||||
|   } | ||||
|   if(message.string().contains(hiscall+"/P")) { | ||||
|   if(message.clean_string ().contains(hiscall+"/P")) { | ||||
|     hiscall+="/P"; | ||||
|     ui->dxCallEntry->setText(hiscall); | ||||
|   } | ||||
| 
 | ||||
|   QStringList w=message.string().mid(22).remove("<").remove(">").split(" ",SkipEmptyParts); | ||||
|   QStringList w=message.clean_string ().mid(22).remove("<").remove(">").split(" ",SkipEmptyParts); | ||||
|   int nw=w.size(); | ||||
|   if(nw>=4) { | ||||
|     if(message_words.size()<3) return; | ||||
| @ -4848,9 +4847,9 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie | ||||
|   } | ||||
| 
 | ||||
|   bool is_73 = message_words.filter (QRegularExpression {"^(73|RR73)$"}).size (); | ||||
|   if (!is_73 and !message.isStandardMessage() and !message.string().contains("<")) { | ||||
|   if (!is_73 and !message.isStandardMessage() and !message.clean_string ().contains("<")) { | ||||
|     qDebug () << "Not processing message - hiscall:" << hiscall << "hisgrid:" << hisgrid | ||||
|               << message.string() << message.isStandardMessage(); | ||||
|               << message.clean_string () << message.isStandardMessage(); | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
| @ -4892,7 +4891,7 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie | ||||
|   auto base_call = Radio::base_callsign (hiscall); | ||||
| 
 | ||||
| // Determine appropriate response to received message
 | ||||
|   auto dtext = " " + message.string () + " "; | ||||
|   auto dtext = " " + message.clean_string () + " "; | ||||
|   dtext=dtext.remove("<").remove(">"); | ||||
|   if(dtext.contains (" " + m_baseCall + " ") | ||||
|      || dtext.contains ("<" + m_baseCall + "> ") | ||||
| @ -4920,7 +4919,7 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie | ||||
|       MessageBox::information_message (this, msg); | ||||
|     } | ||||
| 
 | ||||
|     QStringList t=message.string().split(' ', SkipEmptyParts); | ||||
|     QStringList t=message.clean_string ().split(' ', SkipEmptyParts); | ||||
|     int n=t.size(); | ||||
|     QString t0=t.at(n-2); | ||||
|     QString t1=t0.right(1); | ||||
| @ -4996,11 +4995,11 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie | ||||
|         } | ||||
|       } else {  // no grid on end of msg
 | ||||
|         auto const& word_3 = message_words.at (3); | ||||
|         bool word_3_is_int; | ||||
|         auto word_3_as_number = word_3.toInt (&word_3_is_int); | ||||
|         if(m_QSOProgress >= ROGER_REPORT && ("RRR" == word_3 | ||||
|                                              || (word_3_is_int && word_3_as_number == 73) | ||||
|                                              || "RR73" == word_3)) { | ||||
|         auto word_3_as_number = word_3.toInt (); | ||||
|         if (("RRR" == word_3 | ||||
|              || word_3_as_number == 73 | ||||
|              || "RR73" == word_3 | ||||
|              || ("R" == word_3 && m_QSOProgress != REPORT))) { | ||||
|           if(m_mode=="FT4" and "RR73" == word_3) m_dateTimeRcvdRR73=QDateTime::currentDateTimeUtc(); | ||||
|           m_bTUmsg=false; | ||||
|           m_nextCall="";   //### Temporary: disable use of "TU;" message
 | ||||
| @ -5013,16 +5012,30 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie | ||||
|             ui->tx3->setText(t); | ||||
|             m_bTUmsg=true; | ||||
|           } else { | ||||
|             if(SpecOp::RTTY == m_config.special_op_id()) { | ||||
|               logQSOTimer.start(0); | ||||
|               m_ntx=6; | ||||
|               ui->txrb6->setChecked(true); | ||||
|             } else { | ||||
|               m_ntx=5; | ||||
|               ui->txrb5->setChecked(true); | ||||
|             } | ||||
|             if (m_QSOProgress > CALLING && m_QSOProgress < SIGNOFF | ||||
|                 && SpecOp::NONE < m_config.special_op_id () && SpecOp::FOX > m_config.special_op_id () | ||||
|                 && ("RR73" == word_3 || 73 == word_3_as_number)) | ||||
|               { | ||||
|                 logQSOTimer.start(0); | ||||
|                 m_ntx=6; | ||||
|                 ui->txrb6->setChecked(true); | ||||
|               } | ||||
|             else if (word_3.contains (QRegularExpression {"^R(?!R73|RR)"}) | ||||
|                      && m_QSOProgress != ROGER_REPORT) | ||||
|               { | ||||
|                 m_ntx=4; | ||||
|                 ui->txrb4->setChecked(true); | ||||
|               } | ||||
|             else | ||||
|               { | ||||
|                 m_ntx=5; | ||||
|                 ui->txrb5->setChecked(true); | ||||
|               } | ||||
|           } | ||||
|           m_QSOProgress = SIGNOFF; | ||||
|           if (m_QSOProgress >= ROGER_REPORT) | ||||
|             { | ||||
|               m_QSOProgress = SIGNOFF; | ||||
|             } | ||||
|         } else if((m_QSOProgress >= REPORT | ||||
|                    || (m_QSOProgress >= REPLYING && | ||||
|                    (m_mode=="MSK144" or m_mode=="FT8" or m_mode=="FT4"))) | ||||
| @ -5037,30 +5050,35 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie | ||||
|           ui->txrb4->setChecked(true); | ||||
|         } else if (m_QSOProgress >= CALLING) | ||||
|           { | ||||
|             if (word_3_is_int | ||||
|                 && ((word_3_as_number >= -50 && word_3_as_number <= 49) | ||||
|                     || (word_3_as_number >= 529 && word_3_as_number <= 599))) { | ||||
|               if(SpecOp::EU_VHF==m_config.special_op_id() or | ||||
|                  SpecOp::FIELD_DAY==m_config.special_op_id() or | ||||
|                  SpecOp::RTTY==m_config.special_op_id()) { | ||||
|                 setTxMsg(2); | ||||
|                 m_QSOProgress=REPORT; | ||||
|             if ((word_3_as_number >= -50 && word_3_as_number <= 49) | ||||
|                 || (word_3_as_number >= 529 && word_3_as_number <= 599)) | ||||
|               { | ||||
|                 if(SpecOp::EU_VHF==m_config.special_op_id() or | ||||
|                    SpecOp::FIELD_DAY==m_config.special_op_id() or | ||||
|                    SpecOp::RTTY==m_config.special_op_id()) | ||||
|                   { | ||||
|                     setTxMsg(2); | ||||
|                     m_QSOProgress=REPORT; | ||||
|                   } | ||||
|                 else | ||||
|                   { | ||||
|                     if (word_3.startsWith ("R-") || word_3.startsWith ("R+")) | ||||
|                       { | ||||
|                         setTxMsg(4); | ||||
|                         m_QSOProgress=ROGERS; | ||||
|                       } | ||||
|                     else | ||||
|                       { | ||||
|                         setTxMsg (3); | ||||
|                         m_QSOProgress = ROGER_REPORT; | ||||
|                       } | ||||
|                   } | ||||
|               } | ||||
|               else { | ||||
|                 setTxMsg (3); | ||||
|                 m_QSOProgress = ROGER_REPORT; | ||||
|               } | ||||
|             } else { | ||||
|               if (word_3.startsWith ("R-") || word_3.startsWith ("R+")) { | ||||
|                 setTxMsg(4); | ||||
|                 m_QSOProgress=ROGERS; | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       else | ||||
|         {                // nothing for us
 | ||||
|           return; | ||||
|         } | ||||
|         else | ||||
|           {                // nothing for us
 | ||||
|             return; | ||||
|           } | ||||
|       } | ||||
|     } | ||||
|     else if (m_QSOProgress >= ROGERS | ||||
| @ -5138,7 +5156,7 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie | ||||
|   } | ||||
| 
 | ||||
|   QString s1 = m_QSOText.trimmed (); | ||||
|   QString s2 = message.string ().trimmed(); | ||||
|   QString s2 = message.clean_string ().trimmed(); | ||||
|   if (s1!=s2 and !message.isTX()) { | ||||
|     if (!s2.contains(m_baseCall) or m_mode=="MSK144") {  // Taken care of elsewhere if for_us and slow mode
 | ||||
|       ui->decodedTextBrowser2->displayDecodedText(message, m_baseCall,m_mode,m_config.DXCC(), | ||||
| @ -5711,7 +5729,7 @@ void MainWindow::msgtype(QString t, QLineEdit* tx)               //msgtype() | ||||
|       p.setColor(QPalette::Base,"#66ffff");     //light blue
 | ||||
|     } else { | ||||
|       p.setColor(QPalette::Base,Qt::transparent); | ||||
|       if(m_mode=="MSK144" and t.mid(0,1)=="<") { | ||||
|       if ("MSK144" == m_mode && t.count ('<') == 1) { | ||||
|         p.setColor(QPalette::Base,"#00ffff");   //another light blue
 | ||||
|       } | ||||
|     } | ||||
| @ -6488,6 +6506,7 @@ void MainWindow::on_actionMSK144_triggered() | ||||
|   } else { | ||||
|     ui->labDXped->setVisible(true); | ||||
|     ui->labDXped->setText(t0); | ||||
|     on_contest_log_action_triggered(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  | ||||