2026-01-18 12:53:55 -06:00
|
|
|
/**
|
|
|
|
|
* @file HamlibTransceiver.cpp
|
|
|
|
|
* @brief Implementation of the HamlibTransceiver class
|
|
|
|
|
* Implements rig control with Hamlib
|
|
|
|
|
*/
|
2026-01-01 15:12:24 -06:00
|
|
|
|
2026-01-18 12:53:55 -06:00
|
|
|
#include "HamlibTransceiver.h"
|
2026-01-01 15:12:24 -06:00
|
|
|
|
|
|
|
|
#include <QByteArray>
|
|
|
|
|
#include <QFile>
|
|
|
|
|
#include <QJsonDocument>
|
|
|
|
|
#include <QJsonObject>
|
|
|
|
|
#include <QJsonValue>
|
|
|
|
|
#include <QLoggingCategory>
|
|
|
|
|
#include <QStandardPaths>
|
|
|
|
|
#include <QString>
|
|
|
|
|
|
2026-01-18 12:53:55 -06:00
|
|
|
#include <cmath>
|
|
|
|
|
#include <cstring>
|
|
|
|
|
|
|
|
|
|
#include "moc_HamlibTransceiver.cpp"
|
|
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
Q_DECLARE_LOGGING_CATEGORY(hamlibtransceiver_js8)
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
// Unfortunately bandwidth is conflated with mode, this is probably
|
|
|
|
|
// because Icom do the same. So we have to care about bandwidth if
|
|
|
|
|
// we want to set mode otherwise we will end up setting unwanted
|
|
|
|
|
// bandwidths every time we change mode. The best we can do via the
|
|
|
|
|
// Hamlib API is to request the normal option for the mode and hope
|
|
|
|
|
// that an appropriate filter is selected. Also ensure that mode is
|
|
|
|
|
// only set is absolutely necessary. On Icoms (and probably others)
|
|
|
|
|
// the filter is selected by number without checking the actual BW
|
|
|
|
|
// so unless the "normal" defaults are set on the rig we won't get
|
|
|
|
|
// desirable results.
|
|
|
|
|
//
|
|
|
|
|
// As an ultimate workaround make sure the user always has the
|
|
|
|
|
// option to skip mode setting altogether.
|
|
|
|
|
|
|
|
|
|
// reroute Hamlib diagnostic messages to Qt
|
|
|
|
|
int debug_callback(enum rig_debug_level_e level, rig_ptr_t /* arg */,
|
|
|
|
|
char const *format, va_list ap) {
|
|
|
|
|
QString message;
|
|
|
|
|
static char constexpr fmt[] = "Hamlib: %s";
|
|
|
|
|
message = message.vasprintf(format, ap).trimmed();
|
|
|
|
|
|
|
|
|
|
switch (level) {
|
|
|
|
|
case RIG_DEBUG_BUG:
|
|
|
|
|
qFatal(fmt, message.toLocal8Bit().data());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case RIG_DEBUG_ERR:
|
|
|
|
|
qCCritical(hamlibtransceiver_js8, fmt, message.toLocal8Bit().data());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case RIG_DEBUG_WARN:
|
|
|
|
|
qCWarning(hamlibtransceiver_js8, fmt, message.toLocal8Bit().data());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
qCDebug(hamlibtransceiver_js8, fmt, message.toLocal8Bit().data());
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// callback function that receives transceiver capabilities from the
|
|
|
|
|
// hamlib libraries
|
|
|
|
|
int register_callback(rig_model_t rig_model, void *callback_data) {
|
|
|
|
|
TransceiverFactory::Transceivers *rigs =
|
|
|
|
|
reinterpret_cast<TransceiverFactory::Transceivers *>(callback_data);
|
|
|
|
|
// We can't use this one because it is only for testing Hamlib and
|
|
|
|
|
// would confuse users, possibly causing operating on the wrong
|
|
|
|
|
// frequency!
|
|
|
|
|
#ifdef RIG_MODEL_DUMMY_NOVFO
|
|
|
|
|
if (RIG_MODEL_DUMMY_NOVFO == rig_model) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
QString key;
|
|
|
|
|
if (RIG_MODEL_DUMMY == rig_model) {
|
|
|
|
|
key = TransceiverFactory::basic_transceiver_name_;
|
|
|
|
|
} else {
|
|
|
|
|
key = QString::fromLatin1(
|
|
|
|
|
rig_get_caps_cptr(rig_model, RIG_CAPS_MFG_NAME_CPTR))
|
|
|
|
|
.trimmed() +
|
|
|
|
|
' ' +
|
|
|
|
|
QString::fromLatin1(
|
|
|
|
|
rig_get_caps_cptr(rig_model, RIG_CAPS_MODEL_NAME_CPTR))
|
|
|
|
|
.trimmed()
|
|
|
|
|
// + ' '+ QString::fromLatin1 (rig_get_caps_cptr (rig_model,
|
|
|
|
|
// RIG_CAPS_VERSION)).trimmed ()
|
|
|
|
|
// + " (" + QString::fromLatin1 (rig_get_caps_cptr (rig_model,
|
|
|
|
|
// RIG_CAPS_STATUS)).trimmed () + ')'
|
|
|
|
|
;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto port_type = TransceiverFactory::Capabilities::none;
|
|
|
|
|
switch (rig_get_caps_int(rig_model, RIG_CAPS_PORT_TYPE)) {
|
|
|
|
|
case RIG_PORT_SERIAL:
|
|
|
|
|
port_type = TransceiverFactory::Capabilities::serial;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case RIG_PORT_NETWORK:
|
|
|
|
|
port_type = TransceiverFactory::Capabilities::network;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case RIG_PORT_USB:
|
|
|
|
|
port_type = TransceiverFactory::Capabilities::usb;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
auto ptt_type = rig_get_caps_int(rig_model, RIG_CAPS_PTT_TYPE);
|
|
|
|
|
(*rigs)[key] = TransceiverFactory::Capabilities(
|
|
|
|
|
rig_model, port_type,
|
|
|
|
|
RIG_MODEL_DUMMY != rig_model &&
|
|
|
|
|
(RIG_PTT_RIG == ptt_type || RIG_PTT_RIG_MICDATA == ptt_type),
|
|
|
|
|
RIG_PTT_RIG_MICDATA == ptt_type);
|
|
|
|
|
|
|
|
|
|
return 1; // keep them coming
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int unregister_callback(rig_model_t rig_model, void *) {
|
|
|
|
|
rig_unregister(rig_model);
|
|
|
|
|
return 1; // keep them coming
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// int frequency_change_callback (RIG * /* rig */, vfo_t vfo, freq_t f,
|
|
|
|
|
// rig_ptr_t arg)
|
|
|
|
|
// {
|
|
|
|
|
// (void)vfo; // unused in release build
|
|
|
|
|
|
|
|
|
|
// Q_ASSERT (vfo == RIG_VFO_CURR); // G4WJS: at the time of writing only
|
|
|
|
|
// current VFO is signalled by hamlib
|
|
|
|
|
|
|
|
|
|
// HamlibTransceiver * transceiver (reinterpret_cast<HamlibTransceiver *>
|
|
|
|
|
// (arg)); Q_EMIT transceiver->frequency_change (f, Transceiver::A); return
|
|
|
|
|
// RIG_OK;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
class hamlib_tx_vfo_fixup final {
|
|
|
|
|
public:
|
|
|
|
|
hamlib_tx_vfo_fixup(RIG *rig, vfo_t tx_vfo) : rig_{rig} {
|
|
|
|
|
original_vfo_ = rig_->state.tx_vfo;
|
|
|
|
|
rig_->state.tx_vfo = tx_vfo;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~hamlib_tx_vfo_fixup() { rig_->state.tx_vfo = original_vfo_; }
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
RIG *rig_;
|
|
|
|
|
vfo_t original_vfo_;
|
|
|
|
|
};
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
freq_t HamlibTransceiver::dummy_frequency_;
|
|
|
|
|
rmode_t HamlibTransceiver::dummy_mode_{RIG_MODE_NONE};
|
|
|
|
|
|
|
|
|
|
void HamlibTransceiver::register_transceivers(
|
|
|
|
|
TransceiverFactory::Transceivers *registry) {
|
|
|
|
|
rig_set_debug_callback(debug_callback, nullptr);
|
|
|
|
|
|
|
|
|
|
if (hamlibtransceiver_js8().isDebugEnabled())
|
|
|
|
|
rig_set_debug(RIG_DEBUG_TRACE);
|
|
|
|
|
else if (hamlibtransceiver_js8().isInfoEnabled())
|
|
|
|
|
rig_set_debug(RIG_DEBUG_VERBOSE);
|
|
|
|
|
else if (hamlibtransceiver_js8().isWarningEnabled())
|
|
|
|
|
rig_set_debug(RIG_DEBUG_WARN);
|
|
|
|
|
else
|
|
|
|
|
rig_set_debug(RIG_DEBUG_ERR);
|
|
|
|
|
|
|
|
|
|
rig_load_all_backends();
|
|
|
|
|
rig_list_foreach_model(register_callback, registry);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HamlibTransceiver::unregister_transceivers() {
|
|
|
|
|
rig_list_foreach_model(unregister_callback, nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HamlibTransceiver::RIGDeleter::cleanup(RIG *rig) {
|
|
|
|
|
if (rig) {
|
|
|
|
|
// rig->state.obj = 0;
|
|
|
|
|
rig_cleanup(rig);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HamlibTransceiver::HamlibTransceiver(TransceiverFactory::PTTMethod ptt_type,
|
|
|
|
|
QString const &ptt_port, QObject *parent)
|
|
|
|
|
: PollingTransceiver{0, parent}, rig_{rig_init(RIG_MODEL_DUMMY)},
|
|
|
|
|
back_ptt_port_{false}, one_VFO_{false}, is_dummy_{true}, reversed_{false},
|
|
|
|
|
freq_query_works_{true}, mode_query_works_{true},
|
|
|
|
|
split_query_works_{true}, tickle_hamlib_{false}, get_vfo_works_{true},
|
|
|
|
|
set_vfo_works_{true} {
|
|
|
|
|
if (!rig_) {
|
|
|
|
|
throw error{tr("Hamlib initialisation error")};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (ptt_type) {
|
|
|
|
|
case TransceiverFactory::PTT_method_VOX:
|
|
|
|
|
set_conf("ptt_type", "None");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TransceiverFactory::PTT_method_CAT:
|
|
|
|
|
// Use the default PTT_TYPE for the rig (defined in the Hamlib
|
|
|
|
|
// rig back-end capabilities).
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TransceiverFactory::PTT_method_DTR:
|
|
|
|
|
case TransceiverFactory::PTT_method_RTS:
|
|
|
|
|
if (!ptt_port.isEmpty()) {
|
|
|
|
|
#if defined(Q_OS_WIN)
|
|
|
|
|
set_conf("ptt_pathname", ("\\\\.\\" + ptt_port).toLatin1().data());
|
|
|
|
|
#else
|
|
|
|
|
set_conf("ptt_pathname", ptt_port.toLatin1().data());
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (TransceiverFactory::PTT_method_DTR == ptt_type) {
|
|
|
|
|
set_conf("ptt_type", "DTR");
|
|
|
|
|
} else {
|
|
|
|
|
set_conf("ptt_type", "RTS");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HamlibTransceiver::HamlibTransceiver(
|
|
|
|
|
int model_number, TransceiverFactory::ParameterPack const ¶ms,
|
|
|
|
|
QObject *parent)
|
|
|
|
|
: PollingTransceiver{params.poll_interval, parent},
|
|
|
|
|
rig_{rig_init(model_number)},
|
|
|
|
|
back_ptt_port_{TransceiverFactory::TX_audio_source_rear ==
|
|
|
|
|
params.audio_source},
|
|
|
|
|
one_VFO_{false}, is_dummy_{RIG_MODEL_DUMMY == model_number},
|
|
|
|
|
reversed_{false}, freq_query_works_{rig_ && rig_->caps->get_freq},
|
|
|
|
|
mode_query_works_{rig_ && rig_->caps->get_mode},
|
|
|
|
|
split_query_works_{rig_ && rig_->caps->get_split_vfo},
|
|
|
|
|
tickle_hamlib_{false}, get_vfo_works_{true}, set_vfo_works_{true} {
|
|
|
|
|
if (!rig_) {
|
|
|
|
|
throw error{tr("Hamlib initialisation error")};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// rig_->state.obj = this;
|
|
|
|
|
|
|
|
|
|
if (!is_dummy_) {
|
|
|
|
|
//
|
|
|
|
|
// user defined Hamlib settings
|
|
|
|
|
//
|
|
|
|
|
auto settings_file_name = QStandardPaths::locate(
|
|
|
|
|
#if QT_VERSION >= 0x050500
|
|
|
|
|
QStandardPaths::AppConfigLocation
|
|
|
|
|
#else
|
|
|
|
|
QStandardPaths::ConfigLocation
|
|
|
|
|
#endif
|
|
|
|
|
,
|
|
|
|
|
"hamlib_settings.json");
|
|
|
|
|
if (!settings_file_name.isEmpty()) {
|
|
|
|
|
QFile settings_file{settings_file_name};
|
|
|
|
|
qCDebug(hamlibtransceiver_js8)
|
|
|
|
|
<< "Using Hamlib settings file:" << settings_file_name;
|
|
|
|
|
if (settings_file.open(QFile::ReadOnly)) {
|
|
|
|
|
QJsonParseError status;
|
|
|
|
|
auto settings_doc =
|
|
|
|
|
QJsonDocument::fromJson(settings_file.readAll(), &status);
|
|
|
|
|
if (status.error) {
|
|
|
|
|
throw error{tr("Hamlib settings file error: %1 at "
|
|
|
|
|
"character offset %2")
|
|
|
|
|
.arg(status.errorString())
|
|
|
|
|
.arg(status.offset)};
|
|
|
|
|
}
|
|
|
|
|
qCDebug(hamlibtransceiver_js8)
|
|
|
|
|
<< "Hamlib settings JSON:" << settings_doc.toJson();
|
|
|
|
|
if (!settings_doc.isObject()) {
|
|
|
|
|
throw error{tr("Hamlib settings file error: top level must "
|
|
|
|
|
"be a JSON object")};
|
|
|
|
|
}
|
|
|
|
|
auto const &settings = settings_doc.object();
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// configuration settings
|
|
|
|
|
//
|
|
|
|
|
auto const &config = settings["config"];
|
|
|
|
|
if (!config.isUndefined()) {
|
|
|
|
|
if (!config.isObject()) {
|
|
|
|
|
throw error{tr("Hamlib settings file error: config "
|
|
|
|
|
"must be a JSON object")};
|
|
|
|
|
}
|
|
|
|
|
auto const &config_list = config.toObject();
|
|
|
|
|
for (auto item = config_list.constBegin();
|
|
|
|
|
item != config_list.constEnd(); ++item) {
|
|
|
|
|
set_conf(item.key().toLocal8Bit().constData(),
|
|
|
|
|
(*item)
|
|
|
|
|
.toVariant()
|
|
|
|
|
.toString()
|
|
|
|
|
.toLocal8Bit()
|
|
|
|
|
.constData());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (rig_->caps->port_type) {
|
|
|
|
|
case RIG_PORT_SERIAL:
|
|
|
|
|
if (!params.serial_port.isEmpty()) {
|
|
|
|
|
set_conf("rig_pathname", params.serial_port.toLatin1().data());
|
|
|
|
|
}
|
|
|
|
|
set_conf("serial_speed", QByteArray::number(params.baud).data());
|
|
|
|
|
if (params.data_bits != TransceiverFactory::default_data_bits) {
|
|
|
|
|
set_conf("data_bits",
|
|
|
|
|
TransceiverFactory::seven_data_bits == params.data_bits
|
|
|
|
|
? "7"
|
|
|
|
|
: "8");
|
|
|
|
|
}
|
|
|
|
|
if (params.stop_bits != TransceiverFactory::default_stop_bits) {
|
|
|
|
|
set_conf("stop_bits",
|
|
|
|
|
TransceiverFactory::one_stop_bit == params.stop_bits
|
|
|
|
|
? "1"
|
|
|
|
|
: "2");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (params.handshake) {
|
|
|
|
|
case TransceiverFactory::handshake_none:
|
|
|
|
|
set_conf("serial_handshake", "None");
|
|
|
|
|
break;
|
|
|
|
|
case TransceiverFactory::handshake_XonXoff:
|
|
|
|
|
set_conf("serial_handshake", "XONXOFF");
|
|
|
|
|
break;
|
|
|
|
|
case TransceiverFactory::handshake_hardware:
|
|
|
|
|
set_conf("serial_handshake", "Hardware");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (params.force_dtr) {
|
|
|
|
|
set_conf("dtr_state", params.dtr_high ? "ON" : "OFF");
|
|
|
|
|
}
|
|
|
|
|
if (params.force_rts) {
|
|
|
|
|
if (TransceiverFactory::handshake_hardware !=
|
|
|
|
|
params.handshake) {
|
|
|
|
|
set_conf("rts_state", params.rts_high ? "ON" : "OFF");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case RIG_PORT_NETWORK:
|
|
|
|
|
if (!params.network_port.isEmpty()) {
|
|
|
|
|
set_conf("rig_pathname", params.network_port.toLatin1().data());
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case RIG_PORT_USB:
|
|
|
|
|
if (!params.usb_port.isEmpty()) {
|
|
|
|
|
set_conf("rig_pathname", params.usb_port.toLatin1().data());
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
throw error{tr("Unsupported CAT type")};
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (params.ptt_type) {
|
|
|
|
|
case TransceiverFactory::PTT_method_VOX:
|
|
|
|
|
set_conf("ptt_type", "None");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TransceiverFactory::PTT_method_CAT:
|
|
|
|
|
// Use the default PTT_TYPE for the rig (defined in the Hamlib
|
|
|
|
|
// rig back-end capabilities).
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TransceiverFactory::PTT_method_DTR:
|
|
|
|
|
case TransceiverFactory::PTT_method_RTS:
|
|
|
|
|
if (!params.ptt_port.isEmpty() && params.ptt_port != "None" &&
|
|
|
|
|
(is_dummy_ || params.ptt_port != params.serial_port)) {
|
|
|
|
|
#if defined(Q_OS_WIN)
|
|
|
|
|
set_conf("ptt_pathname",
|
|
|
|
|
("\\\\.\\" + params.ptt_port).toLatin1().data());
|
|
|
|
|
#else
|
|
|
|
|
set_conf("ptt_pathname", params.ptt_port.toLatin1().data());
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (TransceiverFactory::PTT_method_DTR == params.ptt_type) {
|
|
|
|
|
set_conf("ptt_type", "DTR");
|
|
|
|
|
} else {
|
|
|
|
|
set_conf("ptt_type", "RTS");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Make Icom CAT split commands less glitchy
|
|
|
|
|
set_conf("no_xchg", "1");
|
|
|
|
|
|
|
|
|
|
// would be nice to get events but not supported on Windows and also not on
|
|
|
|
|
// a lot of rigs rig_set_freq_callback (rig_.data (),
|
|
|
|
|
// &frequency_change_callback, this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HamlibTransceiver::error_check(int ret_code, QString const &doing) const {
|
|
|
|
|
if (RIG_OK != ret_code) {
|
|
|
|
|
TRACE_CAT_POLL("HamlibTransceiver", "error:" << rigerror(ret_code));
|
|
|
|
|
throw error{
|
|
|
|
|
tr("Hamlib error: %1 while %2").arg(rigerror(ret_code)).arg(doing)};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int HamlibTransceiver::do_start() {
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
QString::fromLatin1(rig_->caps->mfg_name).trimmed()
|
|
|
|
|
<< QString::fromLatin1(rig_->caps->model_name).trimmed());
|
|
|
|
|
|
|
|
|
|
error_check(rig_open(rig_.data()), tr("opening connection to rig"));
|
|
|
|
|
|
|
|
|
|
// reset dynamic state
|
|
|
|
|
one_VFO_ = false;
|
|
|
|
|
reversed_ = false;
|
|
|
|
|
freq_query_works_ = rig_->caps->get_freq;
|
|
|
|
|
mode_query_works_ = rig_->caps->get_mode;
|
|
|
|
|
split_query_works_ = rig_->caps->get_split_vfo;
|
|
|
|
|
tickle_hamlib_ = false;
|
|
|
|
|
get_vfo_works_ = true;
|
|
|
|
|
set_vfo_works_ = true;
|
|
|
|
|
|
|
|
|
|
// the Net rigctl back end promises all functions work but we must
|
|
|
|
|
// test get_vfo as it determines our strategy for Icom rigs
|
|
|
|
|
vfo_t vfo;
|
|
|
|
|
int rc = rig_get_vfo(rig_.data(), &vfo);
|
|
|
|
|
if (-RIG_ENAVAIL == rc || -RIG_ENIMPL == rc) {
|
|
|
|
|
get_vfo_works_ = false;
|
|
|
|
|
// determine if the rig uses single VFO addressing i.e. A/B and
|
|
|
|
|
// no get_vfo function
|
|
|
|
|
if (rig_->state.vfo_list & RIG_VFO_B) {
|
|
|
|
|
one_VFO_ = true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
error_check(rc, "testing getting current VFO");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((WSJT_RIG_NONE_CAN_SPLIT || !is_dummy_) &&
|
|
|
|
|
rig_->caps->set_split_vfo) // if split is possible do some extra setup
|
|
|
|
|
{
|
|
|
|
|
freq_t f1;
|
|
|
|
|
freq_t f2;
|
|
|
|
|
rmode_t m{RIG_MODE_USB};
|
|
|
|
|
rmode_t mb;
|
|
|
|
|
pbwidth_t w{RIG_PASSBAND_NORMAL};
|
|
|
|
|
pbwidth_t wb;
|
|
|
|
|
if (freq_query_works_ && (!get_vfo_works_ || !rig_->caps->get_vfo)) {
|
|
|
|
|
// Icom have deficient CAT protocol with no way of reading which
|
|
|
|
|
// VFO is selected or if SPLIT is selected so we have to simply
|
|
|
|
|
// assume it is as when we started by setting at open time right
|
|
|
|
|
// here. We also gather/set other initial state.
|
|
|
|
|
error_check(rig_get_freq(rig_.data(), RIG_VFO_CURR, &f1),
|
|
|
|
|
tr("getting current frequency"));
|
|
|
|
|
f1 = std::round(f1);
|
|
|
|
|
TRACE_CAT("HamlibTransceiver", "current frequency =" << f1);
|
|
|
|
|
|
|
|
|
|
error_check(rig_get_mode(rig_.data(), RIG_VFO_CURR, &m, &w),
|
|
|
|
|
tr("getting current mode"));
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"current mode =" << rig_strrmode(m) << "bw =" << w);
|
|
|
|
|
|
|
|
|
|
if (!rig_->caps->set_vfo) {
|
|
|
|
|
TRACE_CAT("HamlibTransceiver", "rig_vfo_op TOGGLE");
|
|
|
|
|
rc = rig_vfo_op(rig_.data(), RIG_VFO_CURR, RIG_OP_TOGGLE);
|
|
|
|
|
} else {
|
|
|
|
|
TRACE_CAT("HamlibTransceiver", "rig_set_vfo to other VFO");
|
|
|
|
|
rc = rig_set_vfo(rig_.data(), rig_->state.vfo_list & RIG_VFO_B
|
|
|
|
|
? RIG_VFO_B
|
|
|
|
|
: RIG_VFO_SUB);
|
|
|
|
|
if (-RIG_ENAVAIL == rc || -RIG_ENIMPL == rc) {
|
|
|
|
|
// if we are talking to netrigctl then toggle VFO op
|
|
|
|
|
// may still work
|
|
|
|
|
TRACE_CAT("HamlibTransceiver", "rig_vfo_op TOGGLE");
|
|
|
|
|
rc = rig_vfo_op(rig_.data(), RIG_VFO_CURR, RIG_OP_TOGGLE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (-RIG_ENAVAIL == rc || -RIG_ENIMPL == rc) {
|
|
|
|
|
// we are probably dealing with rigctld so we do not
|
|
|
|
|
// have completely accurate rig capabilities
|
|
|
|
|
set_vfo_works_ = false;
|
|
|
|
|
one_VFO_ = false; // we do not need single VFO addressing
|
|
|
|
|
} else {
|
|
|
|
|
error_check(rc, tr("exchanging VFOs"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (set_vfo_works_) {
|
|
|
|
|
// without the above we cannot proceed but we know we
|
|
|
|
|
// are on VFO A and that will not change so there's no
|
|
|
|
|
// need to execute this block
|
|
|
|
|
error_check(rig_get_freq(rig_.data(), RIG_VFO_CURR, &f2),
|
|
|
|
|
tr("getting other VFO frequency"));
|
|
|
|
|
f2 = std::round(f2);
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"rig_get_freq other frequency =" << f2);
|
|
|
|
|
|
|
|
|
|
error_check(rig_get_mode(rig_.data(), RIG_VFO_CURR, &mb, &wb),
|
|
|
|
|
tr("getting other VFO mode"));
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"rig_get_mode other mode =" << rig_strrmode(mb)
|
|
|
|
|
<< "bw =" << wb);
|
|
|
|
|
|
|
|
|
|
update_other_frequency(f2);
|
|
|
|
|
|
|
|
|
|
if (!rig_->caps->set_vfo) {
|
|
|
|
|
TRACE_CAT("HamlibTransceiver", "rig_vfo_op TOGGLE");
|
|
|
|
|
error_check(
|
|
|
|
|
rig_vfo_op(rig_.data(), RIG_VFO_CURR, RIG_OP_TOGGLE),
|
|
|
|
|
tr("exchanging VFOs"));
|
|
|
|
|
} else {
|
|
|
|
|
TRACE_CAT("HamlibTransceiver", "rig_set_vfo A/MAIN");
|
|
|
|
|
error_check(rig_set_vfo(rig_.data(),
|
|
|
|
|
rig_->state.vfo_list & RIG_VFO_A
|
|
|
|
|
? RIG_VFO_A
|
|
|
|
|
: RIG_VFO_MAIN),
|
|
|
|
|
tr("setting current VFO"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (f1 != f2 || m != mb ||
|
|
|
|
|
w != wb) // we must have started with MAIN/A
|
|
|
|
|
{
|
|
|
|
|
update_rx_frequency(f1);
|
|
|
|
|
} else {
|
|
|
|
|
error_check(rig_get_freq(rig_.data(), RIG_VFO_CURR, &f1),
|
|
|
|
|
tr("getting frequency"));
|
|
|
|
|
f1 = std::round(f1);
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"rig_get_freq frequency =" << f1);
|
|
|
|
|
|
|
|
|
|
error_check(rig_get_mode(rig_.data(), RIG_VFO_CURR, &m, &w),
|
|
|
|
|
tr("getting mode"));
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"rig_get_mode mode =" << rig_strrmode(m)
|
|
|
|
|
<< "bw =" << w);
|
|
|
|
|
|
|
|
|
|
update_rx_frequency(f1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TRACE_CAT ("HamlibTransceiver", "rig_set_split_vfo split off");
|
|
|
|
|
// error_check (rig_set_split_vfo (rig_.data (), RIG_VFO_CURR,
|
|
|
|
|
// RIG_SPLIT_OFF, RIG_VFO_CURR), tr ("setting split off"));
|
|
|
|
|
// update_split (false);
|
|
|
|
|
} else {
|
|
|
|
|
vfo_t v{RIG_VFO_A}; // assume RX always on VFO A/MAIN
|
|
|
|
|
|
|
|
|
|
if (get_vfo_works_ && rig_->caps->get_vfo) {
|
|
|
|
|
error_check(rig_get_vfo(rig_.data(), &v),
|
|
|
|
|
tr("getting current VFO")); // has side effect of
|
|
|
|
|
// establishing current
|
|
|
|
|
// VFO inside hamlib
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"rig_get_vfo current VFO = " << rig_strvfo(v));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reversed_ = RIG_VFO_B == v;
|
|
|
|
|
|
|
|
|
|
if (mode_query_works_ &&
|
|
|
|
|
!(rig_->caps->targetable_vfo &
|
|
|
|
|
(RIG_TARGETABLE_MODE | RIG_TARGETABLE_PURE))) {
|
|
|
|
|
if (RIG_OK == rig_get_mode(rig_.data(), RIG_VFO_CURR, &m, &w)) {
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"rig_get_mode current mode =" << rig_strrmode(m)
|
|
|
|
|
<< "bw =" << w);
|
|
|
|
|
} else {
|
|
|
|
|
mode_query_works_ = false;
|
|
|
|
|
// Some rigs (HDSDR) don't have a working way of
|
|
|
|
|
// reporting MODE so we give up on mode queries -
|
|
|
|
|
// sets will still cause an error
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"rig_get_mode can't do on this rig");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
update_mode(map_mode(m));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tickle_hamlib_ = true;
|
|
|
|
|
|
|
|
|
|
if (is_dummy_ && dummy_frequency_) {
|
|
|
|
|
// return to where last dummy instance was
|
|
|
|
|
// TODO: this is going to break down if multiple dummy rigs are used
|
|
|
|
|
rig_set_freq(rig_.data(), RIG_VFO_CURR, dummy_frequency_);
|
|
|
|
|
update_rx_frequency(dummy_frequency_);
|
|
|
|
|
if (RIG_MODE_NONE != dummy_mode_) {
|
|
|
|
|
rig_set_mode(rig_.data(), RIG_VFO_CURR, dummy_mode_,
|
|
|
|
|
RIG_PASSBAND_NOCHANGE);
|
|
|
|
|
update_mode(map_mode(dummy_mode_));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int resolution{0};
|
|
|
|
|
if (freq_query_works_) {
|
|
|
|
|
freq_t current_frequency;
|
|
|
|
|
error_check(rig_get_freq(rig_.data(), RIG_VFO_CURR, ¤t_frequency),
|
|
|
|
|
tr("getting current VFO frequency"));
|
|
|
|
|
current_frequency = std::round(current_frequency);
|
|
|
|
|
Frequency f = current_frequency;
|
|
|
|
|
if (f && !(f % 10)) {
|
|
|
|
|
auto test_frequency = f - f % 100 + 55;
|
|
|
|
|
error_check(rig_set_freq(rig_.data(), RIG_VFO_CURR, test_frequency),
|
|
|
|
|
tr("setting frequency"));
|
|
|
|
|
freq_t new_frequency;
|
|
|
|
|
error_check(rig_get_freq(rig_.data(), RIG_VFO_CURR, &new_frequency),
|
|
|
|
|
tr("getting current VFO frequency"));
|
|
|
|
|
new_frequency = std::round(new_frequency);
|
|
|
|
|
switch (static_cast<Radio::FrequencyDelta>(new_frequency -
|
|
|
|
|
test_frequency)) {
|
|
|
|
|
case -5:
|
|
|
|
|
resolution = -1;
|
|
|
|
|
break; // 10Hz truncated
|
|
|
|
|
case 5:
|
|
|
|
|
resolution = 1;
|
|
|
|
|
break; // 10Hz rounded
|
|
|
|
|
case -15:
|
|
|
|
|
resolution = -2;
|
|
|
|
|
break; // 20Hz truncated
|
|
|
|
|
case -55:
|
|
|
|
|
resolution = -3;
|
|
|
|
|
break; // 100Hz truncated
|
|
|
|
|
case 45:
|
|
|
|
|
resolution = 3;
|
|
|
|
|
break; // 100Hz rounded
|
|
|
|
|
}
|
|
|
|
|
if (1 == resolution) // may be 20Hz rounded
|
|
|
|
|
{
|
|
|
|
|
test_frequency = f - f % 100 + 51;
|
|
|
|
|
error_check(
|
|
|
|
|
rig_set_freq(rig_.data(), RIG_VFO_CURR, test_frequency),
|
|
|
|
|
tr("setting frequency"));
|
|
|
|
|
error_check(
|
|
|
|
|
rig_get_freq(rig_.data(), RIG_VFO_CURR, &new_frequency),
|
|
|
|
|
tr("getting current VFO frequency"));
|
|
|
|
|
if (9 == static_cast<Radio::FrequencyDelta>(new_frequency -
|
|
|
|
|
test_frequency)) {
|
|
|
|
|
resolution = 2; // 20Hz rounded
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
error_check(
|
|
|
|
|
rig_set_freq(rig_.data(), RIG_VFO_CURR, current_frequency),
|
|
|
|
|
tr("setting frequency"));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
resolution = -1; // best guess
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
poll();
|
|
|
|
|
|
|
|
|
|
TRACE_CAT("HamlibTransceiver", "exit" << state()
|
|
|
|
|
<< "reversed =" << reversed_
|
|
|
|
|
<< "resolution = " << resolution);
|
|
|
|
|
return resolution;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HamlibTransceiver::do_stop() {
|
|
|
|
|
if (is_dummy_) {
|
|
|
|
|
rig_get_freq(rig_.data(), RIG_VFO_CURR, &dummy_frequency_);
|
|
|
|
|
dummy_frequency_ = std::round(dummy_frequency_);
|
|
|
|
|
if (mode_query_works_) {
|
|
|
|
|
pbwidth_t width;
|
|
|
|
|
rig_get_mode(rig_.data(), RIG_VFO_CURR, &dummy_mode_, &width);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (rig_) {
|
|
|
|
|
rig_close(rig_.data());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"state:" << state() << "reversed =" << reversed_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto HamlibTransceiver::get_vfos(bool for_split) const
|
|
|
|
|
-> std::tuple<vfo_t, vfo_t> {
|
|
|
|
|
if (get_vfo_works_ && rig_->caps->get_vfo) {
|
|
|
|
|
vfo_t v;
|
|
|
|
|
error_check(
|
|
|
|
|
rig_get_vfo(rig_.data(), &v),
|
|
|
|
|
tr("getting current VFO")); // has side effect of establishing
|
|
|
|
|
// current VFO inside hamlib
|
|
|
|
|
TRACE_CAT("HamlibTransceiver", "rig_get_vfo VFO = " << rig_strvfo(v));
|
|
|
|
|
|
|
|
|
|
reversed_ = RIG_VFO_B == v;
|
|
|
|
|
} else if (!for_split && set_vfo_works_ && rig_->caps->set_vfo &&
|
|
|
|
|
rig_->caps->set_split_vfo) {
|
|
|
|
|
// use VFO A/MAIN for main frequency and B/SUB for Tx
|
|
|
|
|
// frequency if split since these type of radios can only
|
|
|
|
|
// support this way around
|
|
|
|
|
|
|
|
|
|
TRACE_CAT("HamlibTransceiver", "rig_set_vfo VFO = A/MAIN");
|
|
|
|
|
error_check(rig_set_vfo(rig_.data(), rig_->state.vfo_list & RIG_VFO_A
|
|
|
|
|
? RIG_VFO_A
|
|
|
|
|
: RIG_VFO_MAIN),
|
|
|
|
|
tr("setting current VFO"));
|
|
|
|
|
}
|
|
|
|
|
// else only toggle available but VFOs should be substitutable
|
|
|
|
|
|
|
|
|
|
auto rx_vfo = rig_->state.vfo_list & RIG_VFO_A ? RIG_VFO_A : RIG_VFO_MAIN;
|
|
|
|
|
auto tx_vfo =
|
|
|
|
|
(WSJT_RIG_NONE_CAN_SPLIT || !is_dummy_) && for_split
|
|
|
|
|
? (rig_->state.vfo_list & RIG_VFO_B ? RIG_VFO_B : RIG_VFO_SUB)
|
|
|
|
|
: rx_vfo;
|
|
|
|
|
if (reversed_) {
|
|
|
|
|
TRACE_CAT("HamlibTransceiver", "reversing VFOs");
|
|
|
|
|
std::swap(rx_vfo, tx_vfo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"RX VFO = " << rig_strvfo(rx_vfo)
|
|
|
|
|
<< " TX VFO = " << rig_strvfo(tx_vfo));
|
|
|
|
|
return std::make_tuple(rx_vfo, tx_vfo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HamlibTransceiver::do_frequency(Frequency f, MODE m, bool no_ignore) {
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
f << "mode:" << m << "reversed:" << reversed_);
|
|
|
|
|
|
|
|
|
|
// only change when receiving or simplex or direct VFO addressing
|
|
|
|
|
// unavailable or forced
|
|
|
|
|
if (!state().ptt() || !state().split() || !one_VFO_ || no_ignore) {
|
|
|
|
|
// for the 1st time as a band change may cause a recalled mode to be
|
|
|
|
|
// set
|
|
|
|
|
error_check(rig_set_freq(rig_.data(), RIG_VFO_CURR, f),
|
|
|
|
|
tr("setting frequency"));
|
|
|
|
|
update_rx_frequency(f);
|
|
|
|
|
|
|
|
|
|
if (mode_query_works_ && UNK != m) {
|
|
|
|
|
rmode_t current_mode;
|
|
|
|
|
pbwidth_t current_width;
|
|
|
|
|
auto new_mode = map_mode(m);
|
|
|
|
|
error_check(rig_get_mode(rig_.data(), RIG_VFO_CURR, ¤t_mode,
|
|
|
|
|
¤t_width),
|
|
|
|
|
tr("getting current VFO mode"));
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"rig_get_mode mode = " << rig_strrmode(current_mode)
|
|
|
|
|
<< "bw =" << current_width);
|
|
|
|
|
|
|
|
|
|
if (new_mode != current_mode) {
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"rig_set_mode mode = " << rig_strrmode(new_mode));
|
|
|
|
|
error_check(rig_set_mode(rig_.data(), RIG_VFO_CURR, new_mode,
|
|
|
|
|
RIG_PASSBAND_NOCHANGE),
|
|
|
|
|
tr("setting current VFO mode"));
|
|
|
|
|
|
|
|
|
|
// for the 2nd time because a mode change may have caused a
|
|
|
|
|
// frequency change
|
|
|
|
|
error_check(rig_set_freq(rig_.data(), RIG_VFO_CURR, f),
|
|
|
|
|
tr("setting frequency"));
|
|
|
|
|
|
|
|
|
|
// for the second time because some rigs change mode according
|
|
|
|
|
// to frequency such as the TS-2000 auto mode setting
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"rig_set_mode mode = " << rig_strrmode(new_mode));
|
|
|
|
|
error_check(rig_set_mode(rig_.data(), RIG_VFO_CURR, new_mode,
|
|
|
|
|
RIG_PASSBAND_NOCHANGE),
|
|
|
|
|
tr("setting current VFO mode"));
|
|
|
|
|
}
|
|
|
|
|
update_mode(m);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Work around hamlib bug
|
|
|
|
|
* [#1966](https://github.com/Hamlib/Hamlib/issues/1966). */
|
|
|
|
|
void HamlibTransceiver::hamlib_bug_bandaid(TransceiverState const &s) {
|
|
|
|
|
if (s.frequency() == s.tx_frequency() && s.split() && s.ptt()) {
|
|
|
|
|
// Change the frequency ever so slightly without telling anybody.
|
|
|
|
|
// Will not matter during the upcoming transmit and will be corrected
|
|
|
|
|
// with the next update when the receive comes in.
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"Fiddling with rx frequency to work around a hamlib bug.");
|
|
|
|
|
error_check(rig_set_freq(rig_.data(), RIG_VFO_CURR, s.frequency() + 1),
|
|
|
|
|
tr("fiddling frequency as a hamlib bug workaround."));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HamlibTransceiver::do_tx_frequency(Frequency tx, MODE mode,
|
|
|
|
|
bool no_ignore) {
|
|
|
|
|
TRACE_CAT("HamlibTransceiver", tx << "reversed:" << reversed_);
|
|
|
|
|
|
|
|
|
|
if (WSJT_RIG_NONE_CAN_SPLIT ||
|
|
|
|
|
!is_dummy_) // split is meaningless if you can't see it
|
|
|
|
|
{
|
|
|
|
|
auto split = tx ? RIG_SPLIT_ON : RIG_SPLIT_OFF;
|
|
|
|
|
auto vfos = get_vfos(tx);
|
|
|
|
|
// auto rx_vfo = std::get<0> (vfos); // or use RIG_VFO_CURR
|
|
|
|
|
auto tx_vfo = std::get<1>(vfos);
|
|
|
|
|
|
|
|
|
|
if (tx) {
|
|
|
|
|
// Doing set split for the 1st of two times, this one
|
|
|
|
|
// ensures that the internal Hamlib state is correct
|
|
|
|
|
// otherwise rig_set_split_freq() will target the wrong VFO
|
|
|
|
|
// on some rigs
|
|
|
|
|
|
|
|
|
|
if (tickle_hamlib_) {
|
|
|
|
|
// This potentially causes issues with the Elecraft K3
|
|
|
|
|
// which will block setting split mode when it deems
|
|
|
|
|
// cross mode split operation not possible. There's not
|
|
|
|
|
// much we can do since the Hamlib Library needs this
|
|
|
|
|
// call at least once to establish the Tx VFO. Best we
|
|
|
|
|
// can do is only do this once per session.
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"rig_set_split_vfo split =" << split);
|
|
|
|
|
auto rc =
|
|
|
|
|
rig_set_split_vfo(rig_.data(), RIG_VFO_CURR, split, tx_vfo);
|
|
|
|
|
if (tx || (-RIG_ENAVAIL != rc && -RIG_ENIMPL != rc)) {
|
|
|
|
|
// On rigs that can't have split controlled only throw an
|
|
|
|
|
// exception when an error other than command not accepted
|
|
|
|
|
// is returned when trying to leave split mode. This allows
|
|
|
|
|
// fake split mode and non-split mode to work without error
|
|
|
|
|
// on such rigs without having to know anything about the
|
|
|
|
|
// specific rig.
|
|
|
|
|
error_check(rc, tr("setting/unsetting split mode"));
|
|
|
|
|
}
|
|
|
|
|
tickle_hamlib_ = false;
|
|
|
|
|
update_split(tx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// just change current when transmitting with single VFO
|
|
|
|
|
// addressing
|
|
|
|
|
if (state().ptt() && one_VFO_) {
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"rig_set_split_vfo split =" << split);
|
|
|
|
|
error_check(
|
|
|
|
|
rig_set_split_vfo(rig_.data(), RIG_VFO_CURR, split, tx_vfo),
|
|
|
|
|
tr("setting split mode"));
|
|
|
|
|
|
|
|
|
|
error_check(rig_set_freq(rig_.data(), RIG_VFO_CURR, tx),
|
|
|
|
|
tr("setting frequency"));
|
|
|
|
|
|
|
|
|
|
if (UNK != mode && mode_query_works_) {
|
|
|
|
|
rmode_t current_mode;
|
|
|
|
|
pbwidth_t current_width;
|
|
|
|
|
auto new_mode = map_mode(mode);
|
|
|
|
|
error_check(rig_get_mode(rig_.data(), RIG_VFO_CURR,
|
|
|
|
|
¤t_mode, ¤t_width),
|
|
|
|
|
tr("getting current VFO mode"));
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"rig_get_mode mode = "
|
|
|
|
|
<< rig_strrmode(current_mode)
|
|
|
|
|
<< "bw =" << current_width);
|
|
|
|
|
|
|
|
|
|
if (new_mode != current_mode) {
|
|
|
|
|
TRACE_CAT(
|
|
|
|
|
"HamlibTransceiver",
|
|
|
|
|
"rig_set_mode mode = " << rig_strrmode(new_mode));
|
|
|
|
|
error_check(rig_set_mode(rig_.data(), RIG_VFO_CURR,
|
|
|
|
|
new_mode,
|
|
|
|
|
RIG_PASSBAND_NOCHANGE),
|
|
|
|
|
tr("setting current VFO mode"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
update_other_frequency(tx);
|
|
|
|
|
} else if (!one_VFO_ ||
|
|
|
|
|
no_ignore) // if not single VFO addressing and not forced
|
|
|
|
|
{
|
|
|
|
|
hamlib_tx_vfo_fixup fixup(rig_.data(), tx_vfo);
|
|
|
|
|
if (UNK != mode) {
|
|
|
|
|
auto new_mode = map_mode(mode);
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"rig_set_split_freq_mode freq = "
|
|
|
|
|
<< tx
|
|
|
|
|
<< " mode = " << rig_strrmode(new_mode));
|
|
|
|
|
error_check(rig_set_split_freq_mode(
|
|
|
|
|
rig_.data(), RIG_VFO_CURR, tx, new_mode,
|
|
|
|
|
RIG_PASSBAND_NOCHANGE),
|
|
|
|
|
tr("setting split TX frequency and mode"));
|
|
|
|
|
} else {
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"rig_set_split_freq freq = " << tx);
|
|
|
|
|
error_check(
|
|
|
|
|
rig_set_split_freq(rig_.data(), RIG_VFO_CURR, tx),
|
|
|
|
|
tr("setting split TX frequency"));
|
|
|
|
|
}
|
|
|
|
|
// Enable split last since some rigs (Kenwood for one) come out
|
|
|
|
|
// of split when you switch RX VFO (to set split mode above for
|
|
|
|
|
// example). Also the Elecraft K3 will refuse to go to split
|
|
|
|
|
// with certain VFO A/B mode combinations.
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"rig_set_split_vfo split =" << split);
|
|
|
|
|
error_check(
|
|
|
|
|
rig_set_split_vfo(rig_.data(), RIG_VFO_CURR, split, tx_vfo),
|
|
|
|
|
tr("setting split mode"));
|
|
|
|
|
update_other_frequency(tx);
|
|
|
|
|
update_split(tx);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Disable split
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"rig_set_split_vfo split =" << split);
|
|
|
|
|
auto rc =
|
|
|
|
|
rig_set_split_vfo(rig_.data(), RIG_VFO_CURR, split, tx_vfo);
|
|
|
|
|
if (tx || (-RIG_ENAVAIL != rc && -RIG_ENIMPL != rc)) {
|
|
|
|
|
// On rigs that can't have split controlled only throw an
|
|
|
|
|
// exception when an error other than command not accepted
|
|
|
|
|
// is returned when trying to leave split mode. This allows
|
|
|
|
|
// fake split mode and non-split mode to work without error
|
|
|
|
|
// on such rigs without having to know anything about the
|
|
|
|
|
// specific rig.
|
|
|
|
|
error_check(rc, tr("setting/unsetting split mode"));
|
|
|
|
|
}
|
|
|
|
|
update_other_frequency(tx);
|
|
|
|
|
update_split(tx);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HamlibTransceiver::do_mode(MODE mode) {
|
|
|
|
|
TRACE_CAT("HamlibTransceiver", mode);
|
|
|
|
|
|
|
|
|
|
auto vfos = get_vfos(state().split());
|
|
|
|
|
// auto rx_vfo = std::get<0> (vfos);
|
|
|
|
|
auto tx_vfo = std::get<1>(vfos);
|
|
|
|
|
|
|
|
|
|
rmode_t current_mode;
|
|
|
|
|
pbwidth_t current_width;
|
|
|
|
|
auto new_mode = map_mode(mode);
|
|
|
|
|
|
|
|
|
|
// only change when receiving or simplex if direct VFO addressing
|
|
|
|
|
// unavailable
|
|
|
|
|
if (!(state().ptt() && state().split() && one_VFO_)) {
|
|
|
|
|
error_check(rig_get_mode(rig_.data(), RIG_VFO_CURR, ¤t_mode,
|
|
|
|
|
¤t_width),
|
|
|
|
|
tr("getting current VFO mode"));
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"rig_get_mode mode = " << rig_strrmode(current_mode)
|
|
|
|
|
<< "bw =" << current_width);
|
|
|
|
|
|
|
|
|
|
if (new_mode != current_mode) {
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"rig_set_mode mode = " << rig_strrmode(new_mode));
|
|
|
|
|
error_check(rig_set_mode(rig_.data(), RIG_VFO_CURR, new_mode,
|
|
|
|
|
RIG_PASSBAND_NOCHANGE),
|
|
|
|
|
tr("setting current VFO mode"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// just change current when transmitting split with one VFO mode
|
|
|
|
|
if (state().ptt() && state().split() && one_VFO_) {
|
|
|
|
|
error_check(rig_get_mode(rig_.data(), RIG_VFO_CURR, ¤t_mode,
|
|
|
|
|
¤t_width),
|
|
|
|
|
tr("getting current VFO mode"));
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"rig_get_mode mode = " << rig_strrmode(current_mode)
|
|
|
|
|
<< "bw =" << current_width);
|
|
|
|
|
|
|
|
|
|
if (new_mode != current_mode) {
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"rig_set_mode mode = " << rig_strrmode(new_mode));
|
|
|
|
|
error_check(rig_set_mode(rig_.data(), RIG_VFO_CURR, new_mode,
|
|
|
|
|
RIG_PASSBAND_NOCHANGE),
|
|
|
|
|
tr("setting current VFO mode"));
|
|
|
|
|
}
|
|
|
|
|
} else if (state().split() && !one_VFO_) {
|
|
|
|
|
error_check(rig_get_split_mode(rig_.data(), RIG_VFO_CURR, ¤t_mode,
|
|
|
|
|
¤t_width),
|
|
|
|
|
tr("getting split TX VFO mode"));
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"rig_get_split_mode mode = " << rig_strrmode(current_mode)
|
|
|
|
|
<< "bw =" << current_width);
|
|
|
|
|
|
|
|
|
|
if (new_mode != current_mode) {
|
|
|
|
|
TRACE_CAT("HamlibTransceiver",
|
|
|
|
|
"rig_set_split_mode mode = " << rig_strrmode(new_mode));
|
|
|
|
|
hamlib_tx_vfo_fixup fixup(rig_.data(), tx_vfo);
|
|
|
|
|
error_check(rig_set_split_mode(rig_.data(), RIG_VFO_CURR, new_mode,
|
|
|
|
|
RIG_PASSBAND_NOCHANGE),
|
|
|
|
|
tr("setting split TX VFO mode"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
update_mode(mode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HamlibTransceiver::poll() {
|
|
|
|
|
if (hamlibtransceiver_js8().isDebugEnabled())
|
|
|
|
|
rig_set_debug(RIG_DEBUG_WARN);
|
|
|
|
|
else
|
|
|
|
|
rig_set_debug(RIG_DEBUG_ERR);
|
|
|
|
|
|
|
|
|
|
freq_t f;
|
|
|
|
|
rmode_t m;
|
|
|
|
|
pbwidth_t w;
|
|
|
|
|
split_t s;
|
|
|
|
|
|
|
|
|
|
if (get_vfo_works_ && rig_->caps->get_vfo) {
|
|
|
|
|
vfo_t v;
|
|
|
|
|
error_check(
|
|
|
|
|
rig_get_vfo(rig_.data(), &v),
|
|
|
|
|
tr("getting current VFO")); // has side effect of establishing
|
|
|
|
|
// current VFO inside hamlib
|
|
|
|
|
TRACE_CAT_POLL("HamlibTransceiver", "VFO =" << rig_strvfo(v));
|
|
|
|
|
reversed_ = RIG_VFO_B == v;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((WSJT_RIG_NONE_CAN_SPLIT || !is_dummy_) && rig_->caps->get_split_vfo &&
|
|
|
|
|
split_query_works_) {
|
|
|
|
|
vfo_t v{RIG_VFO_NONE}; // so we can tell if it doesn't get updated :(
|
|
|
|
|
auto rc = rig_get_split_vfo(rig_.data(), RIG_VFO_CURR, &s, &v);
|
|
|
|
|
if (-RIG_OK == rc && RIG_SPLIT_ON == s) {
|
|
|
|
|
TRACE_CAT_POLL("HamlibTransceiver",
|
|
|
|
|
"rig_get_split_vfo split = " << s << " VFO = "
|
|
|
|
|
<< rig_strvfo(v));
|
|
|
|
|
update_split(true);
|
|
|
|
|
// if (RIG_VFO_A == v)
|
|
|
|
|
// {
|
|
|
|
|
// reversed_ = true; // not sure if this helps us here
|
|
|
|
|
// }
|
|
|
|
|
} else if (-RIG_OK == rc) // not split
|
|
|
|
|
{
|
|
|
|
|
TRACE_CAT_POLL("HamlibTransceiver",
|
|
|
|
|
"rig_get_split_vfo split = " << s << " VFO = "
|
|
|
|
|
<< rig_strvfo(v));
|
|
|
|
|
update_split(false);
|
|
|
|
|
} else {
|
|
|
|
|
// Some rigs (Icom) don't have a way of reporting SPLIT
|
|
|
|
|
// mode
|
|
|
|
|
TRACE_CAT_POLL("HamlibTransceiver",
|
|
|
|
|
"rig_get_split_vfo can't do on this rig");
|
|
|
|
|
// just report how we see it based on prior commands
|
|
|
|
|
split_query_works_ = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (freq_query_works_) {
|
|
|
|
|
// only read if possible and when receiving or simplex
|
|
|
|
|
if (!state().ptt() || !state().split()) {
|
|
|
|
|
error_check(rig_get_freq(rig_.data(), RIG_VFO_CURR, &f),
|
|
|
|
|
tr("getting current VFO frequency"));
|
|
|
|
|
f = std::round(f);
|
|
|
|
|
TRACE_CAT_POLL("HamlibTransceiver",
|
|
|
|
|
"rig_get_freq frequency =" << f);
|
|
|
|
|
update_rx_frequency(f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((WSJT_RIG_NONE_CAN_SPLIT || !is_dummy_) && state().split() &&
|
|
|
|
|
(rig_->caps->targetable_vfo &
|
|
|
|
|
(RIG_TARGETABLE_FREQ | RIG_TARGETABLE_PURE)) &&
|
|
|
|
|
!one_VFO_) {
|
|
|
|
|
// only read "other" VFO if in split, this allows rigs like
|
|
|
|
|
// FlexRadio to work in Kenwood TS-2000 mode despite them
|
|
|
|
|
// not having a FB; command
|
|
|
|
|
|
|
|
|
|
// we can only probe current VFO unless rig supports reading
|
|
|
|
|
// the other one directly because we can't glitch the Rx
|
|
|
|
|
error_check(rig_get_freq(rig_.data(),
|
|
|
|
|
reversed_
|
|
|
|
|
? (rig_->state.vfo_list & RIG_VFO_A
|
|
|
|
|
? RIG_VFO_A
|
|
|
|
|
: RIG_VFO_MAIN)
|
|
|
|
|
: (rig_->state.vfo_list & RIG_VFO_B
|
|
|
|
|
? RIG_VFO_B
|
|
|
|
|
: RIG_VFO_SUB),
|
|
|
|
|
&f),
|
|
|
|
|
tr("getting other VFO frequency"));
|
|
|
|
|
f = std::round(f);
|
|
|
|
|
TRACE_CAT_POLL("HamlibTransceiver",
|
|
|
|
|
"rig_get_freq other VFO =" << f);
|
|
|
|
|
update_other_frequency(f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// only read when receiving or simplex if direct VFO addressing unavailable
|
|
|
|
|
if ((!state().ptt() || !state().split()) && mode_query_works_) {
|
|
|
|
|
// We have to ignore errors here because Yaesu FTdx... rigs can
|
|
|
|
|
// report the wrong mode when transmitting split with different
|
|
|
|
|
// modes per VFO. This is unfortunate because that is exactly
|
|
|
|
|
// what you need to do to get 4kHz Rx b.w and modulation into
|
|
|
|
|
// the rig through the data socket or USB. I.e. USB for Rx and
|
|
|
|
|
// DATA-USB for Tx.
|
|
|
|
|
auto rc = rig_get_mode(rig_.data(), RIG_VFO_CURR, &m, &w);
|
|
|
|
|
if (RIG_OK == rc) {
|
|
|
|
|
TRACE_CAT_POLL("HamlibTransceiver",
|
|
|
|
|
"rig_get_mode mode =" << rig_strrmode(m)
|
|
|
|
|
<< "bw =" << w);
|
|
|
|
|
update_mode(map_mode(m));
|
|
|
|
|
} else {
|
|
|
|
|
TRACE_CAT_POLL("HamlibTransceiver",
|
|
|
|
|
"rig_get_mode mode failed with rc:" << rc
|
|
|
|
|
<< "ignoring");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (RIG_PTT_NONE != rig_->state.pttport.type.ptt && rig_->caps->get_ptt) {
|
|
|
|
|
ptt_t p;
|
|
|
|
|
auto rc = rig_get_ptt(rig_.data(), RIG_VFO_CURR, &p);
|
|
|
|
|
if (-RIG_ENAVAIL != rc && -RIG_ENIMPL != rc) // may fail if
|
|
|
|
|
// Net rig ctl and target
|
|
|
|
|
// doesn't support command
|
|
|
|
|
{
|
|
|
|
|
error_check(rc, tr("getting PTT state"));
|
|
|
|
|
TRACE_CAT_POLL("HamlibTransceiver", "rig_get_ptt PTT =" << p);
|
|
|
|
|
update_PTT(!(RIG_PTT_OFF == p));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hamlibtransceiver_js8().isDebugEnabled())
|
|
|
|
|
rig_set_debug(RIG_DEBUG_TRACE);
|
|
|
|
|
else if (hamlibtransceiver_js8().isInfoEnabled())
|
|
|
|
|
rig_set_debug(RIG_DEBUG_VERBOSE);
|
|
|
|
|
else if (hamlibtransceiver_js8().isWarningEnabled())
|
|
|
|
|
rig_set_debug(RIG_DEBUG_WARN);
|
|
|
|
|
else
|
|
|
|
|
rig_set_debug(RIG_DEBUG_ERR);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HamlibTransceiver::do_ptt(bool on) {
|
|
|
|
|
TRACE_CAT("HamlibTransceiver", on << state() << "reversed =" << reversed_);
|
|
|
|
|
if (on) {
|
|
|
|
|
if (RIG_PTT_NONE != rig_->state.pttport.type.ptt) {
|
|
|
|
|
TRACE_CAT("HamlibTransceiver", "rig_set_ptt PTT = true");
|
|
|
|
|
error_check(
|
|
|
|
|
rig_set_ptt(rig_.data(), RIG_VFO_CURR,
|
|
|
|
|
RIG_PTT_RIG_MICDATA == rig_->caps->ptt_type &&
|
|
|
|
|
back_ptt_port_
|
|
|
|
|
? RIG_PTT_ON_DATA
|
|
|
|
|
: RIG_PTT_ON),
|
|
|
|
|
tr("setting PTT on"));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (RIG_PTT_NONE != rig_->state.pttport.type.ptt) {
|
|
|
|
|
TRACE_CAT("HamlibTransceiver", "rig_set_ptt PTT = false");
|
|
|
|
|
error_check(rig_set_ptt(rig_.data(), RIG_VFO_CURR, RIG_PTT_OFF),
|
|
|
|
|
tr("setting PTT off"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
update_PTT(on);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HamlibTransceiver::set_conf(char const *item, char const *value) {
|
|
|
|
|
token_t token = rig_token_lookup(rig_.data(), item);
|
|
|
|
|
if (RIG_CONF_END != token) // only set if valid for rig model
|
|
|
|
|
{
|
|
|
|
|
error_check(rig_set_conf(rig_.data(), token, value),
|
|
|
|
|
tr("setting a configuration item"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QByteArray HamlibTransceiver::get_conf(char const *item) {
|
|
|
|
|
token_t token = rig_token_lookup(rig_.data(), item);
|
2026-03-23 20:57:59 -04:00
|
|
|
constexpr int valueSize = 128;
|
|
|
|
|
QByteArray value{valueSize, '\0'};
|
2026-01-01 15:12:24 -06:00
|
|
|
if (RIG_CONF_END != token) // only get if valid for rig model
|
|
|
|
|
{
|
2026-03-23 20:57:59 -04:00
|
|
|
error_check(rig_get_conf2(rig_.data(), token, value.data(), valueSize),
|
2026-01-01 15:12:24 -06:00
|
|
|
tr("getting a configuration item"));
|
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto HamlibTransceiver::map_mode(rmode_t m) const -> MODE {
|
|
|
|
|
switch (m) {
|
|
|
|
|
case RIG_MODE_AM:
|
|
|
|
|
case RIG_MODE_SAM:
|
|
|
|
|
case RIG_MODE_AMS:
|
|
|
|
|
case RIG_MODE_DSB:
|
|
|
|
|
return AM;
|
|
|
|
|
|
|
|
|
|
case RIG_MODE_CW:
|
|
|
|
|
return CW;
|
|
|
|
|
|
|
|
|
|
case RIG_MODE_CWR:
|
|
|
|
|
return CW_R;
|
|
|
|
|
|
|
|
|
|
case RIG_MODE_USB:
|
|
|
|
|
case RIG_MODE_ECSSUSB:
|
|
|
|
|
case RIG_MODE_SAH:
|
|
|
|
|
case RIG_MODE_FAX:
|
|
|
|
|
return USB;
|
|
|
|
|
|
|
|
|
|
case RIG_MODE_LSB:
|
|
|
|
|
case RIG_MODE_ECSSLSB:
|
|
|
|
|
case RIG_MODE_SAL:
|
|
|
|
|
return LSB;
|
|
|
|
|
|
|
|
|
|
case RIG_MODE_RTTY:
|
|
|
|
|
return FSK;
|
|
|
|
|
|
|
|
|
|
case RIG_MODE_RTTYR:
|
|
|
|
|
return FSK_R;
|
|
|
|
|
|
|
|
|
|
case RIG_MODE_PKTLSB:
|
|
|
|
|
return DIG_L;
|
|
|
|
|
|
|
|
|
|
case RIG_MODE_PKTUSB:
|
|
|
|
|
return DIG_U;
|
|
|
|
|
|
|
|
|
|
case RIG_MODE_FM:
|
|
|
|
|
case RIG_MODE_WFM:
|
|
|
|
|
return FM;
|
|
|
|
|
|
|
|
|
|
case RIG_MODE_PKTFM:
|
|
|
|
|
return DIG_FM;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return UNK;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rmode_t HamlibTransceiver::map_mode(MODE mode) const {
|
|
|
|
|
switch (mode) {
|
|
|
|
|
case AM:
|
|
|
|
|
return RIG_MODE_AM;
|
|
|
|
|
case CW:
|
|
|
|
|
return RIG_MODE_CW;
|
|
|
|
|
case CW_R:
|
|
|
|
|
return RIG_MODE_CWR;
|
|
|
|
|
case USB:
|
|
|
|
|
return RIG_MODE_USB;
|
|
|
|
|
case LSB:
|
|
|
|
|
return RIG_MODE_LSB;
|
|
|
|
|
case FSK:
|
|
|
|
|
return RIG_MODE_RTTY;
|
|
|
|
|
case FSK_R:
|
|
|
|
|
return RIG_MODE_RTTYR;
|
|
|
|
|
case DIG_L:
|
|
|
|
|
return RIG_MODE_PKTLSB;
|
|
|
|
|
case DIG_U:
|
|
|
|
|
return RIG_MODE_PKTUSB;
|
|
|
|
|
case FM:
|
|
|
|
|
return RIG_MODE_FM;
|
|
|
|
|
case DIG_FM:
|
|
|
|
|
return RIG_MODE_PKTFM;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return RIG_MODE_USB; // quieten compiler grumble
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Q_LOGGING_CATEGORY(hamlibtransceiver_js8, "hamlibtransceiver.js8", QtWarningMsg)
|