From 12b6efb189f9eabe4727c429f289b7aef10d3a46 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Mon, 21 Jan 2019 13:35:18 +0000 Subject: [PATCH 01/11] Make sure database models are synchronized before using QSqlTableModel::rowCount() This is necessary as the cached model will not reflect a correct row count if there are un-fetched rows. --- models/CabrilloLog.cpp | 21 +++++++++++++++++++-- models/FoxLog.cpp | 22 ++++++++++++++++++++-- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/models/CabrilloLog.cpp b/models/CabrilloLog.cpp index aaedd3bd1..3a30380e3 100644 --- a/models/CabrilloLog.cpp +++ b/models/CabrilloLog.cpp @@ -46,10 +46,25 @@ CabrilloLog::impl::impl (Configuration const * configuration) } SQL_error_check (dupe_query_, &QSqlQuery::prepare, - "SELECT COUNT(*) FROM cabrillo_log WHERE call = :call AND band = :band"); + "SELECT " + " COUNT(*) " + " FROM " + " cabrillo_log " + " WHERE " + " call = :call " + " AND band = :band"); SQL_error_check (export_query_, &QSqlQuery::prepare, - "SELECT frequency, \"when\", exchange_sent, call, exchange_rcvd FROM cabrillo_log ORDER BY \"when\""); + "SELECT " + " frequency" + " , \"when\"" + " , exchange_sent" + " , call" + " , exchange_rcvd" + " FROM " + " cabrillo_log " + " ORDER BY " + " \"when\""); setEditStrategy (QSqlTableModel::OnFieldChange); setTable ("cabrillo_log"); @@ -138,6 +153,8 @@ bool CabrilloLog::dupe (Frequency frequency, QString const& call) const void CabrilloLog::reset () { + // synchronize model + while (m_->canFetchMore ()) m_->fetchMore (); if (m_->rowCount ()) { m_->setEditStrategy (QSqlTableModel::OnManualSubmit); diff --git a/models/FoxLog.cpp b/models/FoxLog.cpp index 504ddd2a8..773a5308f 100644 --- a/models/FoxLog.cpp +++ b/models/FoxLog.cpp @@ -46,10 +46,26 @@ FoxLog::impl::impl (Configuration const * configuration) } SQL_error_check (dupe_query_, &QSqlQuery::prepare, - "SELECT COUNT(*) FROM fox_log WHERE call = :call AND band = :band"); + "SELECT " + " COUNT(*) " + " FROM " + " fox_log " + " WHERE " + " call = :call " + " AND band = :band"); SQL_error_check (export_query_, &QSqlQuery::prepare, - "SELECT band, \"when\", call, grid, report_sent, report_rcvd FROM fox_log ORDER BY \"when\""); + "SELECT " + " band" + " , \"when\"" + " , call" + " , grid" + " , report_sent" + " , report_rcvd " + " FROM " + " fox_log " + " ORDER BY " + " \"when\""); setEditStrategy (QSqlTableModel::OnFieldChange); setTable ("fox_log"); @@ -141,6 +157,8 @@ bool FoxLog::dupe (QString const& call, QString const& band) const void FoxLog::reset () { + // synchronize model + while (m_->canFetchMore ()) m_->fetchMore (); if (m_->rowCount ()) { m_->setEditStrategy (QSqlTableModel::OnManualSubmit); From b6b1f3b3b63cde6f66c7dcbd9acbafad6c0f2a84 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Mon, 21 Jan 2019 22:03:33 +0000 Subject: [PATCH 02/11] Correctly report 6m and up frequencies in Cabrillo export Cabrillo format expects specific frequency strings for 6m and up bands rather than the actual QSO frequency. --- models/CabrilloLog.cpp | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/models/CabrilloLog.cpp b/models/CabrilloLog.cpp index 3a30380e3..1fee9b148 100644 --- a/models/CabrilloLog.cpp +++ b/models/CabrilloLog.cpp @@ -21,6 +21,8 @@ class CabrilloLog::impl final public: impl (Configuration const *); + QString cabrillo_frequency_string (Radio::Frequency frequency) const; + Configuration const * configuration_; QSqlQuery mutable dupe_query_; QSqlQuery mutable export_query_; @@ -83,6 +85,32 @@ CabrilloLog::impl::impl (Configuration const * configuration) SQL_error_check (*this, &QSqlTableModel::select); } +// frequency here is in kHz +QString CabrilloLog::impl::cabrillo_frequency_string (Radio::Frequency frequency) const +{ + QString result; + auto band = configuration_->bands ()->find (frequency * 1000ull); + if ("1mm" == band) result = "LIGHT"; + else if ("2mm" == band) result = "241G"; + else if ("2.5mm" == band) result = "134G"; + else if ("4mm" == band) result = "75G"; + else if ("6mm" == band) result = "47G"; + else if ("1.25cm" == band) result = "24G"; + else if ("3cm" == band) result = "10G"; + else if ("6cm" == band) result = "5.7G"; + else if ("9cm" == band) result = "3.4G"; + else if ("13cm" == band) result = "2.3G"; + else if ("23cm" == band) result = "1.2G"; + else if ("33cm" == band) result = "902"; + else if ("70cm" == band) result = "432"; + else if ("1.25m" == band) result = "222"; + else if ("2m" == band) result = "144"; + else if ("4m" == band) result = "70"; + else if ("6m" == band) result = "50"; + else result = QString::number (frequency); + return result; +} + CabrilloLog::CabrilloLog (Configuration const * configuration) : m_ {configuration} { @@ -177,12 +205,10 @@ void CabrilloLog::export_qsos (QTextStream& stream) const auto rcvd_index = record.indexOf ("exchange_rcvd"); while (m_->export_query_.next ()) { - auto frequency = m_->export_query_.value (frequency_index).value (); auto my_call = m_->configuration_->my_callsign (); - frequency = frequency > 50000000ull ? frequency / 1000ull : frequency; stream << QString {"QSO: %1 DG %2 %3 %4 %5 %6\n"} - .arg (frequency, 5) - .arg (QDateTime::fromMSecsSinceEpoch (m_->export_query_.value (when_index).toULongLong () * 1000ull, Qt::UTC).toString ("yyyy-MM-dd hhmm")) + .arg (m_->cabrillo_frequency_string (m_->export_query_.value (frequency_index).value ()), 5) + .arg (QDateTime::fromMSecsSinceEpoch (m_->export_query_.value (when_index).toULongLong () * 1000ull, Qt::UTC).toString ("yyyy-MM-dd hhmm")) .arg (my_call, -12) .arg (m_->export_query_.value (sent_index).toString (), -13) .arg (m_->export_query_.value (call_index).toString (), -12) From 6ac24e75cbf73b18b3b1c54ccc5ede47c921fcaa Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Tue, 22 Jan 2019 20:23:17 +0000 Subject: [PATCH 03/11] Add the new rigctlcom utility to the installer as rigctlcom-wsjtx Thanks to Mike, W9MDB, for this contribution. --- CMakeLists.txt | 8 ++++++++ manpages/CMakeLists.txt | 1 + manpages/man1/rigctl-wsjtx.1.txt | 4 ++-- manpages/man1/rigctlcom-wsjtx.1.txt | 30 +++++++++++++++++++++++++++++ manpages/man1/rigctld-wsjtx.1.txt | 4 ++-- 5 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 manpages/man1/rigctlcom-wsjtx.1.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 1fc3d7162..71a59a5fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -848,6 +848,7 @@ set (hamlib_STATIC 1) find_package (hamlib 3 REQUIRED) find_program (RIGCTL_EXE rigctl) find_program (RIGCTLD_EXE rigctld) +find_program (RIGCTLCOM_EXE rigctlcom) message (STATUS "hamlib_INCLUDE_DIRS: ${hamlib_INCLUDE_DIRS}") message (STATUS "hamlib_LIBRARIES: ${hamlib_LIBRARIES}") @@ -1417,6 +1418,13 @@ install (PROGRAMS RENAME rigctld-wsjtx${CMAKE_EXECUTABLE_SUFFIX} ) +install (PROGRAMS + ${RIGCTLCOM_EXE} + DESTINATION ${CMAKE_INSTALL_BINDIR} + #COMPONENT runtime + RENAME rigctlcom-wsjtx${CMAKE_EXECUTABLE_SUFFIX} + ) + install (FILES README COPYING diff --git a/manpages/CMakeLists.txt b/manpages/CMakeLists.txt index f5ed53fba..7ab7c7646 100644 --- a/manpages/CMakeLists.txt +++ b/manpages/CMakeLists.txt @@ -4,6 +4,7 @@ set (ASCIIDOC_MANS man1/jt65code.1.txt man1/rigctl-wsjtx.1.txt man1/rigctld-wsjtx.1.txt + man1/rigctlcom-wsjtx.1.txt man1/message_aggregator.1.txt man1/udp_daemon.1.txt ) diff --git a/manpages/man1/rigctl-wsjtx.1.txt b/manpages/man1/rigctl-wsjtx.1.txt index e96667f2e..c7adef22c 100644 --- a/manpages/man1/rigctl-wsjtx.1.txt +++ b/manpages/man1/rigctl-wsjtx.1.txt @@ -6,7 +6,7 @@ == NAME -rigctl-wsjtx - Hamlib 3 rigctld server. +rigctl-wsjtx - Hamlib 4 rigctld server. == SYNOPSIS @@ -15,7 +15,7 @@ rigctl-wsjtx - Hamlib 3 rigctld server. == DESCRIPTION *wsjtx* uses a version of the *hamlib* CAT control library. This - library is heavily modified over the current release version of + library is virtually identical to the the current release version of *hamlib*. If a *wsjtx* user wishes to use the *hamlib* network rig server *rigctld* to remotely control their transceiver; then this special version of the *rigctl* client should be used since that too diff --git a/manpages/man1/rigctlcom-wsjtx.1.txt b/manpages/man1/rigctlcom-wsjtx.1.txt new file mode 100644 index 000000000..4bbee73d0 --- /dev/null +++ b/manpages/man1/rigctlcom-wsjtx.1.txt @@ -0,0 +1,30 @@ +:doctype: manpage +:man source: AsciiDoc +:man version: {revnumber} +:man manual: WSJT-X Manual += rigctlcom-wsjtx(1) + +== NAME + +rigctlcom-wsjtx - Hamlib 4 rigctlcom Serial port passthru Kenwood TS-2000 emulator. + +== SYNOPSIS + +*rigctlcom-wsjtx* [OPTIONS] + +== DESCRIPTION + +*wsjtx* uses a version of the *hamlib* CAT control library. This + library is virtually identical to the current release version of + *hamlib*. If a *wsjtx* user wishes to use the *hamlib* TS-2000 + emulator *rigctlcom* to allow CAT applications that do not talk to + *hamlib* to share CAT control control their transceiver; then this + special version of *rigctlcom* should be used since that too has the + modified *hamlib* code embedded with it. + +WSJT-X home page:: http://www.physics.princeton.edu/pulsar/K1JT/wsjtx.html + +WSJT-X User's Guide:: http://www.physics.princeton.edu/pulsar/K1JT/wsjtx-doc/wsjtx-main-toc2.html + +== OPTIONS +Refer to the *hamlib* documentation. diff --git a/manpages/man1/rigctld-wsjtx.1.txt b/manpages/man1/rigctld-wsjtx.1.txt index 6cf9ebfc1..375aefaa6 100644 --- a/manpages/man1/rigctld-wsjtx.1.txt +++ b/manpages/man1/rigctld-wsjtx.1.txt @@ -6,7 +6,7 @@ == NAME -rigctld-wsjtx - Hamlib 3 rigctld server. +rigctld-wsjtx - Hamlib 4 rigctld server. == SYNOPSIS @@ -15,7 +15,7 @@ rigctld-wsjtx - Hamlib 3 rigctld server. == DESCRIPTION *wsjtx* uses a version of the *hamlib* CAT control library. This - library is heavily modified over the current release version of + library is virtually identical to the current release version of *hamlib*. If a *wsjtx* user wishes to use the *hamlib* network rig server *rigctld* to remotely control their transceiver; then this special version of *rigctld* should be used since that too has the From 4920a6aeeefc2130b444f832e9a7b529fcda9e0f Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Wed, 30 Jan 2019 11:10:02 +0000 Subject: [PATCH 04/11] Remove temporary FT8 v2 working frequencies from defaults list --- models/FrequencyList.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/models/FrequencyList.cpp b/models/FrequencyList.cpp index 57f850eb7..e57977d1d 100644 --- a/models/FrequencyList.cpp +++ b/models/FrequencyList.cpp @@ -68,7 +68,6 @@ namespace {7038600, Modes::WSPR, IARURegions::ALL}, {7074000, Modes::FT8, IARURegions::ALL}, {7076000, Modes::JT65, IARURegions::ALL}, - {7078000, Modes::FT8, IARURegions::ALL}, {7078000, Modes::JT9, IARURegions::ALL}, {10136000, Modes::FT8, IARURegions::ALL}, @@ -79,7 +78,6 @@ namespace {14095600, Modes::WSPR, IARURegions::ALL}, {14074000, Modes::FT8, IARURegions::ALL}, {14076000, Modes::JT65, IARURegions::ALL}, - {14078000, Modes::FT8, IARURegions::ALL}, {14078000, Modes::JT9, IARURegions::ALL}, {18100000, Modes::FT8, IARURegions::ALL}, From 846918e3aa94e6d3897abe8b8c7aa39181a907bb Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Sat, 2 Feb 2019 20:38:56 +0000 Subject: [PATCH 05/11] Remove normal mouse double-click action for decode display windows The normal action selects the word under the mouse pointer and this disrupts callsign highlighting. Normal single-click selection behaviour is unchanged so text can still be selected using the mouse and copied if desired. --- widgets/displaytext.cpp | 1 - widgets/displaytext.h | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/widgets/displaytext.cpp b/widgets/displaytext.cpp index 8058908ba..ca3372f48 100644 --- a/widgets/displaytext.cpp +++ b/widgets/displaytext.cpp @@ -82,7 +82,6 @@ void DisplayText::setContentFont(QFont const& font) void DisplayText::mouseDoubleClickEvent(QMouseEvent *e) { Q_EMIT selectCallsign(e->modifiers ()); - QTextEdit::mouseDoubleClickEvent(e); } void DisplayText::insertLineSpacer(QString const& line) diff --git a/widgets/displaytext.h b/widgets/displaytext.h index feb599303..cd3426af9 100644 --- a/widgets/displaytext.h +++ b/widgets/displaytext.h @@ -44,10 +44,9 @@ public: Q_SLOT void erase (); Q_SLOT void highlight_callsign (QString const& callsign, QColor const& bg, QColor const& fg, bool last_only); -protected: - void mouseDoubleClickEvent(QMouseEvent *e); - private: + void mouseDoubleClickEvent (QMouseEvent *) override; + void extend_vertical_scrollbar (int min, int max); Configuration const * m_config; From 4dfc4685e9c80bc3d8f66c90834dcdd73eaaec0a Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Sun, 3 Feb 2019 00:49:35 +0000 Subject: [PATCH 06/11] Make the UDP protocol Clear (3) message two-way External servers can clear either or both of the Band Activity and Rx Frequency decodes windows. This was requested by Dave, AA6YQ, so that DX Lab Suite applications can clear old decodes on band changes to ensure that decode highlighing is consistent. --- MessageClient.cpp | 14 +++++++++++- MessageClient.hpp | 6 ++++- MessageServer.cpp | 14 +++++++++++- MessageServer.hpp | 5 ++++- NetworkMessage.hpp | 14 ++++++++++-- UDPExamples/BeaconsModel.cpp | 2 +- UDPExamples/BeaconsModel.hpp | 2 +- UDPExamples/ClientWidget.cpp | 25 +++++++++++++++++++-- UDPExamples/ClientWidget.hpp | 7 +++++- UDPExamples/DecodesModel.cpp | 2 +- UDPExamples/DecodesModel.hpp | 2 +- UDPExamples/MessageAggregatorMainWindow.cpp | 11 ++++----- widgets/mainwindow.cpp | 13 ++++++++++- 13 files changed, 98 insertions(+), 19 deletions(-) diff --git a/MessageClient.cpp b/MessageClient.cpp index 7ccfbc3ce..873ba1fd4 100644 --- a/MessageClient.cpp +++ b/MessageClient.cpp @@ -188,6 +188,18 @@ void MessageClient::impl::parse_message (QByteArray const& msg) } break; + case NetworkMessage::Clear: + { + quint8 window {0}; + in >> window; + TRACE_UDP ("Clear window:" << window); + if (check_status (in) != Fail) + { + Q_EMIT self_->clear_decodes (window); + } + } + break; + case NetworkMessage::Replay: TRACE_UDP ("Replay"); if (check_status (in) != Fail) @@ -477,7 +489,7 @@ void MessageClient::WSPR_decode (bool is_new, QTime time, qint32 snr, float delt } } -void MessageClient::clear_decodes () +void MessageClient::decodes_cleared () { if (m_->server_port_ && !m_->server_string_.isEmpty ()) { diff --git a/MessageClient.hpp b/MessageClient.hpp index 0deb1a49d..ca5f02037 100644 --- a/MessageClient.hpp +++ b/MessageClient.hpp @@ -59,7 +59,7 @@ public: Q_SLOT void WSPR_decode (bool is_new, QTime time, qint32 snr, float delta_time, Frequency , qint32 drift, QString const& callsign, QString const& grid, qint32 power , bool off_air); - Q_SLOT void clear_decodes (); + Q_SLOT void decodes_cleared (); Q_SLOT void qso_logged (QDateTime time_off, QString const& dx_call, QString const& dx_grid , Frequency dial_frequency, QString const& mode, QString const& report_sent , QString const& report_received, QString const& tx_power, QString const& comments @@ -80,6 +80,10 @@ public: // with send_raw_datagram() above) Q_SLOT void add_blocked_destination (QHostAddress const&); + // this signal is emitted if the server has requested a decode + // window clear action + Q_SIGNAL void clear_decodes (quint8 window); + // this signal is emitted if the server sends us a reply, the only // reply supported is reply to a prior CQ or QRZ message Q_SIGNAL void reply (QTime, qint32 snr, float delta_time, quint32 delta_frequency, QString const& mode diff --git a/MessageServer.cpp b/MessageServer.cpp index 491afdbdd..dadf3d984 100644 --- a/MessageServer.cpp +++ b/MessageServer.cpp @@ -224,7 +224,7 @@ void MessageServer::impl::parse_message (QHostAddress const& sender, port_type s break; case NetworkMessage::Clear: - Q_EMIT self_->clear_decodes (id); + Q_EMIT self_->decodes_cleared (id); break; case NetworkMessage::Status: @@ -455,6 +455,18 @@ void MessageServer::start (port_type port, QHostAddress const& multicast_group_a } } +void MessageServer::clear_decodes (QString const& id, quint8 window) +{ + auto iter = m_->clients_.find (id); + if (iter != std::end (m_->clients_)) + { + QByteArray message; + NetworkMessage::Builder out {&message, NetworkMessage::Clear, id, (*iter).negotiated_schema_number_}; + out << window; + m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_); + } +} + void MessageServer::reply (QString const& id, QTime time, qint32 snr, float delta_time , quint32 delta_frequency, QString const& mode , QString const& message_text, bool low_confidence, quint8 modifiers) diff --git a/MessageServer.hpp b/MessageServer.hpp index 4a6022bfd..12377bef9 100644 --- a/MessageServer.hpp +++ b/MessageServer.hpp @@ -41,6 +41,9 @@ public: Q_SLOT void start (port_type port, QHostAddress const& multicast_group_address = QHostAddress {}); + // ask the client to clear one or both of the decode windows + Q_SLOT void clear_decodes (QString const& id, quint8 window = 0); + // ask the client with identification 'id' to make the same action // as a double click on the decode would // @@ -91,7 +94,7 @@ public: , QString const& name, QDateTime time_on, QString const& operator_call , QString const& my_call, QString const& my_grid , QString const& exchange_sent, QString const& exchange_rcvd); - Q_SIGNAL void clear_decodes (QString const& id); + Q_SIGNAL void decodes_cleared (QString const& id); Q_SIGNAL void logged_ADIF (QString const& id, QByteArray const& ADIF); // this signal is emitted when a network error occurs diff --git a/NetworkMessage.hpp b/NetworkMessage.hpp index a4d9ba0d5..4783bb369 100644 --- a/NetworkMessage.hpp +++ b/NetworkMessage.hpp @@ -186,16 +186,26 @@ * back a .WAV file. * * - * Clear Out 3 quint32 + * Clear Out/In 3 quint32 * Id (unique key) utf8 + * Window quint8 (In only) * * This message is send when all prior "Decode" messages in the - * "Band activity" window have been discarded and therefore are + * "Band Activity" window have been discarded and therefore are * no long available for actioning with a "Reply" message. It is * sent when the user erases the "Band activity" window and when * WSJT-X closes down normally. The server should discard all * decode messages upon receipt of this message. * + * It may also be sent to a WSJT-X instance in which case it + * clears one or both of the "Band Activity" and "Rx Frequency" + * windows. The Window argument can be one of the following + * values: + * + * 0 - clear the "Band Activity" window (default) + * 1 - clear the "Rx Frequency" window + * 2 - clear both "Band Activity" and "Rx Frequency" windows + * * * Reply In 4 quint32 * Id (target unique key) utf8 diff --git a/UDPExamples/BeaconsModel.cpp b/UDPExamples/BeaconsModel.cpp index 8955ea26a..7ac1323a1 100644 --- a/UDPExamples/BeaconsModel.cpp +++ b/UDPExamples/BeaconsModel.cpp @@ -121,7 +121,7 @@ void BeaconsModel::add_beacon_spot (bool is_new, QString const& client_id, QTime appendRow (make_row (client_id, time, snr, delta_time, frequency, drift, callsign, grid, power, off_air)); } -void BeaconsModel::clear_decodes (QString const& client_id) +void BeaconsModel::decodes_cleared (QString const& client_id) { for (auto row = rowCount () - 1; row >= 0; --row) { diff --git a/UDPExamples/BeaconsModel.hpp b/UDPExamples/BeaconsModel.hpp index 977a93ccc..b089349cc 100644 --- a/UDPExamples/BeaconsModel.hpp +++ b/UDPExamples/BeaconsModel.hpp @@ -32,7 +32,7 @@ public: Q_SLOT void add_beacon_spot (bool is_new, QString const& client_id, QTime time, qint32 snr, float delta_time , Frequency frequency, qint32 drift, QString const& callsign, QString const& grid , qint32 power, bool off_air); - Q_SLOT void clear_decodes (QString const& client_id); + Q_SLOT void decodes_cleared (QString const& client_id); }; #endif diff --git a/UDPExamples/ClientWidget.cpp b/UDPExamples/ClientWidget.cpp index 7423f6d81..1715670bf 100644 --- a/UDPExamples/ClientWidget.cpp +++ b/UDPExamples/ClientWidget.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "validators/MaidenheadLocatorValidator.hpp" @@ -120,6 +121,9 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod , id_ {id} , calls_of_interest_ {calls_of_interest} , decodes_proxy_model_ {id_} + , erase_action_ {new QAction {tr ("&Erase Band Activity"), this}} + , erase_rx_frequency_action_ {new QAction {tr ("Erase &Rx Frequency"), this}} + , erase_both_action_ {new QAction {tr ("Erase &Both"), this}} , decodes_table_view_ {new QTableView} , beacons_table_view_ {new QTableView} , message_line_edit_ {new QLineEdit} @@ -143,6 +147,10 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod decodes_table_view_->verticalHeader ()->hide (); decodes_table_view_->hideColumn (0); decodes_table_view_->horizontalHeader ()->setStretchLastSection (true); + decodes_table_view_->setContextMenuPolicy (Qt::ActionsContextMenu); + decodes_table_view_->insertAction (nullptr, erase_action_); + decodes_table_view_->insertAction (nullptr, erase_rx_frequency_action_); + decodes_table_view_->insertAction (nullptr, erase_both_action_); auto form_layout = new QFormLayout; form_layout->addRow (tr ("Free text:"), message_line_edit_); @@ -171,6 +179,8 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod beacons_table_view_->verticalHeader ()->hide (); beacons_table_view_->hideColumn (0); beacons_table_view_->horizontalHeader ()->setStretchLastSection (true); + beacons_table_view_->setContextMenuPolicy (Qt::ActionsContextMenu); + beacons_table_view_->insertAction (nullptr, erase_action_); auto beacons_page = new QWidget; auto beacons_layout = new QVBoxLayout {beacons_page}; @@ -219,8 +229,19 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod setAllowedAreas (Qt::BottomDockWidgetArea); setFloating (true); + // connect context menu actions + connect (erase_action_, &QAction::triggered, [this] (bool /*checked*/) { + Q_EMIT do_clear_decodes (id_); + }); + connect (erase_rx_frequency_action_, &QAction::triggered, [this] (bool /*checked*/) { + Q_EMIT do_clear_decodes (id_, 1); + }); + connect (erase_both_action_, &QAction::triggered, [this] (bool /*checked*/) { + Q_EMIT do_clear_decodes (id_, 2); + }); + // connect up table view signals - connect (decodes_table_view_, &QTableView::doubleClicked, this, [this] (QModelIndex const& index) { + connect (decodes_table_view_, &QTableView::doubleClicked, [this] (QModelIndex const& index) { Q_EMIT do_reply (decodes_proxy_model_.mapToSource (index), QApplication::keyboardModifiers () >> 24); }); @@ -313,7 +334,7 @@ void ClientWidget::beacon_spot_added (bool /*is_new*/, QString const& client_id, beacons_table_view_->scrollToBottom (); } -void ClientWidget::clear_decodes (QString const& client_id) +void ClientWidget::decodes_cleared (QString const& client_id) { if (client_id == id_) { diff --git a/UDPExamples/ClientWidget.hpp b/UDPExamples/ClientWidget.hpp index 0459ad036..22488531c 100644 --- a/UDPExamples/ClientWidget.hpp +++ b/UDPExamples/ClientWidget.hpp @@ -12,6 +12,7 @@ class QAbstractItemModel; class QModelIndex; class QColor; +class QAction; using Frequency = MessageServer::Frequency; @@ -41,8 +42,9 @@ public: , float delta_time, Frequency delta_frequency, qint32 drift , QString const& callsign, QString const& grid, qint32 power , bool off_air); - Q_SLOT void clear_decodes (QString const& client_id); + Q_SLOT void decodes_cleared (QString const& client_id); + Q_SIGNAL void do_clear_decodes (QString const& id, quint8 window = 0); Q_SIGNAL void do_reply (QModelIndex const&, quint8 modifier); Q_SIGNAL void do_halt_tx (QString const& id, bool auto_only); Q_SIGNAL void do_free_text (QString const& id, QString const& text, bool); @@ -74,6 +76,9 @@ private: QRegularExpression base_call_re_; int rx_df_; } decodes_proxy_model_; + QAction * erase_action_; + QAction * erase_rx_frequency_action_; + QAction * erase_both_action_; QTableView * decodes_table_view_; QTableView * beacons_table_view_; QLineEdit * message_line_edit_; diff --git a/UDPExamples/DecodesModel.cpp b/UDPExamples/DecodesModel.cpp index 88b071c11..6121ff31a 100644 --- a/UDPExamples/DecodesModel.cpp +++ b/UDPExamples/DecodesModel.cpp @@ -125,7 +125,7 @@ void DecodesModel::add_decode (bool is_new, QString const& client_id, QTime time , off_air, is_fast)); } -void DecodesModel::clear_decodes (QString const& client_id) +void DecodesModel::decodes_cleared (QString const& client_id) { for (auto row = rowCount () - 1; row >= 0; --row) { diff --git a/UDPExamples/DecodesModel.hpp b/UDPExamples/DecodesModel.hpp index fa51732bf..17c9ae125 100644 --- a/UDPExamples/DecodesModel.hpp +++ b/UDPExamples/DecodesModel.hpp @@ -34,7 +34,7 @@ public: Q_SLOT void add_decode (bool is_new, QString const& client_id, QTime time, qint32 snr, float delta_time , quint32 delta_frequency, QString const& mode, QString const& message , bool low_confidence, bool off_air, bool is_fast); - Q_SLOT void clear_decodes (QString const& client_id); + Q_SLOT void decodes_cleared (QString const& client_id); Q_SLOT void do_reply (QModelIndex const& source, quint8 modifiers); Q_SIGNAL void reply (QString const& id, QTime time, qint32 snr, float delta_time, quint32 delta_frequency diff --git a/UDPExamples/MessageAggregatorMainWindow.cpp b/UDPExamples/MessageAggregatorMainWindow.cpp index 0c2c51c30..e6937e6cb 100644 --- a/UDPExamples/MessageAggregatorMainWindow.cpp +++ b/UDPExamples/MessageAggregatorMainWindow.cpp @@ -181,8 +181,8 @@ MessageAggregatorMainWindow::MessageAggregatorMainWindow () }); connect (server_, &MessageServer::client_opened, this, &MessageAggregatorMainWindow::add_client); connect (server_, &MessageServer::client_closed, this, &MessageAggregatorMainWindow::remove_client); - connect (server_, &MessageServer::client_closed, decodes_model_, &DecodesModel::clear_decodes); - connect (server_, &MessageServer::client_closed, beacons_model_, &BeaconsModel::clear_decodes); + connect (server_, &MessageServer::client_closed, decodes_model_, &DecodesModel::decodes_cleared); + connect (server_, &MessageServer::client_closed, beacons_model_, &BeaconsModel::decodes_cleared); connect (server_, &MessageServer::decode, [this] (bool is_new, QString const& id, QTime time , qint32 snr, float delta_time , quint32 delta_frequency, QString const& mode @@ -191,8 +191,8 @@ MessageAggregatorMainWindow::MessageAggregatorMainWindow () decodes_model_->add_decode (is_new, id, time, snr, delta_time, delta_frequency, mode, message , low_confidence, off_air, dock_widgets_[id]->fast_mode ());}); connect (server_, &MessageServer::WSPR_decode, beacons_model_, &BeaconsModel::add_beacon_spot); - connect (server_, &MessageServer::clear_decodes, decodes_model_, &DecodesModel::clear_decodes); - connect (server_, &MessageServer::clear_decodes, beacons_model_, &BeaconsModel::clear_decodes); + connect (server_, &MessageServer::decodes_cleared, decodes_model_, &DecodesModel::decodes_cleared); + connect (server_, &MessageServer::decodes_cleared, beacons_model_, &BeaconsModel::decodes_cleared); connect (decodes_model_, &DecodesModel::reply, server_, &MessageServer::reply); // UI behaviour @@ -248,7 +248,8 @@ void MessageAggregatorMainWindow::add_client (QString const& id, QString const& connect (server_, &MessageServer::status_update, dock, &ClientWidget::update_status); connect (server_, &MessageServer::decode, dock, &ClientWidget::decode_added); connect (server_, &MessageServer::WSPR_decode, dock, &ClientWidget::beacon_spot_added); - connect (server_, &MessageServer::clear_decodes, dock, &ClientWidget::clear_decodes); + connect (server_, &MessageServer::decodes_cleared, dock, &ClientWidget::decodes_cleared); + connect (dock, &ClientWidget::do_clear_decodes, server_, &MessageServer::clear_decodes); connect (dock, &ClientWidget::do_reply, decodes_model_, &DecodesModel::do_reply); connect (dock, &ClientWidget::do_halt_tx, server_, &MessageServer::halt_tx); connect (dock, &ClientWidget::do_free_text, server_, &MessageServer::free_text); diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 5a6665716..83830fb40 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -487,6 +487,17 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, }); // Network message handlers + connect (m_messageClient, &MessageClient::clear_decodes, [this] (quint8 window) { + ++window; + if (window & 1) + { + ui->decodedTextBrowser->erase (); + } + if (window & 2) + { + ui->decodedTextBrowser2->erase (); + } + }); connect (m_messageClient, &MessageClient::reply, this, &MainWindow::replyToCQ); connect (m_messageClient, &MessageClient::replay, this, &MainWindow::replayDecodes); connect (m_messageClient, &MessageClient::location, this, &MainWindow::locationChange); @@ -3310,7 +3321,7 @@ void MainWindow::on_EraseButton_clicked () void MainWindow::band_activity_cleared () { - m_messageClient->clear_decodes (); + m_messageClient->decodes_cleared (); QFile f(m_config.temp_dir ().absoluteFilePath ("decoded.txt")); if(f.exists()) f.remove(); } From 7d66f442d66f7257648b8462f228952946656090 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Sun, 10 Feb 2019 20:43:51 +0000 Subject: [PATCH 07/11] Fix up some compiler errors when using gfortran v9 --- lib/ft8/chkcrc13a.f90 | 48 ++++++++++++++++---------------- lib/ft8/chkcrc14a.f90 | 48 ++++++++++++++++---------------- lib/ft8/extractmessage174.f90 | 4 +-- lib/ft8/extractmessage174_91.f90 | 4 +-- lib/msk40decodeframe.f90 | 2 +- 5 files changed, 53 insertions(+), 53 deletions(-) diff --git a/lib/ft8/chkcrc13a.f90 b/lib/ft8/chkcrc13a.f90 index 74259cfcf..25e145842 100644 --- a/lib/ft8/chkcrc13a.f90 +++ b/lib/ft8/chkcrc13a.f90 @@ -1,24 +1,24 @@ -subroutine chkcrc13a(decoded,nbadcrc) - - use crc - integer*1 decoded(90) - integer*1, target:: i1Dec8BitBytes(12) - character*90 cbits - -! Write decoded bits into cbits: 77-bit message plus 13-bit CRC - write(cbits,1000) decoded -1000 format(90i1) - read(cbits,1001) i1Dec8BitBytes -1001 format(12b8) - read(cbits,1002) ncrc13 !Received CRC13 -1002 format(77x,b13) - - i1Dec8BitBytes(10)=iand(i1Dec8BitBytes(10),128+64+32+16+8) - i1Dec8BitBytes(11:12)=0 - icrc13=crc13(c_loc(i1Dec8BitBytes),12) !CRC13 computed from 77 msg bits - - nbadcrc=1 - if(ncrc13.eq.icrc13) nbadcrc=0 - - return -end subroutine chkcrc13a +subroutine chkcrc13a(decoded,nbadcrc) + + use crc + integer*1 decoded(90) + integer*1, target:: i1Dec8BitBytes(12) + character*90 cbits + +! Write decoded bits into cbits: 77-bit message plus 13-bit CRC + write(cbits,1000) decoded +1000 format(90i1) + read(cbits,1001) i1Dec8BitBytes +1001 format(12b8) + read(cbits,1002) ncrc13 !Received CRC13 +1002 format(77x,b13) + + i1Dec8BitBytes(10)=iand(i1Dec8BitBytes(10),transfer(128+64+32+16+8,0_1)) + i1Dec8BitBytes(11:12)=0 + icrc13=crc13(c_loc(i1Dec8BitBytes),12) !CRC13 computed from 77 msg bits + + nbadcrc=1 + if(ncrc13.eq.icrc13) nbadcrc=0 + + return +end subroutine chkcrc13a diff --git a/lib/ft8/chkcrc14a.f90 b/lib/ft8/chkcrc14a.f90 index d9d3d7a4d..9ecda9b28 100644 --- a/lib/ft8/chkcrc14a.f90 +++ b/lib/ft8/chkcrc14a.f90 @@ -1,24 +1,24 @@ -subroutine chkcrc14a(decoded,nbadcrc) - - use crc - integer*1 decoded(91) - integer*1, target:: i1Dec8BitBytes(12) - character*91 cbits - -! Write decoded bits into cbits: 77-bit message plus 14-bit CRC - write(cbits,1000) decoded -1000 format(91i1) - read(cbits,1001) i1Dec8BitBytes -1001 format(12b8) - read(cbits,1002) ncrc14 !Received CRC14 -1002 format(77x,b14) - - i1Dec8BitBytes(10)=iand(i1Dec8BitBytes(10),128+64+32+16+8) - i1Dec8BitBytes(11:12)=0 - icrc14=crc14(c_loc(i1Dec8BitBytes),12) !CRC14 computed from 77 msg bits - - nbadcrc=1 - if(ncrc14.eq.icrc14) nbadcrc=0 - - return -end subroutine chkcrc14a +subroutine chkcrc14a(decoded,nbadcrc) + + use crc + integer*1 decoded(91) + integer*1, target:: i1Dec8BitBytes(12) + character*91 cbits + +! Write decoded bits into cbits: 77-bit message plus 14-bit CRC + write(cbits,1000) decoded +1000 format(91i1) + read(cbits,1001) i1Dec8BitBytes +1001 format(12b8) + read(cbits,1002) ncrc14 !Received CRC14 +1002 format(77x,b14) + + i1Dec8BitBytes(10)=iand(i1Dec8BitBytes(10),transfer(128+64+32+16+8,0_1)) + i1Dec8BitBytes(11:12)=0 + icrc14=crc14(c_loc(i1Dec8BitBytes),12) !CRC14 computed from 77 msg bits + + nbadcrc=1 + if(ncrc14.eq.icrc14) nbadcrc=0 + + return +end subroutine chkcrc14a diff --git a/lib/ft8/extractmessage174.f90 b/lib/ft8/extractmessage174.f90 index 4bac6694d..252547c43 100644 --- a/lib/ft8/extractmessage174.f90 +++ b/lib/ft8/extractmessage174.f90 @@ -17,7 +17,7 @@ subroutine extractmessage174(decoded,msgreceived,ncrcflag) read(cbits,1002) ncrc12 !Received CRC12 1002 format(75x,b12) - i1Dec8BitBytes(10)=iand(i1Dec8BitBytes(10),128+64+32) + i1Dec8BitBytes(10)=iand(i1Dec8BitBytes(10),transfer(128+64+32,0_1)) i1Dec8BitBytes(11)=0 icrc12=crc12(c_loc(i1Dec8BitBytes),11) !CRC12 computed from 75 msg bits @@ -26,7 +26,7 @@ subroutine extractmessage174(decoded,msgreceived,ncrcflag) do ibyte=1,12 itmp=0 do ibit=1,6 - itmp=ishft(itmp,1)+iand(1,decoded((ibyte-1)*6+ibit)) + itmp=ishft(itmp,1)+iand(1_1,decoded((ibyte-1)*6+ibit)) enddo i4Dec6BitWords(ibyte)=itmp enddo diff --git a/lib/ft8/extractmessage174_91.f90 b/lib/ft8/extractmessage174_91.f90 index d2d3019eb..ecc142469 100644 --- a/lib/ft8/extractmessage174_91.f90 +++ b/lib/ft8/extractmessage174_91.f90 @@ -17,7 +17,7 @@ subroutine extractmessage174_91(decoded,msgreceived,ncrcflag) read(cbits,1002) ncrc14 !Received CRC12 1002 format(77x,b14) - i1Dec8BitBytes(10)=iand(i1Dec8BitBytes(10),128+64+32+16+8) + i1Dec8BitBytes(10)=iand(i1Dec8BitBytes(10),transfer(128+64+32+16+8,0_1)) i1Dec8BitBytes(11:12)=0 icrc14=crc14(c_loc(i1Dec8BitBytes),12) !CRC12 computed from 75 msg bits @@ -26,7 +26,7 @@ subroutine extractmessage174_91(decoded,msgreceived,ncrcflag) do ibyte=1,12 itmp=0 do ibit=1,6 - itmp=ishft(itmp,1)+iand(1,decoded((ibyte-1)*6+ibit)) + itmp=ishft(itmp,1)+iand(1_1,decoded((ibyte-1)*6+ibit)) enddo i4Dec6BitWords(ibyte)=itmp enddo diff --git a/lib/msk40decodeframe.f90 b/lib/msk40decodeframe.f90 index 2023412ec..277f89de2 100644 --- a/lib/msk40decodeframe.f90 +++ b/lib/msk40decodeframe.f90 @@ -128,7 +128,7 @@ subroutine msk40decodeframe(c,mycall,hiscall,xsnr,bswl,nhasharray, & imsg=0 do i=1,16 - imsg=ishft(imsg,1)+iand(1,decoded(17-i)) + imsg=ishft(imsg,1)+iand(1_1,decoded(17-i)) enddo nrxrpt=iand(imsg,15) nrxhash=(imsg-nrxrpt)/16 From 7ec99d2a7571e5befbc0406e9ba57387d528ed4b Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Sun, 10 Feb 2019 22:08:04 +0000 Subject: [PATCH 08/11] Update CMakeLists.txt to reflect the oldest supported macOS version (10.10) --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 71a59a5fe..1ab053397 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,10 +10,10 @@ if (APPLE) # # otool -l | grep -A3 LC_VERSION_MIN_MACOSX # - set (CMAKE_OSX_DEPLOYMENT_TARGET 10.9 + set (CMAKE_OSX_DEPLOYMENT_TARGET 10.10 CACHE STRING "Earliest version of OS X supported -Earliest version we can support with Qt 5.8, C++11 & libc++ is 10.9. +Earliest version we can support with Qt 5.8, C++11 & libc++ is 10.10. Do not override this if you intend to build an official deployable installer.") set (CMAKE_OSX_SYSROOT /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk CACHE STRING "Mac OS X SDK to build with From 8a1ff9e1a3f2242493cf0c02e126081bfdf5c913 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Wed, 13 Feb 2019 18:47:31 +0000 Subject: [PATCH 09/11] Reset QSO start time when aborting a QSO or clearing messages (ESC and F4) --- widgets/mainwindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 83830fb40..47b2e3bb6 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -5051,6 +5051,7 @@ void MainWindow::TxAgain() void MainWindow::clearDX () { + set_dateTimeQSO (-1); if (m_QSOProgress != CALLING) { auto_tx_mode (false); From fd7bca080a7282d56baa338bff2c518ec91934af Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Wed, 20 Feb 2019 19:40:36 +0000 Subject: [PATCH 10/11] Revert to direct Log QSO invocation rather than deferred for Fox logging Deferring using a 0mS timer does not work for Fox logging as it may be invoked more than once in a loop when multiple streams are active. This change directly calls the logging member function(s) inside the message processing loop. This change was initially made because an unhelpful database query order along with a need to display the latest logged QSO at the bottom of the Fox log table view was causing GUI unresponsiveness when the Fox log table had many rows. The current implementation shows the Fox log table view in descending time order with the latest logged QSO at the top which avoids this unresponsiveness issue (tested with a Fox log table with over 15,000 rows). --- widgets/mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 47b2e3bb6..e1491bdb7 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -8266,7 +8266,7 @@ list2Done: { writeFoxQSO (QString {" Log: %1 %2 %3 %4 %5"}.arg (m_hisCall).arg (m_hisGrid) .arg (m_rptSent).arg (m_rptRcvd).arg (m_lastBand)); - logQSOTimer.start(0); + on_logQSOButton_clicked (); m_foxRateQueue.enqueue (now); //Add present time in seconds //to Rate queue. } From b5a84ba4d071bfd076b31182b7c2f2456c75ff6a Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Mon, 25 Feb 2019 00:37:34 +0000 Subject: [PATCH 11/11] Update version number for v2.0.1 GA --- Versions.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Versions.cmake b/Versions.cmake index 7c1d68841..450a98894 100644 --- a/Versions.cmake +++ b/Versions.cmake @@ -3,4 +3,4 @@ set (WSJTX_VERSION_MAJOR 2) set (WSJTX_VERSION_MINOR 0) set (WSJTX_VERSION_PATCH 1) set (WSJTX_RC 1) # release candidate number, comment out or zero for development versions -set (WSJTX_VERSION_IS_RELEASE 0) # set to 1 for final release build +set (WSJTX_VERSION_IS_RELEASE 1) # set to 1 for final release build