From 1d921405cc8c31df436a3a2e4a73f76b3d2811ba Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Tue, 2 Feb 2021 19:07:28 +0000 Subject: [PATCH 1/2] Bump RC number --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b1f964f3b..f4bda4cd7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,7 +71,7 @@ message (STATUS "******************************************************") include (set_build_type) # RC 0 or omitted is a development build, GA is a General Availability release build -set_build_type (RC 1) +set_build_type (RC 2) set (wsjtx_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${BUILD_TYPE_REVISION}") # From f972fc18e14cd4ce1b46ac66956108fc7db263a0 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Tue, 2 Feb 2021 22:45:45 +0000 Subject: [PATCH 2/2] Remove direct struct access from usage of the Hamlib API Preparation for safe dynamic linking to Hamlib where minor Hamlib upgrades can be deployed just by replacing the DLL/SO. --- .../DXLabSuiteCommanderTransceiver.cpp | 2 +- .../DXLabSuiteCommanderTransceiver.hpp | 2 +- Transceiver/HRDTransceiver.cpp | 2 +- Transceiver/HRDTransceiver.hpp | 2 +- Transceiver/HamlibTransceiver.cpp | 1789 +++++++++-------- Transceiver/HamlibTransceiver.hpp | 42 +- Transceiver/OmniRigTransceiver.cpp | 2 +- Transceiver/OmniRigTransceiver.hpp | 2 +- Transceiver/TransceiverFactory.cpp | 2 +- Transceiver/TransceiverFactory.hpp | 4 +- 10 files changed, 939 insertions(+), 910 deletions(-) diff --git a/Transceiver/DXLabSuiteCommanderTransceiver.cpp b/Transceiver/DXLabSuiteCommanderTransceiver.cpp index 8dca88520..8f2063c7e 100644 --- a/Transceiver/DXLabSuiteCommanderTransceiver.cpp +++ b/Transceiver/DXLabSuiteCommanderTransceiver.cpp @@ -39,7 +39,7 @@ namespace void DXLabSuiteCommanderTransceiver::register_transceivers (logger_type * /*logger*/, TransceiverFactory::Transceivers * registry, - int id) + unsigned id) { (*registry)[commander_transceiver_name] = TransceiverFactory::Capabilities {id, TransceiverFactory::Capabilities::network, true}; } diff --git a/Transceiver/DXLabSuiteCommanderTransceiver.hpp b/Transceiver/DXLabSuiteCommanderTransceiver.hpp index ac051f5c5..fc060fb72 100644 --- a/Transceiver/DXLabSuiteCommanderTransceiver.hpp +++ b/Transceiver/DXLabSuiteCommanderTransceiver.hpp @@ -24,7 +24,7 @@ class DXLabSuiteCommanderTransceiver final Q_OBJECT; // for translation context public: - static void register_transceivers (logger_type *, TransceiverFactory::Transceivers *, int id); + static void register_transceivers (logger_type *, TransceiverFactory::Transceivers *, unsigned id); // takes ownership of wrapped Transceiver explicit DXLabSuiteCommanderTransceiver (logger_type *, std::unique_ptr wrapped, diff --git a/Transceiver/HRDTransceiver.cpp b/Transceiver/HRDTransceiver.cpp index 9ea10949c..f9c148a3b 100644 --- a/Transceiver/HRDTransceiver.cpp +++ b/Transceiver/HRDTransceiver.cpp @@ -25,7 +25,7 @@ namespace void HRDTransceiver::register_transceivers (logger_type *, TransceiverFactory::Transceivers * registry, - int id) + unsigned id) { (*registry)[HRD_transceiver_name] = TransceiverFactory::Capabilities (id, TransceiverFactory::Capabilities::network, true, true /* maybe */); } diff --git a/Transceiver/HRDTransceiver.hpp b/Transceiver/HRDTransceiver.hpp index 7d8598030..f3db8bce7 100644 --- a/Transceiver/HRDTransceiver.hpp +++ b/Transceiver/HRDTransceiver.hpp @@ -30,7 +30,7 @@ class HRDTransceiver final Q_OBJECT public: - static void register_transceivers (logger_type *, TransceiverFactory::Transceivers *, int id); + static void register_transceivers (logger_type *, TransceiverFactory::Transceivers *, unsigned id); // takes ownership of wrapped Transceiver explicit HRDTransceiver (logger_type * diff --git a/Transceiver/HamlibTransceiver.cpp b/Transceiver/HamlibTransceiver.cpp index 0ae5cd65c..27fe16a64 100644 --- a/Transceiver/HamlibTransceiver.cpp +++ b/Transceiver/HamlibTransceiver.cpp @@ -2,7 +2,7 @@ #include #include - +#include #include #include #include @@ -11,7 +11,8 @@ #include #include #include - +#include +#include "pimpl_impl.hpp" #include "moc_HamlibTransceiver.cpp" #if HAVE_HAMLIB_OLD_CACHING @@ -36,36 +37,35 @@ namespace // callback function that receives transceiver capabilities from the // hamlib libraries - int register_callback (rig_caps const * caps, void * callback_data) + int register_callback (rig_model_t rig_model, void * callback_data) { TransceiverFactory::Transceivers * rigs = reinterpret_cast (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 == caps->rig_model) + if (RIG_MODEL_DUMMY_NOVFO == rig_model) { return 1; } #endif QString key; - if (RIG_MODEL_DUMMY == caps->rig_model) + if (RIG_MODEL_DUMMY == rig_model) { key = TransceiverFactory::basic_transceiver_name_; } else { - key = QString::fromLatin1 (caps->mfg_name).trimmed () - + ' '+ QString::fromLatin1 (caps->model_name).trimmed () - // + ' '+ QString::fromLatin1 (caps->version).trimmed () - // + " (" + QString::fromLatin1 (rig_strstatus (caps->status)).trimmed () + ')' + 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 (caps->port_type) + switch(rig_get_caps_int (rig_model, RIG_CAPS_PORT_TYPE)) { case RIG_PORT_SERIAL: port_type = TransceiverFactory::Capabilities::serial; @@ -81,19 +81,20 @@ namespace default: break; } - (*rigs)[key] = TransceiverFactory::Capabilities (caps->rig_model + auto ptt_type = rig_get_caps_int (rig_model, RIG_CAPS_PTT_TYPE); + (*rigs)[key] = TransceiverFactory::Capabilities (rig_model , port_type - , RIG_MODEL_DUMMY != caps->rig_model - && (RIG_PTT_RIG == caps->ptt_type - || RIG_PTT_RIG_MICDATA == caps->ptt_type) - , RIG_PTT_RIG_MICDATA == caps->ptt_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_caps const * caps, void *) + int unregister_callback (rig_model_t rig_model, void *) { - rig_unregister (caps->rig_model); + rig_unregister (rig_get_caps_int (rig_model, RIG_CAPS_RIG_MODEL)); return 1; // keep them coming } @@ -129,11 +130,92 @@ namespace }; } -freq_t HamlibTransceiver::dummy_frequency_; -rmode_t HamlibTransceiver::dummy_mode_ {RIG_MODE_NONE}; +class HamlibTransceiver::impl final +{ +public: + impl (HamlibTransceiver::logger_type * logger) + : logger_ {logger} + , model_ {RIG_MODEL_DUMMY} + , rig_ {rig_init (model_)} + , ptt_only_ {true} + , 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} + { + } + + impl (HamlibTransceiver::logger_type * logger, unsigned model_number + , TransceiverFactory::ParameterPack const& params) + : logger_ {logger} + , model_ {model_number} + , rig_ {rig_init (model_)} + , ptt_only_ {false} + , back_ptt_port_ {TransceiverFactory::TX_audio_source_rear == params.audio_source} + , one_VFO_ {false} + , is_dummy_ {RIG_MODEL_DUMMY == model_} + , reversed_ {false} + , freq_query_works_ {rig_ && rig_get_function_ptr (model_, RIG_FUNCTION_GET_FREQ)} + , mode_query_works_ {rig_ && rig_get_function_ptr (model_, RIG_FUNCTION_GET_MODE)} + , split_query_works_ {rig_ && rig_get_function_ptr (model_, RIG_FUNCTION_GET_SPLIT_VFO)} + , tickle_hamlib_ {false} + , get_vfo_works_ {true} + , set_vfo_works_ {true} + { + } + + HamlibTransceiver::logger_type& logger () const + { + return *logger_; + } + + void error_check (int ret_code, QString const& doing) const; + void set_conf (char const * item, char const * value); + QByteArray get_conf (char const * item); + Transceiver::MODE map_mode (rmode_t) const; + rmode_t map_mode (Transceiver::MODE mode) const; + std::tuple get_vfos (bool for_split) const; + + HamlibTransceiver::logger_type mutable * logger_; + unsigned model_; + struct RIGDeleter {static void cleanup (RIG *);}; + QScopedPointer rig_; + + bool ptt_only_; // we can use a dummy device for PTT + bool back_ptt_port_; + bool one_VFO_; + bool is_dummy_; + + // these are saved on destruction so we can start new instances + // where the last one left off + static freq_t dummy_frequency_; + static rmode_t dummy_mode_; + + bool mutable reversed_; + + bool freq_query_works_; + bool mode_query_works_; + bool split_query_works_; + bool tickle_hamlib_; // Hamlib requires a + // rig_set_split_vfo() call to + // establish the Tx VFO + bool get_vfo_works_; // Net rigctl promises what it can't deliver + bool set_vfo_works_; // More rigctl promises which it can't deliver + + static int debug_callback (enum rig_debug_level_e level, rig_ptr_t arg, char const * format, va_list ap); +}; + +freq_t HamlibTransceiver::impl::dummy_frequency_; +rmode_t HamlibTransceiver::impl::dummy_mode_ {RIG_MODE_NONE}; // reroute Hamlib diagnostic messages to Qt -int HamlibTransceiver::debug_callback (enum rig_debug_level_e level, rig_ptr_t arg, char const * format, va_list ap) +int HamlibTransceiver::impl::debug_callback (enum rig_debug_level_e level, rig_ptr_t arg, char const * format, va_list ap) { auto logger = reinterpret_cast (arg); auto message = QString::vasprintf (format, ap); @@ -159,256 +241,27 @@ int HamlibTransceiver::debug_callback (enum rig_debug_level_e level, rig_ptr_t a void HamlibTransceiver::register_transceivers (logger_type * logger, TransceiverFactory::Transceivers * registry) { - rig_set_debug_callback (debug_callback, logger); + rig_set_debug_callback (impl::debug_callback, logger); rig_set_debug (RIG_DEBUG_TRACE); BOOST_LOG_SEV (*logger, boost::log::trivial::info) << "Hamlib version: " << rig_version (); rig_load_all_backends (); - rig_list_foreach (register_callback, registry); + rig_list_foreach_model (register_callback, registry); } void HamlibTransceiver::unregister_transceivers () { - rig_list_foreach (unregister_callback, nullptr); + rig_list_foreach_model (unregister_callback, nullptr); } -void HamlibTransceiver::RIGDeleter::cleanup (RIG * rig) +void HamlibTransceiver::impl::RIGDeleter::cleanup (RIG * rig) { if (rig) { - // rig->state.obj = 0; rig_cleanup (rig); } } -HamlibTransceiver::HamlibTransceiver (logger_type * logger, - TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port, - QObject * parent) - : PollingTransceiver {logger, 0, parent} - , rig_ {rig_init (RIG_MODEL_DUMMY)} - , ptt_only_ {true} - , 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 (WIN32) - 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 (logger_type * logger, - int model_number, - TransceiverFactory::ParameterPack const& params, - QObject * parent) - : PollingTransceiver {logger, params.poll_interval, parent} - , rig_ {rig_init (model_number)} - , ptt_only_ {false} - , 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; - - // - // user defined Hamlib settings - // - auto settings_file_name = QStandardPaths::locate (QStandardPaths::AppConfigLocation - , "hamlib_settings.json"); - if (!settings_file_name.isEmpty ()) - { - QFile settings_file {settings_file_name}; - qDebug () << "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)}; - } - qDebug () << "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 ()); - } - } - } - } - - if (!is_dummy_) - { - 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.size () - && params.ptt_port != "None" - && (is_dummy_ - || RIG_PORT_SERIAL != rig_->caps->port_type - || params.ptt_port != params.serial_port)) - { -#if defined (WIN32) - 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 +void HamlibTransceiver::impl::error_check (int ret_code, QString const& doing) const { if (RIG_OK != ret_code) { @@ -417,267 +270,9 @@ void HamlibTransceiver::error_check (int ret_code, QString const& doing) const } } -int HamlibTransceiver::do_start () +std::tuple HamlibTransceiver::impl::get_vfos (bool for_split) const { - CAT_TRACE ("starting: " << rig_->caps->mfg_name - << ": " << rig_->caps->model_name); - - 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); - CAT_TRACE ("current frequency=" << f1); - - error_check (rig_get_mode (rig_.data (), RIG_VFO_CURR, &m, &w), tr ("getting current mode")); - CAT_TRACE ("current mode=" << rig_strrmode (m) << " bw=" << w); - - if (!rig_->caps->set_vfo) - { - CAT_TRACE ("rig_vfo_op TOGGLE"); - rc = rig_vfo_op (rig_.data (), RIG_VFO_CURR, RIG_OP_TOGGLE); - } - else - { - CAT_TRACE ("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 - CAT_TRACE ("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); - CAT_TRACE ("rig_get_freq other frequency=" << f2); - - error_check (rig_get_mode (rig_.data (), RIG_VFO_CURR, &mb, &wb), tr ("getting other VFO mode")); - CAT_TRACE ("rig_get_mode other mode=" << rig_strrmode (mb) << " bw=" << wb); - - update_other_frequency (f2); - - if (!rig_->caps->set_vfo) - { - CAT_TRACE ("rig_vfo_op TOGGLE"); - error_check (rig_vfo_op (rig_.data (), RIG_VFO_CURR, RIG_OP_TOGGLE), tr ("exchanging VFOs")); - } - else - { - CAT_TRACE ("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); - CAT_TRACE ("rig_get_freq frequency=" << f1); - - error_check (rig_get_mode (rig_.data (), RIG_VFO_CURR, &m, &w), tr ("getting mode")); - CAT_TRACE ("rig_get_mode mode=" << rig_strrmode (m) << " bw=" << w); - - update_rx_frequency (f1); - } - } - - // TRACE_CAT ("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 - CAT_TRACE ("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)) - { - CAT_TRACE ("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 - CAT_TRACE ("rig_get_mode can't do on this rig"); - } - } - } - update_mode (map_mode (m)); - } - - tickle_hamlib_ = true; - - if (is_dummy_ && !ptt_only_ && 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_)); - } - } - -#if HAVE_HAMLIB_CACHING || HAVE_HAMLIB_OLD_CACHING - // we must disable Hamlib caching because it lies about frequency - // for less than 1 Hz resolution rigs - auto orig_cache_timeout = rig_get_cache_timeout_ms (rig_.data (), HAMLIB_CACHE_ALL); - rig_set_cache_timeout_ms (rig_.data (), HAMLIB_CACHE_ALL, 0); -#endif - - 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 (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 (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 - } - -#if HAVE_HAMLIB_CACHING || HAVE_HAMLIB_OLD_CACHING - // revert Hamlib cache timeout - rig_set_cache_timeout_ms (rig_.data (), HAMLIB_CACHE_ALL, orig_cache_timeout); -#endif - - do_poll (); - - CAT_TRACE ("finished start " << state () << " reversed=" << reversed_ << " resolution=" << resolution); - return resolution; -} - -void HamlibTransceiver::do_stop () -{ - if (is_dummy_ && !ptt_only_) - { - 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 ()); - } - - CAT_TRACE ("state: " << state () << " reversed=" << reversed_); -} - -std::tuple HamlibTransceiver::get_vfos (bool for_split) const -{ - if (get_vfo_works_ && rig_->caps->get_vfo) + if (get_vfo_works_ && rig_get_function_ptr (model_, RIG_FUNCTION_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 @@ -685,7 +280,7 @@ std::tuple HamlibTransceiver::get_vfos (bool for_split) const reversed_ = RIG_VFO_B == v; } - else if (!for_split && set_vfo_works_ && rig_->caps->set_vfo && rig_->caps->set_split_vfo) + else if (!for_split && set_vfo_works_ && rig_get_function_ptr (model_, RIG_FUNCTION_SET_VFO) && rig_get_function_ptr (model_, RIG_FUNCTION_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 @@ -710,351 +305,7 @@ std::tuple HamlibTransceiver::get_vfos (bool for_split) const return std::make_tuple (rx_vfo, tx_vfo); } -void HamlibTransceiver::do_frequency (Frequency f, MODE m, bool no_ignore) -{ - CAT_TRACE ("f: " << 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")); - CAT_TRACE ("rig_get_mode mode=" << rig_strrmode (current_mode) << " bw=" << current_width); - - if (new_mode != current_mode) - { - CAT_TRACE ("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 - CAT_TRACE ("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); - } - } -} - -void HamlibTransceiver::do_tx_frequency (Frequency tx, MODE mode, bool no_ignore) -{ - CAT_TRACE ("txf: " << 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. - CAT_TRACE ("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_) - { - CAT_TRACE ("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")); - CAT_TRACE ("rig_get_mode mode=" << rig_strrmode (current_mode) << " bw=" << current_width); - - if (new_mode != current_mode) - { - CAT_TRACE ("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); - CAT_TRACE ("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 - { - CAT_TRACE ("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. - CAT_TRACE ("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 - CAT_TRACE ("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) -{ - CAT_TRACE (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")); - CAT_TRACE ("rig_get_mode mode=" << rig_strrmode (current_mode) << " bw=" << current_width); - - if (new_mode != current_mode) - { - CAT_TRACE ("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")); - CAT_TRACE ("rig_get_mode mode=" << rig_strrmode (current_mode) << " bw=" << current_width); - - if (new_mode != current_mode) - { - CAT_TRACE ("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")); - CAT_TRACE ("rig_get_split_mode mode=" << rig_strrmode (current_mode) << " bw=" << current_width); - - if (new_mode != current_mode) - { - CAT_TRACE ("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::do_poll () -{ - 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 - CAT_TRACE ("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) - { - CAT_TRACE ("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 - { - CAT_TRACE ("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 - CAT_TRACE ("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); - CAT_TRACE ("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); - CAT_TRACE ("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) - { - CAT_TRACE ("rig_get_mode mode=" << rig_strrmode (m) << " bw=" << w); - update_mode (map_mode (m)); - } - else - { - CAT_TRACE ("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")); - CAT_TRACE ("rig_get_ptt PTT=" << p); - update_PTT (!(RIG_PTT_OFF == p)); - } - } -} - -void HamlibTransceiver::do_ptt (bool on) -{ - CAT_TRACE ("PTT: " << on << " " << state () << " reversed=" << reversed_); - if (on) - { - if (RIG_PTT_NONE != rig_->state.pttport.type.ptt) - { - CAT_TRACE ("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) - { - CAT_TRACE ("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) +void HamlibTransceiver::impl::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 @@ -1063,7 +314,7 @@ void HamlibTransceiver::set_conf (char const * item, char const * value) } } -QByteArray HamlibTransceiver::get_conf (char const * item) +QByteArray HamlibTransceiver::impl::get_conf (char const * item) { token_t token = rig_token_lookup (rig_.data (), item); QByteArray value {128, '\0'}; @@ -1074,7 +325,7 @@ QByteArray HamlibTransceiver::get_conf (char const * item) return value; } -auto HamlibTransceiver::map_mode (rmode_t m) const -> MODE +auto HamlibTransceiver::impl::map_mode (rmode_t m) const -> MODE { switch (m) { @@ -1125,7 +376,7 @@ auto HamlibTransceiver::map_mode (rmode_t m) const -> MODE } } -rmode_t HamlibTransceiver::map_mode (MODE mode) const +rmode_t HamlibTransceiver::impl::map_mode (MODE mode) const { switch (mode) { @@ -1144,3 +395,813 @@ rmode_t HamlibTransceiver::map_mode (MODE mode) const } return RIG_MODE_USB; // quieten compiler grumble } + +HamlibTransceiver::HamlibTransceiver (logger_type * logger, + TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port, + QObject * parent) + : PollingTransceiver {logger, 0, parent} + , m_ {logger} +{ + if (!m_->rig_) + { + throw error {tr ("Hamlib initialisation error")}; + } + switch (ptt_type) + { + case TransceiverFactory::PTT_method_VOX: + m_->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 (WIN32) + m_->set_conf ("ptt_pathname", ("\\\\.\\" + ptt_port).toLatin1 ().data ()); +#else + m_->set_conf ("ptt_pathname", ptt_port.toLatin1 ().data ()); +#endif + } + + if (TransceiverFactory::PTT_method_DTR == ptt_type) + { + m_->set_conf ("ptt_type", "DTR"); + } + else + { + m_->set_conf ("ptt_type", "RTS"); + } + } +} + +HamlibTransceiver::HamlibTransceiver (logger_type * logger, + unsigned model_number, + TransceiverFactory::ParameterPack const& params, + QObject * parent) + : PollingTransceiver {logger, params.poll_interval, parent} + , m_ {logger, model_number, params} +{ + if (!m_->rig_) + { + throw error {tr ("Hamlib initialisation error")}; + } + + // m_->rig_->state.obj = this; + + // + // user defined Hamlib settings + // + auto settings_file_name = QStandardPaths::locate (QStandardPaths::AppConfigLocation + , "hamlib_settings.json"); + if (!settings_file_name.isEmpty ()) + { + QFile settings_file {settings_file_name}; + qDebug () << "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)}; + } + qDebug () << "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) + { + m_->set_conf (item.key ().toLocal8Bit ().constData () + , (*item).toVariant ().toString ().toLocal8Bit ().constData ()); + } + } + } + } + + if (!m_->is_dummy_) + { + switch (rig_get_caps_int (m_->model_, RIG_CAPS_PORT_TYPE)) + { + case RIG_PORT_SERIAL: + if (!params.serial_port.isEmpty ()) + { + m_->set_conf ("rig_pathname", params.serial_port.toLatin1 ().data ()); + } + m_->set_conf ("serial_speed", QByteArray::number (params.baud).data ()); + if (params.data_bits != TransceiverFactory::default_data_bits) + { + m_->set_conf ("data_bits", TransceiverFactory::seven_data_bits == params.data_bits ? "7" : "8"); + } + if (params.stop_bits != TransceiverFactory::default_stop_bits) + { + m_->set_conf ("stop_bits", TransceiverFactory::one_stop_bit == params.stop_bits ? "1" : "2"); + } + + switch (params.handshake) + { + case TransceiverFactory::handshake_none: m_->set_conf ("serial_handshake", "None"); break; + case TransceiverFactory::handshake_XonXoff: m_->set_conf ("serial_handshake", "XONXOFF"); break; + case TransceiverFactory::handshake_hardware: m_->set_conf ("serial_handshake", "Hardware"); break; + default: break; + } + + if (params.force_dtr) + { + m_->set_conf ("dtr_state", params.dtr_high ? "ON" : "OFF"); + } + if (params.force_rts) + { + if (TransceiverFactory::handshake_hardware != params.handshake) + { + m_->set_conf ("rts_state", params.rts_high ? "ON" : "OFF"); + } + } + break; + + case RIG_PORT_NETWORK: + if (!params.network_port.isEmpty ()) + { + m_->set_conf ("rig_pathname", params.network_port.toLatin1 ().data ()); + } + break; + + case RIG_PORT_USB: + if (!params.usb_port.isEmpty ()) + { + m_->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: + m_->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.size () + && params.ptt_port != "None" + && (m_->is_dummy_ + || RIG_PORT_SERIAL != rig_get_caps_int (m_->model_, RIG_CAPS_PORT_TYPE) + || params.ptt_port != params.serial_port)) + { +#if defined (WIN32) + m_->set_conf ("ptt_pathname", ("\\\\.\\" + params.ptt_port).toLatin1 ().data ()); +#else + m_->set_conf ("ptt_pathname", params.ptt_port.toLatin1 ().data ()); +#endif + } + + if (TransceiverFactory::PTT_method_DTR == params.ptt_type) + { + m_->set_conf ("ptt_type", "DTR"); + } + else + { + m_->set_conf ("ptt_type", "RTS"); + } + } + + // Make Icom CAT split commands less glitchy + m_->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 (m_->rig_.data (), &frequency_change_callback, this); +} + +HamlibTransceiver::~HamlibTransceiver () = default; + +int HamlibTransceiver::do_start () +{ + CAT_TRACE ("starting: " << rig_get_caps_cptr (m_->model_, RIG_CAPS_MFG_NAME_CPTR) + << ": " << rig_get_caps_cptr (m_->model_, RIG_CAPS_MODEL_NAME_CPTR)); + + m_->error_check (rig_open (m_->rig_.data ()), tr ("opening connection to rig")); + + // reset dynamic state + m_->one_VFO_ = false; + m_->reversed_ = false; + m_->freq_query_works_ = rig_get_function_ptr (m_->model_, RIG_FUNCTION_GET_FREQ); + m_->mode_query_works_ = rig_get_function_ptr (m_->model_, RIG_FUNCTION_GET_MODE); + m_->split_query_works_ = rig_get_function_ptr (m_->model_, RIG_FUNCTION_GET_SPLIT_VFO); + m_->tickle_hamlib_ = false; + m_->get_vfo_works_ = true; + m_->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 (m_->rig_.data (), &vfo); + if (-RIG_ENAVAIL == rc || -RIG_ENIMPL == rc) + { + m_->get_vfo_works_ = false; + // determine if the rig uses single VFO addressing i.e. A/B and + // no get_vfo function + if (m_->rig_->state.vfo_list & RIG_VFO_B) + { + m_->one_VFO_ = true; + } + } + else + { + m_->error_check (rc, "testing getting current VFO"); + } + + if ((WSJT_RIG_NONE_CAN_SPLIT || !m_->is_dummy_) + && rig_get_function_ptr (m_->model_, RIG_FUNCTION_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 (m_->freq_query_works_ + && (!m_->get_vfo_works_ || !rig_get_function_ptr (m_->model_, RIG_FUNCTION_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. + m_->error_check (rig_get_freq (m_->rig_.data (), RIG_VFO_CURR, &f1), tr ("getting current frequency")); + f1 = std::round (f1); + CAT_TRACE ("current frequency=" << f1); + + m_->error_check (rig_get_mode (m_->rig_.data (), RIG_VFO_CURR, &m, &w), tr ("getting current mode")); + CAT_TRACE ("current mode=" << rig_strrmode (m) << " bw=" << w); + + if (!rig_get_function_ptr (m_->model_, RIG_FUNCTION_SET_VFO)) + { + CAT_TRACE ("rig_vfo_op TOGGLE"); + rc = rig_vfo_op (m_->rig_.data (), RIG_VFO_CURR, RIG_OP_TOGGLE); + } + else + { + CAT_TRACE ("rig_set_vfo to other VFO"); + rc = rig_set_vfo (m_->rig_.data (), m_->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 + CAT_TRACE ("rig_vfo_op TOGGLE"); + rc = rig_vfo_op (m_->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 + m_->set_vfo_works_ = false; + m_->one_VFO_ = false; // we do not need single VFO addressing + } + else + { + m_->error_check (rc, tr ("exchanging VFOs")); + } + + if (m_->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 + m_->error_check (rig_get_freq (m_->rig_.data (), RIG_VFO_CURR, &f2), tr ("getting other VFO frequency")); + f2 = std::round (f2); + CAT_TRACE ("rig_get_freq other frequency=" << f2); + + m_->error_check (rig_get_mode (m_->rig_.data (), RIG_VFO_CURR, &mb, &wb), tr ("getting other VFO mode")); + CAT_TRACE ("rig_get_mode other mode=" << rig_strrmode (mb) << " bw=" << wb); + + update_other_frequency (f2); + + if (!rig_get_function_ptr (m_->model_, RIG_FUNCTION_SET_VFO)) + { + CAT_TRACE ("rig_vfo_op TOGGLE"); + m_->error_check (rig_vfo_op (m_->rig_.data (), RIG_VFO_CURR, RIG_OP_TOGGLE), tr ("exchanging VFOs")); + } + else + { + CAT_TRACE ("rig_set_vfo A/MAIN"); + m_->error_check (rig_set_vfo (m_->rig_.data (), m_->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 + { + m_->error_check (rig_get_freq (m_->rig_.data (), RIG_VFO_CURR, &f1), tr ("getting frequency")); + f1 = std::round (f1); + CAT_TRACE ("rig_get_freq frequency=" << f1); + + m_->error_check (rig_get_mode (m_->rig_.data (), RIG_VFO_CURR, &m, &w), tr ("getting mode")); + CAT_TRACE ("rig_get_mode mode=" << rig_strrmode (m) << " bw=" << w); + + update_rx_frequency (f1); + } + } + + // TRACE_CAT ("rig_set_split_vfo split off"); + // m_->error_check (rig_set_split_vfo (m_->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 (m_->get_vfo_works_ && rig_get_function_ptr (m_->model_, RIG_FUNCTION_GET_VFO)) + { + m_->error_check (rig_get_vfo (m_->rig_.data (), &v), tr ("getting current VFO")); // has side effect of establishing current VFO inside hamlib + CAT_TRACE ("rig_get_vfo current VFO=" << rig_strvfo (v)); + } + + m_->reversed_ = RIG_VFO_B == v; + + if (m_->mode_query_works_ && !(rig_get_caps_int (m_->model_, RIG_CAPS_TARGETABLE_VFO) & (RIG_TARGETABLE_MODE | RIG_TARGETABLE_PURE))) + { + if (RIG_OK == rig_get_mode (m_->rig_.data (), RIG_VFO_CURR, &m, &w)) + { + CAT_TRACE ("rig_get_mode current mode=" << rig_strrmode (m) << " bw=" << w); + } + else + { + m_->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 + CAT_TRACE ("rig_get_mode can't do on this rig"); + } + } + } + update_mode (m_->map_mode (m)); + } + + m_->tickle_hamlib_ = true; + + if (m_->is_dummy_ && !m_->ptt_only_ && impl::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 (m_->rig_.data (), RIG_VFO_CURR, impl::dummy_frequency_); + update_rx_frequency (impl::dummy_frequency_); + if (RIG_MODE_NONE != impl::dummy_mode_) + { + rig_set_mode (m_->rig_.data (), RIG_VFO_CURR, impl::dummy_mode_, RIG_PASSBAND_NOCHANGE); + update_mode (m_->map_mode (impl::dummy_mode_)); + } + } + +#if HAVE_HAMLIB_CACHING || HAVE_HAMLIB_OLD_CACHING + // we must disable Hamlib caching because it lies about frequency + // for less than 1 Hz resolution rigs + auto orig_cache_timeout = rig_get_cache_timeout_ms (m_->rig_.data (), HAMLIB_CACHE_ALL); + rig_set_cache_timeout_ms (m_->rig_.data (), HAMLIB_CACHE_ALL, 0); +#endif + + int resolution {0}; + if (m_->freq_query_works_) + { + freq_t current_frequency; + m_->error_check (rig_get_freq (m_->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; + m_->error_check (rig_set_freq (m_->rig_.data (), RIG_VFO_CURR, test_frequency), tr ("setting frequency")); + freq_t new_frequency; + m_->error_check (rig_get_freq (m_->rig_.data (), RIG_VFO_CURR, &new_frequency), tr ("getting current VFO frequency")); + new_frequency = std::round (new_frequency); + switch (static_cast (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; + m_->error_check (rig_set_freq (m_->rig_.data (), RIG_VFO_CURR, test_frequency), tr ("setting frequency")); + m_->error_check (rig_get_freq (m_->rig_.data (), RIG_VFO_CURR, &new_frequency), tr ("getting current VFO frequency")); + if (9 == static_cast (new_frequency - test_frequency)) + { + resolution = 2; // 20Hz rounded + } + } + m_->error_check (rig_set_freq (m_->rig_.data (), RIG_VFO_CURR, current_frequency), tr ("setting frequency")); + } + } + else + { + resolution = -1; // best guess + } + +#if HAVE_HAMLIB_CACHING || HAVE_HAMLIB_OLD_CACHING + // revert Hamlib cache timeout + rig_set_cache_timeout_ms (m_->rig_.data (), HAMLIB_CACHE_ALL, orig_cache_timeout); +#endif + + do_poll (); + + CAT_TRACE ("finished start " << state () << " reversed=" << m_->reversed_ << " resolution=" << resolution); + return resolution; +} + +void HamlibTransceiver::do_stop () +{ + if (m_->is_dummy_ && !m_->ptt_only_) + { + rig_get_freq (m_->rig_.data (), RIG_VFO_CURR, &impl::dummy_frequency_); + impl::dummy_frequency_ = std::round (impl::dummy_frequency_); + if (m_->mode_query_works_) + { + pbwidth_t width; + rig_get_mode (m_->rig_.data (), RIG_VFO_CURR, &impl::dummy_mode_, &width); + } + } + if (m_->rig_) + { + rig_close (m_->rig_.data ()); + } + + CAT_TRACE ("state: " << state () << " reversed=" << m_->reversed_); +} + +void HamlibTransceiver::do_frequency (Frequency f, MODE m, bool no_ignore) +{ + CAT_TRACE ("f: " << f << " mode: " << m << " reversed: " << m_->reversed_); + + // only change when receiving or simplex or direct VFO addressing + // unavailable or forced + if (!state ().ptt () || !state ().split () || !m_->one_VFO_ || no_ignore) + { + // for the 1st time as a band change may cause a recalled mode to be + // set + m_->error_check (rig_set_freq (m_->rig_.data (), RIG_VFO_CURR, f), tr ("setting frequency")); + update_rx_frequency (f); + + if (m_->mode_query_works_ && UNK != m) + { + rmode_t current_mode; + pbwidth_t current_width; + auto new_mode = m_->map_mode (m); + m_->error_check (rig_get_mode (m_->rig_.data (), RIG_VFO_CURR, ¤t_mode, ¤t_width), tr ("getting current VFO mode")); + CAT_TRACE ("rig_get_mode mode=" << rig_strrmode (current_mode) << " bw=" << current_width); + + if (new_mode != current_mode) + { + CAT_TRACE ("rig_set_mode mode=" << rig_strrmode (new_mode)); + m_->error_check (rig_set_mode (m_->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 + m_->error_check (rig_set_freq (m_->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 + CAT_TRACE ("rig_set_mode mode=" << rig_strrmode (new_mode)); + m_->error_check (rig_set_mode (m_->rig_.data (), RIG_VFO_CURR, new_mode, RIG_PASSBAND_NOCHANGE), tr ("setting current VFO mode")); + } + update_mode (m); + } + } +} + +void HamlibTransceiver::do_tx_frequency (Frequency tx, MODE mode, bool no_ignore) +{ + CAT_TRACE ("txf: " << tx << " reversed: " << m_->reversed_); + + if (WSJT_RIG_NONE_CAN_SPLIT || !m_->is_dummy_) // split is meaningless if you can't see it + { + auto split = tx ? RIG_SPLIT_ON : RIG_SPLIT_OFF; + auto vfos = m_->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 (m_->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. + CAT_TRACE ("rig_set_split_vfo split=" << split); + auto rc = rig_set_split_vfo (m_->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. + m_->error_check (rc, tr ("setting/unsetting split mode")); + } + m_->tickle_hamlib_ = false; + update_split (tx); + } + + // just change current when transmitting with single VFO + // addressing + if (state ().ptt () && m_->one_VFO_) + { + CAT_TRACE ("rig_set_split_vfo split=" << split); + m_->error_check (rig_set_split_vfo (m_->rig_.data (), RIG_VFO_CURR, split, tx_vfo), tr ("setting split mode")); + + m_->error_check (rig_set_freq (m_->rig_.data (), RIG_VFO_CURR, tx), tr ("setting frequency")); + + if (UNK != mode && m_->mode_query_works_) + { + rmode_t current_mode; + pbwidth_t current_width; + auto new_mode = m_->map_mode (mode); + m_->error_check (rig_get_mode (m_->rig_.data (), RIG_VFO_CURR, ¤t_mode, ¤t_width), tr ("getting current VFO mode")); + CAT_TRACE ("rig_get_mode mode=" << rig_strrmode (current_mode) << " bw=" << current_width); + + if (new_mode != current_mode) + { + CAT_TRACE ("rig_set_mode mode=" << rig_strrmode (new_mode)); + m_->error_check (rig_set_mode (m_->rig_.data (), RIG_VFO_CURR, new_mode, RIG_PASSBAND_NOCHANGE), tr ("setting current VFO mode")); + } + } + update_other_frequency (tx); + } + else if (!m_->one_VFO_ || no_ignore) // if not single VFO addressing and not forced + { + hamlib_tx_vfo_fixup fixup (m_->rig_.data (), tx_vfo); + if (UNK != mode) + { + auto new_mode = m_->map_mode (mode); + CAT_TRACE ("rig_set_split_freq_mode freq=" << tx + << " mode = " << rig_strrmode (new_mode)); + m_->error_check (rig_set_split_freq_mode (m_->rig_.data (), RIG_VFO_CURR, tx, new_mode, RIG_PASSBAND_NOCHANGE), tr ("setting split TX frequency and mode")); + } + else + { + CAT_TRACE ("rig_set_split_freq freq=" << tx); + m_->error_check (rig_set_split_freq (m_->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. + CAT_TRACE ("rig_set_split_vfo split=" << split); + m_->error_check (rig_set_split_vfo (m_->rig_.data (), RIG_VFO_CURR, split, tx_vfo), tr ("setting split mode")); + update_other_frequency (tx); + update_split (tx); + } + } + else + { + // Disable split + CAT_TRACE ("rig_set_split_vfo split=" << split); + auto rc = rig_set_split_vfo (m_->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. + m_->error_check (rc, tr ("setting/unsetting split mode")); + } + update_other_frequency (tx); + update_split (tx); + } + } +} + +void HamlibTransceiver::do_mode (MODE mode) +{ + CAT_TRACE (mode); + + auto vfos = m_->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 = m_->map_mode (mode); + + // only change when receiving or simplex if direct VFO addressing unavailable + if (!(state ().ptt () && state ().split () && m_->one_VFO_)) + { + m_->error_check (rig_get_mode (m_->rig_.data (), RIG_VFO_CURR, ¤t_mode, ¤t_width), tr ("getting current VFO mode")); + CAT_TRACE ("rig_get_mode mode=" << rig_strrmode (current_mode) << " bw=" << current_width); + + if (new_mode != current_mode) + { + CAT_TRACE ("rig_set_mode mode=" << rig_strrmode (new_mode)); + m_->error_check (rig_set_mode (m_->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 () && m_->one_VFO_) + { + m_->error_check (rig_get_mode (m_->rig_.data (), RIG_VFO_CURR, ¤t_mode, ¤t_width), tr ("getting current VFO mode")); + CAT_TRACE ("rig_get_mode mode=" << rig_strrmode (current_mode) << " bw=" << current_width); + + if (new_mode != current_mode) + { + CAT_TRACE ("rig_set_mode mode=" << rig_strrmode (new_mode)); + m_->error_check (rig_set_mode (m_->rig_.data (), RIG_VFO_CURR, new_mode, RIG_PASSBAND_NOCHANGE), tr ("setting current VFO mode")); + } + } + else if (state ().split () && !m_->one_VFO_) + { + m_->error_check (rig_get_split_mode (m_->rig_.data (), RIG_VFO_CURR, ¤t_mode, ¤t_width), tr ("getting split TX VFO mode")); + CAT_TRACE ("rig_get_split_mode mode=" << rig_strrmode (current_mode) << " bw=" << current_width); + + if (new_mode != current_mode) + { + CAT_TRACE ("rig_set_split_mode mode=" << rig_strrmode (new_mode)); + hamlib_tx_vfo_fixup fixup (m_->rig_.data (), tx_vfo); + m_->error_check (rig_set_split_mode (m_->rig_.data (), RIG_VFO_CURR, new_mode, RIG_PASSBAND_NOCHANGE), tr ("setting split TX VFO mode")); + } + } + update_mode (mode); +} + +void HamlibTransceiver::do_poll () +{ + freq_t f; + rmode_t m; + pbwidth_t w; + split_t s; + + if (m_->get_vfo_works_ && rig_get_function_ptr (m_->model_, RIG_FUNCTION_GET_VFO)) + { + vfo_t v; + m_->error_check (rig_get_vfo (m_->rig_.data (), &v), tr ("getting current VFO")); // has side effect of establishing current VFO inside hamlib + CAT_TRACE ("VFO=" << rig_strvfo (v)); + m_->reversed_ = RIG_VFO_B == v; + } + + if ((WSJT_RIG_NONE_CAN_SPLIT || !m_->is_dummy_) + && rig_get_function_ptr (m_->model_, RIG_FUNCTION_GET_SPLIT_VFO) && m_->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 (m_->rig_.data (), RIG_VFO_CURR, &s, &v); + if (-RIG_OK == rc && RIG_SPLIT_ON == s) + { + CAT_TRACE ("rig_get_split_vfo split=" << s << " VFO=" << rig_strvfo (v)); + update_split (true); + // if (RIG_VFO_A == v) + // { + // m_->reversed_ = true; // not sure if this helps us here + // } + } + else if (-RIG_OK == rc) // not split + { + CAT_TRACE ("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 + CAT_TRACE ("rig_get_split_vfo can't do on this rig"); + // just report how we see it based on prior commands + m_->split_query_works_ = false; + } + } + + if (m_->freq_query_works_) + { + // only read if possible and when receiving or simplex + if (!state ().ptt () || !state ().split ()) + { + m_->error_check (rig_get_freq (m_->rig_.data (), RIG_VFO_CURR, &f), tr ("getting current VFO frequency")); + f = std::round (f); + CAT_TRACE ("rig_get_freq frequency=" << f); + update_rx_frequency (f); + } + + if ((WSJT_RIG_NONE_CAN_SPLIT || !m_->is_dummy_) + && state ().split () + && (rig_get_caps_int (m_->model_, RIG_CAPS_TARGETABLE_VFO) & (RIG_TARGETABLE_FREQ | RIG_TARGETABLE_PURE)) + && !m_->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 + m_->error_check (rig_get_freq (m_->rig_.data () + , m_->reversed_ + ? (m_->rig_->state.vfo_list & RIG_VFO_A ? RIG_VFO_A : RIG_VFO_MAIN) + : (m_->rig_->state.vfo_list & RIG_VFO_B ? RIG_VFO_B : RIG_VFO_SUB) + , &f), tr ("getting other VFO frequency")); + f = std::round (f); + CAT_TRACE ("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 ()) + && m_->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 (m_->rig_.data (), RIG_VFO_CURR, &m, &w); + if (RIG_OK == rc) + { + CAT_TRACE ("rig_get_mode mode=" << rig_strrmode (m) << " bw=" << w); + update_mode (m_->map_mode (m)); + } + else + { + CAT_TRACE ("rig_get_mode mode failed with rc: " << rc << " ignoring"); + } + } + + if (RIG_PTT_NONE != m_->rig_->state.pttport.type.ptt && rig_get_function_ptr (m_->model_, RIG_FUNCTION_GET_PTT)) + { + ptt_t p; + auto rc = rig_get_ptt (m_->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 + { + m_->error_check (rc, tr ("getting PTT state")); + CAT_TRACE ("rig_get_ptt PTT=" << p); + update_PTT (!(RIG_PTT_OFF == p)); + } + } +} + +void HamlibTransceiver::do_ptt (bool on) +{ + CAT_TRACE ("PTT: " << on << " " << state () << " reversed=" << m_->reversed_); + if (on) + { + if (RIG_PTT_NONE != m_->rig_->state.pttport.type.ptt) + { + CAT_TRACE ("rig_set_ptt PTT=true"); + auto ptt_type = rig_get_caps_int (m_->model_, RIG_CAPS_PTT_TYPE); + m_->error_check (rig_set_ptt (m_->rig_.data (), RIG_VFO_CURR + , RIG_PTT_RIG_MICDATA == ptt_type && m_->back_ptt_port_ + ? RIG_PTT_ON_DATA : RIG_PTT_ON), tr ("setting PTT on")); + } + } + else + { + if (RIG_PTT_NONE != m_->rig_->state.pttport.type.ptt) + { + CAT_TRACE ("rig_set_ptt PTT=false"); + m_->error_check (rig_set_ptt (m_->rig_.data (), RIG_VFO_CURR, RIG_PTT_OFF), tr ("setting PTT off")); + } + } + + update_PTT (on); +} diff --git a/Transceiver/HamlibTransceiver.hpp b/Transceiver/HamlibTransceiver.hpp index e1b5dece4..9548fe65c 100644 --- a/Transceiver/HamlibTransceiver.hpp +++ b/Transceiver/HamlibTransceiver.hpp @@ -1,14 +1,11 @@ #ifndef HAMLIB_TRANSCEIVER_HPP_ #define HAMLIB_TRANSCEIVER_HPP_ -#include - #include -#include - #include "TransceiverFactory.hpp" #include "PollingTransceiver.hpp" +#include "pimpl_h.hpp" // hamlib transceiver and PTT mostly delegated directly to hamlib Rig class class HamlibTransceiver final @@ -20,10 +17,11 @@ public: static void register_transceivers (logger_type *, TransceiverFactory::Transceivers *); static void unregister_transceivers (); - explicit HamlibTransceiver (logger_type *, int model_number, TransceiverFactory::ParameterPack const&, + explicit HamlibTransceiver (logger_type *, unsigned model_number, TransceiverFactory::ParameterPack const&, QObject * parent = nullptr); explicit HamlibTransceiver (logger_type *, TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port, QObject * parent = nullptr); + ~HamlibTransceiver (); private: int do_start () override; @@ -35,38 +33,8 @@ private: void do_poll () override; - void error_check (int ret_code, QString const& doing) const; - void set_conf (char const * item, char const * value); - QByteArray get_conf (char const * item); - Transceiver::MODE map_mode (rmode_t) const; - rmode_t map_mode (Transceiver::MODE mode) const; - std::tuple get_vfos (bool for_split) const; - - struct RIGDeleter {static void cleanup (RIG *);}; - QScopedPointer rig_; - - bool ptt_only_; // we can use a dummy device for PTT - bool back_ptt_port_; - bool one_VFO_; - bool is_dummy_; - - // these are saved on destruction so we can start new instances - // where the last one left off - static freq_t dummy_frequency_; - static rmode_t dummy_mode_; - - bool mutable reversed_; - - bool freq_query_works_; - bool mode_query_works_; - bool split_query_works_; - bool tickle_hamlib_; // Hamlib requires a - // rig_set_split_vfo() call to - // establish the Tx VFO - bool get_vfo_works_; // Net rigctl promises what it can't deliver - bool set_vfo_works_; // More rigctl promises which it can't deliver - - static int debug_callback (enum rig_debug_level_e level, rig_ptr_t arg, char const * format, va_list ap); + class impl; + pimpl m_; }; #endif diff --git a/Transceiver/OmniRigTransceiver.cpp b/Transceiver/OmniRigTransceiver.cpp index 236bdcf84..da62c0c29 100644 --- a/Transceiver/OmniRigTransceiver.cpp +++ b/Transceiver/OmniRigTransceiver.cpp @@ -76,7 +76,7 @@ OmniRig::RigParamX OmniRigTransceiver::map_mode (MODE mode) void OmniRigTransceiver::register_transceivers (logger_type *, TransceiverFactory::Transceivers * registry, - int id1, int id2) + unsigned id1, unsigned id2) { (*registry)[OmniRig_transceiver_one_name] = TransceiverFactory::Capabilities { id1 diff --git a/Transceiver/OmniRigTransceiver.hpp b/Transceiver/OmniRigTransceiver.hpp index e447d21f1..1e8865649 100644 --- a/Transceiver/OmniRigTransceiver.hpp +++ b/Transceiver/OmniRigTransceiver.hpp @@ -26,7 +26,7 @@ class OmniRigTransceiver final Q_OBJECT; public: - static void register_transceivers (logger_type *, TransceiverFactory::Transceivers *, int id1, int id2); + static void register_transceivers (logger_type *, TransceiverFactory::Transceivers *, unsigned id1, unsigned id2); enum RigNumber {One = 1, Two}; diff --git a/Transceiver/TransceiverFactory.cpp b/Transceiver/TransceiverFactory.cpp index 7cc62bb4a..ac1af5eb7 100644 --- a/Transceiver/TransceiverFactory.cpp +++ b/Transceiver/TransceiverFactory.cpp @@ -23,7 +23,7 @@ namespace { enum // supported non-hamlib radio interfaces { - NonHamlibBaseId = 9899 + NonHamlibBaseId = 99899 , CommanderId , HRDId , OmniRigOneId diff --git a/Transceiver/TransceiverFactory.hpp b/Transceiver/TransceiverFactory.hpp index adcaaca2e..70e5fe2ca 100644 --- a/Transceiver/TransceiverFactory.hpp +++ b/Transceiver/TransceiverFactory.hpp @@ -35,7 +35,7 @@ public: { enum PortType {none, serial, network, usb}; - explicit Capabilities (int model_number = 0 + explicit Capabilities (unsigned model_number = 0 , PortType port_type = none , bool has_CAT_PTT = false , bool has_CAT_PTT_mic_data = false @@ -50,7 +50,7 @@ public: { } - int model_number_; + unsigned model_number_; PortType port_type_; bool has_CAT_PTT_; bool has_CAT_PTT_mic_data_;