From a87f3d3a2732727cd47ec0a2e9ff13aedf80afdb Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Tue, 13 Nov 2018 18:55:46 +0000 Subject: [PATCH 01/27] Bump RC number to 5 --- Versions.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Versions.cmake b/Versions.cmake index c21a34d08..3ba5db895 100644 --- a/Versions.cmake +++ b/Versions.cmake @@ -2,5 +2,5 @@ set (WSJTX_VERSION_MAJOR 2) set (WSJTX_VERSION_MINOR 0) set (WSJTX_VERSION_PATCH 0) -set (WSJTX_RC 4) # release candidate number, comment out or zero for development versions +set (WSJTX_RC 5) # release candidate number, comment out or zero for development versions set (WSJTX_VERSION_IS_RELEASE 0) # set to 1 for final release build From dd9da021dde283714c55b1c413191391d1c47357 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Wed, 14 Nov 2018 03:03:12 +0000 Subject: [PATCH 02/27] Fix badly broken worked before database indexing --- logbook/WorkedBefore.cpp | 204 ++++++++++++++++++++++++++------------- qt_helpers.hpp | 21 ++++ 2 files changed, 157 insertions(+), 68 deletions(-) diff --git a/logbook/WorkedBefore.cpp b/logbook/WorkedBefore.cpp index b6cd9eca8..41723e003 100644 --- a/logbook/WorkedBefore.cpp +++ b/logbook/WorkedBefore.cpp @@ -1,18 +1,34 @@ #include "WorkedBefore.hpp" +#include +#include #include +#include #include #include +#include +#include #include #include #include #include #include +#include +#include "qt_helpers.hpp" #include "pimpl_impl.hpp" using namespace boost::multi_index; +// hash function for QString members in hashed indexes +inline +std::size_t hash_value (QString const& s) +{ + return std::hash {} (s); +} + +// // worked before set element +// struct worked_entry { explicit worked_entry (QString const& call, QString const& grid, QString const& band @@ -39,7 +55,52 @@ struct worked_entry int ITU_zone_; }; -// less then predidate for the Continent enum class +bool operator == (worked_entry const& lhs, worked_entry const& rhs) +{ + return + lhs.continent_ == rhs.continent_ // check 1st as it is fast + && lhs.CQ_zone_ == rhs.CQ_zone_ // ditto + && lhs.ITU_zone_ == rhs.ITU_zone_ // ditto + && lhs.call_ == rhs.call_ // check the rest in decreasing + && lhs.grid_ == rhs.grid_ // domain size order to shortcut + && lhs.country_ == rhs.country_ // differences as quickly as possible + && lhs.band_ == rhs.band_ + && lhs.mode_ == rhs.mode_; +} + +std::size_t hash_value (worked_entry const& we) +{ + std::size_t seed {0}; + boost::hash_combine (seed, we.call_); + boost::hash_combine (seed, we.grid_); + boost::hash_combine (seed, we.band_); + boost::hash_combine (seed, we.mode_); + boost::hash_combine (seed, we.country_); + boost::hash_combine (seed, we.continent_); + boost::hash_combine (seed, we.CQ_zone_); + boost::hash_combine (seed, we.ITU_zone_); + return seed; +} + +#if !defined (QT_NO_DEBUG_STREAM) +QDebug operator << (QDebug dbg, worked_entry const& e) +{ + QDebugStateSaver saver {dbg}; + dbg.nospace () << "worked_entry(" + << e.call_ << ", " + << e.grid_ << ", " + << e.band_ << ", " + << e.mode_ << ", " + << e.country_ << ", " + << e.continent_ << ", " + << e.CQ_zone_ << ", " + << e.ITU_zone_ << ')'; + return dbg; +} +#endif + +// less then predidate for the Continent enum class, needed for +// ordered indexes struct Continent_less { bool operator () (AD1CCty::Continent lhs, AD1CCty::Continent rhs) const @@ -48,7 +109,7 @@ struct Continent_less } }; -// tags +// index tags struct call_mode_band {}; struct call_band {}; struct grid_mode_band {}; @@ -62,85 +123,92 @@ struct CQ_zone_band {}; struct ITU_zone_mode_band {}; struct ITU_zone_band {}; -// set with multiple ordered unique indexes that allow for efficient -// determination of various categories of worked before status +// set with multiple ordered unique indexes that allow for optimally +// efficient determination of various categories of worked before +// status typedef multi_index_container< worked_entry, indexed_by< + // basic unordered set constraint - we don't need duplicate worked entries + hashed_unique>, + + // + // The following indexes are used to discover worked before stuff. + // + // They are ordered so as to support partial lookups and + // non-unique because container inserts must be valid for all + // indexes. + // + // call+mode+band - ordered_unique, - composite_key, - member, - member > >, + ordered_non_unique, + composite_key, + member, + member > >, // call+band - ordered_unique, - composite_key, - member > >, + ordered_non_unique, + composite_key, + member > >, // grid+mode+band - ordered_unique, - composite_key, - member, - member > >, + ordered_non_unique, + composite_key, + member, + member > >, // grid+band - ordered_unique, - composite_key, - member > >, + ordered_non_unique, + composite_key, + member > >, // country+mode+band - ordered_unique, - composite_key, - member, - member > >, + ordered_non_unique, + composite_key, + member, + member > >, // country+band - ordered_unique, - composite_key, - member > >, + ordered_non_unique, + composite_key, + member > >, // continent+mode+band - ordered_unique, - composite_key, - member, - member >, - composite_key_compare< - Continent_less, - std::less, - std::less > >, + ordered_non_unique, + composite_key, + member, + member >, + composite_key_compare, std::less > >, // continent+band - ordered_unique, - composite_key, - member >, - composite_key_compare< - Continent_less, - std::less > >, + ordered_non_unique, + composite_key, + member >, + composite_key_compare > >, // CQ-zone+mode+band - ordered_unique, - composite_key, - member, - member > >, + ordered_non_unique, + composite_key, + member, + member > >, // CQ-zone+band - ordered_unique, - composite_key, - member > >, + ordered_non_unique, + composite_key, + member > >, // ITU-zone+mode+band - ordered_unique, - composite_key, - member, - member > >, + ordered_non_unique, + composite_key, + member, + member > >, // ITU-zone+band - ordered_unique, - composite_key, - member > > > - > worked_type; + ordered_non_unique, + composite_key, + member > > > + > worked_before_database_type; namespace { @@ -188,7 +256,7 @@ public: QString path_; AD1CCty prefixes_; - worked_type worked_; + worked_before_database_type worked_; }; WorkedBefore::WorkedBefore () @@ -196,7 +264,7 @@ WorkedBefore::WorkedBefore () QFile inputFile {m_->path_}; if (inputFile.open (QFile::ReadOnly)) { - QTextStream in(&inputFile); + QTextStream in {&inputFile}; QString buffer; bool pre_read {false}; int end_position {-1}; diff --git a/qt_helpers.hpp b/qt_helpers.hpp index 0e9439a8a..b2ec7a49f 100644 --- a/qt_helpers.hpp +++ b/qt_helpers.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -77,6 +78,26 @@ public: } }; +namespace std +{ + // std::hash<> specialization for QString based on the dbj2 + // algorithm http://www.cse.yorku.ca/~oz/hash.html because qHash() + // is poor on 64-bit platforms due to being a 32-bit hash value + template<> + struct hash + { + std::size_t operator () (QString const& s) const noexcept + { + std::size_t hash {5381}; + for (int i = 0; i < s.size (); ++i) + { + hash = ((hash << 5) + hash) + ((s.at (i).row () << 8) | s.at (i).cell ()); + } + return hash; + } + }; +} + // Register some useful Qt types with QMetaType Q_DECLARE_METATYPE (QHostAddress); From 982b76da54fc266feca90938d406cfb07bfeba8a Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Wed, 14 Nov 2018 03:35:36 +0000 Subject: [PATCH 03/27] Update Linux packaging dependencies Notably setting the Qt minimum version to 5.5 and adding the Debian libqt5sql5-sqlite package dependency. --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c684ab4aa..39411f8a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1708,11 +1708,11 @@ endif () set (CPACK_DEBIAN_PACKAGE_DESCRIPTION "${PROJECT_DESCRIPTION}") set (CPACK_DEBIAN_PACKAGE_HOMEPAGE "${PROJECT_HOMEPAGE}") -set (CPACK_DEBIAN_PACKAGE_DEPENDS "libgfortran3 (>=4.8.2), libqt5serialport5 (>=5.2), libqt5multimedia5-plugins (>=5.2), libqt5widgets5 (>=5.2), libusb-1.0-0, libudev1, libc6 (>=2.19)") +set (CPACK_DEBIAN_PACKAGE_DEPENDS "libgfortran3 (>=4.8.2), libqt5serialport5 (>=5.5), libqt5multimedia5-plugins (>=5.5), libqt5widgets5 (>=5.5), libqt5sql5-sqlite (>=5.5), libusb-1.0-0, libudev1, libc6 (>=2.19)") set (CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) set (CPACK_RPM_PACKAGE_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) -set (CPACK_RPM_PACKAGE_REQUIRES "qt5-qtserialport >= 5.2, qt5-qtmultimedia >= 5.2, qt5-qtsvg, libusb, systemd-udev, glibc >= 2, libgfortran >= 4.8.2") +set (CPACK_RPM_PACKAGE_REQUIRES "qt5-qtserialport >= 5.5, qt5-qtmultimedia >= 5.5, qt5-qtsvg, libusb, systemd-udev, glibc >= 2, libgfortran >= 4.8.2") set (CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION /usr/share/pixmaps /usr/share/applications /usr/share/man /usr/share/man1) configure_file ("${PROJECT_SOURCE_DIR}/CMakeCPackOptions.cmake.in" From b842ab74164d7dc23c4f151ac4f441b16e27185e Mon Sep 17 00:00:00 2001 From: Steve Franke Date: Wed, 14 Nov 2018 07:09:59 -0600 Subject: [PATCH 04/27] Fix a bug that caused many false decodes in JT65 VHF/UHF operation, when AP decoding was enabled. --- lib/sync65.f90 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/sync65.f90 b/lib/sync65.f90 index e0bc924a0..4a681a0a7 100644 --- a/lib/sync65.f90 +++ b/lib/sync65.f90 @@ -57,8 +57,9 @@ subroutine sync65(nfa,nfb,naggressive,ntol,nqsym,ca,ncand,nrobust, & freq=i*df itry=0 ! if(naggressive.gt.0 .and. ntol.lt.1000 .and. ccfmax.ge.thresh0) then - if(naggressive.gt.0 .and. ccfmax.ge.thresh0) then - if(i.ne.ipk) cycle +! if(naggressive.gt.0 .and. ccfmax.ge.thresh0) then + if(bVHF) then + if(i.ne.ipk .or. ccfmax.lt.thresh0) cycle itry=1 ncand=ncand+1 else From 43a5d7b5198acf31c527d3cc43da4264af2cd399 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 14 Nov 2018 10:44:36 -0500 Subject: [PATCH 05/27] Starting branch hotfix-2.0.0-rc5. --- CMakeLists.txt | 4 +- Versions.cmake | 4 +- lib/sync65.f90 | 5 +- logbook/WorkedBefore.cpp | 204 ++++++++++++++++++++++++++------------- qt_helpers.hpp | 21 ++++ 5 files changed, 164 insertions(+), 74 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c684ab4aa..39411f8a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1708,11 +1708,11 @@ endif () set (CPACK_DEBIAN_PACKAGE_DESCRIPTION "${PROJECT_DESCRIPTION}") set (CPACK_DEBIAN_PACKAGE_HOMEPAGE "${PROJECT_HOMEPAGE}") -set (CPACK_DEBIAN_PACKAGE_DEPENDS "libgfortran3 (>=4.8.2), libqt5serialport5 (>=5.2), libqt5multimedia5-plugins (>=5.2), libqt5widgets5 (>=5.2), libusb-1.0-0, libudev1, libc6 (>=2.19)") +set (CPACK_DEBIAN_PACKAGE_DEPENDS "libgfortran3 (>=4.8.2), libqt5serialport5 (>=5.5), libqt5multimedia5-plugins (>=5.5), libqt5widgets5 (>=5.5), libqt5sql5-sqlite (>=5.5), libusb-1.0-0, libudev1, libc6 (>=2.19)") set (CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) set (CPACK_RPM_PACKAGE_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) -set (CPACK_RPM_PACKAGE_REQUIRES "qt5-qtserialport >= 5.2, qt5-qtmultimedia >= 5.2, qt5-qtsvg, libusb, systemd-udev, glibc >= 2, libgfortran >= 4.8.2") +set (CPACK_RPM_PACKAGE_REQUIRES "qt5-qtserialport >= 5.5, qt5-qtmultimedia >= 5.5, qt5-qtsvg, libusb, systemd-udev, glibc >= 2, libgfortran >= 4.8.2") set (CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION /usr/share/pixmaps /usr/share/applications /usr/share/man /usr/share/man1) configure_file ("${PROJECT_SOURCE_DIR}/CMakeCPackOptions.cmake.in" diff --git a/Versions.cmake b/Versions.cmake index 6cb4d89c2..3ba5db895 100644 --- a/Versions.cmake +++ b/Versions.cmake @@ -1,6 +1,6 @@ # Version number components 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_PATCH 0) +set (WSJTX_RC 5) # release candidate number, comment out or zero for development versions set (WSJTX_VERSION_IS_RELEASE 0) # set to 1 for final release build diff --git a/lib/sync65.f90 b/lib/sync65.f90 index e0bc924a0..4a681a0a7 100644 --- a/lib/sync65.f90 +++ b/lib/sync65.f90 @@ -57,8 +57,9 @@ subroutine sync65(nfa,nfb,naggressive,ntol,nqsym,ca,ncand,nrobust, & freq=i*df itry=0 ! if(naggressive.gt.0 .and. ntol.lt.1000 .and. ccfmax.ge.thresh0) then - if(naggressive.gt.0 .and. ccfmax.ge.thresh0) then - if(i.ne.ipk) cycle +! if(naggressive.gt.0 .and. ccfmax.ge.thresh0) then + if(bVHF) then + if(i.ne.ipk .or. ccfmax.lt.thresh0) cycle itry=1 ncand=ncand+1 else diff --git a/logbook/WorkedBefore.cpp b/logbook/WorkedBefore.cpp index b6cd9eca8..41723e003 100644 --- a/logbook/WorkedBefore.cpp +++ b/logbook/WorkedBefore.cpp @@ -1,18 +1,34 @@ #include "WorkedBefore.hpp" +#include +#include #include +#include #include #include +#include +#include #include #include #include #include #include +#include +#include "qt_helpers.hpp" #include "pimpl_impl.hpp" using namespace boost::multi_index; +// hash function for QString members in hashed indexes +inline +std::size_t hash_value (QString const& s) +{ + return std::hash {} (s); +} + +// // worked before set element +// struct worked_entry { explicit worked_entry (QString const& call, QString const& grid, QString const& band @@ -39,7 +55,52 @@ struct worked_entry int ITU_zone_; }; -// less then predidate for the Continent enum class +bool operator == (worked_entry const& lhs, worked_entry const& rhs) +{ + return + lhs.continent_ == rhs.continent_ // check 1st as it is fast + && lhs.CQ_zone_ == rhs.CQ_zone_ // ditto + && lhs.ITU_zone_ == rhs.ITU_zone_ // ditto + && lhs.call_ == rhs.call_ // check the rest in decreasing + && lhs.grid_ == rhs.grid_ // domain size order to shortcut + && lhs.country_ == rhs.country_ // differences as quickly as possible + && lhs.band_ == rhs.band_ + && lhs.mode_ == rhs.mode_; +} + +std::size_t hash_value (worked_entry const& we) +{ + std::size_t seed {0}; + boost::hash_combine (seed, we.call_); + boost::hash_combine (seed, we.grid_); + boost::hash_combine (seed, we.band_); + boost::hash_combine (seed, we.mode_); + boost::hash_combine (seed, we.country_); + boost::hash_combine (seed, we.continent_); + boost::hash_combine (seed, we.CQ_zone_); + boost::hash_combine (seed, we.ITU_zone_); + return seed; +} + +#if !defined (QT_NO_DEBUG_STREAM) +QDebug operator << (QDebug dbg, worked_entry const& e) +{ + QDebugStateSaver saver {dbg}; + dbg.nospace () << "worked_entry(" + << e.call_ << ", " + << e.grid_ << ", " + << e.band_ << ", " + << e.mode_ << ", " + << e.country_ << ", " + << e.continent_ << ", " + << e.CQ_zone_ << ", " + << e.ITU_zone_ << ')'; + return dbg; +} +#endif + +// less then predidate for the Continent enum class, needed for +// ordered indexes struct Continent_less { bool operator () (AD1CCty::Continent lhs, AD1CCty::Continent rhs) const @@ -48,7 +109,7 @@ struct Continent_less } }; -// tags +// index tags struct call_mode_band {}; struct call_band {}; struct grid_mode_band {}; @@ -62,85 +123,92 @@ struct CQ_zone_band {}; struct ITU_zone_mode_band {}; struct ITU_zone_band {}; -// set with multiple ordered unique indexes that allow for efficient -// determination of various categories of worked before status +// set with multiple ordered unique indexes that allow for optimally +// efficient determination of various categories of worked before +// status typedef multi_index_container< worked_entry, indexed_by< + // basic unordered set constraint - we don't need duplicate worked entries + hashed_unique>, + + // + // The following indexes are used to discover worked before stuff. + // + // They are ordered so as to support partial lookups and + // non-unique because container inserts must be valid for all + // indexes. + // + // call+mode+band - ordered_unique, - composite_key, - member, - member > >, + ordered_non_unique, + composite_key, + member, + member > >, // call+band - ordered_unique, - composite_key, - member > >, + ordered_non_unique, + composite_key, + member > >, // grid+mode+band - ordered_unique, - composite_key, - member, - member > >, + ordered_non_unique, + composite_key, + member, + member > >, // grid+band - ordered_unique, - composite_key, - member > >, + ordered_non_unique, + composite_key, + member > >, // country+mode+band - ordered_unique, - composite_key, - member, - member > >, + ordered_non_unique, + composite_key, + member, + member > >, // country+band - ordered_unique, - composite_key, - member > >, + ordered_non_unique, + composite_key, + member > >, // continent+mode+band - ordered_unique, - composite_key, - member, - member >, - composite_key_compare< - Continent_less, - std::less, - std::less > >, + ordered_non_unique, + composite_key, + member, + member >, + composite_key_compare, std::less > >, // continent+band - ordered_unique, - composite_key, - member >, - composite_key_compare< - Continent_less, - std::less > >, + ordered_non_unique, + composite_key, + member >, + composite_key_compare > >, // CQ-zone+mode+band - ordered_unique, - composite_key, - member, - member > >, + ordered_non_unique, + composite_key, + member, + member > >, // CQ-zone+band - ordered_unique, - composite_key, - member > >, + ordered_non_unique, + composite_key, + member > >, // ITU-zone+mode+band - ordered_unique, - composite_key, - member, - member > >, + ordered_non_unique, + composite_key, + member, + member > >, // ITU-zone+band - ordered_unique, - composite_key, - member > > > - > worked_type; + ordered_non_unique, + composite_key, + member > > > + > worked_before_database_type; namespace { @@ -188,7 +256,7 @@ public: QString path_; AD1CCty prefixes_; - worked_type worked_; + worked_before_database_type worked_; }; WorkedBefore::WorkedBefore () @@ -196,7 +264,7 @@ WorkedBefore::WorkedBefore () QFile inputFile {m_->path_}; if (inputFile.open (QFile::ReadOnly)) { - QTextStream in(&inputFile); + QTextStream in {&inputFile}; QString buffer; bool pre_read {false}; int end_position {-1}; diff --git a/qt_helpers.hpp b/qt_helpers.hpp index 0e9439a8a..b2ec7a49f 100644 --- a/qt_helpers.hpp +++ b/qt_helpers.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -77,6 +78,26 @@ public: } }; +namespace std +{ + // std::hash<> specialization for QString based on the dbj2 + // algorithm http://www.cse.yorku.ca/~oz/hash.html because qHash() + // is poor on 64-bit platforms due to being a 32-bit hash value + template<> + struct hash + { + std::size_t operator () (QString const& s) const noexcept + { + std::size_t hash {5381}; + for (int i = 0; i < s.size (); ++i) + { + hash = ((hash << 5) + hash) + ((s.at (i).row () << 8) | s.at (i).cell ()); + } + return hash; + } + }; +} + // Register some useful Qt types with QMetaType Q_DECLARE_METATYPE (QHostAddress); From dd28dd2670938547f9417afdf77888e15b55623c Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 14 Nov 2018 11:33:46 -0500 Subject: [PATCH 06/27] Remove the Mouse Timer. Make the AutoSeq checkbox sticky, again. --- widgets/mainwindow.cpp | 27 +-------------------------- widgets/mainwindow.h | 5 ----- 2 files changed, 1 insertion(+), 31 deletions(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 19243df09..e7bfc9f7c 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -931,13 +931,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, ui->cbMenus->setChecked(true); ui->cbMenus->setChecked(false); } - - mouseLastPos=QCursor::pos(); - m_mouseIdleSeconds=0; - connect(&mouseTimer, &QTimer::timeout, this, &MainWindow::mouseTimerTick); - mouseTimer.start(1000); - - // this must be the last statement of constructor +// this must be the last statement of constructor if (!m_valid) throw std::runtime_error {"Fatal initialization exception"}; } @@ -962,22 +956,6 @@ void MainWindow::initialize_fonts () setDecodedTextFont (m_config.decoded_text_font ()); } -void MainWindow::mouseTimerTick() -{ - QPoint point = QCursor::pos(); - if(point != mouseLastPos) - m_mouseIdleSeconds = 0; - else - m_mouseIdleSeconds++; - mouseLastPos = point; -//Here we should do what's necessary when mouseIdleSeconds gets too big. -// qDebug() << m_mouseIdleSeconds; - if(ui->cbAutoSeq->isChecked() and m_mouseIdleSeconds>300) { - auto_tx_mode (false); - } -} - - void MainWindow::splash_done () { m_splash && m_splash->close (); @@ -1880,7 +1858,6 @@ void MainWindow::keyPressEvent (QKeyEvent * e) if((e->modifiers() & Qt::ControlModifier) and (e->modifiers() & Qt::ShiftModifier)) { m_bandEdited = true; band_changed(m_freqNominal-2000); -// qDebug() << "Down" << m_freqNominal; } else { n=11; if(e->modifiers() & Qt::ControlModifier) n+=100; @@ -1895,7 +1872,6 @@ void MainWindow::keyPressEvent (QKeyEvent * e) if((e->modifiers() & Qt::ControlModifier) and (e->modifiers() & Qt::ShiftModifier)) { m_bandEdited = true; band_changed(m_freqNominal+2000); -// qDebug() << "Up " << m_freqNominal; } else { n=12; if(e->modifiers() & Qt::ControlModifier) n+=100; @@ -5464,7 +5440,6 @@ void MainWindow::displayWidgets(qint64 n) if(i==32) ui->cbCQonly->setVisible(b); j=j>>1; } - if(!ui->cbAutoSeq->isVisible()) ui->cbAutoSeq->setChecked(false); b=SpecOp::EU_VHF==m_config.special_op_id() or (SpecOp::RTTY==m_config.special_op_id() and (m_config.RTTY_Exchange()=="#" or m_config.RTTY_Exchange()=="DX")); ui->sbSerialNumber->setVisible(b); diff --git a/widgets/mainwindow.h b/widgets/mainwindow.h index 2ef380e74..e5b9de9a8 100644 --- a/widgets/mainwindow.h +++ b/widgets/mainwindow.h @@ -446,7 +446,6 @@ private: qint32 m_Nslots=5; qint32 m_nFoxMsgTimes[5]={0,0,0,0,0}; qint32 m_tAutoOn; - qint32 m_mouseIdleSeconds; qint32 m_tFoxTx=0; qint32 m_tFoxTx0=0; qint32 m_maxStrikes=3; //Max # of repeats: 3 strikes and you're out @@ -570,9 +569,6 @@ private: QTimer minuteTimer; QTimer splashTimer; QTimer p1Timer; - QTimer mouseTimer; - - QPoint mouseLastPos; QString m_path; QString m_baseCall; @@ -701,7 +697,6 @@ private: void CQTxFreq(); void useNextCall(); void abortQSO(); - void mouseTimerTick(); bool isWorked(int itype, QString key, float fMHz=0, QString=""); QString save_wave_file (QString const& name From b7b30452b136ff378b102b46ad896d965f27b914 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Thu, 15 Nov 2018 01:28:19 +0000 Subject: [PATCH 07/27] Fix CMake Hamlib package finder on systems that use */lib64 LIBDIRS --- CMake/Modules/Findhamlib.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMake/Modules/Findhamlib.cmake b/CMake/Modules/Findhamlib.cmake index 1590f051d..7eebf1066 100644 --- a/CMake/Modules/Findhamlib.cmake +++ b/CMake/Modules/Findhamlib.cmake @@ -16,7 +16,7 @@ set (hamlib_LIBRARY_DIRS) # pkg-config? find_path (__hamlib_pc_path NAMES hamlib.pc - PATH_SUFFIXES lib/pkgconfig + PATH_SUFFIXES lib/pkgconfig lib64/pkgconfig ) if (__hamlib_pc_path) set (ENV{PKG_CONFIG_PATH} "${__hamlib_pc_path}" "$ENV{PKG_CONFIG_PATH}") From 44710d13ba5b95fcb3968ad957dd59040d7afc9c Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Thu, 15 Nov 2018 01:31:21 +0000 Subject: [PATCH 08/27] Workaround for a compiler bug on the ancient g++-4.8.5 --- qt_db_helpers.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qt_db_helpers.hpp b/qt_db_helpers.hpp index 3f159a889..da823b0cb 100644 --- a/qt_db_helpers.hpp +++ b/qt_db_helpers.hpp @@ -25,7 +25,7 @@ class ConditionalTransaction final { public: explicit ConditionalTransaction (QSqlTableModel& model) - : model_ {model} + : model_ (model) , submitted_ {false} { model_.database ().transaction (); From c3ba6b83b99733693872e497be9678656abcf6d5 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 16 Nov 2018 09:30:40 -0500 Subject: [PATCH 09/27] Add "No own call decodes" checkbox for WSPR mode. --- widgets/mainwindow.cpp | 7 +- widgets/mainwindow.ui | 855 +++++++++++++++++++++-------------------- 2 files changed, 437 insertions(+), 425 deletions(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index e7bfc9f7c..186f5f40c 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -1055,6 +1055,7 @@ void MainWindow::writeSettings() m_settings->setValue("RR73",m_send_RR73); m_settings->setValue ("WSPRPreferType1", ui->WSPR_prefer_type_1_check_box->isChecked ()); m_settings->setValue("UploadSpots",m_uploadSpots); + m_settings->setValue("NoOwnCall",ui->cbNoOwnCall->isChecked()); m_settings->setValue ("BandHopping", ui->band_hopping_group_box->isChecked ()); m_settings->setValue ("TRPeriod", ui->sbTR->value ()); m_settings->setValue("FastMode",m_bFastMode); @@ -1146,6 +1147,7 @@ void MainWindow::readSettings() ui->WSPR_prefer_type_1_check_box->setChecked (m_settings->value ("WSPRPreferType1", true).toBool ()); m_uploadSpots=m_settings->value("UploadSpots",false).toBool(); if(!m_uploadSpots) ui->cbUploadWSPR_Spots->setStyleSheet("QCheckBox{background-color: yellow}"); + ui->cbNoOwnCall->setChecked(m_settings->value("NoOwnCall",false).toBool()); ui->band_hopping_group_box->setChecked (m_settings->value ("BandHopping", false).toBool()); // setup initial value of tx attenuator m_block_pwr_tooltip = true; @@ -7241,6 +7243,10 @@ void MainWindow::p1ReadFromStdout() //p1readFromStdout QString t1; while(p1.canReadLine()) { QString t(p1.readLine()); + if(ui->cbNoOwnCall->isChecked()) { + if(t.contains(" " + m_config.my_callsign() + " ")) continue; + if(t.contains(" <" + m_config.my_callsign() + "> ")) continue; + } if(t.indexOf("") >= 0) { m_bDecoded = m_nWSPRdecodes > 0; if(!m_diskData) { @@ -7366,7 +7372,6 @@ void MainWindow::WSPR_history(Frequency dialFreq, int ndecodes) } } - void MainWindow::uploadSpots() { // do not spot replays or if rig control not working diff --git a/widgets/mainwindow.ui b/widgets/mainwindow.ui index fe9aa30bc..ee0342cdc 100644 --- a/widgets/mainwindow.ui +++ b/widgets/mainwindow.ui @@ -573,6 +573,430 @@ QLabel[oob="true"] { + + + + + 0 + 0 + + + + + 100 + 16777215 + + + + <html><head/><body><p>30dB recommended when only noise present<br/>Green when good<br/>Red when clipping may occur<br/>Yellow when too low</p></body></html> + + + QFrame::Panel + + + QFrame::Sunken + + + + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + + 252 + 252 + 252 + + + + + + + 159 + 175 + 213 + + + + + + + + + 252 + 252 + 252 + + + + + + + 159 + 175 + 213 + + + + + + + + + 159 + 175 + 213 + + + + + + + 159 + 175 + 213 + + + + + + + + true + + + DX Call + + + Qt::AlignCenter + + + 5 + + + 2 + + + + + + + + 0 + 0 + + + + + + + + + 252 + 252 + 252 + + + + + + + 159 + 175 + 213 + + + + + + + + + 252 + 252 + 252 + + + + + + + 159 + 175 + 213 + + + + + + + + + 159 + 175 + 213 + + + + + + + 159 + 175 + 213 + + + + + + + + true + + + DX Grid + + + Qt::AlignCenter + + + 5 + + + 2 + + + + + + + Callsign of station to be worked + + + + + + Qt::AlignCenter + + + + + + + Search for callsign in database + + + &Lookup + + + + + + + Locator of station to be worked + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + true + + + Az: 251 16553 km + + + Qt::AlignCenter + + + 4 + + + + + + + Add callsign and locator to database + + + Add + + + + + + + + + + Pwr + + + + + + + false + + + <html><head/><body><p>If orange or red there has been a rig control failure, click to reset and read the dial frequency. S implies split mode.</p></body></html> + + + QPushButton { + font-family: helvetica; + font-size: 9pt; + font-weight: bold; + background-color: white; + color: black; + border-style: solid; + border-width:1px; + border-radius:10px; + border-color: gray; + max-width:20px; + max-height:20px; + min-width:20px; + min-height:20px; +} +QPushButton[state="error"] { + background-color: red; +} +QPushButton[state="warning"] { + background-color: orange; +} +QPushButton[state="ok"] { + background-color: #00ff00; +} + + + ? + + + + + + + Adjust Tx audio level + + + 450 + + + 0 + + + Qt::Vertical + + + true + + + true + + + QSlider::TicksBelow + + + 50 + + + + + + + <html><head/><body><p>Select operating band or enter frequency in MHz or enter kHz increment followed by k.</p></body></html> + + + true + + + QComboBox::NoInsert + + + QComboBox::AdjustToMinimumContentsLength + + + + + + + + 0 + 0 + + + + QLabel { + font-family: MS Shell Dlg 2; + font-size: 16pt; + background-color : black; + color : yellow; +} + + + QFrame::StyledPanel + + + QFrame::Sunken + + + 2 + + + 0 + + + <html><head/><body><p align="center"> 2015 Jun 17 </p><p align="center"> 01:23:45 </p></body></html> + + + Qt::AlignCenter + + + 5 + + + @@ -2105,6 +2529,13 @@ list. The list can be maintained in Settings (F2). + + + + No own call decodes + + + @@ -2199,430 +2630,6 @@ list. The list can be maintained in Settings (F2). - - - - - 0 - 0 - - - - - 100 - 16777215 - - - - <html><head/><body><p>30dB recommended when only noise present<br/>Green when good<br/>Red when clipping may occur<br/>Yellow when too low</p></body></html> - - - QFrame::Panel - - - QFrame::Sunken - - - - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - - - - - 252 - 252 - 252 - - - - - - - 159 - 175 - 213 - - - - - - - - - 252 - 252 - 252 - - - - - - - 159 - 175 - 213 - - - - - - - - - 159 - 175 - 213 - - - - - - - 159 - 175 - 213 - - - - - - - - true - - - DX Call - - - Qt::AlignCenter - - - 5 - - - 2 - - - - - - - - 0 - 0 - - - - - - - - - 252 - 252 - 252 - - - - - - - 159 - 175 - 213 - - - - - - - - - 252 - 252 - 252 - - - - - - - 159 - 175 - 213 - - - - - - - - - 159 - 175 - 213 - - - - - - - 159 - 175 - 213 - - - - - - - - true - - - DX Grid - - - Qt::AlignCenter - - - 5 - - - 2 - - - - - - - Callsign of station to be worked - - - - - - Qt::AlignCenter - - - - - - - Locator of station to be worked - - - - - - Qt::AlignCenter - - - - - - - Search for callsign in database - - - &Lookup - - - - - - - Add callsign and locator to database - - - Add - - - - - - - - 0 - 0 - - - - true - - - Az: 251 16553 km - - - Qt::AlignCenter - - - 4 - - - - - - - - - - Pwr - - - - - - - false - - - <html><head/><body><p>If orange or red there has been a rig control failure, click to reset and read the dial frequency. S implies split mode.</p></body></html> - - - QPushButton { - font-family: helvetica; - font-size: 9pt; - font-weight: bold; - background-color: white; - color: black; - border-style: solid; - border-width:1px; - border-radius:10px; - border-color: gray; - max-width:20px; - max-height:20px; - min-width:20px; - min-height:20px; -} -QPushButton[state="error"] { - background-color: red; -} -QPushButton[state="warning"] { - background-color: orange; -} -QPushButton[state="ok"] { - background-color: #00ff00; -} - - - ? - - - - - - - Adjust Tx audio level - - - 450 - - - 0 - - - Qt::Vertical - - - true - - - true - - - QSlider::TicksBelow - - - 50 - - - - - - - <html><head/><body><p>Select operating band or enter frequency in MHz or enter kHz increment followed by k.</p></body></html> - - - true - - - QComboBox::NoInsert - - - QComboBox::AdjustToMinimumContentsLength - - - - - - - - 0 - 0 - - - - QLabel { - font-family: MS Shell Dlg 2; - font-size: 16pt; - background-color : black; - color : yellow; -} - - - QFrame::StyledPanel - - - QFrame::Sunken - - - 2 - - - 0 - - - <html><head/><body><p align="center"> 2015 Jun 17 </p><p align="center"> 01:23:45 </p></body></html> - - - Qt::AlignCenter - - - 5 - - - From c4d4097bb3da11f425c7d0e04a66a57823e49813 Mon Sep 17 00:00:00 2001 From: Steve Franke Date: Sat, 17 Nov 2018 07:19:08 -0600 Subject: [PATCH 10/27] Tweak a tooltip to remove reference to type 2 callsigns. --- widgets/mainwindow.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widgets/mainwindow.ui b/widgets/mainwindow.ui index ee0342cdc..8256eb606 100644 --- a/widgets/mainwindow.ui +++ b/widgets/mainwindow.ui @@ -1269,7 +1269,7 @@ QPushButton[state="ok"] { false - <html><head/><body><p>Check this to call CQ on the &quot;Tx CQ&quot; frequency. Rx will be on the current frequency and the CQ message wiill include the current Rx frequency so callers know which frequency to reply on.</p><p>Not available to type 2 compound callsign holders.</p></body></html> + <html><head/><body><p>Check this to call CQ on the &quot;Tx CQ&quot; frequency. Rx will be on the current frequency and the CQ message wiill include the current Rx frequency so callers know which frequency to reply on.</p><p>Not available to nonstandard callsign holders.</p></body></html> From e835adbc1a2341e4cb0e7077819567c866399382 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 19 Nov 2018 09:15:37 -0500 Subject: [PATCH 11/27] Dsable ToolTip on lbNextCall. Add text to the startup message box. --- widgets/mainwindow.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 186f5f40c..baf85ccad 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -735,6 +735,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, ui->labDXped->setStyleSheet("QLabel {background-color: red; color: white;}"); ui->labNextCall->setText(""); ui->labNextCall->setVisible(false); + ui->labNextCall->setToolTip(""); //### Possibly temporary ? ### for(int i=0; i<28; i++) { //Initialize dBm values float dbm=(10.0*i)/3.0 - 30.0; @@ -942,10 +943,16 @@ void MainWindow::not_GA_warning_message () MessageBox::critical_message (this, "This version of WSJT-X is a beta-level Release Candidate.\n\n" + "In FT8 and MSK144 modes it uses ONLY the new 77-bit\n" + "message formats. It will not decode 75-bit or 72-bit\n" + "messages.\n\n" + "On December 10, 2018, 77-bit messages will become the\n" + "standard. Everyone should upgrade to WSJT-X 2.0 by\n" + "January 1, 2019.\n\n" "On-the-air use carries an obligation to report problems\n" "to the WSJT Development group and to upgrade to a GA\n" "(General Availability) release when it becomes available.\n\n" - "This version cannot be used after December 31, 2018\n\n"); + "This version cannot be used after December 31, 2018.\n\n"); if(now.daysTo(timeout) < 0) Q_EMIT finished(); } From 6f966f613d6cb33b0a38184f3a888f8960e332ec Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Mon, 19 Nov 2018 21:18:41 -0500 Subject: [PATCH 12/27] Display and export contest log times in UTC, not local time --- item_delegates/DateTimeAsSecsSinceEpochDelegate.hpp | 2 +- models/CabrilloLog.cpp | 2 +- widgets/astro.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/item_delegates/DateTimeAsSecsSinceEpochDelegate.hpp b/item_delegates/DateTimeAsSecsSinceEpochDelegate.hpp index 66d3b6769..5b7f76eb8 100644 --- a/item_delegates/DateTimeAsSecsSinceEpochDelegate.hpp +++ b/item_delegates/DateTimeAsSecsSinceEpochDelegate.hpp @@ -28,7 +28,7 @@ public: static QDateTime to_date_time (QVariant const& value) { - return QDateTime::fromMSecsSinceEpoch (value.toULongLong () * 1000ull); + return QDateTime::fromMSecsSinceEpoch (value.toULongLong () * 1000ull, Qt::UTC); } QString displayText (QVariant const& value, QLocale const& locale) const override diff --git a/models/CabrilloLog.cpp b/models/CabrilloLog.cpp index 3e218d643..055f8ae07 100644 --- a/models/CabrilloLog.cpp +++ b/models/CabrilloLog.cpp @@ -128,7 +128,7 @@ void CabrilloLog::export_qsos (QTextStream& stream) const 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).toString ("yyyy-MM-dd hhmm")) + .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) diff --git a/widgets/astro.cpp b/widgets/astro.cpp index c0e2b1fe3..4ee55da4a 100644 --- a/widgets/astro.cpp +++ b/widgets/astro.cpp @@ -213,7 +213,7 @@ auto Astro::astroUpdate(QDateTime const& t, QString const& mygrid, QString const // we do the next period if we calculate just before it starts auto sec_since_epoch = t.toMSecsSinceEpoch () / 1000 + 2; auto target_sec = sec_since_epoch - sec_since_epoch % TR_period + TR_period / 2; - auto target_date_time = QDateTime::fromMSecsSinceEpoch (target_sec * 1000, QTimeZone::utc ()); + auto target_date_time = QDateTime::fromMSecsSinceEpoch (target_sec * 1000, Qt::UTC); QString date {target_date_time.date().toString("yyyy MMM dd").trimmed ()}; QString utc {target_date_time.time().toString().trimmed ()}; int nyear {target_date_time.date().year()}; From 179e093262d87b1ad12b3294c85d6ef1ea596b10 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Tue, 20 Nov 2018 23:43:18 +0000 Subject: [PATCH 13/27] Improve handling of uncaught exceptions by catching them early and displaying in a message box --- main.cpp | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/main.cpp b/main.cpp index 784b709cd..601ae6a7e 100644 --- a/main.cpp +++ b/main.cpp @@ -52,6 +52,38 @@ namespace qsrand (seed); // this is good for rand() as well } } seeding; + + // We can't use the GUI after QApplication::exit() is called so + // uncaught exceptions can get lost on Windows systems where there + // is no console terminal, so here we override + // QApplication::notify() and wrap the base class call with a try + // block to catch and display exceptions in a message box. + class ExceptionCatchingApplication final + : public QApplication + { + public: + explicit ExceptionCatchingApplication (int& argc, char * * argv) + : QApplication {argc, argv} + { + } + bool notify (QObject * receiver, QEvent * e) override + { + try + { + return QApplication::notify (receiver, e); + } + catch (std::exception const& e) + { + MessageBox::critical_message (nullptr, translate ("main", "Fatal error"), e.what ()); + throw; + } + catch (...) + { + MessageBox::critical_message (nullptr, translate ("main", "Unexpected fatal error")); + throw; + } + } + }; } int main(int argc, char *argv[]) @@ -68,7 +100,7 @@ int main(int argc, char *argv[]) // Multiple instances communicate with jt9 via this QSharedMemory mem_jt9; - QApplication a(argc, argv); + ExceptionCatchingApplication a(argc, argv); try { setlocale (LC_NUMERIC, "C"); // ensure number forms are in @@ -339,12 +371,10 @@ int main(int argc, char *argv[]) } catch (std::exception const& e) { - MessageBox::critical_message (nullptr, a.translate ("main", "Fatal error"), e.what ()); std::cerr << "Error: " << e.what () << '\n'; } catch (...) { - MessageBox::critical_message (nullptr, a.translate ("main", "Unexpected fatal error")); std::cerr << "Unexpected fatal error\n"; throw; // hoping the runtime might tell us more about the exception } From 8fce78473fae17d52803a8a09e79b774b7fc32bc Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Tue, 20 Nov 2018 23:47:29 +0000 Subject: [PATCH 14/27] Switch to .cbr as the default Cabrillo file extension Should help to avoid accidents with other .log extension files in the WSJT-X log files directory. --- widgets/ExportCabrillo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widgets/ExportCabrillo.cpp b/widgets/ExportCabrillo.cpp index 928a60dc9..0e61f0f08 100644 --- a/widgets/ExportCabrillo.cpp +++ b/widgets/ExportCabrillo.cpp @@ -76,7 +76,7 @@ void ExportCabrillo::save_log () auto fname = QFileDialog::getSaveFileName (this , tr ("Save Log File") , configuration_->writeable_data_dir ().absolutePath () - , tr ("Cabrillo Log (*.log)")); + , tr ("Cabrillo Log (*.cbr)")); if (fname.size ()) { QFile f {fname}; From a488c64e43d181ec904574cd9e5074de53bb4a98 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Wed, 21 Nov 2018 01:32:31 +0000 Subject: [PATCH 15/27] Partially revert the merge below because of a bad merge of Configuration.ui "Merge tag 'wsjtx-2.0.0-rc3' into develop" This partially reverts commit e3f4efefe6128e586e3c2f31abb3e932e2862d2c, reversing changes made to 388fb94698193f26649cfbc482dde8b9931385f9. --- Configuration.ui | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Configuration.ui b/Configuration.ui index 1e8feae69..7a3ec96be 100644 --- a/Configuration.ui +++ b/Configuration.ui @@ -1712,24 +1712,24 @@ QListView::item:hover { - Some logging programs will not accept JT-65 or JT9 as a recognized mode. + Some logging programs will not accept the type of reports +saved by this program. +Check this option to save the sent and received reports in the +comments field. - Con&vert mode to RTTY + d&B reports to comments - <html><head/><body><p>The callsign of the operator, if different from the station callsign.</p></body></html> + Check this option to force the clearing of the DX Call +and DX Grid fields when a 73 or free text message is sent. - - - - - Log automatically + Clear &DX call and grid after logging From eb0930294d115c684610afcd903eba2fc5281f22 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Wed, 21 Nov 2018 10:17:16 -0500 Subject: [PATCH 16/27] Add suggested message type i3.n3 = 0.6. --- lib/77bit/77bit.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/77bit/77bit.txt b/lib/77bit/77bit.txt index 0d6c4b733..ef370c111 100644 --- a/lib/77bit/77bit.txt +++ b/lib/77bit/77bit.txt @@ -10,12 +10,12 @@ subtypes. i3.n3 Example message Bits Total Purpose ---------------------------------------------------------------------------------- 0.0 FREE TEXT MSG 71 71 Free text -0.1 K1ABC RR73; W9XYZ -11 28 28 10 5 71 DXpedition Mode +0.1 K1ABC RR73; W9XYZ -12 28 28 10 5 71 DXpedition Mode 0.2 PA3XYZ/P R 590003 IO91NP 28 1 1 3 12 25 70 EU VHF contest 0.3 WA9XYZ KA1ABC R 16A EMA 28 28 1 4 3 7 71 ARRL Field Day 0.4 WA9XYZ KA1ABC R 32A EMA 28 28 1 4 3 7 71 ARRL Field Day 0.5 123456789ABCDEF012 71 71 Telemetry (18 hex) -0.6 ... tbd +0.6 K1ABC RR73; CQ W9XYZ EN37 28 28 15 71 Contesting 0.7 ... tbd 1 WA9XYZ/R KA1ABC/R R FN42 28 1 28 1 1 15 74 Standard msg From c81b3c8e6541cb0cfd4a657e912a92a72dce0ac8 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Fri, 23 Nov 2018 01:18:39 +0000 Subject: [PATCH 17/27] Validate contest QSO details before allowing logging Basic validation, must have non-empty exchange sent and received. Abstracted log view window widget behaviour into a base class. Turned on auto resize to row height in log view windows and enabled alternating colours. Convert empty fields to NULL when inserting new log table rows to signify missing data. Trap insert row errors when adding to contest log table so that logging can be held back if constraints are not met. Re-factored log QSO processing to try insert row into log table first and pop up a message box if constraints are not met, this pops up the Log QSO window in case it was initiated by an auto log event. --- CMakeLists.txt | 1 + models/CabrilloLog.cpp | 41 ++++++++++--- models/CabrilloLog.hpp | 2 +- models/FoxLog.cpp | 13 ++++- widgets/AbstractLogWindow.cpp | 76 ++++++++++++++++++++++++ widgets/AbstractLogWindow.hpp | 32 +++++++++++ widgets/CabrilloLogWindow.cpp | 105 ++++++++++++++++------------------ widgets/CabrilloLogWindow.hpp | 26 ++------- widgets/CabrilloLogWindow.ui | 20 ++++++- widgets/FoxLogWindow.cpp | 84 ++++++++------------------- widgets/FoxLogWindow.hpp | 23 ++------ widgets/FoxLogWindow.ui | 20 ++++++- widgets/logqso.cpp | 57 +++++++++++++----- widgets/logqso.h | 6 +- widgets/mainwindow.cpp | 40 +++++++------ widgets/widgets.pri | 10 ++-- 16 files changed, 349 insertions(+), 207 deletions(-) create mode 100644 widgets/AbstractLogWindow.cpp create mode 100644 widgets/AbstractLogWindow.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 39411f8a2..1cf9d88ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -265,6 +265,7 @@ set (wsjt_qt_CXXSRCS models/DecodeHighlightingModel.cpp widgets/DecodeHighlightingListView.cpp models/FoxLog.cpp + widgets/AbstractLogWindow.cpp widgets/FoxLogWindow.cpp widgets/CabrilloLogWindow.cpp item_delegates/CallsignDelegate.cpp diff --git a/models/CabrilloLog.cpp b/models/CabrilloLog.cpp index 055f8ae07..f447bca79 100644 --- a/models/CabrilloLog.cpp +++ b/models/CabrilloLog.cpp @@ -78,19 +78,46 @@ QAbstractItemModel * CabrilloLog::model () return &*m_; } -void CabrilloLog::add_QSO (Frequency frequency, QDateTime const& when, QString const& call +namespace +{ + void set_value_maybe_null (QSqlRecord& record, QString const& name, QString const& value) + { + if (value.size ()) + { + record.setValue (name, value); + } + else + { + record.setNull (name); + } + } +} + +bool CabrilloLog::add_QSO (Frequency frequency, QDateTime const& when, QString const& call , QString const& exchange_sent, QString const& exchange_received) { ConditionalTransaction transaction {*m_}; auto record = m_->record (); record.setValue ("frequency", frequency / 1000ull); // kHz - record.setValue ("when", when.toMSecsSinceEpoch () / 1000ull); - record.setValue ("call", call); - record.setValue ("exchange_sent", exchange_sent); - record.setValue ("exchange_rcvd", exchange_received); - record.setValue ("band", m_->configuration_->bands ()->find (frequency)); + if (!when.isNull ()) + { + record.setValue ("when", when.toMSecsSinceEpoch () / 1000ull); + } + else + { + record.setNull ("when"); + } + set_value_maybe_null (record, "call", call); + set_value_maybe_null (record, "exchange_sent", exchange_sent); + set_value_maybe_null (record, "exchange_rcvd", exchange_received); + set_value_maybe_null (record, "band", m_->configuration_->bands ()->find (frequency)); SQL_error_check (*m_, &QSqlTableModel::insertRecord, -1, record); - transaction.submit (); + if (!transaction.submit (false)) + { + transaction.revert (); + return false; + } + return true; } bool CabrilloLog::dupe (Frequency frequency, QString const& call) const diff --git a/models/CabrilloLog.hpp b/models/CabrilloLog.hpp index 9571adeeb..e328d0633 100644 --- a/models/CabrilloLog.hpp +++ b/models/CabrilloLog.hpp @@ -21,7 +21,7 @@ public: ~CabrilloLog (); // returns false if insert fails - void add_QSO (Frequency, QDateTime const&, QString const& call + bool add_QSO (Frequency, QDateTime const&, QString const& call , QString const& report_sent, QString const& report_received); bool dupe (Frequency, QString const& call) const; diff --git a/models/FoxLog.cpp b/models/FoxLog.cpp index 81763d9be..43cdd2b8f 100644 --- a/models/FoxLog.cpp +++ b/models/FoxLog.cpp @@ -88,12 +88,19 @@ bool FoxLog::add_QSO (QDateTime const& when, QString const& call, QString const& { ConditionalTransaction transaction {*m_}; auto record = m_->record (); - record.setValue ("when", when.toMSecsSinceEpoch () / 1000); - record.setValue ("call", call); + if (!when.isNull ()) + { + record.setValue ("when", when.toMSecsSinceEpoch () / 1000); + } + else + { + record.setNull ("when"); + } + set_value_maybe_null (record, "call", call); set_value_maybe_null (record, "grid", grid); set_value_maybe_null (record, "report_sent", report_sent); set_value_maybe_null (record, "report_rcvd", report_received); - record.setValue ("band", band); + set_value_maybe_null (record, "band", band); SQL_error_check (*m_, &QSqlTableModel::insertRecord, -1, record); if (!transaction.submit (false)) { diff --git a/widgets/AbstractLogWindow.cpp b/widgets/AbstractLogWindow.cpp new file mode 100644 index 000000000..ebc901cd8 --- /dev/null +++ b/widgets/AbstractLogWindow.cpp @@ -0,0 +1,76 @@ +#include "AbstractLogWindow.hpp" + +#include +#include +#include +#include +#include "Configuration.hpp" +#include "SettingsGroup.hpp" +#include "models/FontOverrideModel.hpp" +#include "pimpl_impl.hpp" + +class AbstractLogWindow::impl final +{ +public: + impl (QString const& settings_key, QSettings * settings, Configuration const * configuration) + : settings_key_ {settings_key} + , settings_ {settings} + , configuration_ {configuration} + , log_view_ {nullptr} + { + } + + QString settings_key_; + QSettings * settings_; + Configuration const * configuration_; + QTableView * log_view_; + FontOverrideModel model_; +}; + +AbstractLogWindow::AbstractLogWindow (QString const& settings_key, QSettings * settings + , Configuration const * configuration + , QWidget * parent) + : QWidget {parent} + , m_ {settings_key, settings, configuration} +{ + // ensure view scrolls to latest new row + connect (&m_->model_, &QAbstractItemModel::rowsInserted, [this] (QModelIndex const& /*parent*/, int /*first*/, int /*last*/) { + if (m_->log_view_) m_->log_view_->scrollToBottom (); + }); +} + +AbstractLogWindow::~AbstractLogWindow () +{ + SettingsGroup g {m_->settings_, m_->settings_key_}; + m_->settings_->setValue ("window/geometry", saveGeometry ()); +} + +void AbstractLogWindow::set_log_model (QAbstractItemModel * log_model) +{ + m_->model_.setSourceModel (log_model); +} + +void AbstractLogWindow::set_log_view (QTableView * log_view) +{ + // do this here because we know the UI must be setup before this + SettingsGroup g {m_->settings_, m_->settings_key_}; + restoreGeometry (m_->settings_->value ("window/geometry").toByteArray ()); + + m_->log_view_ = log_view; + m_->log_view_->setModel (&m_->model_); + m_->log_view_->setColumnHidden (0, true); + auto horizontal_header = log_view->horizontalHeader (); + horizontal_header->setSectionResizeMode (QHeaderView::ResizeToContents); + horizontal_header->setSectionsMovable (true); + m_->log_view_->verticalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents); + set_log_view_font (m_->configuration_->decoded_text_font ()); + m_->log_view_->scrollToBottom (); +} + +void AbstractLogWindow::set_log_view_font (QFont const& font) +{ + // m_->log_view_->setFont (font); + // m_->log_view_->horizontalHeader ()->setFont (font); + // m_->log_view_->verticalHeader ()->setFont (font); + m_->model_.set_font (font); +} diff --git a/widgets/AbstractLogWindow.hpp b/widgets/AbstractLogWindow.hpp new file mode 100644 index 000000000..35d5d27cf --- /dev/null +++ b/widgets/AbstractLogWindow.hpp @@ -0,0 +1,32 @@ +#ifndef ABSTRACT_LOG_WINDOW_HPP_ +#define ABSTRACT_LOG_WINDOW_HPP_ + +#include +#include "pimpl_h.hpp" + +class QString; +class QSettings; +class Configuration; +class QAbstractItemModel; +class QTableView; +class QFont; + +class AbstractLogWindow + : public QWidget +{ +public: + AbstractLogWindow (QString const& settings_key, QSettings * settings + , Configuration const * configuration + , QWidget * parent = nullptr); + virtual ~AbstractLogWindow () = 0; + + void set_log_model (QAbstractItemModel *); + void set_log_view (QTableView *); + void set_log_view_font (QFont const&); + +private: + class impl; + pimpl m_; +}; + +#endif diff --git a/widgets/CabrilloLogWindow.cpp b/widgets/CabrilloLogWindow.cpp index 4a86b4509..ab83a79f2 100644 --- a/widgets/CabrilloLogWindow.cpp +++ b/widgets/CabrilloLogWindow.cpp @@ -1,77 +1,68 @@ #include "CabrilloLogWindow.hpp" -#include #include -#include -#include -#include -#include -#include - -#include "SettingsGroup.hpp" +#include #include "Configuration.hpp" #include "models/Bands.hpp" #include "item_delegates/ForeignKeyDelegate.hpp" #include "item_delegates/DateTimeAsSecsSinceEpochDelegate.hpp" #include "item_delegates/CallsignDelegate.hpp" -#include "item_delegates/MaidenheadLocatorDelegate.hpp" -#include "widgets/MessageBox.hpp" -#include "qt_helpers.hpp" +#include "pimpl_impl.hpp" #include "ui_CabrilloLogWindow.h" +namespace +{ + class FormatProxyModel final + : public QIdentityProxyModel + { + public: + explicit FormatProxyModel (QObject * parent = nullptr) + : QIdentityProxyModel {parent} + { + } + + QVariant data (QModelIndex const& index, int role) const override + { + if (Qt::TextAlignmentRole == role && index.isValid ()) + { + switch (index.column ()) + { + case 1: + case 6: + return Qt::AlignRight + Qt::AlignVCenter; + default: + break; + } + } + return QIdentityProxyModel::data (index, role); + } + }; +} + +class CabrilloLogWindow::impl final +{ +public: + explicit impl () = default; + FormatProxyModel format_model_; + Ui::CabrilloLogWindow ui_; +}; + CabrilloLogWindow::CabrilloLogWindow (QSettings * settings, Configuration const * configuration , QAbstractItemModel * cabrillo_log_model, QWidget * parent) - : QWidget {parent} - , settings_ {settings} - , configuration_ {configuration} - , ui_ {new Ui::CabrilloLogWindow} + : AbstractLogWindow {"Cabrillo Log Window", settings, configuration, parent} { - cabrillo_log_model_.setSourceModel (cabrillo_log_model); setWindowTitle (QApplication::applicationName () + " - Cabrillo Log"); - ui_->setupUi (this); - read_settings (); - change_font (configuration_->decoded_text_font ()); - ui_->log_table_view->setModel (&cabrillo_log_model_); - ui_->log_table_view->setColumnHidden (0, true); - ui_->log_table_view->setItemDelegateForColumn (2, new DateTimeAsSecsSinceEpochDelegate {this}); - ui_->log_table_view->setItemDelegateForColumn (3, new CallsignDelegate {this}); - ui_->log_table_view->setItemDelegateForColumn (6, new ForeignKeyDelegate {configuration_->bands (), &cabrillo_log_model_, 0, 6, this}); - ui_->log_table_view->setSelectionMode (QTableView::SingleSelection); - auto horizontal_header = ui_->log_table_view->horizontalHeader (); - horizontal_header->setStretchLastSection (true); - horizontal_header->setSectionResizeMode (QHeaderView::ResizeToContents); - horizontal_header->setSectionsMovable (true); - horizontal_header->moveSection (6, 1); // band to first column - ui_->log_table_view->scrollToBottom (); - - // ensure view scrolls to latest new row - connect (&cabrillo_log_model_, &QAbstractItemModel::rowsInserted, [this] (QModelIndex const& /*parent*/, int /*first*/, int /*last*/) { - ui_->log_table_view->scrollToBottom (); - }); + m_->ui_.setupUi (this); + m_->format_model_.setSourceModel (cabrillo_log_model); + set_log_model (&m_->format_model_); + set_log_view (m_->ui_.log_table_view); + m_->ui_.log_table_view->setItemDelegateForColumn (2, new DateTimeAsSecsSinceEpochDelegate {this}); + m_->ui_.log_table_view->setItemDelegateForColumn (3, new CallsignDelegate {this}); + m_->ui_.log_table_view->setItemDelegateForColumn (6, new ForeignKeyDelegate {configuration->bands (), cabrillo_log_model, 0, 6, this}); + m_->ui_.log_table_view->horizontalHeader ()->moveSection (6, 1); // band to first column } CabrilloLogWindow::~CabrilloLogWindow () { - write_settings (); -} - -void CabrilloLogWindow::read_settings () -{ - SettingsGroup g {settings_, "Cabrillo Log Window"}; - restoreGeometry (settings_->value ("window/geometery").toByteArray ()); -} - -void CabrilloLogWindow::write_settings () const -{ - SettingsGroup g {settings_, "Cabrillo Log Window"}; - settings_->setValue ("window/geometery", saveGeometry ()); -} - -void CabrilloLogWindow::change_font (QFont const& font) -{ - // ui_->log_table_view->setFont (font); - // ui_->log_table_view->horizontalHeader ()->setFont (font); - // ui_->log_table_view->verticalHeader ()->setFont (font); - cabrillo_log_model_.set_font (font); } diff --git a/widgets/CabrilloLogWindow.hpp b/widgets/CabrilloLogWindow.hpp index 9a352ce9d..c0a964ce5 100644 --- a/widgets/CabrilloLogWindow.hpp +++ b/widgets/CabrilloLogWindow.hpp @@ -1,39 +1,25 @@ #ifndef CABRILLO_LOG_WINDOW_HPP_ #define CABRILLO_LOG_WINDOW_HPP_ -#include -#include -#include -#include "models/FontOverrideModel.hpp" +#include "AbstractLogWindow.hpp" +#include "pimpl_h.hpp" class QSettings; class Configuration; class QFont; -class QDateTime; class QAbstractItemModel; -namespace Ui -{ - class CabrilloLogWindow; -} class CabrilloLogWindow final - : public QWidget + : public AbstractLogWindow { public: explicit CabrilloLogWindow (QSettings *, Configuration const *, QAbstractItemModel * cabrillo_log_model - , QWidget * parent = nullptr); + , QWidget * parent = nullptr); ~CabrilloLogWindow (); - void change_font (QFont const&); - private: - void read_settings (); - void write_settings () const; - - QSettings * settings_; - Configuration const * configuration_; - FontOverrideModel cabrillo_log_model_; - QScopedPointer ui_; + class impl; + pimpl m_; }; #endif diff --git a/widgets/CabrilloLogWindow.ui b/widgets/CabrilloLogWindow.ui index eb25d1a6b..43061f132 100644 --- a/widgets/CabrilloLogWindow.ui +++ b/widgets/CabrilloLogWindow.ui @@ -2,12 +2,30 @@ CabrilloLogWindow + + + 0 + 0 + 274 + 210 + + Contest Log - + + + true + + + QAbstractItemView::SingleSelection + + + true + + diff --git a/widgets/FoxLogWindow.cpp b/widgets/FoxLogWindow.cpp index 252045359..f5e7e517d 100644 --- a/widgets/FoxLogWindow.cpp +++ b/widgets/FoxLogWindow.cpp @@ -1,12 +1,6 @@ #include "FoxLogWindow.hpp" -#include #include -#include -#include -#include -#include -#include #include "SettingsGroup.hpp" #include "Configuration.hpp" @@ -15,82 +9,50 @@ #include "item_delegates/DateTimeAsSecsSinceEpochDelegate.hpp" #include "item_delegates/CallsignDelegate.hpp" #include "item_delegates/MaidenheadLocatorDelegate.hpp" -#include "widgets/MessageBox.hpp" -#include "qt_helpers.hpp" +#include "pimpl_impl.hpp" #include "ui_FoxLogWindow.h" +class FoxLogWindow::impl final +{ +public: + explicit impl () = default; + Ui::FoxLogWindow ui_; +}; + FoxLogWindow::FoxLogWindow (QSettings * settings, Configuration const * configuration , QAbstractItemModel * fox_log_model, QWidget * parent) - : QWidget {parent} - , settings_ {settings} - , configuration_ {configuration} - , ui_ {new Ui::FoxLogWindow} + : AbstractLogWindow {"Fox Log Window", settings, configuration, parent} { - fox_log_model_.setSourceModel (fox_log_model); setWindowTitle (QApplication::applicationName () + " - Fox Log"); - ui_->setupUi (this); - read_settings (); - change_font (configuration_->decoded_text_font ()); - ui_->log_table_view->setModel (&fox_log_model_); - ui_->log_table_view->setColumnHidden (0, true); - ui_->log_table_view->setItemDelegateForColumn (1, new DateTimeAsSecsSinceEpochDelegate {this}); - ui_->log_table_view->setItemDelegateForColumn (2, new CallsignDelegate {this}); - ui_->log_table_view->setItemDelegateForColumn (3, new MaidenheadLocatorDelegate {this}); - ui_->log_table_view->setItemDelegateForColumn (6, new ForeignKeyDelegate {configuration_->bands (), &fox_log_model_, 0, 6, this}); - ui_->log_table_view->setSelectionMode (QTableView::SingleSelection); - auto horizontal_header = ui_->log_table_view->horizontalHeader (); - horizontal_header->setStretchLastSection (true); - horizontal_header->setSectionResizeMode (QHeaderView::ResizeToContents); - horizontal_header->setSectionsMovable (true); - horizontal_header->moveSection (6, 1); // move band to first column - ui_->rate_label->setNum (0); - ui_->queued_label->setNum (0); - ui_->callers_label->setNum (0); - ui_->log_table_view->scrollToBottom (); - - // ensure view scrolls to latest new row - connect (&fox_log_model_, &QAbstractItemModel::rowsInserted, [this] (QModelIndex const& /*parent*/, int /*first*/, int /*last*/) { - ui_->log_table_view->scrollToBottom (); - }); + m_->ui_.setupUi (this); + set_log_model (fox_log_model); + set_log_view (m_->ui_.log_table_view); + m_->ui_.log_table_view->setItemDelegateForColumn (1, new DateTimeAsSecsSinceEpochDelegate {this}); + m_->ui_.log_table_view->setItemDelegateForColumn (2, new CallsignDelegate {this}); + m_->ui_.log_table_view->setItemDelegateForColumn (3, new MaidenheadLocatorDelegate {this}); + m_->ui_.log_table_view->setItemDelegateForColumn (6, new ForeignKeyDelegate {configuration->bands (), fox_log_model, 0, 6, this}); + m_->ui_.log_table_view->horizontalHeader ()->moveSection (6, 1); // move band to first column + m_->ui_.rate_label->setNum (0); + m_->ui_.queued_label->setNum (0); + m_->ui_.callers_label->setNum (0); } FoxLogWindow::~FoxLogWindow () { - write_settings (); -} - -void FoxLogWindow::read_settings () -{ - SettingsGroup g {settings_, "Fox Log Window"}; - restoreGeometry (settings_->value ("window/geometery").toByteArray ()); -} - -void FoxLogWindow::write_settings () const -{ - SettingsGroup g {settings_, "Fox Log Window"}; - settings_->setValue ("window/geometery", saveGeometry ()); -} - -void FoxLogWindow::change_font (QFont const& font) -{ - // ui_->log_table_view->setFont (font); - // ui_->log_table_view->horizontalHeader ()->setFont (font); - // ui_->log_table_view->verticalHeader ()->setFont (font); - fox_log_model_.set_font (font); } void FoxLogWindow::callers (int n) { - ui_->callers_label->setNum (n); + m_->ui_.callers_label->setNum (n); } void FoxLogWindow::queued (int n) { - ui_->queued_label->setNum (n); + m_->ui_.queued_label->setNum (n); } void FoxLogWindow::rate (int n) { - ui_->rate_label->setNum (n); + m_->ui_.rate_label->setNum (n); } diff --git a/widgets/FoxLogWindow.hpp b/widgets/FoxLogWindow.hpp index 62895a763..d27ccdea6 100644 --- a/widgets/FoxLogWindow.hpp +++ b/widgets/FoxLogWindow.hpp @@ -1,42 +1,29 @@ #ifndef FOX_LOG_WINDOW_HPP_ #define FOX_LOG_WINDOW_HPP_ -#include -#include -#include -#include "models/FontOverrideModel.hpp" +#include "AbstractLogWindow.hpp" +#include "pimpl_h.hpp" class QSettings; class Configuration; class QFont; -class QDateTime; class QAbstractItemModel; -namespace Ui -{ - class FoxLogWindow; -} class FoxLogWindow final - : public QWidget + : public AbstractLogWindow { public: explicit FoxLogWindow (QSettings *, Configuration const *, QAbstractItemModel * fox_log_model , QWidget * parent = nullptr); ~FoxLogWindow (); - void change_font (QFont const&); void callers (int); void queued (int); void rate (int); private: - void read_settings (); - void write_settings () const; - - QSettings * settings_; - Configuration const * configuration_; - FontOverrideModel fox_log_model_; - QScopedPointer ui_; + class impl; + pimpl m_; }; #endif diff --git a/widgets/FoxLogWindow.ui b/widgets/FoxLogWindow.ui index 2a3e84aba..96ef8b112 100644 --- a/widgets/FoxLogWindow.ui +++ b/widgets/FoxLogWindow.ui @@ -2,12 +2,30 @@ FoxLogWindow + + + 0 + 0 + 331 + 238 + + Fox Log - + + + true + + + QAbstractItemView::SingleSelection + + + true + + diff --git a/widgets/logqso.cpp b/widgets/logqso.cpp index 77324e52b..b6412cb59 100644 --- a/widgets/logqso.cpp +++ b/widgets/logqso.cpp @@ -10,6 +10,7 @@ #include "MessageBox.hpp" #include "Configuration.hpp" #include "models/Bands.hpp" +#include "models/CabrilloLog.hpp" #include "validators/MaidenheadLocatorValidator.hpp" #include "ui_logqso.h" @@ -57,11 +58,10 @@ void LogQSO::storeSettings () const void LogQSO::initLogQSO(QString const& hisCall, QString const& hisGrid, QString mode, QString const& rptSent, QString const& rptRcvd, QDateTime const& dateTimeOn, QDateTime const& dateTimeOff, - Radio::Frequency dialFreq, bool noSuffix, QString xSent, QString xRcvd) + Radio::Frequency dialFreq, bool noSuffix, QString xSent, QString xRcvd, + CabrilloLog * cabrillo_log) { if(!isHidden()) return; - m_xSent=xSent; - m_xRcvd=xRcvd; ui->call->setText(hisCall); ui->grid->setText(hisGrid); ui->name->setText(""); @@ -87,17 +87,23 @@ void LogQSO::initLogQSO(QString const& hisCall, QString const& hisGrid, QString m_myGrid=m_config->my_grid(); ui->band->setText (m_config->bands ()->find (dialFreq)); ui->loggedOperator->setText(m_config->opCall()); - ui->exchSent->setText(m_xSent); - ui->exchRcvd->setText(m_xRcvd); + ui->exchSent->setText (xSent); + ui->exchRcvd->setText (xRcvd); + m_cabrilloLog = cabrillo_log; using SpOp = Configuration::SpecialOperatingActivity; - if( SpOp::FOX == m_config->special_op_id() or - (m_config->autoLog() and SpOp::NONE < m_config->special_op_id() and - m_xSent!="" and m_xRcvd!="")) { - accept(); - } else { - show(); - } + auto special_op = m_config->special_op_id (); + if (SpOp::FOX == special_op + || (m_config->autoLog () + && SpOp::NONE < special_op && special_op < SpOp::FOX)) + { + // allow auto logging in Fox mode and contests + accept(); + } + else + { + show(); + } } void LogQSO::accept() @@ -120,6 +126,29 @@ void LogQSO::accept() QString strDialFreq(QString::number(m_dialFreq / 1.e6,'f',6)); operator_call = ui->loggedOperator->text(); + // validate + using SpOp = Configuration::SpecialOperatingActivity; + auto special_op = m_config->special_op_id (); + if (SpOp::NONE < special_op && special_op < SpOp::FOX) + { + if (ui->exchSent->text ().isEmpty () || ui->exchRcvd->text ().isEmpty ()) + { + show (); + MessageBox::warning_message (this, tr ("Invalid QSO Data"), + tr ("Check exchange sent and received")); + return; // without accepting + } + + if (!m_cabrilloLog->add_QSO (m_dialFreq, QDateTime::currentDateTimeUtc (), hisCall, + ui->exchSent->text (), ui->exchRcvd->text ())) + { + show (); + MessageBox::warning_message (this, tr ("Invalid QSO Data"), + tr ("Check all fields")); + return; // without accepting + } + } + //Log this QSO to file "wsjtx.log" static QFile f {QDir {QStandardPaths::writableLocation (QStandardPaths::DataLocation)}.absoluteFilePath ("wsjtx.log")}; if(!f.open(QIODevice::Text | QIODevice::Append)) { @@ -169,8 +198,8 @@ void LogQSO::accept() , m_myGrid , m_txPower , operator_call - , m_xSent - , m_xRcvd)); + , ui->exchSent->text () + , ui->exchRcvd->text ())); QDialog::accept(); } diff --git a/widgets/logqso.h b/widgets/logqso.h index 2f2e4c29e..b0e703a93 100644 --- a/widgets/logqso.h +++ b/widgets/logqso.h @@ -17,6 +17,7 @@ namespace Ui { class QSettings; class Configuration; class QByteArray; +class CabrilloLog; class LogQSO : public QDialog { @@ -28,7 +29,7 @@ public: void initLogQSO(QString const& hisCall, QString const& hisGrid, QString mode, QString const& rptSent, QString const& rptRcvd, QDateTime const& dateTimeOn, QDateTime const& dateTimeOff, Radio::Frequency dialFreq, - bool noSuffix, QString xSent, QString xRcvd); + bool noSuffix, QString xSent, QString xRcvd, CabrilloLog *); public slots: void accept(); @@ -56,10 +57,9 @@ private: Radio::Frequency m_dialFreq; QString m_myCall; QString m_myGrid; - QString m_xSent; - QString m_xRcvd; QDateTime m_dateTimeOn; QDateTime m_dateTimeOff; + CabrilloLog * m_cabrilloLog; }; #endif // LogQSO_H diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 186f5f40c..c1a09c227 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -1228,10 +1228,10 @@ void MainWindow::setDecodedTextFont (QFont const& font) m_msgAvgWidget->changeFont (font); } if (m_foxLogWindow) { - m_foxLogWindow->change_font (font); + m_foxLogWindow->set_log_view_font (font); } if (m_contestLogWindow) { - m_contestLogWindow->change_font (font); + m_contestLogWindow->set_log_view_font (font); } updateGeometry (); } @@ -5331,9 +5331,16 @@ void MainWindow::on_logQSOButton_clicked() //Log QSO button default: break; } + using SpOp = Configuration::SpecialOperatingActivity; + auto special_op = m_config.special_op_id (); + if (SpecOp::NONE < special_op && special_op < SpecOp::FOX) + { + if (!m_cabrilloLog) m_cabrilloLog.reset (new CabrilloLog {&m_config}); + } m_logDlg->initLogQSO (m_hisCall, grid, m_modeTx, m_rptSent, m_rptRcvd, m_dateTimeQSOOn, dateTimeQSOOff, m_freqNominal + - ui->TxFreqSpinBox->value(), m_noSuffix, m_xSent, m_xRcvd); + ui->TxFreqSpinBox->value(), m_noSuffix, m_xSent, m_xRcvd, + m_cabrilloLog.data ()); } void MainWindow::acceptQSO (QDateTime const& QSO_date_off, QString const& call, QString const& grid @@ -5350,15 +5357,6 @@ void MainWindow::acceptQSO (QDateTime const& QSO_date_off, QString const& call, tr ("Cannot open \"%1\"").arg (m_logBook.path ())); } - - if (SpecOp::NONE < m_config.special_op_id() && SpecOp::FOX > m_config.special_op_id()) { - if (!m_cabrilloLog) m_cabrilloLog.reset (new CabrilloLog {&m_config}); - m_cabrilloLog->add_QSO (m_freqNominal, QDateTime::currentDateTimeUtc (), m_hisCall, m_xSent, m_xRcvd); - m_xSent=""; - m_xRcvd=""; - ui->sbSerialNumber->setValue (ui->sbSerialNumber->value () + 1); - } - m_messageClient->qso_logged (QSO_date_off, call, grid, dial_freq, mode, rpt_sent, rpt_received , tx_power, comments, name, QSO_date_on, operator_call, my_call, my_grid); m_messageClient->logged_ADIF (ADIF); @@ -5378,6 +5376,15 @@ void MainWindow::acceptQSO (QDateTime const& QSO_date_off, QString const& call, if (m_config.clear_DX () and SpecOp::HOUND != m_config.special_op_id()) clearDX (); m_dateTimeQSOOn = QDateTime {}; + using SpOp = Configuration::SpecialOperatingActivity; + auto special_op = m_config.special_op_id (); + if (SpecOp::NONE < special_op && special_op < SpecOp::FOX) + { + ui->sbSerialNumber->setValue (ui->sbSerialNumber->value () + 1); + } + + m_xSent.clear (); + m_xRcvd.clear (); } qint64 MainWindow::nWidgets(QString t) @@ -6125,11 +6132,12 @@ void MainWindow::on_reset_fox_log_action_triggered () void MainWindow::on_reset_cabrillo_log_action_triggered () { - if (MessageBox::Yes == MessageBox::query_message(this, tr("Confirm Erase"), - tr("Are you sure you want to erase file cabrillo.log" - " and start a new Cabrillo log?"))) + if (MessageBox::Yes == MessageBox::query_message (this, tr ("Confirm Reset"), + tr ("Are you sure you want to erase your contest log?"), + tr ("Doing this will remove all QSO records for the current contest. " + "They will be kept in the ADIF log file but will not be available " + "for export in your Cabrillo log."))) { - QFile {m_config.writeable_data_dir ().absoluteFilePath ("cabrillo.log")}.remove (); ui->sbSerialNumber->setValue (1); if (!m_cabrilloLog) m_cabrilloLog.reset (new CabrilloLog {&m_config}); m_cabrilloLog->reset (); diff --git a/widgets/widgets.pri b/widgets/widgets.pri index 019fdcd17..e70c5cb9e 100644 --- a/widgets/widgets.pri +++ b/widgets/widgets.pri @@ -7,7 +7,8 @@ SOURCES += \ widgets/echoplot.cpp widgets/echograph.cpp widgets/fastgraph.cpp \ widgets/fastplot.cpp widgets/MessageBox.cpp \ widgets/colorhighlighting.cpp widgets/ExportCabrillo.cpp \ - widgets/CabrilloLogWindow.cpp + widgets/AbstractLogWindow.cpp \ + widgets/FoxLogWindow.cpp widgets/CabrilloLogWindow.cpp HEADERS += \ widgets/mainwindow.h widgets/plotter.h \ @@ -17,8 +18,8 @@ HEADERS += \ widgets/meterwidget.h widgets/messageaveraging.h \ widgets/echoplot.h widgets/echograph.h widgets/fastgraph.h \ widgets/fastplot.h widgets/MessageBox.hpp widgets/colorhighlighting.h \ - widgets/ExportCabrillo.h \ - widgets/CabrilloLogWindow.cpp + widgets/ExportCabrillo.h widgets/AbstractLogWindow.hpp \ + widgets/FoxLogWindow.cpp widgets/CabrilloLogWindow.cpp FORMS += \ widgets/mainwindow.ui widgets/about.ui \ @@ -26,5 +27,4 @@ FORMS += \ widgets/logqso.ui widgets/messageaveraging.ui \ widgets/echograph.ui widgets/fastgraph.ui \ widgets/colorhighlighting.ui widgets/ExportCabrillo.ui \ - widgets/FoxLogWindow.ui \ - widgets/CabrilloLogWindow.ui + widgets/FoxLogWindow.ui widgets/CabrilloLogWindow.ui From e434bc5b550ec34632cfa2b9fc46beb3641cf363 Mon Sep 17 00:00:00 2001 From: Steve Franke Date: Fri, 23 Nov 2018 15:10:44 -0600 Subject: [PATCH 18/27] Remove obsolete routines related to msk144. --- CMakeLists.txt | 13 +-- lib/Makefile.mskWin | 75 --------------- lib/encode_msk144.f90 | 111 --------------------- lib/extractmessage144.f90 | 51 ---------- lib/genmsk144.f90 | 146 ---------------------------- lib/genmsk_short.f90 | 66 ------------- lib/ldpcsim144.f90 | 175 --------------------------------- lib/msk144d.f90 | 136 -------------------------- lib/msk144sd.f90 | 197 -------------------------------------- lib/msk144sim.f90 | 21 ++-- lib/platanh.f90 | 24 +++++ lib/pltanh.f90 | 24 +++++ lib/unpackmsg144.f90 | 117 ---------------------- 13 files changed, 59 insertions(+), 1097 deletions(-) delete mode 100644 lib/Makefile.mskWin delete mode 100644 lib/encode_msk144.f90 delete mode 100644 lib/extractmessage144.f90 delete mode 100644 lib/genmsk144.f90 delete mode 100644 lib/genmsk_short.f90 delete mode 100644 lib/ldpcsim144.f90 delete mode 100644 lib/msk144d.f90 delete mode 100644 lib/msk144sd.f90 create mode 100644 lib/platanh.f90 create mode 100644 lib/pltanh.f90 delete mode 100644 lib/unpackmsg144.f90 diff --git a/CMakeLists.txt b/CMakeLists.txt index 39411f8a2..5818cc8b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -382,7 +382,6 @@ set (wsjt_FSRCS lib/badmsg.f90 lib/ft8/baseline.f90 lib/bpdecode40.f90 - lib/bpdecode144.f90 lib/bpdecode128_90.f90 lib/ft8/bpdecode174.f90 lib/ft8/bpdecode174_91.f90 @@ -414,7 +413,6 @@ set (wsjt_FSRCS lib/encode232.f90 lib/encode4.f90 lib/encode_msk40.f90 - lib/encode_msk144.f90 lib/encode_128_90.f90 lib/ft8/encode174.f90 lib/ft8/encode174_91.f90 @@ -422,7 +420,6 @@ set (wsjt_FSRCS lib/ephem.f90 lib/extract.f90 lib/extract4.f90 - lib/extractmessage144.f90 lib/extractmessage77.f90 lib/ft8/extractmessage174.f90 lib/ft8/extractmessage174_91.f90 @@ -497,10 +494,8 @@ set (wsjt_FSRCS lib/moondopjpl.f90 lib/morse.f90 lib/move.f90 - lib/msk144d.f90 lib/msk40decodeframe.f90 lib/msk144decodeframe.f90 - lib/msk144sd.f90 lib/msk40spd.f90 lib/msk144spd.f90 lib/msk40sync.f90 @@ -519,6 +514,8 @@ set (wsjt_FSRCS lib/peakdt9.f90 lib/peakup.f90 lib/plotsave.f90 + lib/platanh.f90 + lib/pltanh.f90 lib/polyfit.f90 lib/prog_args.f90 lib/ps4.f90 @@ -561,7 +558,6 @@ set (wsjt_FSRCS lib/twkfreq.f90 lib/ft8/twkfreq1.f90 lib/twkfreq65.f90 - lib/unpackmsg144.f90 lib/update_recent_calls.f90 lib/update_hasharray.f90 lib/ft8/watterson.f90 @@ -1252,14 +1248,9 @@ target_link_libraries (ft8code wsjt_fort wsjt_cxx) add_executable (ft8sim lib/ft8/ft8sim.f90 wsjtx.rc) target_link_libraries (ft8sim wsjt_fort wsjt_cxx) -add_executable (msk144sd lib/msk144sd.f90 wsjtx.rc) -target_link_libraries (msk144sd wsjt_fort wsjt_cxx) - add_executable (msk144sim lib/msk144sim.f90 wsjtx.rc) target_link_libraries (msk144sim wsjt_fort wsjt_cxx) -add_executable (msk144d lib/msk144d.f90 wsjtx.rc) -target_link_libraries (msk144d wsjt_fort wsjt_cxx) endif(WSJT_BUILD_UTILS) # build the main application diff --git a/lib/Makefile.mskWin b/lib/Makefile.mskWin deleted file mode 100644 index 9d5e6e62b..000000000 --- a/lib/Makefile.mskWin +++ /dev/null @@ -1,75 +0,0 @@ - -# Set paths -EXE_DIR = ..\\..\\wsjtx_install -QT_DIR = C:/wsjt-env/Qt5/5.2.1/mingw48_32 -FFTW3_DIR = .. - -INCPATH = -I${QT_DIR}/include/QtCore -I${QT_DIR}/include - -# Compilers -CC = gcc -CXX = g++ -FC = gfortran -AR = ar cr -RANLIB = ranlib -MKDIR = mkdir -p -CP = cp -RM = rm -f - -FFLAGS = -O2 -fbounds-check -Wall -Wno-conversion -CFLAGS = -O2 -I. - -# Default rules -%.o: %.c - ${CC} ${CFLAGS} -c $< -%.o: %.f - ${FC} ${FFLAGS} -c $< -%.o: %.F - ${FC} ${FFLAGS} -c $< -%.o: %.f90 - ${FC} ${FFLAGS} -c $< -%.o: %.F90 - ${FC} ${FFLAGS} -c $< - -#all: jt9code JTMSKcode.exe -all: jtmsk.exe JTMSKsim.exe JTMSKcode.exe fixwav.exe - -OBJS3 = JTMSKsim.o wavhdr.o gran.o four2a.o db.o -JTMSKsim.exe: $(OBJS3) - $(FC) -o JTMSKsim.exe $(OBJS3) C:\JTSDK\fftw3f\libfftw3f-3.dll - -OBJS4 = jt9code.o packjt.o fmtmsg.o gen9.o deg2grid.o grid2deg.o \ - entail.o encode232.o interleave9.o graycode.o igray.o -jt9code: $(OBJS4) - $(FC) -o jt9code $(OBJS4) - -OBJS5 = JTMSKcode.o packjt.o fmtmsg.o genmsk.o deg2grid.o grid2deg.o \ - entail.o tab.o vit213.o hashing.o nhash.o -JTMSKcode.exe: $(OBJS5) - $(FC) -o JTMSKcode.exe $(OBJS5) - -OBJS6 = jtmsk.o analytic.o four2a.o db.o pctile.o \ - shell.o tweak1.o syncmsk.o genmsk.o packjt.o fmtmsg.o indexx.o \ - deg2grid.o grid2deg.o entail.o hashing.o nhash.o tab.o vit213.o \ - mskdt.o rectify_msk.o timer.o jtmsk_decode.o genmsk_short.o \ - jtmsk_short.o golay24_table.o hash.o - -jtmsk.exe: $(OBJS6) - $(FC) -o jtmsk.exe $(OBJS6) C:\JTSDK\fftw3f\libfftw3f-3.dll - -OBJS1 = fixwav.o wavhdr.o -fixwav.exe: $(OBJS1) - $(FC) -o fixwav.exe $(OBJS1) - -OBJS2 = t2.o four2a.o db.o -t2: $(OBJS2) - $(FC) -o t2 $(OBJS2) C:\JTSDK\fftw3f\libfftw3f-3.dll - -OBJS6 = t6.o four2a.o db.o -t6: $(OBJS6) - $(FC) -o t6 $(OBJS6) C:\JTSDK\fftw3f\libfftw3f-3.dll - -.PHONY : clean - -clean: - $(RM) *.o JTMSKcode JTMSKcode.exe diff --git a/lib/encode_msk144.f90 b/lib/encode_msk144.f90 deleted file mode 100644 index 4e4d8968e..000000000 --- a/lib/encode_msk144.f90 +++ /dev/null @@ -1,111 +0,0 @@ -subroutine encode_msk144(message,codeword) -! Encode an 80-bit message and return a 128-bit codeword. -! The generator matrix has dimensions (48,80). -! The code is a (128,80) regular ldpc code with column weight 3. -! The code was generated using the PEG algorithm. -! After creating the codeword, the columns are re-ordered according to -! "colorder" to make the codeword compatible with the parity-check -! matrix stored in Radford Neal's "pchk" format. -! -character*20 g(48) -integer*1 codeword(128) -integer*1 colorder(128) -integer*1 gen144(48,80) -integer*1 itmp(128) -integer*1 message(80) -integer*1 pchecks(48) -logical first -data first/.true./ -data g/ & !parity-check generator matrix for (128,80) code - "24084000800020008000", & - "b39678f7ccdb1baf5f4c", & - "10001000400408012000", & - "08104000100002010800", & - "dc9c18f61ea0e4b7f05c", & - "42c040160909ca002c00", & - "cc50b52b9a80db0d7f9e", & - "dde5ace80780bae74740", & - "00800080020000890080", & - "01020040010400400040", & - "20008010020000100030", & - "80400008004000040050", & - "a4b397810915126f5604", & - "04040100001040200008", & - "00800006000888000800", & - "00010c00000104040001", & - "cc7cd7d953cdc204eba0", & - "0094abe7dd146beb16ce", & - "5af2aec8c7b051c7544a", & - "14040508801840200088", & - "7392f5e720f8f5a62c1e", & - "503cc2a06bff4e684ec9", & - "5a2efd46f1efbb513b80", & - "ac06e9513fd411f1de03", & - "16a31be3dd3082ca2bd6", & - "28542e0daf62fe1d9332", & - "00210c002001540c0401", & - "0ed90d56f84298706a98", & - "939670f7ecdf9baf4f4c", & - "cfe41dec47a433e66240", & - "16d2179c2d5888222630", & - "408000160108ca002800", & - "808000830a00018900a0", & - "9ae2ed8ef3afbf8c3a52", & - "5aaafd86f3efbfc83b02", & - "f39658f68cdb0baf1f4c", & - "9414bb6495106261366a", & - "71ba18670c08411bf682", & - "7298f1a7217cf5c62e5e", & - "86d7a4864396a981369b", & - "a8042c01ae22fe191362", & - "9235ae108b2d60d0e306", & - "dfe5ade807a03be74640", & - "d2451588e6e27ccd9bc4", & - "12b51ae39d20e2ea3bde", & - "a49387810d95136fd604", & - "467e7578e51d5b3b8a0e", & - "f6ad1ac7cc3aaa3fe580"/ - -data colorder/0,1,2,3,4,5,6,7,8,9, & - 10,11,12,13,14,15,24,26,29,30, & - 32,43,44,47,60,77,79,97,101,111, & - 96,38,64,53,93,34,59,94,74,90, & - 108,123,85,57,70,25,69,62,48,49, & - 50,51,52,33,54,55,56,21,58,36, & - 16,61,23,63,20,65,66,67,68,46, & - 22,71,72,73,31,75,76,45,78,17, & - 80,81,82,83,84,42,86,87,88,89, & - 39,91,92,35,37,95,19,27,98,99, & - 100,28,102,103,104,105,106,107,40,109, & - 110,18,112,113,114,115,116,117,118,119, & - 120,121,122,41,124,125,126,127/ - -save first,gen144 - -if( first ) then ! fill the generator matrix - gen144=0 - do i=1,48 - do j=1,5 - read(g(i)( (j-1)*4+1:(j-1)*4+4 ),"(Z4)") istr - do jj=1,16 - icol=(j-1)*16+jj - if( btest(istr,16-jj) ) gen144(i,icol)=1 - enddo - enddo - enddo -first=.false. -endif - -do i=1,48 - nsum=0 - do j=1,80 - nsum=nsum+message(j)*gen144(i,j) - enddo - pchecks(i)=mod(nsum,2) -enddo -itmp(1:48)=pchecks -itmp(49:128)=message(1:80) -codeword(colorder+1)=itmp(1:128) - -return -end subroutine encode_msk144 diff --git a/lib/extractmessage144.f90 b/lib/extractmessage144.f90 deleted file mode 100644 index a5dadb0b4..000000000 --- a/lib/extractmessage144.f90 +++ /dev/null @@ -1,51 +0,0 @@ -subroutine extractmessage144(decoded,msgreceived,nhashflag,recent_calls,nrecent) - use iso_c_binding, only: c_loc,c_size_t - use packjt - use hashing - - character*22 msgreceived - character*12 call1,call2 - character*12 recent_calls(nrecent) - integer*1 decoded(80) - integer*1, target:: i1Dec8BitBytes(10) - integer*1 i1hashdec - integer*4 i4Dec6BitWords(12) - -! Collapse 80 decoded bits to 10 bytes. Bytes 1-9 are the message, byte 10 is the hash - do ibyte=1,10 - itmp=0 - do ibit=1,8 - itmp=ishft(itmp,1)+iand(1,decoded((ibyte-1)*8+ibit)) - enddo - i1Dec8BitBytes(ibyte)=itmp - enddo - -! Calculate the hash using the first 9 bytes. - ihashdec=nhash(c_loc(i1Dec8BitBytes),int(9,c_size_t),146) - ihashdec=2*iand(ihashdec,255) - -! Compare calculated hash with received byte 10 - if they agree, keep the message. - i1hashdec=ihashdec - if( i1hashdec .eq. i1Dec8BitBytes(10) ) then -! Good hash --- unpack 72-bit message - do ibyte=1,12 - itmp=0 - do ibit=1,6 - itmp=ishft(itmp,1)+iand(1,decoded((ibyte-1)*6+ibit)) - enddo - i4Dec6BitWords(ibyte)=itmp - enddo - call unpackmsg144(i4Dec6BitWords,msgreceived,call1,call2) - nhashflag=1 - if( call1(1:2) .ne. 'CQ' .and. call1(1:2) .ne. ' ' ) then - call update_recent_calls(call1,recent_calls,nrecent) - endif - if( call2(1:2) .ne. ' ' ) then - call update_recent_calls(call2,recent_calls,nrecent) - endif - else - msgreceived=' ' - nhashflag=-1 - endif - return - end subroutine extractmessage144 diff --git a/lib/genmsk144.f90 b/lib/genmsk144.f90 deleted file mode 100644 index f02344439..000000000 --- a/lib/genmsk144.f90 +++ /dev/null @@ -1,146 +0,0 @@ -subroutine genmsk144(msg0,mygrid,ichk,msgsent,i4tone,itype) -! s8 + 48bits + s8 + 80 bits = 144 bits (72ms message duration) -! -! Encode an MSK144 message -! Input: -! - msg0 requested message to be transmitted -! - ichk if ichk=1, return only msgsent -! if ichk.ge.10000, set imsg=ichk-10000 for short msg -! - msgsent message as it will be decoded -! - i4tone array of audio tone values, 0 or 1 -! - itype message type -! 1 = standard message "Call_1 Call_2 Grid/Rpt" -! 2 = type 1 prefix -! 3 = type 1 suffix -! 4 = type 2 prefix -! 5 = type 2 suffix -! 6 = free text (up to 13 characters) -! 7 = short message " Rpt" - - use iso_c_binding, only: c_loc,c_size_t - use packjt - use hashing - character*22 msg0 - character*22 message !Message to be generated - character*22 msgsent !Message as it will be received - character*6 mygrid - integer*4 i4Msg6BitWords(13) !72-bit message as 6-bit words - integer*4 i4tone(144) ! - integer*1, target:: i1Msg8BitBytes(10) !80 bits represented in 10 bytes - integer*1 codeword(128) !Encoded bits before re-ordering - integer*1 msgbits(80) !72-bit message + 8-bit hash - integer*1 bitseq(144) !Tone #s, data and sync (values 0-1) - integer*1 i1hash(4) - integer*1 s8(8) - real*8 pp(12) - real*8 xi(864),xq(864),pi,twopi - data s8/0,1,1,1,0,0,1,0/ - equivalence (ihash,i1hash) - logical first - data first/.true./ - save - - if(first) then - first=.false. - nsym=128 - pi=4.0*atan(1.0) - twopi=8.*atan(1.0) - do i=1,12 - pp(i)=sin((i-1)*pi/12) - enddo - endif - - if(msg0(1:1).eq.'@') then !Generate a fixed tone - read(msg0(2:5),*,end=1,err=1) nfreq !at specified frequency - go to 2 -1 nfreq=1000 -2 i4tone(1)=nfreq - else - message=msg0 - do i=1,22 - if(ichar(message(i:i)).eq.0) then - message(i:)=' ' - exit - endif - enddo - - do i=1,22 !Strip leading blanks - if(message(1:1).ne.' ') exit - message=message(i+1:) - enddo - - if(message(1:1).eq.'<') then - call genmsk40(message,msgsent,ichk,i4tone,itype) - if(itype.lt.0) go to 999 - i4tone(41)=-40 - go to 999 - endif - - call packmsg(message,i4Msg6BitWords,itype) !Pack into 12 6-bit bytes - call unpackmsg(i4Msg6BitWords,msgsent) !Unpack to get msgsent - - if(ichk.eq.1) go to 999 - i4=0 - ik=0 - im=0 - do i=1,12 - nn=i4Msg6BitWords(i) - do j=1, 6 - ik=ik+1 - i4=i4+i4+iand(1,ishft(nn,j-6)) - i4=iand(i4,255) - if(ik.eq.8) then - im=im+1 - i1Msg8BitBytes(im)=i4 - ik=0 - endif - enddo - enddo - - ihash=nhash(c_loc(i1Msg8BitBytes),int(9,c_size_t),146) - ihash=2*iand(ihash,32767) !Generate the 8-bit hash - i1Msg8BitBytes(10)=i1hash(1) !Hash code to byte 10 - - mbit=0 - do i=1, 10 - i1=i1Msg8BitBytes(i) - do ibit=1,8 - mbit=mbit+1 - msgbits(mbit)=iand(1,ishft(i1,ibit-8)) - enddo - enddo - - call encode_msk144(msgbits,codeword) - -!Create 144-bit channel vector: -!8-bit sync word + 48 bits + 8-bit sync word + 80 bits - bitseq=0 - bitseq(1:8)=s8 - bitseq(9:56)=codeword(1:48) - bitseq(57:64)=s8 - bitseq(65:144)=codeword(49:128) - bitseq=2*bitseq-1 - - xq(1:6)=bitseq(1)*pp(7:12) !first bit is mapped to 1st half-symbol on q - do i=1,71 - is=(i-1)*12+7 - xq(is:is+11)=bitseq(2*i+1)*pp - enddo - xq(864-5:864)=bitseq(1)*pp(1:6) !last half symbol - do i=1,72 - is=(i-1)*12+1 - xi(is:is+11)=bitseq(2*i)*pp - enddo -! Map I and Q to tones. - i4tone=0 - do i=1,72 - i4tone(2*i-1)=(bitseq(2*i)*bitseq(2*i-1)+1)/2; - i4tone(2*i)=-(bitseq(2*i)*bitseq(mod(2*i,144)+1)-1)/2; - enddo - endif - -! Flip polarity - i4tone=-i4tone+1 - -999 return -end subroutine genmsk144 diff --git a/lib/genmsk_short.f90 b/lib/genmsk_short.f90 deleted file mode 100644 index f1b555f34..000000000 --- a/lib/genmsk_short.f90 +++ /dev/null @@ -1,66 +0,0 @@ -subroutine genmsk_short(msg,msgsent,ichk,itone,itype) - - use hashing - character*22 msg,msgsent - character*3 crpt,rpt(0:7) - logical first - integer itone(35) - integer ig24(0:4096-1) !Codewords for Golay (24,12) code - integer b11(11) - data b11/1,1,1,0,0,0,1,0,0,1,0/ !Barker 11 code - data rpt /'26 ','27 ','28 ','R26','R27','R28','RRR','73 '/ - data first/.true./ - save first,ig24 - - if(first) then - call golay24_table(ig24) !Define the Golay(24,12) codewords - first=.false. - endif - - itype=-1 - msgsent='*** bad message ***' - itone=0 - i1=index(msg,'>') - if(i1.lt.9) go to 900 - call fmtmsg(msg,iz) - crpt=msg(i1+2:i1+5) - do i=0,7 - if(crpt.eq.rpt(i)) go to 10 - enddo - go to 900 - -10 irpt=i !Report index, 0-7 - if(ichk.lt.10000) then - call hash(msg(2:i1-1),i1-2,ihash) - ihash=iand(ihash,511) !9-bit hash for the two callsigns - ig=8*ihash + irpt !12-bit message information - else - ig=ichk-10000 - endif - ncodeword=ig24(ig) - itone(1:11)=b11 !Insert the Barker-11 code - n=2**24 - do i=12,35 !Insert codeword into itone array - n=n/2 - itone(i)=0 - if(iand(ncodeword,n).ne.0) itone(i)=1 - enddo - msgsent=msg - itype=7 - - n=count(itone(1:35).eq.0) - if(mod(n,2).ne.0) stop 'Parity error in genmsk_short.' - -900 return -end subroutine genmsk_short - -subroutine hash_calls(calls,ih9) - - use hashing - character*(*) calls - i1=index(calls,'>') - call hash(calls(2:i1-1),i1-2,ih9) - ih9=iand(ih9,511) !9-bit hash for the two callsigns - - return -end subroutine hash_calls diff --git a/lib/ldpcsim144.f90 b/lib/ldpcsim144.f90 deleted file mode 100644 index 3121ada2c..000000000 --- a/lib/ldpcsim144.f90 +++ /dev/null @@ -1,175 +0,0 @@ -program ldpcsim - -use, intrinsic :: iso_c_binding -use iso_c_binding, only: c_loc,c_size_t -use hashing -use packjt -parameter(NRECENT=10) -character*12 recent_calls(NRECENT) -character*22 msg,msgsent,msgreceived -character*8 arg -integer*1, allocatable :: codeword(:), decoded(:), message(:) -integer*1, target:: i1Msg8BitBytes(10) -integer*1 i1hash(4) -integer*1 msgbits(80) -integer*4 i4Msg6BitWords(13) -integer ihash -integer nerrtot(0:128),nerrdec(0:128) -real*8, allocatable :: lratio(:), rxdata(:), rxavgd(:) -real, allocatable :: yy(:), llr(:) -equivalence(ihash,i1hash) - -do i=1,NRECENT - recent_calls(i)=' ' -enddo -nerrtot=0 -nerrdec=0 - -nargs=iargc() -if(nargs.ne.4) then - print*,'Usage: ldpcsim niter navg #trials s ' - print*,'eg: ldpcsim 10 1 1000 0.75' - return -endif -call getarg(1,arg) -read(arg,*) max_iterations -call getarg(2,arg) -read(arg,*) navg -call getarg(3,arg) -read(arg,*) ntrials -call getarg(4,arg) -read(arg,*) s - -! don't count hash bits as data bits -K=72 -N=128 -rate=real(K)/real(N) - -write(*,*) "rate: ",rate - -write(*,*) "niter= ",max_iterations," navg= ",navg," s= ",s - -allocate ( codeword(N), decoded(K), message(K) ) -allocate ( lratio(N), rxdata(N), rxavgd(N), yy(N), llr(N) ) - -msg="K9AN K1JT EN50" - call packmsg(msg,i4Msg6BitWords,itype) !Pack into 12 6-bit bytes - call unpackmsg(i4Msg6BitWords,msgsent) !Unpack to get msgsent - write(*,*) "message sent ",msgsent - - i4=0 - ik=0 - im=0 - do i=1,12 - nn=i4Msg6BitWords(i) - do j=1, 6 - ik=ik+1 - i4=i4+i4+iand(1,ishft(nn,j-6)) - i4=iand(i4,255) - if(ik.eq.8) then - im=im+1 -! if(i4.gt.127) i4=i4-256 - i1Msg8BitBytes(im)=i4 - ik=0 - endif - enddo - enddo - - ihash=nhash(c_loc(i1Msg8BitBytes),int(9,c_size_t),146) - ihash=2*iand(ihash,32767) !Generate the 8-bit hash - i1Msg8BitBytes(10)=i1hash(1) !Hash code to byte 10 - mbit=0 - do i=1, 10 - i1=i1Msg8BitBytes(i) - do ibit=1,8 - mbit=mbit+1 - msgbits(mbit)=iand(1,ishft(i1,ibit-8)) - enddo - enddo - call encode_msk144(msgbits,codeword) - call init_random_seed() - -write(*,*) "Eb/N0 SNR2500 ngood nundetected nbadhash sigma" -do idb = 14,-6,-1 - db=idb/2.0-1.0 - sigma=1/sqrt( 2*rate*(10**(db/10.0)) ) - ngood=0 - nue=0 - nbadhash=0 - - do itrial=1, ntrials - rxavgd=0d0 - do iav=1,navg - call sgran() -! Create a realization of a noisy received word - do i=1,N - rxdata(i) = 2.0*codeword(i)-1.0 + sigma*gran() - enddo - rxavgd=rxavgd+rxdata - enddo - rxdata=rxavgd - nerr=0 - do i=1,N - if( rxdata(i)*(2*codeword(i)-1.0) .lt. 0 ) nerr=nerr+1 - enddo - nerrtot(nerr)=nerrtot(nerr)+1 - -! Correct signal normalization is important for this decoder. - rxav=sum(rxdata)/N - rx2av=sum(rxdata*rxdata)/N - rxsig=sqrt(rx2av-rxav*rxav) - rxdata=rxdata/rxsig -! To match the metric to the channel, s should be set to the noise standard deviation. -! For now, set s to the value that optimizes decode probability near threshold. -! The s parameter can be tuned to trade a few tenth's dB of threshold for an order of -! magnitude in UER - if( s .lt. 0 ) then - ss=sigma - else - ss=s - endif - - llr=2.0*rxdata/(ss*ss) - lratio=exp(llr) - yy=rxdata - -! max_iterations is max number of belief propagation iterations -! call ldpc_decode(lratio, decoded, max_iterations, niterations, max_dither, ndither) -! call amsdecode(yy, max_iterations, decoded, niterations) -! call bitflipmsk144(rxdata, decoded, niterations) - call bpdecode144(llr, max_iterations, decoded, niterations) - -! If the decoder finds a valid codeword, niterations will be .ge. 0. - if( niterations .ge. 0 ) then - call extractmessage144(decoded,msgreceived,nhashflag,recent_calls,nrecent) - if( nhashflag .ne. 1 ) then - nbadhash=nbadhash+1 - endif - nueflag=0 - -! Check the message plus hash against what was sent. - do i=1,K - if( msgbits(i) .ne. decoded(i) ) then - nueflag=1 - endif - enddo - if( nhashflag .eq. 1 .and. nueflag .eq. 0 ) then - ngood=ngood+1 - nerrdec(nerr)=nerrdec(nerr)+1 - else if( nhashflag .eq. 1 .and. nueflag .eq. 1 ) then - nue=nue+1; - endif - endif - enddo - snr2500=db-3.5 - write(*,"(f4.1,4x,f5.1,1x,i8,1x,i8,1x,i8,8x,f5.2)") db,snr2500,ngood,nue,nbadhash,ss - -enddo - -open(unit=23,file='nerrhisto.dat',status='unknown') -do i=0,128 - write(23,'(i4,2x,i10,i10,f10.2)') i,nerrdec(i),nerrtot(i),real(nerrdec(i))/real(nerrtot(i)+1e-10) -enddo -close(23) - -end program ldpcsim diff --git a/lib/msk144d.f90 b/lib/msk144d.f90 deleted file mode 100644 index 60c8b0c1d..000000000 --- a/lib/msk144d.f90 +++ /dev/null @@ -1,136 +0,0 @@ -program msk144d - - ! Test the msk144 decoder for WSJT-X - - use options - use timer_module, only: timer - use timer_impl, only: init_timer - use readwav - - character c - character*80 line - character*512 datadir - character*500 infile - character*12 mycall,hiscall - character*6 mygrid - character(len=500) optarg - - logical :: display_help=.false. - logical*1 bShMsgs - logical*1 btrain - logical*1 bswl - - type(wav_header) :: wav - - integer*2 id2(30*12000) - integer*2 ichunk(7*1024) - - real*8 pcoeffs(5) - - type (option) :: long_options(9) = [ & - option ('ndepth',.true.,'c','ndepth',''), & - option ('dxcall',.true.,'d','hiscall',''), & - option ('evemode',.true.,'e','Must be used with -s.',''), & - option ('frequency',.true.,'f','rxfreq',''), & - option ('help',.false.,'h','Display this help message',''), & - option ('mycall',.true.,'m','mycall',''), & - option ('nftol',.true.,'n','nftol',''), & - option ('rxequalize',.false.,'r','Rx Equalize',''), & - option ('short',.false.,'s','enable Sh','') & - ] - t0=0.0 - ndepth=3 - ntol=100 - nrxfreq=1500 - mycall='' - mygrid='EN50WC' - hiscall='' - bShMsgs=.false. - btrain=.false. - bswl=.false. - datadir='.' - pcoeffs=0.d0 - - do - call getopt('c:d:ef:hm:n:rs',long_options,c,optarg,narglen,nstat,noffset,nremain,.true.) - if( nstat .ne. 0 ) then - exit - end if - select case (c) - case ('c') - read (optarg(:narglen), *) ndepth - case ('d') - read (optarg(:narglen), *) hiscall - case ('e') - bswl=.true. - case ('f') - read (optarg(:narglen), *) nrxfreq - case ('h') - display_help = .true. - case ('m') - read (optarg(:narglen), *) mycall - case ('n') - read (optarg(:narglen), *) ntol - case ('r') - btrain=.true. - case ('s') - bShMsgs=.true. - end select - end do - - if(display_help .or. nstat.lt.0 .or. nremain.lt.1) then - print *, '' - print *, 'Usage: msk144d [OPTIONS] file1 [file2 ...]' - print *, '' - print *, ' msk144 decode pre-recorded .WAV file(s)' - print *, '' - print *, 'OPTIONS:' - do i = 1, size (long_options) - call long_options(i) % print (6) - end do - go to 999 - endif - - call init_timer ('timer.out') - call timer('msk144 ',0) - ndecoded=0 - do ifile=noffset+1,noffset+nremain - call get_command_argument(ifile,optarg,narglen) - infile=optarg(:narglen) - call timer('read ',0) - call wav%read (infile) - i1=index(infile,'.wav') - if( i1 .eq. 0 ) i1=index(infile,'.WAV') - read(infile(i1-6:i1-1),*,err=998) nutc - inquire(FILE=infile,SIZE=isize) - npts=min((isize-216)/2,360000) - read(unit=wav%lun) id2(1:npts) - close(unit=wav%lun) - call timer('read ',1) - - do i=1,npts-7*1024+1,7*512 - ichunk=id2(i:i+7*1024-1) - tsec=(i-1)/12000.0 - tt=sum(float(abs(id2(i:i+7*512-1)))) - if( tt .ne. 0.0 ) then - call mskrtd(ichunk,nutc,tsec,ntol,nrxfreq,ndepth,mycall,mygrid,hiscall,bShMsgs, & - btrain,pcoeffs,bswl,datadir,line) - if( index(line,"&") .ne. 0 .or. & - index(line,"^") .ne. 0 .or. & - index(line,"!") .ne. 0 .or. & - index(line,"@") .ne. 0 ) then - write(*,*) line - endif - endif - enddo - enddo - - call timer('msk144 ',1) - call timer('msk144 ',101) - go to 999 - -998 print*,'Cannot read from file:' - print*,infile - -999 continue -end program msk144d diff --git a/lib/msk144sd.f90 b/lib/msk144sd.f90 deleted file mode 100644 index 267fff545..000000000 --- a/lib/msk144sd.f90 +++ /dev/null @@ -1,197 +0,0 @@ -program msk144sd -! -! A simple decoder for slow msk144. -! Can be used as a (slow) brute-force multi-decoder by looping -! over a set of carrier frequencies. -! - use options - use timer_module, only: timer - use timer_impl, only: init_timer - use readwav - - parameter (NRECENT=10) - parameter (NSPM=864) - parameter (NPATTERNS=4) - - character ch - character*80 line - character*500 infile - character*12 mycall,hiscall - character*6 mygrid - character(len=500) optarg - character*22 msgreceived - character*12 recent_calls(NRECENT) - - complex cdat(30*375) - complex c(NSPM) - complex ct(NSPM) - - real softbits(144) - real xmc(NPATTERNS) - - logical :: display_help=.false. - - type(wav_header) :: wav - - integer iavmask(8) - integer iavpatterns(8,NPATTERNS) - integer npkloc(10) - - integer*2 id2(30*12000) - integer*2 ichunk(7*1024) - - data iavpatterns/ & - 1,1,1,1,0,0,0,0, & - 0,1,1,1,1,0,0,0, & - 0,0,1,1,1,1,0,0, & - 1,1,1,1,1,1,0,0/ - data xmc/2.0,4.5,2.5,3.0/ - - type (option) :: long_options(2) = [ & - option ('frequency',.true.,'f','rxfreq',''), & - option ('help',.false.,'h','Display this help message','') & - ] - t0=0.0 - ntol=100 - nrxfreq=1500 - - do - call getopt('f:h',long_options,ch,optarg,narglen,nstat,noffset,nremain,.true.) - if( nstat .ne. 0 ) then - exit - end if - select case (ch) - case ('f') - read (optarg(:narglen), *) nrxfreq - case ('h') - display_help = .true. - end select - end do - - if(display_help .or. nstat.lt.0 .or. nremain.lt.1) then - print *, '' - print *, 'Usage: msk144sd [OPTIONS] file1 [file2 ...]' - print *, '' - print *, ' decode pre-recorded .WAV file(s)' - print *, '' - print *, 'OPTIONS:' - do i = 1, size (long_options) - call long_options(i) % print (6) - end do - go to 999 - endif - - call init_timer ('timer.out') - call timer('msk144 ',0) - ndecoded=0 - do ifile=noffset+1,noffset+nremain - call get_command_argument(ifile,optarg,narglen) - infile=optarg(:narglen) - call timer('read ',0) - call wav%read (infile) - i1=index(infile,'.wav') - if( i1 .eq. 0 ) i1=index(infile,'.WAV') - read(infile(i1-6:i1-1),*,err=998) nutc - inquire(FILE=infile,SIZE=isize) - npts=min((isize-216)/2,360000) - read(unit=wav%lun) id2(1:npts) - close(unit=wav%lun) - call timer('read ',1) - -! do if=1,89 ! brute force multi-decoder - fo=nrxfreq -! fo=(if-1)*25.0+300.0 - call msksddc(id2,npts,fo,cdat) - np=npts/32 - ntol=200 ! actual ntol is ntol/32=6.25 Hz. Detection window is 12.5 Hz wide - fc=1500.0 - call msk144spd(cdat,np,ntol,ndecodesuccess,msgreceived,fc,fest,tdec,navg,ct, & - softbits,recent_calls,nrecent) - nsnr=0 ! need an snr estimate - if( ndecodesuccess .eq. 1 ) then - fest=fo+fest-fc ! fudging because spd thinks input signal is at 1500 Hz - goto 900 - endif -! If short ping decoder doesn't find a decode - npat=NPATTERNS - do iavg=1,npat - iavmask=iavpatterns(1:8,iavg) - navg=sum(iavmask) - deltaf=4.0/real(navg) ! search increment for frequency sync - npeaks=4 - ntol=200 - fc=1500.0 - call msk144sync(cdat(1:6*NSPM),6,ntol,deltaf,iavmask,npeaks,fc, & - fest,npkloc,nsyncsuccess,xmax,c) - if( nsyncsuccess .eq. 0 ) cycle - - do ipk=1,npeaks - do is=1,3 - ic0=npkloc(ipk) - if(is.eq.2) ic0=max(1,ic0-1) - if(is.eq.3) ic0=min(NSPM,ic0+1) - ct=cshift(c,ic0-1) - call msk144decodeframe(ct,softbits,msgreceived,ndecodesuccess, & - recent_calls,nrecent) - if(ndecodesuccess .gt. 0) then - tdec=tsec+xmc(iavg)*tframe - fest=fo+(fest-fc)/32.0 - goto 900 - endif - enddo !Slicer dither - enddo !Peak loop - enddo - -! enddo -900 continue - if( ndecodesuccess .gt. 0 ) then - write(*,1020) nutc,nsnr,tdec,nint(fest),' % ',msgreceived,navg -1020 format(i6.6,i4,f5.1,i5,a3,a22,i4) - endif - enddo - - call timer('msk144 ',1) - call timer('msk144 ',101) - go to 999 - -998 print*,'Cannot read from file:' - print*,infile - -999 continue -end program msk144sd - -subroutine msksddc(id2,npts,fc,cdat) - -! The msk144 detector/demodulator/decoder will decode signals -! with carrier frequency, fc, in the range fN/4 +/- 0.03333*fN. -! -! For slow MSK144 with nslow=32: -! fs=12000/32=375 Hz, fN=187.5 Hz -! -! This routine accepts input samples with fs=12000 Hz. It -! downconverts and decimates by 32 to center a signal with input carrier -! frequency fc at new carrier frequency 1500/32=46.875 Hz. -! The analytic signal is returned. - - parameter (NFFT1=30*12000,NFFT2=30*375) - integer*2 id2(npts) - complex cx(0:NFFT1) - complex cdat(30*375) - - dt=1.0/12000.0 - df=1.0/(NFFT1*dt) - icenter=int(fc/df+0.5) - i46p875=int(46.875/df+0.5) - ishift=icenter-i46p875 - cx=cmplx(0.0,0.0) - cx(1:npts)=id2 - call four2a(cx,NFFT1,1,-1,1) - cx=cshift(cx,ishift) - cx(1)=0.5*cx(1) - cx(2*i46p875+1:)=cmplx(0.0,0.0) - call four2a(cx,NFFT2,1,1,1) - cdat(1:npts/32)=cx(0:npts/32-1)/NFFT1 - return - -end subroutine msksddc - diff --git a/lib/msk144sim.f90 b/lib/msk144sim.f90 index 75c6a1e64..7b2a8a00b 100644 --- a/lib/msk144sim.f90 +++ b/lib/msk144sim.f90 @@ -13,10 +13,10 @@ program msk144sim integer itone(144) !Message bits nargs=iargc() - if(nargs.ne.6) then - print*,'Usage: msk144sim message freq width nslow snr nfiles' - print*,'Example: msk144sim "K1ABC W9XYZ EN37" 1500 0.12 1 2 1' - print*,' msk144sim "K1ABC W9XYZ EN37" 1500 2.5 32 15 1' + if(nargs.ne.5) then + print*,'Usage: msk144sim message freq width snr nfiles' + print*,'Example: msk144sim "K1ABC W9XYZ EN37" 1500 0.12 2 1' + print*,' msk144sim "K1ABC W9XYZ EN37" 1500 2.5 15 1' go to 999 endif call getarg(1,msg) @@ -25,10 +25,8 @@ program msk144sim call getarg(3,arg) read(arg,*) width call getarg(4,arg) - read(arg,*) nslow - call getarg(5,arg) read(arg,*) snrdb - call getarg(6,arg) + call getarg(5,arg) read(arg,*) nfiles !sig is the peak amplitude of the ping. @@ -50,9 +48,9 @@ program msk144sim twopi=8.d0*atan(1.d0) nsym=144 - nsps=6*nslow + nsps=6 if( itone(41) .lt. 0 ) nsym=40 - baud=2000.d0/nslow + baud=2000.d0 dphi0=twopi*(freq-0.25d0*baud)/12000.d0 dphi1=twopi*(freq+0.25d0*baud)/12000.d0 phi=0.0 @@ -79,7 +77,7 @@ program msk144sim go to 999 endif - if(nslow.eq.1) call makepings(pings,NMAX,width,sig) + call makepings(pings,NMAX,width,sig) ! call sgran() do ifile=1,nfiles !Loop over requested number of files @@ -92,8 +90,7 @@ program msk144sim fac=sqrt(6000.0/2500.0) do i=0,NMAX-1 xx=gran() - if(nslow.eq.1) wave(i)=pings(i)*waveform(i) + fac*xx - if(nslow.gt.1) wave(i)=sig*waveform(i) + fac*xx + wave(i)=pings(i)*waveform(i) + fac*xx iwave(i)=30.0*wave(i) enddo diff --git a/lib/platanh.f90 b/lib/platanh.f90 new file mode 100644 index 000000000..e610366d7 --- /dev/null +++ b/lib/platanh.f90 @@ -0,0 +1,24 @@ +subroutine platanh(x,y) + isign=+1 + z=x + if( x.lt.0 ) then + isign=-1 + z=abs(x) + endif + if( z.le. 0.664 ) then + y=x/0.83 + return + elseif( z.le. 0.9217 ) then + y=isign*(z-0.4064)/0.322 + return + elseif( z.le. 0.9951 ) then + y=isign*(z-0.8378)/0.0524 + return + elseif( z.le. 0.9998 ) then + y=isign*(z-0.9914)/0.0012 + return + else + y=isign*7.0 + return + endif +end subroutine platanh diff --git a/lib/pltanh.f90 b/lib/pltanh.f90 new file mode 100644 index 000000000..4c6c2b6d6 --- /dev/null +++ b/lib/pltanh.f90 @@ -0,0 +1,24 @@ +subroutine pltanh(x,y) + isign=+1 + z=x + if( x.lt.0 ) then + isign=-1 + z=abs(x) + endif + if( z.le. 0.8 ) then + y=0.83*x + return + elseif( z.le. 1.6 ) then + y=isign*(0.322*z+0.4064) + return + elseif( z.le. 3.0 ) then + y=isign*(0.0524*z+0.8378) + return + elseif( z.lt. 7.0 ) then + y=isign*(0.0012*z+0.9914) + return + else + y=isign*0.9998 + return + endif +end subroutine pltanh diff --git a/lib/unpackmsg144.f90 b/lib/unpackmsg144.f90 deleted file mode 100644 index 96423ff69..000000000 --- a/lib/unpackmsg144.f90 +++ /dev/null @@ -1,117 +0,0 @@ - subroutine unpackmsg144(dat,msg,c1,c2) -! special unpackmsg for MSK144 - returns call1 and call2 to enable -! maintenance of a recent-calls-heard list - - use packjt - parameter (NBASE=37*36*10*27*27*27) - parameter (NGBASE=180*180) - integer dat(12) - character c1*12,c2*12,grid*4,msg*22,grid6*6,psfx*4,junk2*4 - logical cqnnn - - cqnnn=.false. - nc1=ishft(dat(1),22) + ishft(dat(2),16) + ishft(dat(3),10)+ & - ishft(dat(4),4) + iand(ishft(dat(5),-2),15) - - nc2=ishft(iand(dat(5),3),26) + ishft(dat(6),20) + & - ishft(dat(7),14) + ishft(dat(8),8) + ishft(dat(9),2) + & - iand(ishft(dat(10),-4),3) - - ng=ishft(iand(dat(10),15),12) + ishft(dat(11),6) + dat(12) - - if(ng.ge.32768) then - call unpacktext(nc1,nc2,ng,msg) - c1(1:12)=' ' - c2(1:12)=' ' - go to 100 - endif - - call unpackcall(nc1,c1,iv2,psfx) - if(iv2.eq.0) then - ! This is an "original JT65" message - if(nc1.eq.NBASE+1) c1='CQ ' - if(nc1.eq.NBASE+2) c1='QRZ ' - nfreq=nc1-NBASE-3 - if(nfreq.ge.0 .and. nfreq.le.999) then - write(c1,1002) nfreq - 1002 format('CQ ',i3.3) - cqnnn=.true. - endif - endif - - call unpackcall(nc2,c2,junk1,junk2) - call unpackgrid(ng,grid) - - if(iv2.gt.0) then - ! This is a JT65v2 message - do i=1,4 - if(ichar(psfx(i:i)).eq.0) psfx(i:i)=' ' - enddo - - n1=len_trim(psfx) - n2=len_trim(c2) - if(iv2.eq.1) msg='CQ '//psfx(:n1)//'/'//c2(:n2)//' '//grid - if(iv2.eq.2) msg='QRZ '//psfx(:n1)//'/'//c2(:n2)//' '//grid - if(iv2.eq.3) msg='DE '//psfx(:n1)//'/'//c2(:n2)//' '//grid - if(iv2.eq.4) msg='CQ '//c2(:n2)//'/'//psfx(:n1)//' '//grid - if(iv2.eq.5) msg='QRZ '//c2(:n2)//'/'//psfx(:n1)//' '//grid - if(iv2.eq.6) msg='DE '//c2(:n2)//'/'//psfx(:n1)//' '//grid - if(iv2.eq.7) msg='DE '//c2(:n2)//' '//grid - if(iv2.eq.8) msg=' ' - go to 100 - else - - endif - - grid6=grid//'ma' - call grid2k(grid6,k) - if(k.ge.1 .and. k.le.450) call getpfx2(k,c1) - if(k.ge.451 .and. k.le.900) call getpfx2(k,c2) - - i=index(c1,char(0)) - if(i.ge.3) c1=c1(1:i-1)//' ' - i=index(c2,char(0)) - if(i.ge.3) c2=c2(1:i-1)//' ' - - msg=' ' - j=0 - if(cqnnn) then - msg=c1//' ' - j=7 !### ??? ### - go to 10 - endif - - do i=1,12 - j=j+1 - msg(j:j)=c1(i:i) - if(c1(i:i).eq.' ') go to 10 - enddo - j=j+1 - msg(j:j)=' ' - - 10 do i=1,12 - if(j.le.21) j=j+1 - msg(j:j)=c2(i:i) - if(c2(i:i).eq.' ') go to 20 - enddo - if(j.le.21) j=j+1 - msg(j:j)=' ' - - 20 if(k.eq.0) then - do i=1,4 - if(j.le.21) j=j+1 - msg(j:j)=grid(i:i) - enddo - if(j.le.21) j=j+1 - msg(j:j)=' ' - endif - - 100 continue - if(msg(1:6).eq.'CQ9DX ') msg(3:3)=' ' - if(msg(1:2).eq.'E9' .and. & - msg(3:3).ge.'A' .and. msg(3:3).le.'Z' .and. & - msg(4:4).ge.'A' .and. msg(4:4).le.'Z' .and. & - msg(5:5).eq.' ') msg='CQ '//msg(3:) - - return - end subroutine unpackmsg144 From 59c74905f3527f66d4cb669577be9d03bb324f9e Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Fri, 23 Nov 2018 22:18:43 +0000 Subject: [PATCH 19/27] Force the Aqua theme on macOS to avoid issues with the Mojave dark theme Qt 5.12 is exected to sort out issues with the Mojave dark theme, until then we just have to stop it trying to make WSJT-X dark. --- Darwin/Info.plist.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Darwin/Info.plist.in b/Darwin/Info.plist.in index b62e03682..830975da5 100644 --- a/Darwin/Info.plist.in +++ b/Darwin/Info.plist.in @@ -36,5 +36,7 @@ NSApplication NSHighResolutionCapable True + NSRequiresAquaSystemAppearance + From ef053e5a9fec6f19980e0b0d56f4a5940353ac86 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Sat, 24 Nov 2018 09:09:50 -0500 Subject: [PATCH 20/27] Protect against execution of on_pbTxMode() when not in "JT9+JT65" mode. --- widgets/mainwindow.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 186f5f40c..26d12a698 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -6482,15 +6482,17 @@ void MainWindow::on_readFreq_clicked() void MainWindow::on_pbTxMode_clicked() { - if(m_modeTx=="JT9") { - m_modeTx="JT65"; - ui->pbTxMode->setText("Tx JT65 #"); - } else { - m_modeTx="JT9"; - ui->pbTxMode->setText("Tx JT9 @"); + if(m_mode=="JT9+JT65") { + if(m_modeTx=="JT9") { + m_modeTx="JT65"; + ui->pbTxMode->setText("Tx JT65 #"); + } else { + m_modeTx="JT9"; + ui->pbTxMode->setText("Tx JT9 @"); + } + m_wideGraph->setModeTx(m_modeTx); + statusChanged(); } - m_wideGraph->setModeTx(m_modeTx); - statusChanged(); } void MainWindow::setXIT(int n, Frequency base) From 7b4b4074554c38ef1a026c90c2715da2c2cba9f0 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Sun, 25 Nov 2018 21:53:38 +0000 Subject: [PATCH 21/27] Improved layout of settings frequencies and station details tables --- Configuration.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Configuration.cpp b/Configuration.cpp index 03f5b2ac2..a75cacc68 100644 --- a/Configuration.cpp +++ b/Configuration.cpp @@ -1091,6 +1091,8 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network frequencies_.sort (FrequencyList_v2::frequency_column); ui_->frequencies_table_view->setModel (&next_frequencies_); + ui_->frequencies_table_view->horizontalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents); + ui_->frequencies_table_view->verticalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents); ui_->frequencies_table_view->sortByColumn (FrequencyList_v2::frequency_column, Qt::AscendingOrder); ui_->frequencies_table_view->setColumnHidden (FrequencyList_v2::frequency_mhz_column, true); @@ -1131,6 +1133,8 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network // stations_.sort (StationList::band_column); ui_->stations_table_view->setModel (&next_stations_); + ui_->stations_table_view->horizontalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents); + ui_->stations_table_view->verticalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents); ui_->stations_table_view->sortByColumn (StationList::band_column, Qt::AscendingOrder); // stations delegates From 86fb68f305ab41d27a0bd6980897826ea47cae54 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Sun, 25 Nov 2018 21:55:19 +0000 Subject: [PATCH 22/27] Set foreign key item delegate references key values combo box size according to contents --- item_delegates/ForeignKeyDelegate.cpp | 22 +++++++++++++++++++++- item_delegates/ForeignKeyDelegate.hpp | 5 +++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/item_delegates/ForeignKeyDelegate.cpp b/item_delegates/ForeignKeyDelegate.cpp index 09df6a731..204738163 100644 --- a/item_delegates/ForeignKeyDelegate.cpp +++ b/item_delegates/ForeignKeyDelegate.cpp @@ -1,7 +1,10 @@ #include "ForeignKeyDelegate.hpp" +#include #include - +#include +#include +#include #include "CandidateKeyFilter.hpp" ForeignKeyDelegate::ForeignKeyDelegate (QAbstractItemModel const * referenced_model @@ -40,3 +43,20 @@ QWidget * ForeignKeyDelegate::createEditor (QWidget * parent editor->setSizeAdjustPolicy (QComboBox::AdjustToContents); return editor; } + +QSize ForeignKeyDelegate::sizeHint (QStyleOptionViewItem const& option, QModelIndex const& index) const +{ + auto size_hint = QStyledItemDelegate::sizeHint (option, index); + QFontMetrics metrics {option.font}; + QStyleOptionComboBox combo_box_option; + combo_box_option.rect = option.rect; + combo_box_option.state = option.state | QStyle::State_Enabled; + for (auto row = 0; row < candidate_key_filter_->rowCount (); ++row) + { + size_hint = size_hint.expandedTo (qApp->style ()->sizeFromContents (QStyle::CT_ComboBox + , &combo_box_option + , {metrics.width (candidate_key_filter_->data (candidate_key_filter_->index (row, 0)).toString ()) + , metrics.height ()})); + } + return size_hint; +} diff --git a/item_delegates/ForeignKeyDelegate.hpp b/item_delegates/ForeignKeyDelegate.hpp index 01b03ee5b..4f58f4608 100644 --- a/item_delegates/ForeignKeyDelegate.hpp +++ b/item_delegates/ForeignKeyDelegate.hpp @@ -33,9 +33,10 @@ public: , int referencing_key_role = Qt::EditRole); ~ForeignKeyDelegate (); - QWidget * createEditor (QWidget * parent, QStyleOptionViewItem const&, QModelIndex const&) const override; - private: + QWidget * createEditor (QWidget * parent, QStyleOptionViewItem const&, QModelIndex const&) const override; + QSize sizeHint (QStyleOptionViewItem const&, QModelIndex const&) const override; + QScopedPointer candidate_key_filter_; }; From 314d8a645b740b1adc2358501e7f1e391dbbd08d Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Sun, 25 Nov 2018 22:13:15 +0000 Subject: [PATCH 23/27] Replace deprecated Qt algorithms with C++ Standard Library equivalents --- Configuration.cpp | 8 ++++---- models/FrequencyList.cpp | 14 +++++--------- models/StationList.cpp | 13 ++++--------- widgets/mainwindow.cpp | 5 +++-- 4 files changed, 16 insertions(+), 24 deletions(-) diff --git a/Configuration.cpp b/Configuration.cpp index a75cacc68..bf57206b2 100644 --- a/Configuration.cpp +++ b/Configuration.cpp @@ -2279,10 +2279,10 @@ void Configuration::impl::delete_selected_macros (QModelIndexList selected_rows) { // sort in reverse row order so that we can delete without changing // indices underneath us - qSort (selected_rows.begin (), selected_rows.end (), [] (QModelIndex const& lhs, QModelIndex const& rhs) - { - return rhs.row () < lhs.row (); // reverse row ordering - }); + std::sort (selected_rows.begin (), selected_rows.end (), [] (QModelIndex const& lhs, QModelIndex const& rhs) + { + return rhs.row () < lhs.row (); // reverse row ordering + }); // now delete them Q_FOREACH (auto index, selected_rows) diff --git a/models/FrequencyList.cpp b/models/FrequencyList.cpp index e50afe2a4..55844bdfa 100644 --- a/models/FrequencyList.cpp +++ b/models/FrequencyList.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -350,14 +351,6 @@ bool FrequencyList_v2::remove (Item f) return m_->removeRow (row); } -namespace -{ - bool row_is_higher (QModelIndex const& lhs, QModelIndex const& rhs) - { - return lhs.row () > rhs.row (); - } -} - bool FrequencyList_v2::removeDisjointRows (QModelIndexList rows) { bool result {true}; @@ -371,7 +364,10 @@ bool FrequencyList_v2::removeDisjointRows (QModelIndexList rows) } // reverse sort by row - qSort (rows.begin (), rows.end (), row_is_higher); + std::sort (rows.begin (), rows.end (), [] (QModelIndex const& lhs, QModelIndex const& rhs) + { + return rhs.row () < lhs.row (); // reverse row ordering + }); Q_FOREACH (auto index, rows) { if (result && !m_->removeRow (index.row ())) diff --git a/models/StationList.cpp b/models/StationList.cpp index cfd629ede..eeeb4d607 100644 --- a/models/StationList.cpp +++ b/models/StationList.cpp @@ -139,14 +139,6 @@ bool StationList::remove (Station s) return removeRow (row); } -namespace -{ - bool row_is_higher (QModelIndex const& lhs, QModelIndex const& rhs) - { - return lhs.row () > rhs.row (); - } -} - bool StationList::removeDisjointRows (QModelIndexList rows) { bool result {true}; @@ -160,7 +152,10 @@ bool StationList::removeDisjointRows (QModelIndexList rows) } // reverse sort by row - qSort (rows.begin (), rows.end (), row_is_higher); + std::sort (rows.begin (), rows.end (), [] (QModelIndex const& lhs, QModelIndex const& rhs) + { + return rhs.row () < lhs.row (); // reverse row ordering + }); Q_FOREACH (auto index, rows) { if (result && !m_->removeRow (index.row ())) diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index e0788a8f9..895569942 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -7923,9 +7924,9 @@ QString MainWindow::sortHoundCalls(QString t, int isort, int max_dB) if(isort>1) { if(bReverse) { - qSort(list.begin(),list.end(),qGreater()); + std::sort (list.begin (), list.end (), std::greater ()); } else { - qSort(list.begin(),list.end()); + std::sort (list.begin (), list.end ()); } } From db51726da2a955b49d73ab8f039ec89df66b05e5 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Sun, 25 Nov 2018 22:19:41 +0000 Subject: [PATCH 24/27] Move Fox log reset action to Fox log window context menu and allow deletes of QSOs Move to OnRowChange edit strategy for log tables so that deletes from view can be implemented cleanly. Improve layout of log view tables by resizing to contents. --- models/CabrilloLog.cpp | 17 ++++---- models/CabrilloLog.hpp | 4 +- models/FoxLog.cpp | 17 ++++---- models/FoxLog.hpp | 4 +- qt_db_helpers.hpp | 32 ++++++++++++--- widgets/AbstractLogWindow.cpp | 75 ++++++++++++++++++++++++++++++----- widgets/AbstractLogWindow.hpp | 13 +++++- widgets/CabrilloLogWindow.cpp | 29 +++++++++++--- widgets/CabrilloLogWindow.hpp | 6 ++- widgets/CabrilloLogWindow.ui | 8 +--- widgets/FoxLogWindow.cpp | 46 +++++++++++++++++++-- widgets/FoxLogWindow.hpp | 10 ++++- widgets/FoxLogWindow.ui | 12 +++--- widgets/mainwindow.cpp | 18 ++------- widgets/mainwindow.h | 1 - widgets/mainwindow.ui | 1 - 16 files changed, 214 insertions(+), 79 deletions(-) diff --git a/models/CabrilloLog.cpp b/models/CabrilloLog.cpp index f447bca79..965d6f957 100644 --- a/models/CabrilloLog.cpp +++ b/models/CabrilloLog.cpp @@ -52,7 +52,7 @@ CabrilloLog::impl::impl (Configuration const * configuration) SQL_error_check (export_query_, &QSqlQuery::prepare, "SELECT frequency, \"when\", exchange_sent, call, exchange_rcvd FROM cabrillo_log ORDER BY \"when\""); - setEditStrategy (QSqlTableModel::OnManualSubmit); + setEditStrategy (QSqlTableModel::OnRowChange); setTable ("cabrillo_log"); setHeaderData (fieldIndex ("frequency"), Qt::Horizontal, tr ("Freq(kHz)")); setHeaderData (fieldIndex ("when"), Qt::Horizontal, tr ("Date & Time(UTC)")); @@ -73,7 +73,7 @@ CabrilloLog::~CabrilloLog () { } -QAbstractItemModel * CabrilloLog::model () +QSqlTableModel * CabrilloLog::model () { return &*m_; } @@ -96,7 +96,6 @@ namespace bool CabrilloLog::add_QSO (Frequency frequency, QDateTime const& when, QString const& call , QString const& exchange_sent, QString const& exchange_received) { - ConditionalTransaction transaction {*m_}; auto record = m_->record (); record.setValue ("frequency", frequency / 1000ull); // kHz if (!when.isNull ()) @@ -111,13 +110,12 @@ bool CabrilloLog::add_QSO (Frequency frequency, QDateTime const& when, QString c set_value_maybe_null (record, "exchange_sent", exchange_sent); set_value_maybe_null (record, "exchange_rcvd", exchange_received); set_value_maybe_null (record, "band", m_->configuration_->bands ()->find (frequency)); - SQL_error_check (*m_, &QSqlTableModel::insertRecord, -1, record); - if (!transaction.submit (false)) + auto ok = m_->insertRecord (-1, record); + if (ok) { - transaction.revert (); - return false; + m_->select (); // to refresh views } - return true; + return ok; } bool CabrilloLog::dupe (Frequency frequency, QString const& call) const @@ -133,9 +131,12 @@ void CabrilloLog::reset () { if (m_->rowCount ()) { + m_->setEditStrategy (QSqlTableModel::OnManualSubmit); ConditionalTransaction transaction {*m_}; SQL_error_check (*m_, &QSqlTableModel::removeRows, 0, m_->rowCount (), QModelIndex {}); transaction.submit (); + m_->select (); // to refresh views + m_->setEditStrategy (QSqlTableModel::OnRowChange); } } diff --git a/models/CabrilloLog.hpp b/models/CabrilloLog.hpp index e328d0633..43b9e8f94 100644 --- a/models/CabrilloLog.hpp +++ b/models/CabrilloLog.hpp @@ -8,7 +8,7 @@ class Configuration; class QDateTime; class QString; -class QAbstractItemModel; +class QSqlTableModel; class QTextStream; class CabrilloLog final @@ -25,7 +25,7 @@ public: , QString const& report_sent, QString const& report_received); bool dupe (Frequency, QString const& call) const; - QAbstractItemModel * model (); + QSqlTableModel * model (); void reset (); void export_qsos (QTextStream&) const; diff --git a/models/FoxLog.cpp b/models/FoxLog.cpp index 43cdd2b8f..1503e5662 100644 --- a/models/FoxLog.cpp +++ b/models/FoxLog.cpp @@ -43,7 +43,7 @@ FoxLog::impl::impl () SQL_error_check (dupe_query_, &QSqlQuery::prepare, "SELECT COUNT(*) FROM fox_log WHERE call = :call AND band = :band"); - setEditStrategy (QSqlTableModel::OnManualSubmit); + setEditStrategy (QSqlTableModel::OnRowChange); setTable ("fox_log"); setHeaderData (fieldIndex ("when"), Qt::Horizontal, tr ("Date & Time(UTC)")); setHeaderData (fieldIndex ("call"), Qt::Horizontal, tr ("Call")); @@ -62,7 +62,7 @@ FoxLog::~FoxLog () { } -QAbstractItemModel * FoxLog::model () +QSqlTableModel * FoxLog::model () { return &*m_; } @@ -86,7 +86,6 @@ bool FoxLog::add_QSO (QDateTime const& when, QString const& call, QString const& , QString const& report_sent, QString const& report_received , QString const& band) { - ConditionalTransaction transaction {*m_}; auto record = m_->record (); if (!when.isNull ()) { @@ -101,13 +100,12 @@ bool FoxLog::add_QSO (QDateTime const& when, QString const& call, QString const& set_value_maybe_null (record, "report_sent", report_sent); set_value_maybe_null (record, "report_rcvd", report_received); set_value_maybe_null (record, "band", band); - SQL_error_check (*m_, &QSqlTableModel::insertRecord, -1, record); - if (!transaction.submit (false)) + auto ok = m_->insertRecord (-1, record); + if (ok) { - transaction.revert (); - return false; + m_->select (); // to refresh views } - return true; + return ok; } bool FoxLog::dupe (QString const& call, QString const& band) const @@ -123,8 +121,11 @@ void FoxLog::reset () { if (m_->rowCount ()) { + m_->setEditStrategy (QSqlTableModel::OnManualSubmit); ConditionalTransaction transaction {*m_}; SQL_error_check (*m_, &QSqlTableModel::removeRows, 0, m_->rowCount (), QModelIndex {}); transaction.submit (); + m_->select (); // to refresh views + m_->setEditStrategy (QSqlTableModel::OnRowChange); } } diff --git a/models/FoxLog.hpp b/models/FoxLog.hpp index 028f8205c..caa8e358f 100644 --- a/models/FoxLog.hpp +++ b/models/FoxLog.hpp @@ -6,7 +6,7 @@ class QDateTime; class QString; -class QAbstractItemModel; +class QSqlTableModel; class FoxLog final : private boost::noncopyable @@ -21,7 +21,7 @@ public: , QString const& band); bool dupe (QString const& call, QString const& band) const; - QAbstractItemModel * model (); + QSqlTableModel * model (); void reset (); private: diff --git a/qt_db_helpers.hpp b/qt_db_helpers.hpp index da823b0cb..49a082f72 100644 --- a/qt_db_helpers.hpp +++ b/qt_db_helpers.hpp @@ -30,32 +30,51 @@ public: { model_.database ().transaction (); } + bool submit (bool throw_on_error = true) { - Q_ASSERT (model_.isDirty ()); bool ok {true}; if (throw_on_error) { - SQL_error_check (model_, &QSqlTableModel::submitAll); + SQL_error_check (model_ + , QSqlTableModel::OnManualSubmit == model_.editStrategy () + ? &QSqlTableModel::submitAll + : &QSqlTableModel::submit); } else { - ok = model_.submitAll (); + ok = QSqlTableModel::OnManualSubmit == model_.editStrategy () + ? model_.submitAll () : model_.submit (); } submitted_ = submitted_ || ok; return ok; } + void revert () { - Q_ASSERT (model_.isDirty ()); - model_.revertAll (); + if (QSqlTableModel::OnManualSubmit == model_.editStrategy ()) + { + model_.revertAll (); + } + else + { + model_.revert (); + } } + ~ConditionalTransaction () { if (model_.isDirty ()) { // abandon un-submitted changes to the model - model_.revertAll (); + if (QSqlTableModel::OnManualSubmit == model_.editStrategy ()) + { + model_.revertAll (); + } + else + { + model_.revert (); + } } auto database = model_.database (); if (submitted_) @@ -67,6 +86,7 @@ public: database.rollback (); } } + private: QSqlTableModel& model_; bool submitted_; diff --git a/widgets/AbstractLogWindow.cpp b/widgets/AbstractLogWindow.cpp index ebc901cd8..e796cdc3e 100644 --- a/widgets/AbstractLogWindow.cpp +++ b/widgets/AbstractLogWindow.cpp @@ -1,25 +1,36 @@ #include "AbstractLogWindow.hpp" +#include #include #include #include #include +#include +#include +#include +#include #include "Configuration.hpp" #include "SettingsGroup.hpp" +#include "MessageBox.hpp" #include "models/FontOverrideModel.hpp" #include "pimpl_impl.hpp" class AbstractLogWindow::impl final { public: - impl (QString const& settings_key, QSettings * settings, Configuration const * configuration) - : settings_key_ {settings_key} + impl (AbstractLogWindow * self, QString const& settings_key, QSettings * settings + , Configuration const * configuration) + : self_ {self} + , settings_key_ {settings_key} , settings_ {settings} , configuration_ {configuration} , log_view_ {nullptr} { } + void delete_QSOs (); + + AbstractLogWindow * self_; QString settings_key_; QSettings * settings_; Configuration const * configuration_; @@ -27,11 +38,51 @@ public: FontOverrideModel model_; }; +namespace +{ + bool row_is_higher (QModelIndex const& lhs, QModelIndex const& rhs) + { + return lhs.row () > rhs.row (); + } +} + +void AbstractLogWindow::impl::delete_QSOs () +{ + auto selection_model = log_view_->selectionModel (); + selection_model->select (selection_model->selection (), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); + auto row_indexes = selection_model->selectedRows (); + + if (row_indexes.size () + && MessageBox::Yes == MessageBox::query_message (self_ + , tr ("Confirm Delete") + , tr ("Are you sure you want to delete the %n " + "selected QSO(s) from the log", "" + , row_indexes.size ()))) + { + // We must work with source model indexes because we don't want row + // removes to invalidate model indexes we haven't yet processed. We + // achieve that by processing them in decending row order. + for (auto& row_index : row_indexes) + { + row_index = model_.mapToSource (row_index); + } + + // reverse sort by row + std::sort (row_indexes.begin (), row_indexes.end (), row_is_higher); + for (auto index : row_indexes) + { + auto row = model_.mapFromSource (index).row (); + model_.removeRow (row); + self_->log_model_changed (); + } + } +} + AbstractLogWindow::AbstractLogWindow (QString const& settings_key, QSettings * settings , Configuration const * configuration , QWidget * parent) : QWidget {parent} - , m_ {settings_key, settings, configuration} + , m_ {this, settings_key, settings, configuration} { // ensure view scrolls to latest new row connect (&m_->model_, &QAbstractItemModel::rowsInserted, [this] (QModelIndex const& /*parent*/, int /*first*/, int /*last*/) { @@ -45,18 +96,17 @@ AbstractLogWindow::~AbstractLogWindow () m_->settings_->setValue ("window/geometry", saveGeometry ()); } -void AbstractLogWindow::set_log_model (QAbstractItemModel * log_model) -{ - m_->model_.setSourceModel (log_model); -} - void AbstractLogWindow::set_log_view (QTableView * log_view) { // do this here because we know the UI must be setup before this SettingsGroup g {m_->settings_, m_->settings_key_}; restoreGeometry (m_->settings_->value ("window/geometry").toByteArray ()); - m_->log_view_ = log_view; + m_->log_view_->setContextMenuPolicy (Qt::ActionsContextMenu); + m_->log_view_->setAlternatingRowColors (true); + m_->log_view_->setSelectionBehavior (QAbstractItemView::SelectRows); + m_->log_view_->setSelectionMode (QAbstractItemView::ExtendedSelection); + m_->model_.setSourceModel (m_->log_view_->model ()); m_->log_view_->setModel (&m_->model_); m_->log_view_->setColumnHidden (0, true); auto horizontal_header = log_view->horizontalHeader (); @@ -65,6 +115,13 @@ void AbstractLogWindow::set_log_view (QTableView * log_view) m_->log_view_->verticalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents); set_log_view_font (m_->configuration_->decoded_text_font ()); m_->log_view_->scrollToBottom (); + + // actions + auto delete_action = new QAction {tr ("&Delete ..."), m_->log_view_}; + m_->log_view_->insertAction (nullptr, delete_action); + connect (delete_action, &QAction::triggered, [this] (bool /*checked*/) { + m_->delete_QSOs (); + }); } void AbstractLogWindow::set_log_view_font (QFont const& font) diff --git a/widgets/AbstractLogWindow.hpp b/widgets/AbstractLogWindow.hpp index 35d5d27cf..581212d82 100644 --- a/widgets/AbstractLogWindow.hpp +++ b/widgets/AbstractLogWindow.hpp @@ -7,10 +7,15 @@ class QString; class QSettings; class Configuration; -class QAbstractItemModel; class QTableView; class QFont; +// +// AbstractLogWindow - Base class for log view windows +// +// QWidget that manages the common functionality shared by windows +// that include a QSO log view. +// class AbstractLogWindow : public QWidget { @@ -20,11 +25,15 @@ public: , QWidget * parent = nullptr); virtual ~AbstractLogWindow () = 0; - void set_log_model (QAbstractItemModel *); + // set the QTableView that shows the log records, must have its + // model set before calling this void set_log_view (QTableView *); + void set_log_view_font (QFont const&); private: + virtual void log_model_changed (int row = -1) = 0; + class impl; pimpl m_; }; diff --git a/widgets/CabrilloLogWindow.cpp b/widgets/CabrilloLogWindow.cpp index ab83a79f2..20f39e4e6 100644 --- a/widgets/CabrilloLogWindow.cpp +++ b/widgets/CabrilloLogWindow.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "Configuration.hpp" #include "models/Bands.hpp" #include "item_delegates/ForeignKeyDelegate.hpp" @@ -43,26 +44,44 @@ namespace class CabrilloLogWindow::impl final { public: - explicit impl () = default; + explicit impl (QSqlTableModel * log_model) + : log_model_ {log_model} + { + } + + QSqlTableModel * log_model_; FormatProxyModel format_model_; Ui::CabrilloLogWindow ui_; }; CabrilloLogWindow::CabrilloLogWindow (QSettings * settings, Configuration const * configuration - , QAbstractItemModel * cabrillo_log_model, QWidget * parent) + , QSqlTableModel * cabrillo_log_model, QWidget * parent) : AbstractLogWindow {"Cabrillo Log Window", settings, configuration, parent} + , m_{cabrillo_log_model} { setWindowTitle (QApplication::applicationName () + " - Cabrillo Log"); m_->ui_.setupUi (this); - m_->format_model_.setSourceModel (cabrillo_log_model); - set_log_model (&m_->format_model_); + m_->format_model_.setSourceModel (m_->log_model_); + m_->ui_.log_table_view->setModel (&m_->format_model_); set_log_view (m_->ui_.log_table_view); m_->ui_.log_table_view->setItemDelegateForColumn (2, new DateTimeAsSecsSinceEpochDelegate {this}); m_->ui_.log_table_view->setItemDelegateForColumn (3, new CallsignDelegate {this}); - m_->ui_.log_table_view->setItemDelegateForColumn (6, new ForeignKeyDelegate {configuration->bands (), cabrillo_log_model, 0, 6, this}); + m_->ui_.log_table_view->setItemDelegateForColumn (6, new ForeignKeyDelegate {configuration->bands (), m_->log_model_, 0, 6, this}); m_->ui_.log_table_view->horizontalHeader ()->moveSection (6, 1); // band to first column } CabrilloLogWindow::~CabrilloLogWindow () { } + +void CabrilloLogWindow::log_model_changed (int row) +{ + if (row >= 0) + { + m_->log_model_->selectRow (row); + } + else + { + m_->log_model_->select (); + } +} diff --git a/widgets/CabrilloLogWindow.hpp b/widgets/CabrilloLogWindow.hpp index c0a964ce5..e2aa1627a 100644 --- a/widgets/CabrilloLogWindow.hpp +++ b/widgets/CabrilloLogWindow.hpp @@ -7,17 +7,19 @@ class QSettings; class Configuration; class QFont; -class QAbstractItemModel; +class QSqlTableModel; class CabrilloLogWindow final : public AbstractLogWindow { public: - explicit CabrilloLogWindow (QSettings *, Configuration const *, QAbstractItemModel * cabrillo_log_model + explicit CabrilloLogWindow (QSettings *, Configuration const *, QSqlTableModel * cabrillo_log_model , QWidget * parent = nullptr); ~CabrilloLogWindow (); private: + void log_model_changed (int row) override; + class impl; pimpl m_; }; diff --git a/widgets/CabrilloLogWindow.ui b/widgets/CabrilloLogWindow.ui index 43061f132..929b41a7b 100644 --- a/widgets/CabrilloLogWindow.ui +++ b/widgets/CabrilloLogWindow.ui @@ -6,7 +6,7 @@ 0 0 - 274 + 493 210 @@ -16,12 +16,6 @@ - - true - - - QAbstractItemView::SingleSelection - true diff --git a/widgets/FoxLogWindow.cpp b/widgets/FoxLogWindow.cpp index f5e7e517d..21029c73c 100644 --- a/widgets/FoxLogWindow.cpp +++ b/widgets/FoxLogWindow.cpp @@ -1,9 +1,14 @@ #include "FoxLogWindow.hpp" #include +#include +#include +#include +#include #include "SettingsGroup.hpp" #include "Configuration.hpp" +#include "MessageBox.hpp" #include "models/Bands.hpp" #include "item_delegates/ForeignKeyDelegate.hpp" #include "item_delegates/DateTimeAsSecsSinceEpochDelegate.hpp" @@ -16,26 +21,47 @@ class FoxLogWindow::impl final { public: - explicit impl () = default; + explicit impl (QSqlTableModel * log_model) + : log_model_ {log_model} + { + } + + QSqlTableModel * log_model_; Ui::FoxLogWindow ui_; }; FoxLogWindow::FoxLogWindow (QSettings * settings, Configuration const * configuration - , QAbstractItemModel * fox_log_model, QWidget * parent) + , QSqlTableModel * fox_log_model, QWidget * parent) : AbstractLogWindow {"Fox Log Window", settings, configuration, parent} + , m_ {fox_log_model} { setWindowTitle (QApplication::applicationName () + " - Fox Log"); m_->ui_.setupUi (this); - set_log_model (fox_log_model); + m_->ui_.log_table_view->setModel (m_->log_model_); set_log_view (m_->ui_.log_table_view); m_->ui_.log_table_view->setItemDelegateForColumn (1, new DateTimeAsSecsSinceEpochDelegate {this}); m_->ui_.log_table_view->setItemDelegateForColumn (2, new CallsignDelegate {this}); m_->ui_.log_table_view->setItemDelegateForColumn (3, new MaidenheadLocatorDelegate {this}); - m_->ui_.log_table_view->setItemDelegateForColumn (6, new ForeignKeyDelegate {configuration->bands (), fox_log_model, 0, 6, this}); + m_->ui_.log_table_view->setItemDelegateForColumn (6, new ForeignKeyDelegate {configuration->bands (), m_->log_model_, 0, 6, this}); m_->ui_.log_table_view->horizontalHeader ()->moveSection (6, 1); // move band to first column m_->ui_.rate_label->setNum (0); m_->ui_.queued_label->setNum (0); m_->ui_.callers_label->setNum (0); + + // actions + auto reset_action = new QAction {tr ("&Reset ..."), m_->ui_.log_table_view}; + m_->ui_.log_table_view->insertAction (nullptr, reset_action); + connect (reset_action, &QAction::triggered, [this, configuration] (bool /*checked*/) { + if (MessageBox::Yes == MessageBox::query_message( this + , tr ("Confirm Reset") + , tr ("Are you sure you want to erase file FoxQSO.txt " + "and start a new Fox log?"))) + { + QFile f{configuration->writeable_data_dir ().absoluteFilePath ("FoxQSO.txt")}; + f.remove (); + Q_EMIT reset_log_model (); + } + }); } FoxLogWindow::~FoxLogWindow () @@ -56,3 +82,15 @@ void FoxLogWindow::rate (int n) { m_->ui_.rate_label->setNum (n); } + +void FoxLogWindow::log_model_changed (int row) +{ + if (row >= 0) + { + m_->log_model_->selectRow (row); + } + else + { + m_->log_model_->select (); + } +} diff --git a/widgets/FoxLogWindow.hpp b/widgets/FoxLogWindow.hpp index d27ccdea6..65bcb0bd0 100644 --- a/widgets/FoxLogWindow.hpp +++ b/widgets/FoxLogWindow.hpp @@ -7,13 +7,15 @@ class QSettings; class Configuration; class QFont; -class QAbstractItemModel; +class QSqlTableModel; class FoxLogWindow final : public AbstractLogWindow { + Q_OBJECT + public: - explicit FoxLogWindow (QSettings *, Configuration const *, QAbstractItemModel * fox_log_model + explicit FoxLogWindow (QSettings *, Configuration const *, QSqlTableModel * fox_log_model , QWidget * parent = nullptr); ~FoxLogWindow (); @@ -21,7 +23,11 @@ public: void queued (int); void rate (int); + Q_SIGNAL void reset_log_model () const; + private: + void log_model_changed (int row) override; + class impl; pimpl m_; }; diff --git a/widgets/FoxLogWindow.ui b/widgets/FoxLogWindow.ui index 96ef8b112..0a9815478 100644 --- a/widgets/FoxLogWindow.ui +++ b/widgets/FoxLogWindow.ui @@ -6,21 +6,21 @@ 0 0 - 331 + 453 238 + + Qt::DefaultContextMenu + Fox Log - - true - - - QAbstractItemView::SingleSelection + + Qt::ActionsContextMenu true diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 895569942..18f6cee84 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -2419,6 +2419,10 @@ void MainWindow::on_fox_log_action_triggered() // Connect signals from fox log window connect (this, &MainWindow::finished, m_foxLogWindow.data (), &FoxLogWindow::close); + connect (m_foxLogWindow.data (), &FoxLogWindow::reset_log_model, [this] () { + if (!m_foxLog) m_foxLog.reset (new FoxLog); + m_foxLog->reset (); + }); } m_foxLogWindow->showNormal (); m_foxLogWindow->raise (); @@ -5332,7 +5336,6 @@ void MainWindow::on_logQSOButton_clicked() //Log QSO button default: break; } - using SpOp = Configuration::SpecialOperatingActivity; auto special_op = m_config.special_op_id (); if (SpecOp::NONE < special_op && special_op < SpecOp::FOX) { @@ -5377,7 +5380,6 @@ void MainWindow::acceptQSO (QDateTime const& QSO_date_off, QString const& call, if (m_config.clear_DX () and SpecOp::HOUND != m_config.special_op_id()) clearDX (); m_dateTimeQSOOn = QDateTime {}; - using SpOp = Configuration::SpecialOperatingActivity; auto special_op = m_config.special_op_id (); if (SpecOp::NONE < special_op && special_op < SpecOp::FOX) { @@ -6119,18 +6121,6 @@ void MainWindow::on_actionErase_ALL_TXT_triggered() //Erase ALL.TXT } } -void MainWindow::on_reset_fox_log_action_triggered () -{ - int ret = MessageBox::query_message(this, tr("Confirm Reset"), - tr("Are you sure you want to erase file FoxQSO.txt and start a new Fox log?")); - if(ret==MessageBox::Yes) { - QFile f{m_config.writeable_data_dir().absoluteFilePath("FoxQSO.txt")}; - f.remove(); - if (!m_foxLog) m_foxLog.reset (new FoxLog); - m_foxLog->reset (); - } -} - void MainWindow::on_reset_cabrillo_log_action_triggered () { if (MessageBox::Yes == MessageBox::query_message (this, tr ("Confirm Reset"), diff --git a/widgets/mainwindow.h b/widgets/mainwindow.h index e5b9de9a8..4cab787b5 100644 --- a/widgets/mainwindow.h +++ b/widgets/mainwindow.h @@ -204,7 +204,6 @@ private slots: void on_actionDeepestDecode_toggled (bool); void bumpFqso(int n); void on_actionErase_ALL_TXT_triggered(); - void on_reset_fox_log_action_triggered (); void on_reset_cabrillo_log_action_triggered (); void on_actionErase_wsjtx_log_adi_triggered(); void on_actionExport_Cabrillo_log_triggered(); diff --git a/widgets/mainwindow.ui b/widgets/mainwindow.ui index 8256eb606..21799434e 100644 --- a/widgets/mainwindow.ui +++ b/widgets/mainwindow.ui @@ -2653,7 +2653,6 @@ list. The list can be maintained in Settings (F2). - From 4334c997ee7526115733963323f3a27b6f4c87ea Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Sun, 25 Nov 2018 22:30:28 +0000 Subject: [PATCH 25/27] Add tool tips to log view windows --- widgets/CabrilloLogWindow.ui | 3 +++ widgets/FoxLogWindow.ui | 3 +++ 2 files changed, 6 insertions(+) diff --git a/widgets/CabrilloLogWindow.ui b/widgets/CabrilloLogWindow.ui index 929b41a7b..efefe5d3c 100644 --- a/widgets/CabrilloLogWindow.ui +++ b/widgets/CabrilloLogWindow.ui @@ -16,6 +16,9 @@ + + <html><head/><body><p>Right-click here for available actions.</p></body></html> + true diff --git a/widgets/FoxLogWindow.ui b/widgets/FoxLogWindow.ui index 0a9815478..cd224ed33 100644 --- a/widgets/FoxLogWindow.ui +++ b/widgets/FoxLogWindow.ui @@ -22,6 +22,9 @@ Qt::ActionsContextMenu + + <html><head/><body><p>Right-click here for available actions.</p></body></html> + true From 4dbba727ecaefd63439ec5b1a435134519bb8769 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Mon, 26 Nov 2018 01:42:57 +0000 Subject: [PATCH 26/27] Explicitly include MOC generated source --- widgets/FoxLogWindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/widgets/FoxLogWindow.cpp b/widgets/FoxLogWindow.cpp index 21029c73c..0f96529aa 100644 --- a/widgets/FoxLogWindow.cpp +++ b/widgets/FoxLogWindow.cpp @@ -17,6 +17,7 @@ #include "pimpl_impl.hpp" #include "ui_FoxLogWindow.h" +#include "moc_FoxLogWindow.cpp" class FoxLogWindow::impl final { From cd8721f7e5bcf5e75a150fb4a3c9d9212b9f82c2 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 26 Nov 2018 10:21:18 -0500 Subject: [PATCH 27/27] Update the Release Notes for RC5. --- Release_Notes.txt | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Release_Notes.txt b/Release_Notes.txt index 8c483f454..c2b2c711b 100644 --- a/Release_Notes.txt +++ b/Release_Notes.txt @@ -12,6 +12,31 @@ Copyright 2001 - 2018 by Joe Taylor, K1JT. + Release: WSJT-X 2.0-rc5 + November 26, 2018 + ----------------------- + +Release Candidate 5 ("RC5") is stable, works well, and fixes the known +problems in RC4. It is likely that the General Availability (GA) +release of WSJT-X 2.0 will be nearly identical to RC5. + +Changes from WSJT-X 2.0-rc4 include the following: + + - Make the "Auto Seq" checkbox sticky, again + - Remove the 5-minute mouse timer + - Correct the "worked before" logic for color highlighting + - Add "No own call decodes" checkbox in WSPR mode + - Display and log UTC (not local time) in contest operations + - Validate contest QSO details before allowing logging + - Force Aqua theme on macOS to avoid issues with Mojave dark theme + - Move Fox log Reset action to Fox log window context menu + - Improve layout of Working Frequencies and Station Information tables + - Allow deletes and editing in Fox and Contest log windows + - Add Tool Tips for Fox and Contest log windows + - Fix a bug causing false AP decodes in JT65 VHF+ operation + - Fix a bug that could switch unexpectedly from JT65 to JT9 mode + + Release: WSJT-X 2.0-rc4 November 12, 2018 -----------------------