diff --git a/core/Migration.h b/core/Migration.h
index 163b32c9..ac969be8 100644
--- a/core/Migration.h
+++ b/core/Migration.h
@@ -14,7 +14,7 @@ class DBSchemaMigration : public QObject
bool run(bool force = false);
static bool backupAllQSOsToADX(bool force = false);
- static constexpr int latestVersion = 41;
+ static constexpr int latestVersion = 42;
private:
bool functionMigration(int version);
diff --git a/data/RigProfile.cpp b/data/RigProfile.cpp
index 0d0c2356..7d7e53d2 100644
--- a/data/RigProfile.cpp
+++ b/data/RigProfile.cpp
@@ -23,7 +23,7 @@ QDataStream& operator<<(QDataStream& out, const RigProfile& v)
<< v.getKeySpeed << v.assignedCWKey << v.keySpeedSync
<< v.driver << v.dxSpot2Rig << v.pttType << v.pttPortPath
<< v.rts << v.dtr << v.civAddr
- << v.shareRigctld << v.rigctldPort << v.rigctldPath << v.rigctldArgs << v.getSplitInfo;
+ << v.shareRigctld << v.rigctldPort << v.rigctldPath << v.rigctldArgs << v.getSplitInfo << v.spotTuneCommand;
return out;
}
@@ -69,6 +69,7 @@ QDataStream& operator>>(QDataStream& in, RigProfile& v)
in >> v.rigctldPath;
in >> v.rigctldArgs;
in >> v.getSplitInfo;
+ in >> v.spotTuneCommand;
return in;
}
@@ -88,7 +89,8 @@ RigProfilesManager::RigProfilesManager() :
"key_speed_sync, driver, dxspot2rig, ptt_type, ptt_port_pathname, "
"IFNULL(rts, '%0'), IFNULL(dtr, '%0'), IFNULL(civaddr, -1), "
"IFNULL(share_rigctld, 0), IFNULL(rigctld_port, 4532), "
- "IFNULL(rigctld_path, ''), IFNULL(rigctld_args, ''), get_split "
+ "IFNULL(rigctld_path, ''), IFNULL(rigctld_args, ''), get_split, "
+ "IFNULL(spot_tune_command, '') "
"FROM rig_profiles").arg(SerialPort::SERIAL_SIGNAL_NONE)))
{
qWarning()<< "Cannot prepare select";
@@ -138,6 +140,7 @@ RigProfilesManager::RigProfilesManager() :
profileDB.rigctldPath = profileQuery.value(36).toString();
profileDB.rigctldArgs = profileQuery.value(37).toString();
profileDB.getSplitInfo = profileQuery.value(38).toBool();
+ profileDB.spotTuneCommand = profileQuery.value(39).toString();
addProfile(profileDB.profileName, profileDB);
}
@@ -166,13 +169,13 @@ void RigProfilesManager::save()
"txfreq_end, get_freq, get_mode, get_vfo, get_pwr, rit_offset, xit_offset, get_rit, "
"get_xit, default_pwr, get_ptt, qsy_wiping, get_key_speed, assigned_cw_key, key_speed_sync, "
"driver, dxSpot2Rig, ptt_type, ptt_port_pathname, rts, dtr, civaddr, "
- "share_rigctld, rigctld_port, rigctld_path, rigctld_args, get_split ) "
+ "share_rigctld, rigctld_port, rigctld_path, rigctld_args, get_split, spot_tune_command ) "
"VALUES (:profile_name, :model, :port_pathname, :hostname, :netport, "
":baudrate, :databits, :stopbits, :flowcontrol, :parity, :pollinterval, :txfreq_start, "
":txfreq_end, :get_freq, :get_mode, :get_vfo, :get_pwr, :rit_offset, :xit_offset, :get_rit, "
":get_xit, :default_pwr, :get_ptt, :qsy_wiping, :get_key_speed, :assigned_cw_key, :key_speed_sync, "
":driver, :dxSpot2Rig, :ptt_type, :ptt_port_pathname, :rts, :dtr, :civaddr, "
- ":share_rigctld, :rigctld_port, :rigctld_path, :rigctld_args, :get_split )") )
+ ":share_rigctld, :rigctld_port, :rigctld_path, :rigctld_args, :get_split, :spot_tune_command )") )
{
qWarning() << "cannot prepare Insert statement";
return;
@@ -224,6 +227,7 @@ void RigProfilesManager::save()
insertQuery.bindValue(":rigctld_path", rigProfile.rigctldPath);
insertQuery.bindValue(":rigctld_args", rigProfile.rigctldArgs);
insertQuery.bindValue(":get_split", rigProfile.getSplitInfo);
+ insertQuery.bindValue(":spot_tune_command", rigProfile.spotTuneCommand);
if ( ! insertQuery.exec() )
{
@@ -280,6 +284,7 @@ bool RigProfile::operator==(const RigProfile &profile)
&& profile.rigctldPath == this->rigctldPath
&& profile.rigctldArgs == this->rigctldArgs
&& profile.getSplitInfo == this->getSplitInfo
+ && profile.spotTuneCommand == this->spotTuneCommand
);
}
diff --git a/data/RigProfile.h b/data/RigProfile.h
index d8d83af7..5da0d808 100644
--- a/data/RigProfile.h
+++ b/data/RigProfile.h
@@ -102,6 +102,7 @@ class RigProfile
bool dxSpot2Rig;
bool shareRigctld;
bool getSplitInfo;
+ QString spotTuneCommand;
bool operator== (const RigProfile &profile);
bool operator!= (const RigProfile &profile);
diff --git a/res/res.qrc b/res/res.qrc
index 58a291ea..b36d73b5 100644
--- a/res/res.qrc
+++ b/res/res.qrc
@@ -56,5 +56,6 @@
sql/migration_039.sql
sql/migration_040.sql
sql/migration_041.sql
+ sql/migration_042.sql
diff --git a/res/sql/migration_042.sql b/res/sql/migration_042.sql
new file mode 100644
index 00000000..8a9d89b3
--- /dev/null
+++ b/res/sql/migration_042.sql
@@ -0,0 +1 @@
+ALTER TABLE rig_profiles ADD COLUMN spot_tune_command TEXT;
diff --git a/rig/Rig.cpp b/rig/Rig.cpp
index 9fb9426e..24ddd78e 100644
--- a/rig/Rig.cpp
+++ b/rig/Rig.cpp
@@ -742,6 +742,30 @@ void Rig::sendDXSpotImpl(const DxSpot &spot)
rigDriver->sendDXSpot(spot);
}
+void Rig::sendRawCommand(const QString &command)
+{
+ FCT_IDENTIFICATION;
+
+ qCDebug(function_parameters) << command;
+
+ QMetaObject::invokeMethod(this, "sendRawCommandImpl", Qt::QueuedConnection,
+ Q_ARG(QString, command));
+}
+
+void Rig::sendRawCommandImpl(const QString &command)
+{
+ FCT_IDENTIFICATION;
+
+ qCDebug(function_parameters) << command;
+
+ MUTEXLOCKER;
+
+ if ( ! rigDriver )
+ return;
+
+ rigDriver->sendRawCommand(command);
+}
+
GenericRigDrv *Rig::getDriver(const RigProfile &profile, qint32 controlledRigModel)
{
FCT_IDENTIFICATION;
diff --git a/rig/Rig.h b/rig/Rig.h
index ba62eaee..ed9d1cf2 100644
--- a/rig/Rig.h
+++ b/rig/Rig.h
@@ -106,6 +106,7 @@ public slots:
void stopMorse();
void sendState();
void sendDXSpot(DxSpot spot);
+ void sendRawCommand(const QString &command);
signals:
void frequencyChanged(VFOID, double, double, double);
@@ -142,6 +143,7 @@ private slots:
void stopMorseImpl();
void sendStateImpl();
void sendDXSpotImpl(const DxSpot &spot);
+ void sendRawCommandImpl(const QString &command);
void sendHeartBeat();
private:
diff --git a/rig/drivers/GenericRigDrv.cpp b/rig/drivers/GenericRigDrv.cpp
index e28e1986..4b0fd8d5 100644
--- a/rig/drivers/GenericRigDrv.cpp
+++ b/rig/drivers/GenericRigDrv.cpp
@@ -44,3 +44,12 @@ void GenericRigDrv::setSplit(bool)
// Default: do nothing — driver does not support split
}
+
+void GenericRigDrv::sendRawCommand(const QString &command)
+{
+ FCT_IDENTIFICATION;
+
+ qCDebug(function_parameters) << command;
+
+ // Default: do nothing - driver has no raw command channel
+}
diff --git a/rig/drivers/GenericRigDrv.h b/rig/drivers/GenericRigDrv.h
index 37024064..b9ddf9e2 100644
--- a/rig/drivers/GenericRigDrv.h
+++ b/rig/drivers/GenericRigDrv.h
@@ -41,6 +41,7 @@ class GenericRigDrv : public QObject
virtual void sendState() = 0;
virtual void stopTimers() = 0;
virtual void sendDXSpot(const DxSpot &spot) = 0;
+ virtual void sendRawCommand(const QString &command);
signals:
// STATE Signals
diff --git a/rig/drivers/HamlibRigDrv.cpp b/rig/drivers/HamlibRigDrv.cpp
index 5189c785..76f55749 100644
--- a/rig/drivers/HamlibRigDrv.cpp
+++ b/rig/drivers/HamlibRigDrv.cpp
@@ -1,4 +1,5 @@
#include
+#include
#include
#include "HamlibRigDrv.h"
@@ -462,6 +463,60 @@ void HamlibRigDrv::setSplit(bool enabled)
commandSleep();
}
+void HamlibRigDrv::sendRawCommand(const QString &command)
+{
+ FCT_IDENTIFICATION;
+
+ qCDebug(function_parameters) << command;
+
+ MUTEXLOCKER;
+
+ if ( !rig )
+ {
+ qCWarning(runtime) << "Rig is not active";
+ return;
+ }
+
+ if ( rigProfile.model == RIG_MODEL_NETRIGCTL )
+ {
+ // The Hamlib link speaks the rigctld protocol, so raw bytes cannot go
+ // down that socket. rigctld takes further clients: a short second
+ // connection forwards the command with rigctld's "send_cmd_rx" and
+ // 0 reply bytes, so a set command returns at once and the protocol
+ // state of the Hamlib link is not touched.
+ QTcpSocket socket;
+
+ socket.connectToHost(rigProfile.hostname, rigProfile.netport);
+
+ if ( !socket.waitForConnected(1000) )
+ {
+ qCWarning(runtime) << "Raw command failed, rigctld not reachable" << command << socket.errorString();
+ return;
+ }
+
+ socket.write(QString("W %1 0\n").arg(command).toLatin1());
+ socket.waitForBytesWritten(1000);
+ socket.waitForReadyRead(1000); // rigctld's answer line, nothing to evaluate for a set command
+ socket.disconnectFromHost();
+ }
+ else
+ {
+#if HAMLIB_VERSION >= HAMLIB_VERSION_CHECK(4,5,0)
+ const QByteArray bytes = command.toLatin1();
+ const int status = rig_send_raw(rig,
+ reinterpret_cast(bytes.constData()), bytes.size(),
+ nullptr, 0, nullptr);
+
+ if ( status < 0 )
+ qCWarning(runtime) << "Raw command failed" << command << rigerror(status);
+#else
+ qCWarning(runtime) << "Raw CAT commands need Hamlib 4.5 or newer" << command;
+#endif
+ }
+
+ commandSleep();
+}
+
void HamlibRigDrv::setRawMode(const QString &rawMode)
{
FCT_IDENTIFICATION;
diff --git a/rig/drivers/HamlibRigDrv.h b/rig/drivers/HamlibRigDrv.h
index 91dda611..5e15d814 100644
--- a/rig/drivers/HamlibRigDrv.h
+++ b/rig/drivers/HamlibRigDrv.h
@@ -41,6 +41,7 @@ class HamlibRigDrv : public GenericRigDrv
virtual void sendState() override;
virtual void stopTimers() override;
virtual void sendDXSpot(const DxSpot &spot) override;
+ virtual void sendRawCommand(const QString &command) override;
private slots:
void checkRigStateChange();
diff --git a/ui/NewContactWidget.cpp b/ui/NewContactWidget.cpp
index 456d0ddb..4f5110a2 100644
--- a/ui/NewContactWidget.cpp
+++ b/ui/NewContactWidget.cpp
@@ -3550,6 +3550,14 @@ void NewContactWidget::tuneDx(const DxSpot &spot)
}
emit userModeChanged(VFO1, QString(), mode, subMode, bandwidthFilter);
}
+
+ // The rig profile may name a raw CAT command for the end of a spot
+ // tune, e.g. "SY2;" lets a Yaesu FTDX101 take its second receiver
+ // along to the same frequency and mode.
+ const QString &spotTuneCommand = RigProfilesManager::instance()->getCurProfile1().spotTuneCommand;
+
+ if ( !spotTuneCommand.isEmpty() )
+ rig->sendRawCommand(spotTuneCommand);
}
resetContact();
diff --git a/ui/SettingsDialog.cpp b/ui/SettingsDialog.cpp
index aeb58b17..0407b943 100644
--- a/ui/SettingsDialog.cpp
+++ b/ui/SettingsDialog.cpp
@@ -998,6 +998,7 @@ void SettingsDialog::addRigProfile()
profile.getKeySpeed = ui->rigGetKeySpeedCheckBox->isChecked();
profile.keySpeedSync = ui->rigKeySpeedSyncCheckBox->isChecked();
profile.dxSpot2Rig = ui->rigDXSpots2RigCheckBox->isChecked();
+ profile.spotTuneCommand = ui->rigSpotTuneCommandEdit->text().trimmed();
profile.getSplitInfo = ui->rigGetSplitCheckBox->isChecked();
// Rigctld sharing settings
@@ -1076,6 +1077,7 @@ void SettingsDialog::doubleClickRigProfile(QModelIndex i)
ui->rigGetKeySpeedCheckBox->setChecked(profile.getKeySpeed);
ui->rigKeySpeedSyncCheckBox->setChecked(profile.keySpeedSync);
ui->rigDXSpots2RigCheckBox->setChecked(profile.dxSpot2Rig);
+ ui->rigSpotTuneCommandEdit->setText(profile.spotTuneCommand);
ui->rigGetSplitCheckBox->setChecked(profile.getSplitInfo);
setComboByData(ui->rigFlowControlSelect, profile.flowcontrol.toLower());
diff --git a/ui/SettingsDialog.ui b/ui/SettingsDialog.ui
index 8903b712..1f0e0605 100644
--- a/ui/SettingsDialog.ui
+++ b/ui/SettingsDialog.ui
@@ -1990,6 +1990,23 @@
+ -
+
+
+ CAT after spot tuning:
+
+
+
+ -
+
+
+ Raw CAT command sent to the rig after QLog has tuned it to a spot. Example for the Yaesu FTDX101: SY2; synchronizes the second receiver (frequency and mode) with the main receiver. Leave empty if not needed.
+
+
+ e.g. SY2;
+
+
+
-