diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..51795576 Binary files /dev/null and b/.DS_Store differ diff --git a/.github/workflows/macOSBuild.yml b/.github/workflows/macOSBuild.yml index 7340a250..0b073e3d 100644 --- a/.github/workflows/macOSBuild.yml +++ b/.github/workflows/macOSBuild.yml @@ -13,7 +13,7 @@ jobs: name: MacOS Build strategy: matrix: - os: [macos-12, macos-13] + os: [macos-13] runs-on: ${{ matrix.os }} @@ -32,11 +32,30 @@ jobs: brew install brotli brew install icu4c brew install pkg-config + brew install automake + brew install autoconf + brew install libtool + brew install libusb-compat + - name: Checkout Code uses: actions/checkout@v4 with: fetch-depth: 0 submodules: recursive + - name: Checkout Code + uses: actions/checkout@v4 + with: + repository: hamlib/hamlib + path: ./hamlib + + - name: Configure and compile + run: | + cd ./hamlib + ./bootstrap + ./configure --prefix=/Users/runner/work/QLog/QLog/hamlib + make -j 4 + make check + make install - name: Get version from tag run : | TAGVERSION=$(git describe --tags) @@ -46,23 +65,116 @@ jobs: run: | mkdir build cd build - qmake -config release .. + qmake "HAMLIBINCLUDEPATH = /Users/runner/work/QLog/QLog/hamlib/include" "HAMLIBLIBPATH = /Users/runner/work/QLog/QLog/hamlib/lib" "HAMLIBVERSION_MAJOR = 4" "HAMLIBVERSION_MINOR = 6" "HAMLIBVERSION_PATCH = 0" -config release .. make -j4 - name: Build dmg run: | cd build + macdeployqt qlog.app -executable=./qlog.app/Contents/MacOS/qlog macdeployqt qlog.app - cp `brew --prefix`/lib/libhamlib.dylib qlog.app/Contents/Frameworks/libhamlib.dylib - cp `brew --prefix`/lib/libqt6keychain.dylib qlog.app/Contents/Frameworks/libqt6keychain.dylib - cp `brew --prefix`/lib/libdbus-1.dylib qlog.app/Contents/Frameworks/libdbus-1.dylib - cp `brew --prefix brotli`/lib/libbrotlicommon.1.dylib qlog.app/Contents/Frameworks/libbrotlicommon.1.dylib - cp `brew --prefix`/opt/icu4c/lib/libicui18n.74.dylib qlog.app/Contents/Frameworks/libicui18n.74.dylib - install_name_tool -change `brew --prefix`/lib/libhamlib.dylib @executable_path/../Frameworks/libhamlib.dylib qlog.app/Contents/MacOS/qlog - install_name_tool -change `brew --prefix`/lib/libqt6keychain.dylib @executable_path/../Frameworks/libqt6keychain.dylib qlog.app/Contents/MacOS/qlog - install_name_tool -change @loader_path/libbrotlicommon.1.dylib @executable_path/../Frameworks/libbrotlicommon.1.dylib qlog.app/Contents/MacOS/qlog - install_name_tool -change /usr/local/opt/icu4c/lib/libicui18n.74.dylib @executable_path/../Frameworks/libicui18n.74.dylib qlog.app/Contents/MacOS/qlog - otool -L qlog.app/Contents/MacOS/qlog - macdeployqt qlog.app -dmg + - name: Codesign app bundle + # Extract the secrets we defined earlier as environment variables + env: + MACOS_CERTIFICATE: ${{ secrets.PROD_MACOS_CERTIFICATE }} + MACOS_CERTIFICATE_PWD: ${{ secrets.PROD_MACOS_CERTIFICATE_PWD }} + MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }} + MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }} + run: | + # Turn our base64-encoded certificate back to a regular .p12 file + echo $MACOS_CERTIFICATE | base64 --decode > certificate.p12 + # We need to create a new keychain, otherwise using the certificate will prompt + # with a UI dialog asking for the certificate password, which we can't + # use in a headless CI environment + security create-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain + security default-keychain -s build.keychain + security unlock-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain + security import certificate.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_CI_KEYCHAIN_PWD" build.keychain + # We finally codesign our app bundle, specifying the Hardened runtime option + sudo codesign --deep --force --verify --verbose --sign "$MACOS_CERTIFICATE_NAME" --options runtime /Users/runner/work/QLog/QLog/build/qlog.app + sudo codesign --force --verify --verbose --sign "$MACOS_CERTIFICATE_NAME" --entitlements /Users/runner/work/QLog/QLog/entitlements.xml --options runtime /Users/runner/work/QLog/QLog/build/qlog.app/Contents/Frameworks/QtWebEngineCore.framework/Helpers/QtWebEngineProcess.app/Contents/MacOS/QtWebEngineProcess + sudo codesign --force --verify --verbose --sign "$MACOS_CERTIFICATE_NAME" --options runtime /Users/runner/work/QLog/QLog/build/qlog.app/Contents/MacOS/qlog + - name: "Notarize app bundle" + # Extract the secrets we defined earlier as environment variables + env: + PROD_MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }} + PROD_MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }} + PROD_MACOS_NOTARIZATION_PWD: ${{ secrets.PROD_MACOS_NOTARIZATION_PWD }} + run: | + # Store the notarization credentials so that we can prevent a UI password dialog + # from blocking the CI + + echo "Create keychain profile" + xcrun notarytool store-credentials "notarytool-profile" --apple-id "$PROD_MACOS_NOTARIZATION_APPLE_ID" --team-id "$PROD_MACOS_NOTARIZATION_TEAM_ID" --password "$PROD_MACOS_NOTARIZATION_PWD" + + echo "Creating temp notarization archive" + ditto -c -k --keepParent "/Users/runner/work/QLog/QLog/build/qlog.app" "notarization.zip" + + # Here we send the notarization request to the Apple's Notarization service, waiting for the result. + # This typically takes a few seconds inside a CI environment, but it might take more depending on the App + # characteristics. Visit the Notarization docs for more information and strategies on how to optimize it if + # you're curious + + echo "Notarize app" + xcrun notarytool submit "notarization.zip" --keychain-profile "notarytool-profile" --wait + + echo "Attach staple" + xcrun stapler staple "/Users/runner/work/QLog/QLog/build/qlog.app" + - name: make dmg + run: | + mkdir out + cp -R "/Users/runner/work/QLog/QLog/build/qlog.app" out + cd out + ln -s /Applications/ Applications + cd .. + hdiutil create -volname "QLog Installer" -srcfolder out -ov -format UDZO "/Users/runner/work/QLog/QLog/build/qlog.dmg" + - name: Codesign dmg bundle + # Extract the secrets we defined earlier as environment variables + env: + MACOS_CERTIFICATE: ${{ secrets.PROD_MACOS_CERTIFICATE }} + MACOS_CERTIFICATE_PWD: ${{ secrets.PROD_MACOS_CERTIFICATE_PWD }} + MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }} + MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }} + run: | + # Turn our base64-encoded certificate back to a regular .p12 file + ##echo $MACOS_CERTIFICATE | base64 --decode > certificate.p12 + # We need to create a new keychain, otherwise using the certificate will prompt + # with a UI dialog asking for the certificate password, which we can't + # use in a headless CI environment + ##security create-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain + ##security default-keychain -s build.keychain + ##security unlock-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain + ##security import certificate.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign + ##security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_CI_KEYCHAIN_PWD" build.keychain + # We finally codesign our app bundle, specifying the Hardened runtime option + /usr/bin/codesign --timestamp -s "$MACOS_CERTIFICATE_NAME" --options runtime --deep -f /Users/runner/work/QLog/QLog/build/qlog.dmg + - name: "Notarize app bundle" + # Extract the secrets we defined earlier as environment variables + env: + PROD_MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }} + PROD_MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }} + PROD_MACOS_NOTARIZATION_PWD: ${{ secrets.PROD_MACOS_NOTARIZATION_PWD }} + run: | + # Store the notarization credentials so that we can prevent a UI password dialog + # from blocking the CI + + echo "Create keychain profile" + xcrun notarytool store-credentials "notarytool-profile" --apple-id "$PROD_MACOS_NOTARIZATION_APPLE_ID" --team-id "$PROD_MACOS_NOTARIZATION_TEAM_ID" --password "$PROD_MACOS_NOTARIZATION_PWD" + + echo "Creating temp notarization archive" + ditto -c -k --keepParent "/Users/runner/work/QLog/QLog/build/qlog.dmg" "notarization.zip" + + # Here we send the notarization request to the Apple's Notarization service, waiting for the result. + # This typically takes a few seconds inside a CI environment, but it might take more depending on the App + # characteristics. Visit the Notarization docs for more information and strategies on how to optimize it if + # you're curious + + echo "Notarize app" + xcrun notarytool submit "notarization.zip" --keychain-profile "notarytool-profile" --wait + + echo "Attach staple" + xcrun stapler staple "/Users/runner/work/QLog/QLog/build/qlog.dmg" + - name: Copy artifact uses: actions/upload-artifact@v4 with: diff --git a/.gitignore b/.gitignore index 0d53acad..89d558ef 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,4 @@ compile_commands.json # QtCreator local machine specific files for imported projects *creator.user* +/build diff --git a/QLog.pro b/QLog.pro index fe93dee5..65e60895 100644 --- a/QLog.pro +++ b/QLog.pro @@ -164,6 +164,7 @@ SOURCES += \ ui/SettingsDialog.cpp \ ui/ShowUploadDialog.cpp \ ui/StatisticsWidget.cpp \ + ui/Steppir.cpp \ ui/SwitchButton.cpp \ ui/WebEnginePage.cpp \ ui/WsjtxFilterDialog.cpp \ @@ -297,6 +298,7 @@ HEADERS += \ ui/QrzDialog.h \ ui/ShowUploadDialog.h \ ui/SplashScreen.h \ + ui/Steppir.h \ ui/StyleItemDelegate.h \ ui/RigWidget.h \ ui/RotatorWidget.h \ @@ -349,6 +351,7 @@ FORMS += \ ui/SettingsDialog.ui \ ui/ShowUploadDialog.ui \ ui/StatisticsWidget.ui \ + ui/Steppir.ui \ ui/WsjtxFilterDialog.ui \ ui/WsjtxWidget.ui diff --git a/core/main.cpp b/core/main.cpp index 57f79eb3..e7bb75e3 100644 --- a/core/main.cpp +++ b/core/main.cpp @@ -562,6 +562,8 @@ int main(int argc, char* argv[]) w.setWindowIcon(icon); w.show(); - +#ifdef Q_OS_OSX + w.setLayoutGeometry(); +#endif return app.exec(); } diff --git a/devtools/.DS_Store b/devtools/.DS_Store new file mode 100644 index 00000000..3bae6b86 Binary files /dev/null and b/devtools/.DS_Store differ diff --git a/entitlements.xml b/entitlements.xml new file mode 100644 index 00000000..36d136cf --- /dev/null +++ b/entitlements.xml @@ -0,0 +1,11 @@ + + + + +com.apple.security.cs.allow-unsigned-executable-memory +com.apple.security.cs.disable-library-validation +com.apple.security.cs.allow-jit +com.apple.security.cs.disable-executable-page-protection + + + diff --git a/rig/drivers/HamlibRigDrv.cpp b/rig/drivers/HamlibRigDrv.cpp index 631d6971..11dabae4 100644 --- a/rig/drivers/HamlibRigDrv.cpp +++ b/rig/drivers/HamlibRigDrv.cpp @@ -577,7 +577,7 @@ bool HamlibRigDrv::checkFreqChange() } if ( rigProfile.getFreqInfo - && rig->caps->get_freq ) + && rig->caps->get_freq ) { freq_t vfo_freq; int status = rig_get_freq(rig, RIG_VFO_CURR, &vfo_freq); @@ -605,6 +605,37 @@ bool HamlibRigDrv::checkFreqChange() return false; } } + else if ( rigProfile.getFreqInfo + && rig->caps->get_vfo ) + { + freq_t vfo_freq; + vfo_t vfo; + rig_get_vfo(rig, &vfo); + int status = rig_get_freq(rig, vfo, &vfo_freq); + + if ( status == RIG_OK ) + { + qCDebug(runtime) << "Rig Freq: "<< QSTRING_FREQ(Hz2MHz(vfo_freq)); + qCDebug(runtime) << "Object Freq: "<< QSTRING_FREQ(Hz2MHz(currFreq)); + + if ( vfo_freq != currFreq || forceSendState ) + { + currFreq = vfo_freq; + qCDebug(runtime) << "emitting FREQ changed"; + emit frequencyChanged(Hz2MHz(currFreq), + Hz2MHz(getRITFreq()), + Hz2MHz(getXITFreq())); + } + } + else + { + lastErrorText = hamlibErrorString(status); + emit errorOccured(tr("Get Frequency Error"), + lastErrorText); + qCWarning(runtime) << "Get Freq error" << lastErrorText; + return false; + } + } else { qCDebug(runtime) << "Get Freq is disabled"; diff --git a/rig/macros.h b/rig/macros.h index f1c09d68..b6f81cac 100644 --- a/rig/macros.h +++ b/rig/macros.h @@ -9,5 +9,8 @@ #ifndef MHz #define MHz(f) ((double)((f)*(double)1000000)) #endif +#ifndef kHz +#define kHz(f) ((double)((f)*(double)1000)) +#endif #endif // RIG_MACROS_H diff --git a/ui/AlertWidget.ui b/ui/AlertWidget.ui index 34a2c802..98501a93 100644 --- a/ui/AlertWidget.ui +++ b/ui/AlertWidget.ui @@ -17,10 +17,10 @@ - Qt::ClickFocus + Qt::FocusPolicy::ClickFocus - QAbstractItemView::NoEditTriggers + QAbstractItemView::EditTrigger::NoEditTriggers false @@ -35,23 +35,32 @@ true - QAbstractItemView::SingleSelection + QAbstractItemView::SelectionMode::SingleSelection - QAbstractItemView::SelectRows + QAbstractItemView::SelectionBehavior::SelectRows - QAbstractItemView::ScrollPerPixel + QAbstractItemView::ScrollMode::ScrollPerPixel - QAbstractItemView::ScrollPerPixel + QAbstractItemView::ScrollMode::ScrollPerPixel + + 24 + false true + + 20 + + + 20 + @@ -79,7 +88,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -92,7 +101,7 @@ - Qt::ClickFocus + Qt::FocusPolicy::ClickFocus Clear All diff --git a/ui/AwardsDialog.cpp b/ui/AwardsDialog.cpp index f99be35f..c7867ce1 100644 --- a/ui/AwardsDialog.cpp +++ b/ui/AwardsDialog.cpp @@ -38,9 +38,12 @@ AwardsDialog::AwardsDialog(QWidget *parent) : ui->awardComboBox->addItem(tr("WAS"), QVariant("was")); ui->awardComboBox->addItem(tr("WPX"), QVariant("wpx")); ui->awardComboBox->addItem(tr("IOTA"), QVariant("iota")); + ui->awardComboBox->addItem(tr("POTA Hunter"), QVariant("potah")); + ui->awardComboBox->addItem(tr("POTA Activator"), QVariant("potaa")); + ui->awardComboBox->addItem(tr("SOTA"), QVariant("sota")); + ui->awardComboBox->addItem(tr("WWFF"), QVariant("wwff")); ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Done")); - ui->awardTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); ui->awardTableView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); @@ -60,216 +63,310 @@ void AwardsDialog::refreshTable(int) { FCT_IDENTIFICATION; + const QList& dxccBands = BandPlan::bandsList(true, true); + + if ( dxccBands.size() == 0 ) + return; + QStringList confirmed("1=2 "); QStringList modes("'NONE'"); QString headersColumns; QString uniqColumns; - QString excludePart; - - QString awardSelected = getSelectedAward(); - QString entitySelected = getSelectedEntity(); + QString addWherePart; + QString sourceContactsTable; + QString sqlPartDetailTable; + QStringList stmt_max_part; + QStringList stmt_total_padding; + QStringList stmt_sum_confirmed; + QStringList stmt_sum_worked; + QStringList stmt_sum_total; + QStringList addlCTEs; - QString sqlPart = "FROM contacts c, modes m " - "WHERE c.mode = m.name" - " AND c.my_dxcc = '" + entitySelected + "' "; + const QString &awardSelected = getSelectedAward(); if ( ui->cwCheckBox->isChecked() ) - { modes << "'CW'"; - } if ( ui->phoneCheckBox->isChecked() ) - { modes << "'PHONE'"; - } if ( ui->digiCheckBox->isChecked() ) - { modes << "'DIGITAL'"; + if ( ui->eqslCheckBox->isChecked() ) + confirmed << " eqsl_qsl_rcvd = 'Y' "; + + if ( ui->lotwCheckBox->isChecked() ) + confirmed << " lotw_qsl_rcvd = 'Y' "; + + if ( ui->paperCheckBox->isChecked() ) + confirmed << " qsl_rcvd = 'Y' "; + + const QString innerConfirmedCase(QLatin1String(" CASE WHEN (%1) THEN 2 ELSE 1 END ").arg(confirmed.join("or"))); + + for ( const Band& band : dxccBands ) + { + stmt_max_part << QString(" MAX(CASE WHEN band = '%1' AND m.dxcc IN (%2) THEN %3 ELSE 0 END) as '%4'").arg(band.name, + modes.join(","), + innerConfirmedCase, + band.name); + stmt_total_padding << QString(" NULL '%1'").arg(band.name); + stmt_sum_confirmed << QString("SUM(CASE WHEN a.'%1' > 1 THEN 1 ELSE 0 END) '%2'").arg(band.name, band.name); + stmt_sum_worked << QString("SUM(CASE WHEN a.'%1' > 0 THEN 1 ELSE 0 END) '%2'").arg(band.name, band.name); + stmt_sum_total << QString("SUM(d.'%1') '%2'").arg(band.name, band.name); } + stmt_max_part << QString(" MAX(CASE WHEN prop_mode = 'SAT' AND m.dxcc IN (%1) THEN %2 ELSE 0 END) as 'SAT' ").arg(modes.join(","), innerConfirmedCase) + << QString(" MAX(CASE WHEN prop_mode = 'EME' AND m.dxcc IN (%1) THEN %2 ELSE 0 END) as 'EME' ").arg(modes.join(","), innerConfirmedCase); + stmt_total_padding << " NULL 'SAT' " + << " NULL 'EME' "; + stmt_sum_confirmed << " SUM(CASE WHEN a.'SAT' > 1 THEN 1 ELSE 0 END) 'SAT' " + << " SUM(CASE WHEN a.'EME' > 1 THEN 1 ELSE 0 END) 'EME' "; + stmt_sum_worked << " SUM(CASE WHEN a.'SAT' > 0 THEN 1 ELSE 0 END) 'SAT' " + << " SUM(CASE WHEN a.'EME' > 0 THEN 1 ELSE 0 END) 'EME' "; + stmt_sum_total << " SUM(d.'SAT') 'SAT' " + << " SUM(d.'EME') 'EME' "; + + sourceContactsTable = " source_contacts AS (" + " SELECT * " + " FROM contacts )"; + if ( awardSelected == "dxcc" ) { + setEntityInputEnabled(true); + const QString &entitySelected = getSelectedEntity(); headersColumns = "translate_to_locale(d.name) col1, d.prefix col2 "; uniqColumns = "c.dxcc"; - sqlPart = " FROM dxcc_entities d " - " LEFT OUTER JOIN contacts c ON d.id = c.dxcc " - " LEFT OUTER JOIN modes m on c.mode = m.name " - "WHERE (c.id is NULL or c.my_dxcc = '" + entitySelected + "') "; + sqlPartDetailTable = " FROM dxcc_entities d" + " LEFT OUTER JOIN source_contacts c ON d.id = c.dxcc" + " LEFT OUTER JOIN modes m on c.mode = m.name" + " WHERE (c.id is NULL or c.my_dxcc = '" + entitySelected + "') "; + addWherePart = " AND (c.id is NULL OR c.my_dxcc = '" + entitySelected + "') "; } else if ( awardSelected == "waz" ) { + setEntityInputEnabled(true); + const QString &entitySelected = getSelectedEntity(); headersColumns = "d.n col1, null col2 "; uniqColumns = "c.cqz"; - sqlPart = " FROM cqzCTE d " - " LEFT OUTER JOIN contacts c ON d.n = c.cqz " - " LEFT OUTER JOIN modes m on c.mode = m.name " - "WHERE (c.id is NULL or c.my_dxcc = '" + entitySelected + "') "; + addlCTEs<< " cqzCTE AS ( " + " SELECT 1 AS n, 1 AS value" + " UNION ALL" + " SELECT n + 1, value + 1" + " FROM cqzCTE" + " WHERE n < 40 )"; + sqlPartDetailTable = " FROM cqzCTE d " + " LEFT OUTER JOIN source_contacts c ON d.n = c.cqz" + " LEFT OUTER JOIN modes m on c.mode = m.name " + " WHERE (c.id IS NULL OR c.my_dxcc = '" + entitySelected + "') "; + addWherePart = " AND (c.id IS NULL OR c.my_dxcc = '" + entitySelected + "') "; } else if ( awardSelected == "itu" ) { + setEntityInputEnabled(true); + const QString &entitySelected = getSelectedEntity(); headersColumns = "d.n col1, null col2 "; uniqColumns = "c.ituz"; - sqlPart = " FROM ituzCTE d " - " LEFT OUTER JOIN contacts c ON d.n = c.ituz " - " LEFT OUTER JOIN modes m on c.mode = m.name " - "WHERE (c.id is NULL or c.my_dxcc = '" + entitySelected + "') "; + addlCTEs << " ituzCTE AS (" + " SELECT 1 AS n, 1 AS value" + " UNION ALL" + " SELECT n + 1, value + 1" + " FROM ituzCTE" + " WHERE n < 90 )"; + sqlPartDetailTable = " FROM ituzCTE d " + " LEFT OUTER JOIN source_contacts c ON d.n = c.ituz" + " LEFT OUTER JOIN modes m on c.mode = m.name" + " WHERE (c.id is NULL or c.my_dxcc = '" + entitySelected + "') "; + addWherePart = " AND (c.id is NULL OR c.my_dxcc = '" + entitySelected + "') "; } else if ( awardSelected == "wac" ) { + setEntityInputEnabled(true); + const QString &entitySelected = getSelectedEntity(); headersColumns = "d.column2 col1, d.column1 col2 "; uniqColumns = "c.cont"; - sqlPart = " FROM continents d " - " LEFT OUTER JOIN contacts c ON d.column1 = c.cont " - " LEFT OUTER JOIN modes m on c.mode = m.name " - "WHERE (c.id is NULL or c.my_dxcc = '" + entitySelected + "') "; + addlCTEs << " continents as " + " (values ('NA', '" + tr("North America") + "')," + " ('SA', '" + tr("South America") + "')," + " ('EU', '" + tr("Europe") + "')," + " ('AF', '" + tr("Africa") + "')," + " ('OC', '" + tr("Oceania") + "')," + " ('AS', '" + tr("Asia") + "')," + " ('AN', '" + tr("Antarctica") + "'))"; + sqlPartDetailTable = " FROM continents d " + " LEFT OUTER JOIN source_contacts c ON d.column1 = c.cont " + " LEFT OUTER JOIN modes m on c.mode = m.name " + " WHERE (c.id is NULL or c.my_dxcc = '" + entitySelected + "') "; + addWherePart = " AND (c.id is NULL OR c.my_dxcc = '" + entitySelected + "') "; } else if ( awardSelected == "was" ) { + setEntityInputEnabled(true); + const QString &entitySelected = getSelectedEntity(); headersColumns = "d.subdivision_name col1, d.code col2 "; uniqColumns = "c.state"; - sqlPart = " FROM adif_enum_primary_subdivision d " - " LEFT OUTER JOIN contacts c ON d.dxcc = c.dxcc AND d.code = c.state " - " LEFT OUTER JOIN modes m on c.mode = m.name " - "WHERE (c.id is NULL or c.my_dxcc = '" + entitySelected + "' AND d.dxcc in (6, 110, 291)) "; + sqlPartDetailTable = " FROM adif_enum_primary_subdivision d" + " LEFT OUTER JOIN source_contacts c ON d.dxcc = c.dxcc AND d.code = c.state" + " LEFT OUTER JOIN modes m on c.mode = m.name" + " WHERE (c.id is NULL or c.my_dxcc = '" + entitySelected + "' AND d.dxcc in (6, 110, 291)) "; + addWherePart = " AND (c.id is NULL or c.my_dxcc = '" + entitySelected + "' AND c.dxcc in (6, 110, 291)) "; } else if ( awardSelected == "wpx" ) { + setEntityInputEnabled(true); + const QString &entitySelected = getSelectedEntity(); headersColumns = "c.pfx col1, null col2 "; uniqColumns = "c.pfx"; - sqlPart = "FROM contacts c, modes m " - "WHERE c.mode = m.name" - " AND c.pfx is not null" - " AND c.my_dxcc = '" + entitySelected + "'"; + sqlPartDetailTable = " FROM source_contacts c" + " INNER JOIN modes m ON c.mode = m.name" + " WHERE c.pfx is not null" + " AND c.my_dxcc = '" + entitySelected + "'"; + addWherePart = " AND c.my_dxcc = '" + entitySelected + "' "; } else if ( awardSelected == "iota" ) { + setEntityInputEnabled(true); + const QString &entitySelected = getSelectedEntity(); headersColumns = "c.iota col1, NULL col2 "; uniqColumns = "c.iota"; - excludePart = " AND c.iota is not NULL "; + sqlPartDetailTable = " FROM source_contacts c" + " INNER JOIN modes m ON c.mode = m.name" + " WHERE c.my_dxcc = '" + entitySelected + "' "; + addWherePart = " AND c.iota is not NULL" + " AND c.my_dxcc = '" + entitySelected + "' "; } - - if ( ui->eqslCheckBox->isChecked() ) + else if ( awardSelected == "potah" ) { - confirmed << " eqsl_qsl_rcvd = 'Y' "; + setEntityInputEnabled(false); + headersColumns = "p.reference col1, p.name col2 "; + uniqColumns = "c.pota"; + sqlPartDetailTable = " FROM pota_directory p " + " INNER JOIN source_contacts c ON SUBSTR(c.pota, 1, COALESCE(NULLIF(INSTR(c.pota, '@'), 0) - 1, LENGTH(c.pota))) = p.reference" + " INNER JOIN modes m on c.mode = m.name "; + addlCTEs << " split(id, callsign, station_callsign, my_dxcc, band, dxcc, eqsl_qsl_rcvd, lotw_qsl_rcvd, qsl_rcvd,prop_mode,mode, pota, str) AS (" + " SELECT id, callsign, station_callsign, my_dxcc, band, " + " dxcc, eqsl_qsl_rcvd, lotw_qsl_rcvd, qsl_rcvd,prop_mode,mode, " + " '', pota_ref||',' " + " FROM contacts " + " UNION ALL " + " SELECT id, callsign, station_callsign, my_dxcc, band, " + " dxcc, eqsl_qsl_rcvd, lotw_qsl_rcvd, qsl_rcvd,prop_mode,mode, " + " substr(str, 0, instr(str, ',')), substr(str, instr(str, ',') + 1) " + " FROM split " + " WHERE str != '') "; + sourceContactsTable = " source_contacts AS (" + " SELECT id, callsign, station_callsign, my_dxcc, band, dxcc, eqsl_qsl_rcvd, lotw_qsl_rcvd, qsl_rcvd,prop_mode,mode, pota " + " FROM split " + " WHERE pota != '' ) "; + addWherePart = " AND c.pota is not NULL "; } - - if ( ui->lotwCheckBox->isChecked() ) + else if ( awardSelected == "potaa" ) { - confirmed << " lotw_qsl_rcvd = 'Y' "; + setEntityInputEnabled(false); + headersColumns = "p.reference col1, p.name col2 "; + uniqColumns = "c.my_pota_ref_str"; + sqlPartDetailTable = " FROM pota_directory p " + " INNER JOIN source_contacts c ON SUBSTR(c.my_pota_ref_str, 1, COALESCE(NULLIF(INSTR(c.my_pota_ref_str, '@'), 0) - 1, LENGTH(c.my_pota_ref_str))) = p.reference" + " INNER JOIN modes m on c.mode = m.name "; + addlCTEs << " split(id, callsign, station_callsign, my_dxcc, band, dxcc, eqsl_qsl_rcvd, lotw_qsl_rcvd, qsl_rcvd, prop_mode, mode, my_pota_ref_str, str) AS (" + " SELECT id, callsign, station_callsign, my_dxcc, band, " + " dxcc, eqsl_qsl_rcvd, lotw_qsl_rcvd, qsl_rcvd,prop_mode,mode, " + " '', my_pota_ref||',' " + " FROM contacts " + " UNION ALL " + " SELECT id, callsign, station_callsign, my_dxcc, band, " + " dxcc, eqsl_qsl_rcvd, lotw_qsl_rcvd, qsl_rcvd, prop_mode, mode, " + " substr(str, 0, instr(str, ',')), substr(str, instr(str, ',') + 1) " + " FROM split " + " WHERE str != '') "; + sourceContactsTable = " source_contacts AS (" + " SELECT id, callsign, station_callsign, my_dxcc, band, dxcc, eqsl_qsl_rcvd, lotw_qsl_rcvd, qsl_rcvd,prop_mode, mode, my_pota_ref_str " + " FROM split " + " WHERE my_pota_ref_str != '' ) "; + addWherePart = " AND c.my_pota_ref_str is not NULL "; } - - if ( ui->paperCheckBox->isChecked() ) + else if ( awardSelected == "sota" ) { - confirmed << " qsl_rcvd = 'Y' "; - } - - QString innerCase = " CASE WHEN (" + confirmed.join("or") + ") THEN 2 ELSE 1 END "; - - const QList& dxccBands = BandPlan::bandsList(true, true); + setEntityInputEnabled(false); + headersColumns = "s.summit_code col1, NULL col2 "; + uniqColumns = "c.sota_ref"; - if ( dxccBands.size() == 0 ) - { - return; + sqlPartDetailTable = " FROM sota_summits s " + " INNER JOIN source_contacts c ON c.sota_ref = s.summit_code " + " INNER JOIN modes m on c.mode = m.name "; } - - QStringList stmt_max_part; - QStringList stmt_total_padding; - QStringList stmt_sum_confirmed; - QStringList stmt_sum_worked; - QStringList stmt_sum_total; - - for ( int i = 0; i < dxccBands.size(); i++ ) + else if ( awardSelected == "wwff" ) { - stmt_max_part << QString(" MAX(CASE WHEN band = '%1' AND m.dxcc IN (" + modes.join(",") + ") THEN " + innerCase + " ELSE 0 END) as '%2'").arg(dxccBands[i].name, dxccBands[i].name); - stmt_total_padding << QString(" NULL '%1'").arg(dxccBands[i].name); - stmt_sum_confirmed << QString("SUM(CASE WHEN a.'%1' > 1 THEN 1 ELSE 0 END) '%2'").arg(dxccBands[i].name, dxccBands[i].name); - stmt_sum_worked << QString("SUM(CASE WHEN a.'%1' > 0 THEN 1 ELSE 0 END) '%2'").arg(dxccBands[i].name, dxccBands[i].name); - stmt_sum_total << QString("SUM(d.'%1') '%2'").arg(dxccBands[i].name, dxccBands[i].name); + setEntityInputEnabled(false); + headersColumns = "w.reference col1, w.name col2 "; + uniqColumns = "c.wwff_ref"; + sqlPartDetailTable = " FROM wwff_directory w " + " INNER JOIN source_contacts c ON c.wwff_ref = w.reference " + " INNER JOIN modes m on c.mode = m.name "; } - detailedViewModel->setQuery( - "WITH dxcc_summary AS ( " - "SELECT " + headersColumns +", " - + stmt_max_part.join(",") + ", " - " MAX(CASE WHEN prop_mode = 'SAT' AND m.dxcc IN (" + modes.join(",") + ") THEN " + innerCase + " ELSE 0 END) as 'SAT', " - " MAX(CASE WHEN prop_mode = 'EME' AND m.dxcc IN (" + modes.join(",") + ") THEN " + innerCase + " ELSE 0 END) as 'EME' " - + sqlPart - + excludePart + - "GROUP BY 1,2), " - " ituzCTE AS ( " - " SELECT 1 AS n, 1 AS value " - " UNION ALL " - " SELECT n + 1, value + 1 " - " FROM ituzCTE " - " WHERE n < 90 ), " - " cqzCTE AS ( " - " SELECT 1 AS n, 1 AS value " - " UNION ALL " - " SELECT n + 1, value + 1 " - " FROM cqzCTE " - " WHERE n < 40 ), " - "continents as " - "(values ('NA', '" + tr("North America") + "'),('SA','" + tr("South America") + "'),('EU', '" + tr("Europe") + "'),('AF', '" + tr("Africa") + "'),('OC', '" + tr("Oceania") + "'),('AS', '" + tr("Asia") + "'),('AN', '" + tr("Antarctica") + "')) " - "SELECT * FROM ( " - "SELECT 0 column_idx, " - " '" + tr("TOTAL Worked") + "', " - " count(DISTINCT " + uniqColumns + "), " - + stmt_total_padding.join(",") + ", " + - " NULL 'SAT', " - " NULL 'EME' " - "FROM contacts c, modes m " - "WHERE c.mode = m.name " - " AND c.my_dxcc = '" + entitySelected + "' " - " AND m.dxcc IN (" + modes.join(",") + ") " - + excludePart + - "UNION ALL " - "SELECT 0 column_idx, " - " '" + tr("TOTAL Confirmed") + "', " - " count(DISTINCT " + uniqColumns + "), " - + stmt_total_padding.join(",") + ", " + - " NULL 'SAT', " - " NULL 'EME' " - "FROM contacts c, modes m " - "WHERE (" + confirmed.join("or") + ") " - " AND c.mode = m.name " - " AND m.dxcc IN (" + modes.join(",") + ") " - " AND c.my_dxcc = '" + entitySelected + "' " - + excludePart + - "UNION ALL " - "SELECT 1 column_idx, " - " '" + tr("Confirmed") + "', NULL prefix, " - + stmt_sum_confirmed.join(",") + ", " + - " SUM(CASE WHEN a.'SAT' > 1 THEN 1 ELSE 0 END) 'SAT', " - " SUM(CASE WHEN a.'EME' > 1 THEN 1 ELSE 0 END) 'EME' " - "FROM dxcc_summary a " - "GROUP BY 1 " - "UNION ALL " - "SELECT 2 column_idx, " - " '" + tr("Worked") + "', NULL prefix, " - + stmt_sum_worked.join(",") + ", " + - " SUM(CASE WHEN a.'SAT' > 0 THEN 1 ELSE 0 END) 'SAT', " - " SUM(CASE WHEN a.'EME' > 0 THEN 1 ELSE 0 END) 'EME' " - "FROM dxcc_summary a " - "GROUP BY 1 " - "UNION ALL " - "SELECT 3 column_idx, " - " col1, col2, " - + stmt_sum_total.join(",") + ", " + - " SUM(d.'SAT') 'SAT', " - " SUM(d.'EME') 'EME' " - " from dxcc_summary d " - "GROUP BY 2,3 " - ") " - "ORDER BY 1,2 COLLATE LOCALEAWARE ASC "); - - qDebug(runtime) << detailedViewModel->query().lastQuery(); + addlCTEs.append(sourceContactsTable); + + QString finalSQL(QString( + "WITH " + " %1, " + " detail_table AS ( " + " SELECT %2, %3 " + " %4" + " %5" + " GROUP BY 1,2) " + "SELECT * FROM ( " + " SELECT 0 column_idx, '%6', COUNT(DISTINCT %7), %8" + " FROM source_contacts c INNER JOIN modes m ON c.mode = m.name" + " WHERE m.dxcc IN (%9)" + " %10" + " UNION ALL " + " SELECT 0 column_idx, '%11', COUNT(DISTINCT %12), %13" + " FROM source_contacts c INNER JOIN modes m ON c.mode = m.name " + " WHERE (%14)" + " AND m.dxcc IN (%15)" + " %16" + " UNION ALL " + " SELECT 1 column_idx, '%17', NULL prefix, %18" + " FROM detail_table a " + " GROUP BY 1 " + " UNION ALL " + " SELECT 2 column_idx, '%19', NULL prefix, %20" + " FROM detail_table a " + " GROUP BY 1 " + " UNION ALL " + " SELECT 3 column_idx, col1, col2, %21" + " FROM detail_table d " + " GROUP BY 2,3 " + ") " + "ORDER BY 1,2 COLLATE LOCALEAWARE ASC ").arg(addlCTEs.join(","), + headersColumns, + stmt_max_part.join(","), + sqlPartDetailTable, + addWherePart, + tr("TOTAL Worked"), + uniqColumns, + stmt_total_padding.join(","), + modes.join(","), + addWherePart, + tr("TOTAL Confirmed"), + uniqColumns, + stmt_total_padding.join(","), + confirmed.join("or"), + modes.join(","), + addWherePart, + tr("Confirmed"), + stmt_sum_confirmed.join(","), + tr("Worked"), + stmt_sum_worked.join(","), + stmt_sum_total.join(","))); + qDebug(runtime) << finalSQL; + + detailedViewModel->setQuery(finalSQL); detailedViewModel->setHeaderData(1, Qt::Horizontal, ""); detailedViewModel->setHeaderData(2, Qt::Horizontal, ""); - ui->awardTableView->setModel(detailedViewModel); ui->awardTableView->setColumnHidden(0,true); } @@ -278,13 +375,13 @@ void AwardsDialog::awardTableDoubleClicked(QModelIndex idx) { FCT_IDENTIFICATION; - QString myEntitySelected = getSelectedEntity(); - QString awardSelected = getSelectedAward(); + const QString &awardSelected = getSelectedAward(); QStringList addlFilters; QString band; QString country; - addlFilters << QString("my_dxcc='%1'").arg(myEntitySelected); + if ( ui->myEntityComboBox->isEnabled() ) + addlFilters << QString("my_dxcc='%1'").arg(getSelectedEntity()); if ( idx.row() > 3 ) { @@ -292,30 +389,46 @@ void AwardsDialog::awardTableDoubleClicked(QModelIndex idx) { country = detailedViewModel->data(detailedViewModel->index(idx.row(),1),Qt::DisplayRole).toString(); } - if ( awardSelected == "itu" ) + else if ( awardSelected == "itu" ) { addlFilters << QString("ituz = '%1'").arg(detailedViewModel->data(detailedViewModel->index(idx.row(),1),Qt::DisplayRole).toString()); } - if ( awardSelected == "iota" ) + else if ( awardSelected == "iota" ) { - addlFilters << QString("upper(iota) = upper('%1')").arg(detailedViewModel->data(detailedViewModel->index(idx.row(),1),Qt::DisplayRole).toString()); + addlFilters << QString("iota = '%1'").arg(detailedViewModel->data(detailedViewModel->index(idx.row(),1),Qt::DisplayRole).toString()); } - if ( awardSelected == "wac" ) + else if ( awardSelected == "wac" ) { addlFilters << QString("cont = '%1'").arg(detailedViewModel->data(detailedViewModel->index(idx.row(),2),Qt::DisplayRole).toString()); } - if ( awardSelected == "was" ) + else if ( awardSelected == "was" ) { addlFilters << QString("state = '%1' and dxcc in (6, 110, 291)").arg(detailedViewModel->data(detailedViewModel->index(idx.row(),2),Qt::DisplayRole).toString()); } - if ( awardSelected == "waz" ) + else if ( awardSelected == "waz" ) { addlFilters << QString("cqz = '%1'").arg(detailedViewModel->data(detailedViewModel->index(idx.row(),1),Qt::DisplayRole).toString()); } - if ( awardSelected == "wpx" ) + else if ( awardSelected == "wpx" ) { addlFilters << QString("pfx = '%1'").arg(detailedViewModel->data(detailedViewModel->index(idx.row(),1),Qt::DisplayRole).toString()); } + else if ( awardSelected == "potah" ) + { + addlFilters << QString("pota_ref LIKE '%%1%'").arg(detailedViewModel->data(detailedViewModel->index(idx.row(),1),Qt::DisplayRole).toString()); + } + else if ( awardSelected == "potaa" ) + { + addlFilters << QString("my_pota_ref LIKE = '%%1%'").arg(detailedViewModel->data(detailedViewModel->index(idx.row(),1),Qt::DisplayRole).toString()); + } + else if ( awardSelected == "sota" ) + { + addlFilters << QString("sota_ref = '%1'").arg(detailedViewModel->data(detailedViewModel->index(idx.row(),1),Qt::DisplayRole).toString()); + } + else if ( awardSelected == "wwff" ) + { + addlFilters << QString("wwff_ref = '%1'").arg(detailedViewModel->data(detailedViewModel->index(idx.row(),1),Qt::DisplayRole).toString()); + } if ( idx.column() > 2 ) { @@ -326,20 +439,20 @@ void AwardsDialog::awardTableDoubleClicked(QModelIndex idx) } } -QString AwardsDialog::getSelectedEntity() +const QString AwardsDialog::getSelectedEntity() const { FCT_IDENTIFICATION; int row = ui->myEntityComboBox->currentIndex(); - QModelIndex idx = ui->myEntityComboBox->model()->index(row,0); - QVariant comboData = ui->myEntityComboBox->model()->data(idx); + const QModelIndex &idx = ui->myEntityComboBox->model()->index(row,0); + const QVariant &comboData = ui->myEntityComboBox->model()->data(idx); qCDebug(runtime) << comboData.toString(); return comboData.toString(); } -QString AwardsDialog::getSelectedAward() +const QString AwardsDialog::getSelectedAward() const { FCT_IDENTIFICATION; @@ -347,3 +460,11 @@ QString AwardsDialog::getSelectedAward() qCDebug(runtime) << ret; return ret; } + +void AwardsDialog::setEntityInputEnabled(bool enabled) +{ + FCT_IDENTIFICATION; + + ui->myEntityComboBox->setEnabled(enabled); + ui->myEntityLabel->setEnabled(enabled); +} diff --git a/ui/AwardsDialog.h b/ui/AwardsDialog.h index 38a59312..b94a9080 100644 --- a/ui/AwardsDialog.h +++ b/ui/AwardsDialog.h @@ -29,9 +29,11 @@ public slots: Ui::AwardsDialog *ui; AwardsTableModel *detailedViewModel; SqlListModel* entityCallsignModel; + QString selectedAward; - QString getSelectedEntity(); - QString getSelectedAward(); + const QString getSelectedEntity() const; + const QString getSelectedAward() const; + void setEntityInputEnabled(bool); }; #endif // QLOG_UI_AWARDSDIALOG_H diff --git a/ui/AwardsDialog.ui b/ui/AwardsDialog.ui index 0ac83aa0..840b9d80 100644 --- a/ui/AwardsDialog.ui +++ b/ui/AwardsDialog.ui @@ -6,7 +6,7 @@ 0 0 - 1044 + 1016 702 @@ -14,12 +14,45 @@ Awards + + 3 + + + 6 + + + 2 + + + 6 + + + 6 + Options + + 3 + + + 3 + + + 6 + + + 6 + + + 6 + + + 6 + @@ -28,7 +61,14 @@ - + + + + 0 + 0 + + + @@ -38,7 +78,14 @@ - + + + + 0 + 0 + + + @@ -49,6 +96,9 @@ + + 6 + @@ -139,6 +189,12 @@ QAbstractItemView::SingleSelection + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + true @@ -154,6 +210,12 @@ false + + 20 + + + 20 + false diff --git a/ui/DxFilterDialog.cpp b/ui/DxFilterDialog.cpp index 3b90bef2..fc7f2fca 100644 --- a/ui/DxFilterDialog.cpp +++ b/ui/DxFilterDialog.cpp @@ -11,6 +11,7 @@ #include "core/debug.h" #include "data/Dxcc.h" #include "core/MembershipQE.h" +#include "DxWidget.h" MODULE_IDENTIFICATION("qlog.ui.dxfilterdialog"); @@ -101,7 +102,11 @@ DxFilterDialog::DxFilterDialog(QWidget *parent) : /* Deduplication */ /*****************/ bool deduplication = settings.value("dxc/filter_deduplication", false).toBool(); - ui->deduplicationcheckbox->setChecked(deduplication); + ui->deduplicationGroupBox->setChecked(deduplication); + int duplicationtime = settings.value("dxc/filter_duplicationtime", DEDUPLICATION_TIME).toInt(); + ui->dedupTimeDiffSpinbox->setValue(duplicationtime); + int duplicationfreq = settings.value("dxc/filter_deduplicationfreq", DEDUPLICATION_FREQ_TOLERANCE).toInt(); + ui->dedupFreqDiffSpinbox->setValue(duplicationfreq); /**********/ /* MEMBER */ @@ -184,7 +189,9 @@ void DxFilterDialog::accept() /*****************/ /* Deduplication */ /*****************/ - settings.setValue("dxc/filter_deduplication", ui->deduplicationcheckbox->isChecked()); + settings.setValue("dxc/filter_deduplication", ui->deduplicationGroupBox->isChecked()); + settings.setValue("dxc/filter_duplicationtime",ui->dedupTimeDiffSpinbox->value() ); + settings.setValue("dxc/filter_deduplicationfreq", ui->dedupFreqDiffSpinbox->value()); /**********/ /* MEMBER */ diff --git a/ui/DxFilterDialog.ui b/ui/DxFilterDialog.ui index 4709dd1a..7d3da130 100644 --- a/ui/DxFilterDialog.ui +++ b/ui/DxFilterDialog.ui @@ -331,24 +331,77 @@ - + + + Do not show the following spots when they have the same Callsign and their time difference is lower than Time Diff and their frequency difference is lower than Freq Diff + - Others + Spots Dedup - - - - - - - Spots Deduplication - - - Spots Dedup - - - - + + true + + + + 4 + + + 4 + + + 6 + + + 6 + + + 6 + + + 6 + + + + + Time difference + + + + + + + s + + + 1 + + + 9999 + + + + + + + Frequency difference + + + + + + + kHz + + + ± + + + 1 + + + 100 + + diff --git a/ui/DxWidget.cpp b/ui/DxWidget.cpp index 37233cc8..198d7be8 100644 --- a/ui/DxWidget.cpp +++ b/ui/DxWidget.cpp @@ -119,7 +119,6 @@ bool DxTableModel::addEntry(DxSpot entry, bool deduplicate, qint16 dedup_interval, double dedup_freq_tolerance) { bool shouldInsert = true; - if ( deduplicate ) { for (const DxSpot &record : qAsConst(dxData)) @@ -128,7 +127,7 @@ bool DxTableModel::addEntry(DxSpot entry, bool deduplicate, break; if ( record.callsign == entry.callsign - && qAbs(record.freq - entry.freq) < dedup_freq_tolerance ) + && qAbs(MHz(record.freq) - MHz(entry.freq)) < kHz(dedup_freq_tolerance) ) { qCDebug(runtime) << "Duplicate spot" << record.callsign << record.freq << entry.callsign << entry.freq; shouldInsert = false; @@ -682,6 +681,22 @@ uint DxWidget::dxccStatusFilterValue() return settings.value("dxc/filter_dxcc_status", DxccStatus::All).toUInt(); } +int DxWidget::getDedupTimeValue() +{ + FCT_IDENTIFICATION; + + QSettings settings; + return settings.value("dxc/filter_duplicationtime", DEDUPLICATION_TIME).toInt(); +} + +int DxWidget::getDedupFreqValue() +{ + FCT_IDENTIFICATION; + + QSettings settings; + return settings.value("dxc/filter_duplicationfreq", DEDUPLICATION_FREQ_TOLERANCE).toInt(); +} + bool DxWidget::spotDedupValue() { FCT_IDENTIFICATION; @@ -1244,6 +1259,8 @@ void DxWidget::reloadSetting() bandregexp.setPattern(bandFilterRegExp()); dxccStatusFilter = dxccStatusFilterValue(); deduplicateSpots = spotDedupValue(); + deduplicatetime = getDedupTimeValue(); + deduplicatefreq = getDedupFreqValue(); QStringList tmp = dxMemberList(); #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) dxMemberFilter = QSet(tmp.begin(), tmp.end()); @@ -1504,7 +1521,7 @@ void DxWidget::processDxSpot(const QString &spotter, || (dxMemberFilter.size() && spot.memberList2Set().intersects(dxMemberFilter))) ) { - if ( dxTableModel->addEntry(spot, deduplicateSpots) ) + if ( dxTableModel->addEntry(spot, deduplicateSpots, deduplicatetime, deduplicatefreq) ) emit newFilteredSpot(spot); } } diff --git a/ui/DxWidget.h b/ui/DxWidget.h index aa7ecf38..8f2c6cce 100644 --- a/ui/DxWidget.h +++ b/ui/DxWidget.h @@ -16,8 +16,11 @@ #include "core/LogLocale.h" #include "core/DxServerString.h" +// in sec #define DEDUPLICATION_TIME 3 -#define DEDUPLICATION_FREQ_TOLERANCE 0.005 + +// in kHz +#define DEDUPLICATION_FREQ_TOLERANCE 5 namespace Ui { class DxWidget; @@ -178,6 +181,9 @@ private slots: QRegularExpression bandregexp; uint dxccStatusFilter; bool deduplicateSpots; + int deduplicatetime; + int deduplicatefreq; + QSet dxMemberFilter; QSqlRecord lastQSO; quint8 reconnectAttempts; @@ -194,6 +200,8 @@ private slots: QString bandFilterRegExp(); uint dxccStatusFilterValue(); bool spotDedupValue(); + int getDedupTimeValue(); + int getDedupFreqValue(); QStringList dxMemberList(); bool getAutoconnectServer(); void saveAutoconnectServer(bool); diff --git a/ui/MainWindow.cpp b/ui/MainWindow.cpp index e78e5694..ae7e6f38 100644 --- a/ui/MainWindow.cpp +++ b/ui/MainWindow.cpp @@ -423,6 +423,19 @@ void MainWindow::rigErrorHandler(const QString &error, const QString &errorDetai ui->actionConnectRig->setChecked(false); } +void MainWindow::steppirConnect() +{ + FCT_IDENTIFICATION; + if ( ui->actionConnect_Steppir->isChecked() ) + { + ui->SteppirDock->open(); + } + else + { + ui->SteppirDock->close(); + } +} + void MainWindow::rotErrorHandler(const QString &error, const QString &errorDetail) { FCT_IDENTIFICATION; @@ -734,6 +747,7 @@ void MainWindow::saveEquipmentConnOptions() settings.setValue("equipment/rigconnected", ui->actionConnectRig->isChecked()); settings.setValue("equipment/rotconnected", ui->actionConnectRotator->isChecked()); settings.setValue("equipment/cwkeyconnected", ui->actionConnectCWKeyer->isChecked()); + settings.setValue("equipment/steppirconnected",ui->actionConnect_Steppir->isChecked()); } } @@ -771,6 +785,15 @@ void MainWindow::restoreConnectionStates() ui->actionConnectCWKeyer->setChecked(true); }); } + + if ( settings.value("equipment/steppirconnected", false).toBool() ) + { + QTimer::singleShot(3000, this, [this]() + { + if ( !ui->actionConnect_Steppir->isChecked() ) + ui->actionConnect_Steppir->setChecked(true); + }); + } } } @@ -1234,3 +1257,5 @@ MainWindow::~MainWindow() delete ui; } + + diff --git a/ui/MainWindow.h b/ui/MainWindow.h index d458d71a..e16a8a44 100644 --- a/ui/MainWindow.h +++ b/ui/MainWindow.h @@ -42,10 +42,12 @@ public slots: void rotErrorHandler(const QString &error, const QString &errorDetail); void cwKeyerErrorHandler(const QString &error, const QString &errorDetail); void stationProfileChanged(); + void setLayoutGeometry(); private slots: void rigConnect(); void rotConnect(); + void steppirConnect(); void cwKeyerConnect(); void cwKeyerConnectProfile(QString); void cwKeyerDisconnectProfile(QString); @@ -75,10 +77,11 @@ private slots: void shortcutALTBackslash(); void setManualContact(bool); void showEditLayout(); - void setLayoutGeometry(); + void saveProfileLayoutGeometry(); void setEquipmentKeepOptions(bool); + private: Ui::MainWindow* ui; QLabel* conditionsLabel; diff --git a/ui/MainWindow.ui b/ui/MainWindow.ui index c946f9b0..4576b711 100644 --- a/ui/MainWindow.ui +++ b/ui/MainWindow.ui @@ -7,7 +7,7 @@ 0 0 913 - 558 + 562 @@ -52,7 +52,7 @@ 0 0 913 - 22 + 24 @@ -83,6 +83,7 @@ + @@ -110,6 +111,7 @@ + @@ -279,10 +281,18 @@ + + + Steppir + + + 2 + + + - - .. + Quit @@ -302,8 +312,7 @@ - - .. + &Settings @@ -314,8 +323,7 @@ - - .. + New QSO - Clear @@ -332,8 +340,7 @@ - - .. + &Import @@ -341,8 +348,7 @@ - - .. + &Export @@ -361,8 +367,7 @@ - - .. + &About @@ -373,8 +378,7 @@ - - .. + New QSO - Save @@ -409,8 +413,7 @@ - - .. + S&tatistics @@ -475,8 +478,7 @@ - - .. + &Awards @@ -530,8 +532,7 @@ - - .. + &Wiki @@ -776,6 +777,19 @@ true + + + Steppir + + + + + true + + + Connect Steppir + + @@ -871,6 +885,12 @@
ui/ProfileImageWidget.h
1 + + Steppir + QWidget +
ui/steppir.h
+ 1 +
@@ -1532,6 +1552,38 @@ + + actionSteppir + triggered() + SteppirdockWidget + show() + + + -1 + -1 + + + 881 + 511 + + + + + actionConnect_Steppir + toggled(bool) + MainWindow + steppirConnect() + + + -1 + -1 + + + 456 + 280 + + + settingsChanged() @@ -1542,6 +1594,7 @@ exportLog() showAbout() rigConnect() + steppirConnect() showStatistics() showLotw() rotConnect() diff --git a/ui/SettingsDialog.cpp b/ui/SettingsDialog.cpp index 21cc84c9..5ccd5183 100644 --- a/ui/SettingsDialog.cpp +++ b/ui/SettingsDialog.cpp @@ -87,6 +87,9 @@ SettingsDialog::SettingsDialog(MainWindow *parent) : ui->rigPortTypeCombo->addItem(tr("Network")); ui->rigPortTypeCombo->addItem(tr("Special - Omnirig")); + ui->steppirPortTypeCombo->addItem(tr("Serial")); + ui->steppirPortTypeCombo->addItem(tr("Network")); + #ifdef QLOG_FLATPAK ui->lotwTextMessage->setVisible(true); ui->tqslPathEdit->setVisible(false); @@ -2279,6 +2282,17 @@ void SettingsDialog::readSettings() { ui->notifWSJTXCQSpotsEdit->setText(NetworkNotification::getNotifWSJTXCQSpotAddrs()); ui->notifSpotAlertEdit->setText(NetworkNotification::getNotifSpotAlertAddrs()); + /***********/ + /* STEPPIR */ + /***********/ + ui->steppirBaudSelect->setCurrentIndex(settings.value("steppir/baud").toInt()); + ui->steppirHostNameEdit->setText(settings.value("steppir/hostname").toString()); + ui->steppirPortTypeCombo->setCurrentIndex(settings.value("steppir/portype").toInt()); + ui->steppirPollIntervalSpinBox->setValue(settings.value("steppir/poll").toInt()); + ui->steppirPortEdit->setText(settings.value("steppir/portedit").toString()); + ui->steppirNetPortSpin->setValue(settings.value("steppir/netport").toInt()); + + /******************/ /* END OF Reading */ /******************/ @@ -2388,6 +2402,17 @@ void SettingsDialog::writeSettings() { Wsjtx::saveConfigMulticastAddress(ui->wsjtMulticastAddressEdit->text()); Wsjtx::saveConfigMulticastTTL(ui->wsjtMulticastTTLSpin->value()); + /***********/ + /* STEPPIR */ + /***********/ + settings.setValue("steppir/baud",ui->steppirBaudSelect->currentIndex()); + settings.setValue("steppir/hostname",ui->steppirHostNameEdit->text()); + settings.setValue("steppir/portype",ui->steppirPortTypeCombo->currentIndex()); + settings.setValue("steppir/poll",ui->steppirPollIntervalSpinBox->value()); + settings.setValue("steppir/portedit",ui->steppirPortEdit->text()); + settings.setValue("steppir/netport",ui->steppirNetPortSpin->value()); + settings.setValue("steppir/baudrate",ui->steppirBaudSelect->currentText()); + NetworkNotification::saveNotifQSOAdiAddrs(ui->notifQSOEdit->text()); NetworkNotification::saveNotifDXSpotAddrs(ui->notifDXSpotsEdit->text()); NetworkNotification::saveNotifWSJTXCQSpotAddrs(ui->notifWSJTXCQSpotsEdit->text()); @@ -2627,3 +2652,61 @@ SettingsDialog::~SettingsDialog() { iotaCompleter->deleteLater(); delete ui; } + +void SettingsDialog::on_steppirPollIntervalSpinBox_valueChanged(int arg1) +{ + +} + + +void SettingsDialog::on_steppirPortTypeCombo_currentIndexChanged(int index) +{ + FCT_IDENTIFICATION; + qWarning() << "steppirPortTypeCombo " << index; + + switch (index) + { + // Serial + case RIGPORT_SERIAL_INDEX: + { + ui->steppirStackedWidget->setCurrentIndex(STACKED_WIDGET_SERIAL_SETTING); + ui->steppirHostNameEdit->clear(); + } + break; + + // Network + case RIGPORT_NETWORK_INDEX: + ui->steppirStackedWidget->setCurrentIndex(STACKED_WIDGET_NETWORK_SETTING); + ui->steppirPortEdit->clear(); + ui->steppirNetPortSpin->setValue(RIG_NET_DEFAULT_PORT); + break; + + default: + qWarning() << "Unsupported Network Port" << index; + } +} + + +void SettingsDialog::on_steppirPortEdit_textChanged(const QString &arg1) +{ + +} + + +void SettingsDialog::on_steppirBaudSelect_currentIndexChanged(int index) +{ + +} + + +void SettingsDialog::on_steppirHostNameEdit_textChanged(const QString &arg1) +{ + +} + + +void SettingsDialog::on_steppirNetPortSpin_valueChanged(int arg1) +{ + +} + diff --git a/ui/SettingsDialog.h b/ui/SettingsDialog.h index cd90835a..66523de4 100644 --- a/ui/SettingsDialog.h +++ b/ui/SettingsDialog.h @@ -115,6 +115,19 @@ public slots: void hrdlogSettingChanged(); void clublogSettingChanged(); +private slots: + void on_steppirPollIntervalSpinBox_valueChanged(int arg1); + + void on_steppirPortTypeCombo_currentIndexChanged(int index); + + void on_steppirPortEdit_textChanged(const QString &arg1); + + void on_steppirBaudSelect_currentIndexChanged(int index); + + void on_steppirHostNameEdit_textChanged(const QString &arg1); + + void on_steppirNetPortSpin_valueChanged(int arg1); + private: void readSettings(); void writeSettings(); diff --git a/ui/SettingsDialog.ui b/ui/SettingsDialog.ui index 5ec4f124..b5acf9b9 100644 --- a/ui/SettingsDialog.ui +++ b/ui/SettingsDialog.ui @@ -27,7 +27,7 @@ - 0 + 1 false @@ -523,17 +523,17 @@
- - + + - Add + Delete - - + + - Delete + Add @@ -557,17 +557,267 @@ - - - Qt::Vertical - - + + - 20 - 40 + 0 + 120 - + + Steppir Controller + + + + + 130 + 50 + 389 + 61 + + + + + 0 + 0 + + + + QFrame::NoFrame + + + 0 + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + 6 + + + 4 + + + 0 + + + 2 + + + 0 + + + 0 + + + + + Port + + + + + + + + 0 + 0 + + + + Use COMxx for Window or path to COM port under Unix-like OS + + + + + + + + + + + 0 + 0 + + + + Baudrate + + + + + + + + 0 + 0 + + + + Qt::StrongFocus + + + + 115200 + + + + + 57600 + + + + + 38400 + + + + + 19200 + + + + + 9600 + + + + + 4800 + + + + + 2400 + + + + + 1200 + + + + + + + + + + 6 + + + 2 + + + 0 + + + 2 + + + 0 + + + 0 + + + + + Host Name + + + + + + + 10 + + + + + + + + Port + + + + + + + + + + 1 + + + 65535 + + + 4532 + + + + + + + + + + + + + 130 + 20 + 391 + 32 + + + + + + + Poll Interval + + + + + + + + 0 + 0 + + + + ms + + + 100 + + + 60000 + + + 500 + + + + + + + Connection Type + + + + + + + + +
@@ -2909,7 +3159,7 @@
- + Qt::Vertical @@ -3443,8 +3693,7 @@ Browse - - .. + diff --git a/ui/Steppir.cpp b/ui/Steppir.cpp new file mode 100644 index 00000000..fa2deb10 --- /dev/null +++ b/ui/Steppir.cpp @@ -0,0 +1,641 @@ +#include "Steppir.h" +#include "ui_Steppir.h" +#include +#include +#include +#include +#include +#include +#include + +Steppir::Steppir(QWidget *parent) + : QWidget(parent) + , ui(new Ui::Steppir) +{ + + ui->setupUi(this); + + socket = new QTcpSocket(this); + serial = new QSerialPort(this); + + +} + +Steppir::~Steppir() +{ + closeSerialPort(); + delete ui; +} + +void Steppir::open() +{ + QSettings settings; + baud = settings.value("steppir/baudrate").toString() ; + serialport = settings.value("steppir/portedit").toString(); + networkport = settings.value("steppir/netport").toInt(); + networkhost = settings.value("steppir/hostname").toString(); + pollint = settings.value("steppir/poll").toInt(); + + + + if (serialport.length() > 5 ) + { + connect(serial, &QSerialPort::readyRead, this, &Steppir::readData); + openSerialPort(); + } + + if (networkhost.length() > 5){ + socket->connectToHost(networkhost, networkport); + if (socket->state() != QAbstractSocket::ConnectingState && socket->state() != QAbstractSocket::ConnectedState) { + qDebug() << "Connection failed: " << socket->errorString(); + } + + if (socket->waitForConnected(3000)) { + qDebug() << "Connected to Steppir antenna."; + } else { + qDebug() << "Failed to connect to Steppir antenna: " << socket->errorString(); + } + connect(socket, &QTcpSocket::readyRead, this, &Steppir::readData); + connect(socket, &QTcpSocket::connected, this, &Steppir::onConnected); + connect(socket, &QTcpSocket::errorOccurred, this, &Steppir::onError); + } + + timer = new QTimer; + connect(timer, &QTimer::timeout, this, &Steppir::pollSteppir); + + if (serial->isOpen() || serial->open(QIODevice::ReadWrite)) { + timer->start(pollint); + } else if (socket->state() == QAbstractSocket::ConnectedState) { + timer->start(pollint); + } +} + +void Steppir::close() +{ + if (serial->isOpen() || serial->open(QIODevice::ReadWrite)) { + serial->close(); + } else if (socket->state() == QAbstractSocket::ConnectedState) { + socket->close(); + } + else { + qDebug() << "Failed to open serial or network port."; + } +} + +void Steppir::openSerialPort() { + qDebug() << "In openSerialPort"; + foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) { + if(serialport ==info.systemLocation()) { + serial->setPortName(serialport); // Set the correct COM port + serial->setBaudRate(baud.toInt()); + serial->setDataBits(QSerialPort::Data8); + serial->setParity(QSerialPort::NoParity); + serial->setStopBits(QSerialPort::OneStop); + serial->setFlowControl(QSerialPort::NoFlowControl); + + if (serial->open(QIODevice::ReadWrite)) { + qDebug() << "Serial port opened"; + } else { + serial->close(); + qDebug() << serial->errorString(); + qDebug() << "Failed to open serial port"; + } + } + + } + + +} + +void Steppir::closeSerialPort() { + if (serial->isOpen()) { + serial->close(); + } +} + +void Steppir::onConnected(){ + qDebug() << "Successfully connected to the Steppir Antenna"; +} + +void Steppir::onError(){ + qDebug() << "Connection error " << socket->errorString(); +} + +void Steppir::sendStatusRequest() { + QByteArray command = "?A\r"; + serial->write(command); +} + +void Steppir::sendSetCommand(QString frequency, char direction, char steppircommand) { + QByteArray command; + command.append('@'); // ASCII @ (40 hex) + command.append('A'); // ASCII A (41 hex) + command.append(char(0x00)); // The zero byte + + // Frequency + int freq = frequency.toInt() * 100; // Multiply by 10 as per the specification + command.append((freq >> 16) & 0xFF); // Fh + command.append((freq >> 8) & 0xFF); // Fm + command.append(freq & 0xFF); // Fl + + command.append(char(0x00)); // ac field placeholder (ignored) + command.append(direction);// direction byte, ensure 'direction' is a char or similar type + command.append(steppircommand); // cmd placeholder + command.append(char(0x00)); // cmd placeholder + command.append(0x0D); // End of message with CR + + if (serial->isOpen() || serial->open(QIODevice::ReadWrite)) { + serial->write(command); + serial->flush(); + } else if (socket->state() == QAbstractSocket::ConnectedState) { + // Write the command to the TCP socket + socket->write(command); + socket->flush(); + } + else { + qDebug() << "Failed to open serial or network port."; + } +} + +void Steppir::readData() { + if (serial->isOpen() || serial->open(QIODevice::ReadWrite)) { + buffer.append(serial->readAll()); + } else if (socket->state() == QAbstractSocket::ConnectedState) { + buffer.append(socket->readAll()); + } + + + int index; + while ((index = buffer.indexOf('\r')) != -1) { + QByteArray response = buffer.left(index + 1); + parseResponse(response); // Process the message + buffer.remove(0, index + 1); // Remove the processed bytes from the buffer + } + + // QByteArray responseData = serial->readAll(); + // qDebug() << "Response: " << responseData; + // Parse and process the response data +} + +void Steppir::parseResponse(const QByteArray &response) { + if (response.size() != 11) { + qDebug() << "Invalid response size"; + return; + } + QString resp = response.toHex(); + bool ok; + + // Frequency + int tfrequency = resp.mid(6,6).toUInt(&ok,16); + Frequency = tfrequency / 100.0; + + // Active Motor Flags + bool dvrActive = response[6] & 0x04; + bool dir1Active = response[6] & 0x08; + bool reflActive = response[6] & 0x10; + bool dir2Active = response[6] & 0x20; + + qDebug() << "DVR Active:" << dvrActive; + qDebug() << "DIR1 Active:" << dir1Active; + qDebug() << "Reflector Active:" << reflActive; + qDebug() << "DIR2 Active:" << dir2Active; + + // Direction + switch ((response[7] & 0xe0) >> 5) { + case 0x00: + Direction = "Normal"; + break; + case 0x02: + Direction = "180"; + break; + case 0x04: + Direction = "Bi-directional"; + break; + case 0x03: + Direction = "3/4 wave"; + break; + default: + Direction = "Unknown"; + } + qDebug() << "Direction:" << Direction; + + // Version Number + QString version = QString("%1%2").arg(response[8]).arg(response[9]); + + Tuning = false; + + if (response[6] != 0x00){ + Tuning = true; + } + + quint8 TrackFlag = 0; + TrackFlag = response[7]; + TrackFlag = (TrackFlag & 0b00000100) >> 2; + AutoTracking = TrackFlag; + UpdateForm(); + qDebug() << "Version:" << version; +} + + +void Steppir::UpdateForm() +{ + + QColor colGreen = QColor(Qt::blue); + QString cssGreen = QString("background-color: %1").arg(colGreen.name()); + QColor colYellow = QColor(Qt::yellow); + QString cssYellow = QString("background-color: %1").arg(colYellow.name()); + + ui->frequencyLabel->setText(QString::number(Frequency,'f',2)); + if(Tuning){ + ui->frequencyLabel->setStyleSheet(cssYellow); + }else + { + ui->frequencyLabel->setStyleSheet(""); + } + + if(AutoTracking){ + ui->pushAutoTrack->setStyleSheet(cssGreen); + } + else + { + ui->pushAutoTrack->setStyleSheet(""); + } + + QStringList Bands; + Bands << "6m" << "10m" << "12m" << "15m" << "17m" << "20m" << "30m" << "40m" << "80m"; + + QString curband = BandPlan::freq2Band(Frequency/1000).name; + + if (band != curband){ + band = curband; + ui->pb10_2->setStyleSheet(""); + ui->pb12_2->setStyleSheet(""); + ui->pb15_2->setStyleSheet(""); + ui->pb17_2->setStyleSheet(""); + ui->pb20_2->setStyleSheet(""); + ui->pb30_2->setStyleSheet(""); + ui->pb40_2->setStyleSheet(""); + ui->pb80_2->setStyleSheet(""); + + if (curband == "6m") { + ui->pb6_2->setStyleSheet(cssGreen); + } else if (curband == "10m") { + ui->pb10_2->setStyleSheet(cssGreen); + } else if (curband == "12m") { + ui->pb12_2->setStyleSheet(cssGreen); + } else if (curband == "15m") { + ui->pb15_2->setStyleSheet(cssGreen); + } else if (curband == "17m") { + ui->pb17_2->setStyleSheet(cssGreen); + } else if (curband == "20m") { + ui->pb20_2->setStyleSheet(cssGreen); + } else if (curband == "30m") { + ui->pb30_2->setStyleSheet(cssGreen); + } else if (curband == "40m") { + ui->pb40_2->setStyleSheet(cssGreen); + } else if (curband == "80m") { + ui->pb80_2->setStyleSheet(cssGreen); + } + } + if(Direction != OldDirection){ + ui->buttonNormal->setStyleSheet(""); + ui->butonReverse->setStyleSheet(""); + ui->buttonBi->setStyleSheet(""); + OldDirection = Direction; + if ( Direction == "Normal") { + ui->buttonNormal->setStyleSheet(cssGreen); + } else if (Direction == "180") { + ui->butonReverse->setStyleSheet(cssGreen); + } else if (Direction =="Bi-directional") { + ui->buttonBi->setStyleSheet(cssGreen); + } + } +} + +void Steppir::pollSteppir() +{ + QByteArray command; + command.append('?'); // Send the status request command + command.append('A'); + command.append(char(0x0D)); // Append CR (0x0D) + + // Write the command to the serial port + if (serial->isOpen() || serial->open(QIODevice::ReadWrite)) { + serial->write(command); + serial->flush(); + } else if (socket->state() == QAbstractSocket::ConnectedState) { + socket->write(command); + socket->flush(); + } + else { + qDebug() << "Steppir - Nothing is open"; + } +} + + + +void Steppir::on_buttonNormal_clicked() +{ + sendSetCommand(QString::number(Frequency),char(0x00),char(0x00)); +} + + +void Steppir::on_butonReverse_clicked() +{ + sendSetCommand(QString::number(Frequency),char(0x40),char(0x00)); +} + + +void Steppir::on_buttonBi_clicked() +{ + sendSetCommand(QString::number(Frequency),char(0x80),char(0x00)); +} + + +void Steppir::on_buttonRetract_clicked() +{ + sendSetCommand(QString::number(Frequency),char(0x00),char(0x53)); +} + + +void Steppir::on_buttonLeftFast_clicked() +{ + char dir = char(0x00); + + if ( Direction == "Normal") { + dir = char(0x00); + } else if (Direction == "180") { + dir = char(0x40); + } else if (Direction =="Bi-directional") { + dir = char(0x80); + } + + //if(AutoTracking){ + // sendSetCommand(QString::number(Frequency),dir,char(0x55)); + // } + sendSetCommand(QString::number(Frequency-100),dir,char(0x00)); +} + + +void Steppir::on_butonLeftSlow_clicked() +{ + char dir = char(0x00); + + if ( Direction == "Normal") { + dir = char(0x00); + } else if (Direction == "180") { + dir = char(0x40); + } else if (Direction =="Bi-directional") { + dir = char(0x80); + } + + //if(AutoTracking){ + // sendSetCommand(QString::number(Frequency),dir,char(0x55)); + // } + sendSetCommand(QString::number(Frequency-25),dir,char(0x00)); +} + + +void Steppir::on_pushAutoTrack_clicked() +{ + char dir = char(0x00); + + if ( Direction == "Normal") { + dir = char(0x00); + } else if (Direction == "180") { + dir = char(0x40); + } else if (Direction =="Bi-directional") { + dir = char(0x80); + } + + if(AutoTracking){ + sendSetCommand(QString::number(Frequency),dir,char(0x55)); + + } else { + sendSetCommand(QString::number(Frequency),dir,char(0x52)); + } +} + + +void Steppir::on_buttonRightSlow_clicked() +{ + char dir = char(0x00); + + if ( Direction == "Normal") { + dir = char(0x00); + } else if (Direction == "180") { + dir = char(0x40); + } else if (Direction =="Bi-directional") { + dir = char(0x80); + } + + //if(AutoTracking){ + // sendSetCommand(QString::number(Frequency),dir,char(0x55)); + // } + sendSetCommand(QString::number(Frequency+25),dir,char(0x00)); +} + + +void Steppir::on_buttonRightFast_clicked() +{ + char dir = char(0x00); + + if ( Direction == "Normal") { + dir = char(0x00); + } else if (Direction == "180") { + dir = char(0x40); + } else if (Direction =="Bi-directional") { + dir = char(0x80); + } + + //if(AutoTracking){ + // sendSetCommand(QString::number(Frequency),dir,char(0x55)); + // } + sendSetCommand(QString::number(Frequency+100),dir,char(0x00)); +} + + +void Steppir::on_pb80_2_clicked() +{ + char dir = char(0x00); + + if ( Direction == "Normal") { + dir = char(0x00); + } else if (Direction == "180") { + dir = char(0x40); + } else if (Direction =="Bi-directional") { + dir = char(0x80); + } + + // if(AutoTracking){ + // sendSetCommand(QString::number(Frequency),dir,char(0x55)); + // } + sendSetCommand(QString::number(3500),dir,char(0x00)); +} + + +void Steppir::on_pb40_2_clicked() +{ + char dir = char(0x00); + + if ( Direction == "Normal") { + dir = char(0x00); + } else if (Direction == "180") { + dir = char(0x40); + } else if (Direction =="Bi-directional") { + dir = char(0x80); + } + + // if(AutoTracking){ + // sendSetCommand(QString::number(Frequency),dir,char(0x55)); + // } + sendSetCommand(QString::number(7000),dir,char(0x00)); + +} + + +void Steppir::on_pb30_2_clicked() +{ + char dir = char(0x00); + + if ( Direction == "Normal") { + dir = char(0x00); + } else if (Direction == "180") { + dir = char(0x40); + } else if (Direction =="Bi-directional") { + dir = char(0x80); + } + + // if(AutoTracking){ + // sendSetCommand(QString::number(Frequency),dir,char(0x55)); + // } + sendSetCommand(QString::number(10100),dir,char(0x00)); + +} + + +void Steppir::on_pb20_2_clicked() +{ + char dir = char(0x00); + + if ( Direction == "Normal") { + dir = char(0x00); + } else if (Direction == "180") { + dir = char(0x40); + } else if (Direction =="Bi-directional") { + dir = char(0x80); + } + + // if(AutoTracking){ + // sendSetCommand(QString::number(Frequency),dir,char(0x55)); + // } + sendSetCommand(QString::number(14000),dir,char(0x00)); + +} + + +void Steppir::on_pb17_2_clicked() +{ + char dir = char(0x00); + + if ( Direction == "Normal") { + dir = char(0x00); + } else if (Direction == "180") { + dir = char(0x40); + } else if (Direction =="Bi-directional") { + dir = char(0x80); + } + + //if(AutoTracking){ + // sendSetCommand(QString::number(Frequency),dir,char(0x55)); + // } + sendSetCommand(QString::number(18068),dir,char(0x00)); + +} + + +void Steppir::on_pb15_2_clicked() +{ + char dir = char(0x00); + + if ( Direction == "Normal") { + dir = char(0x00); + } else if (Direction == "180") { + dir = char(0x40); + } else if (Direction =="Bi-directional") { + dir = char(0x80); + } + + // if(AutoTracking){ + // sendSetCommand(QString::number(Frequency),dir,char(0x55)); + // } + sendSetCommand(QString::number(21000),dir,char(0x00)); + +} + + +void Steppir::on_pb12_2_clicked() +{ + char dir = char(0x00); + + if ( Direction == "Normal") { + dir = char(0x00); + } else if (Direction == "180") { + dir = char(0x40); + } else if (Direction =="Bi-directional") { + dir = char(0x80); + } + + // if(AutoTracking){ + // sendSetCommand(QString::number(Frequency),dir,char(0x55)); + // } + sendSetCommand(QString::number(24890),dir,char(0x00)); + +} + + +void Steppir::on_pb10_2_clicked() +{ + char dir = char(0x00); + + if ( Direction == "Normal") { + dir = char(0x00); + } else if (Direction == "180") { + dir = char(0x40); + } else if (Direction =="Bi-directional") { + dir = char(0x80); + } + + // if(AutoTracking){ + // sendSetCommand(QString::number(Frequency),dir,char(0x55)); + // } + sendSetCommand(QString::number(28000),dir,char(0x00)); + +} + + +void Steppir::on_pb6_2_clicked() +{ + char dir = char(0x00); + + if ( Direction == "Normal") { + dir = char(0x00); + } else if (Direction == "180") { + dir = char(0x40); + } else if (Direction =="Bi-directional") { + dir = char(0x80); + } + + // if(AutoTracking){ + // sendSetCommand(QString::number(Frequency),dir,char(0x55)); + // } + sendSetCommand(QString::number(50000),dir,char(0x00)); + +} + + +void Steppir::on_buttonCalibrate_clicked() +{ + sendSetCommand(QString::number(Frequency),char(0x00),char(0x56)); +} + diff --git a/ui/Steppir.h b/ui/Steppir.h new file mode 100644 index 00000000..7ba3814a --- /dev/null +++ b/ui/Steppir.h @@ -0,0 +1,77 @@ +#ifndef STEPPIR_H +#define STEPPIR_H + +#include +#include +#include +#include +#include +#include +#include > + +namespace Ui { +class Steppir; +} + +class Steppir : public QWidget +{ + Q_OBJECT + +public: + explicit Steppir(QWidget *parent = nullptr); + ~Steppir(); + void openSerialPort(); + void closeSerialPort(); + void sendStatusRequest(); + void sendSetCommand(QString frequency, char direction, char steppircommand); + void open(); + void close(); + +private slots: + void readData(); + void parseResponse(const QByteArray &response); + void pollSteppir(); + void onConnected(); + void onError(); + void UpdateForm(); + void on_buttonNormal_clicked(); + void on_butonReverse_clicked(); + void on_buttonBi_clicked(); + void on_buttonRetract_clicked(); + void on_buttonLeftFast_clicked(); + void on_butonLeftSlow_clicked(); + void on_pushAutoTrack_clicked(); + void on_buttonRightSlow_clicked(); + void on_buttonRightFast_clicked(); + void on_pb80_2_clicked(); + void on_pb40_2_clicked(); + void on_pb30_2_clicked(); + void on_pb20_2_clicked(); + void on_pb17_2_clicked(); + void on_pb15_2_clicked(); + void on_pb12_2_clicked(); + void on_pb10_2_clicked(); + void on_pb6_2_clicked(); + + void on_buttonCalibrate_clicked(); + +private: + Ui::Steppir *ui; + QSerialPort *serial; + QTcpSocket *socket; + QTimer *timer; + QByteArray buffer; + double Frequency; + QString Direction; + QString OldDirection; + bool AutoTracking; + bool Tuning; + QString band; + QString baud; + QString serialport; + int networkport; + QString networkhost; + int pollint; +}; + +#endif // STEPPIR_H diff --git a/ui/Steppir.ui b/ui/Steppir.ui new file mode 100644 index 00000000..3470419a --- /dev/null +++ b/ui/Steppir.ui @@ -0,0 +1,234 @@ + + + Steppir + + + + 0 + 0 + 325 + 130 + + + + + 325 + 130 + + + + Form + + + + + 10 + 0 + 231 + 31 + + + + + 18 + true + + + + 0.000 + + + Qt::AlignmentFlag::AlignCenter + + + + + + 0 + 30 + 321 + 32 + + + + + 0 + + + + + 80 + + + false + + + + + + + 40 + + + + + + + 30 + + + + + + + 20 + + + + + + + 17 + + + + + + + 15 + + + + + + + 12 + + + + + + + 10 + + + + + + + 6 + + + + + + + + + 0 + 60 + 321 + 32 + + + + + 0 + + + + + Normal + + + + + + + Reverse + + + + + + + Bi-Dir + + + + + + + Home + + + + + + + + + 0 + 90 + 321 + 32 + + + + + 0 + + + + + << + + + + + + + < + + + + + + + Autotrack + + + + + + + > + + + + + + + >> + + + + + + + + + 240 + 0 + 80 + 32 + + + + Calibrate + + + + + +