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
@@ -110,6 +111,7 @@
+
+
+
+ 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 @@
1
+
+ Steppir
+ QWidget
+
+ 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
+
+
+
+
+
+