diff --git a/Configuration.cpp b/Configuration.cpp index a14157d62..18dea9bc3 100644 --- a/Configuration.cpp +++ b/Configuration.cpp @@ -372,7 +372,7 @@ private: void set_application_font (QFont const&); void initialize_models (); - bool open_rig (); + bool open_rig (bool force = false); //bool set_mode (); void close_rig (); TransceiverFactory::ParameterPack gather_rig_data (); @@ -416,8 +416,8 @@ private: Q_SLOT void insert_frequency (); Q_SLOT void delete_stations (); Q_SLOT void insert_station (); - Q_SLOT void handle_transceiver_update (TransceiverState); - Q_SLOT void handle_transceiver_failure (QString reason); + Q_SLOT void handle_transceiver_update (TransceiverState const&, unsigned sequence_number); + Q_SLOT void handle_transceiver_failure (QString const& reason); Q_SLOT void on_pbCQmsg_clicked(); Q_SLOT void on_pbMyCall_clicked(); Q_SLOT void on_pbTxMsg_clicked(); @@ -425,17 +425,14 @@ private: Q_SLOT void on_pbNewCall_clicked(); // typenames used as arguments must match registered type names :( - Q_SIGNAL void start_transceiver () const; - Q_SIGNAL void stop_transceiver (bool reset_split) const; - Q_SIGNAL void frequency (Frequency rx, Transceiver::MODE) const; - Q_SIGNAL void tx_frequency (Frequency tx, bool rationalize_mode) const; - Q_SIGNAL void mode (Transceiver::MODE, bool rationalize) const; - Q_SIGNAL void ptt (bool) const; - Q_SIGNAL void sync (bool force_signal) const; + Q_SIGNAL void start_transceiver (unsigned seqeunce_number) const; + Q_SIGNAL void set_transceiver (Transceiver::TransceiverState const&, + unsigned sequence_number) const; + Q_SIGNAL void stop_transceiver () const; Configuration * const self_; // back pointer to public interface - QThread transceiver_thread_; + QThread * transceiver_thread_; TransceiverFactory transceiver_factory_; QList rig_connections_; @@ -473,6 +470,7 @@ private: StationList stations_; StationList next_stations_; FrequencyDelta current_offset_; + FrequencyDelta current_tx_offset_; QAction * frequency_delete_action_; QAction * frequency_insert_action_; @@ -489,24 +487,10 @@ private: bool have_rig_; bool rig_changed_; TransceiverState cached_rig_state_; + int rig_resolution_; // see Transceiver::resolution signal double frequency_calibration_intercept_; double frequency_calibration_slope_ppm_; - - // the following members are required to get the rig into split the - // first time monitor or tune or Tx occur - bool setup_split_; - Frequency required_tx_frequency_; // this is needed because DX Lab - // Suite Commander in particular - // insists on reporting out of - // date state after successful - // commands to change the rig - // state :( Zero is valid and it - // means that we don't know the Tx - // frequency rather than implying - // no split. - - bool enforce_mode_and_split_; - bool reset_split_; + unsigned transceiver_command_number_; // configuration fields that we publish QString my_callsign_; @@ -588,6 +572,7 @@ QDir Configuration::data_dir () const {return m_->data_dir_;} QDir Configuration::temp_dir () const {return m_->temp_dir_;} int Configuration::exec () {return m_->exec ();} +bool Configuration::is_active () const {return m_->isVisible ();} QAudioDeviceInfo const& Configuration::audio_input_device () const {return m_->audio_input_device_;} AudioDevice::Channel Configuration::audio_input_channel () const {return m_->audio_input_channel_;} @@ -668,13 +653,18 @@ bool Configuration::transceiver_online (bool open_if_closed) return m_->have_rig (open_if_closed); } +int Configuration::transceiver_resolution () const +{ + return m_->rig_resolution_; +} + void Configuration::transceiver_offline () { #if WSJT_TRACE_CAT qDebug () << "Configuration::transceiver_offline:" << m_->cached_rig_state_; #endif - return m_->close_rig (); + m_->close_rig (); } void Configuration::transceiver_frequency (Frequency f) @@ -691,8 +681,6 @@ void Configuration::transceiver_tx_frequency (Frequency f) qDebug () << "Configuration::transceiver_tx_frequency:" << f << m_->cached_rig_state_; #endif - m_->setup_split_ = true; - m_->required_tx_frequency_ = f; m_->transceiver_tx_frequency (f); } @@ -720,10 +708,11 @@ void Configuration::sync_transceiver (bool force_signal, bool enforce_mode_and_s qDebug () << "Configuration::sync_transceiver: force signal:" << force_signal << "enforce_mode_and_split:" << enforce_mode_and_split << m_->cached_rig_state_; #endif - m_->enforce_mode_and_split_ = enforce_mode_and_split; - m_->setup_split_ = enforce_mode_and_split; - m_->required_tx_frequency_ = 0; m_->sync_transceiver (force_signal); + if (!enforce_mode_and_split) + { + m_->transceiver_tx_frequency (0); + } } Configuration::impl::impl (Configuration * self, QSettings * settings, QWidget * parent) @@ -738,15 +727,14 @@ Configuration::impl::impl (Configuration * self, QSettings * settings, QWidget * , stations_ {&bands_} , next_stations_ {&bands_} , current_offset_ {0} + , current_tx_offset_ {0} , frequency_dialog_ {new FrequencyDialog {&modes_, this}} , station_dialog_ {new StationDialog {&next_stations_, &bands_, this}} , rig_active_ {false} , have_rig_ {false} , rig_changed_ {false} - // , ptt_state_ {false} - , setup_split_ {false} - , required_tx_frequency_ {0} - , enforce_mode_and_split_ {false} + , rig_resolution_ {0} + , transceiver_command_number_ {0} , degrade_ {0.} // initialize to zero each run, not // saved in settings , default_audio_input_device_selected_ {false} @@ -1018,18 +1006,15 @@ Configuration::impl::impl (Configuration * self, QSettings * settings, QWidget * enumerate_rigs (); initialize_models (); - transceiver_thread_.start (); + transceiver_thread_ = new QThread {this}; + transceiver_thread_->start (); } Configuration::impl::~impl () { + transceiver_thread_->quit (); + transceiver_thread_->wait (); write_settings (); - - close_rig (); - - transceiver_thread_.quit (); - transceiver_thread_.wait (); - temp_dir_.removeRecursively (); // clean up temp files } @@ -1092,7 +1077,6 @@ void Configuration::impl::initialize_models () ui_->rig_combo_box->setCurrentText (rig_params_.rig_name); ui_->TX_mode_button_group->button (data_mode_)->setChecked (true); ui_->split_mode_button_group->button (rig_params_.split_mode)->setChecked (true); - ui_->reset_split_check_box->setChecked (reset_split_); ui_->CAT_serial_baud_combo_box->setCurrentText (QString::number (rig_params_.baud)); ui_->CAT_data_bits_button_group->button (rig_params_.data_bits)->setChecked (true); ui_->CAT_stop_bits_button_group->button (rig_params_.stop_bits)->setChecked (true); @@ -1318,8 +1302,8 @@ void Configuration::impl::read_settings () EMEonly_ = settings_->value("EMEonly",false).toBool (); offsetRxFreq_ = settings_->value("OffsetRx",false).toBool(); rig_params_.poll_interval = settings_->value ("Polling", 0).toInt (); + rig_params_.set_rig_mode = data_mode_ != data_mode_none; rig_params_.split_mode = settings_->value ("SplitMode", QVariant::fromValue (TransceiverFactory::split_mode_none)).value (); - reset_split_ = settings_->value ("ResetSplitOnExit", true).toBool (); udp_server_name_ = settings_->value ("UDPServer", "127.0.0.1").toString (); udp_server_port_ = settings_->value ("UDPServerPort", 2237).toUInt (); accept_udp_requests_ = settings_->value ("AcceptUDPRequests", false).toBool (); @@ -1407,7 +1391,6 @@ void Configuration::impl::write_settings () settings_->setValue ("TXAudioSource", QVariant::fromValue (rig_params_.audio_source)); settings_->setValue ("Polling", rig_params_.poll_interval); settings_->setValue ("SplitMode", QVariant::fromValue (rig_params_.split_mode)); - settings_->setValue ("ResetSplitOnExit", reset_split_); settings_->setValue ("VHFUHF", enable_VHF_features_); settings_->setValue ("Decode52", decode_at_52s_); settings_->setValue ("SingleDecode", single_decode_); @@ -1644,6 +1627,7 @@ TransceiverFactory::ParameterPack Configuration::impl::gather_rig_data () result.ptt_type = static_cast (ui_->PTT_method_button_group->checkedId ()); result.ptt_port = ui_->PTT_port_combo_box->currentText (); result.audio_source = static_cast (ui_->TX_audio_source_button_group->checkedId ()); + result.set_rig_mode = static_cast (ui_->TX_mode_button_group->checkedId ()) != data_mode_none; result.split_mode = static_cast (ui_->split_mode_button_group->checkedId ()); return result; } @@ -1815,7 +1799,6 @@ void Configuration::impl::accept () NDxG_ = ui_->cbNDxG->isChecked (); NN_ = ui_->cbNN->isChecked (); EMEonly_ = ui_->cbEMEonly->isChecked (); - reset_split_ = ui_->reset_split_check_box->isChecked (); offsetRxFreq_ = ui_->offset_Rx_freq_check_box->isChecked(); frequency_calibration_intercept_ = ui_->calibration_intercept_spin_box->value (); @@ -2003,8 +1986,6 @@ void Configuration::impl::on_CAT_poll_interval_spin_box_valueChanged (int /* val void Configuration::impl::on_split_mode_button_group_buttonClicked (int /* id */) { - setup_split_ = true; - required_tx_frequency_ = 0; set_rig_invariants (); } @@ -2016,9 +1997,9 @@ void Configuration::impl::on_test_CAT_push_button_clicked () } ui_->test_CAT_push_button->setStyleSheet ({}); - if (open_rig ()) + if (open_rig (true)) { - Q_EMIT sync (true); + //Q_EMIT sync (true); } set_rig_invariants (); @@ -2218,52 +2199,58 @@ bool Configuration::impl::have_rig (bool open_if_closed) return rig_active_; } -bool Configuration::impl::open_rig () +bool Configuration::impl::open_rig (bool force) { auto result = false; auto const rig_data = gather_rig_data (); - if (!rig_active_ || rig_data != saved_rig_params_) + if (force || !rig_active_ || rig_data != saved_rig_params_) { try { close_rig (); // create a new Transceiver object - auto rig = transceiver_factory_.create (rig_data, &transceiver_thread_); + auto rig = transceiver_factory_.create (rig_data, transceiver_thread_); + cached_rig_state_ = Transceiver::TransceiverState {}; // hook up Configuration transceiver control signals to Transceiver slots // // these connections cross the thread boundary - rig_connections_ << connect (this, &Configuration::impl::frequency, rig.get (), &Transceiver::frequency); - rig_connections_ << connect (this, &Configuration::impl::tx_frequency, rig.get (), &Transceiver::tx_frequency); - rig_connections_ << connect (this, &Configuration::impl::mode, rig.get (), &Transceiver::mode); - rig_connections_ << connect (this, &Configuration::impl::ptt, rig.get (), &Transceiver::ptt); - rig_connections_ << connect (this, &Configuration::impl::sync, rig.get (), &Transceiver::sync); + rig_connections_ << connect (this, &Configuration::impl::set_transceiver, + rig.get (), &Transceiver::set); // hook up Transceiver signals to Configuration signals // // these connections cross the thread boundary - connect (rig.get (), &Transceiver::update, this, &Configuration::impl::handle_transceiver_update); - connect (rig.get (), &Transceiver::failure, this, &Configuration::impl::handle_transceiver_failure); + rig_connections_ << connect (rig.get (), &Transceiver::resolution, this, [=] (int resolution) { + rig_resolution_ = resolution; + }); + rig_connections_ << connect (rig.get (), &Transceiver::update, this, &Configuration::impl::handle_transceiver_update); + rig_connections_ << connect (rig.get (), &Transceiver::failure, this, &Configuration::impl::handle_transceiver_failure); // setup thread safe startup and close down semantics rig_connections_ << connect (this, &Configuration::impl::start_transceiver, rig.get (), &Transceiver::start); - connect (this, &Configuration::impl::stop_transceiver, rig.get (), &Transceiver::stop); + rig_connections_ << connect (this, &Configuration::impl::stop_transceiver, rig.get (), &Transceiver::stop); auto p = rig.release (); // take ownership - // schedule eventual destruction + + // schedule destruction on thread quit + connect (transceiver_thread_, &QThread::finished, p, &QObject::deleteLater); + + // schedule eventual destruction for non-closing situations // - // must be queued connection to avoid premature self-immolation - // since finished signal is going to be emitted from the object - // that will get destroyed in its own stop slot i.e. a same - // thread signal to slot connection which by default will be - // reduced to a method function call. + // must be queued connection to avoid premature + // self-immolation since finished signal is going to be + // emitted from the object that will get destroyed in its + // own stop slot i.e. a same thread signal to slot + // connection which by default will be reduced to a method + // function call. connect (p, &Transceiver::finished, p, &Transceiver::deleteLater, Qt::QueuedConnection); ui_->test_CAT_push_button->setStyleSheet ({}); rig_active_ = true; - Q_EMIT start_transceiver (); // start rig on its thread + Q_EMIT start_transceiver (++transceiver_command_number_); // start rig on its thread result = true; } catch (std::exception const& e) @@ -2284,98 +2271,85 @@ bool Configuration::impl::open_rig () void Configuration::impl::transceiver_frequency (Frequency f) { Transceiver::MODE mode {Transceiver::UNK}; - if (ui_->mode_group_box->isEnabled ()) + switch (data_mode_) { - switch (static_cast (ui_->TX_mode_button_group->checkedId ())) - { - case data_mode_USB: mode = Transceiver::USB; break; - case data_mode_data: mode = Transceiver::DIG_U; break; - case data_mode_none: break; - } + case data_mode_USB: mode = Transceiver::USB; break; + case data_mode_data: mode = Transceiver::DIG_U; break; + case data_mode_none: break; } - if (cached_rig_state_.frequency () != f - || (mode != Transceiver::UNK && mode != cached_rig_state_.mode ())) - { - cached_rig_state_.frequency (f); - cached_rig_state_.mode (mode); + cached_rig_state_.online (true); // we want the rig online + cached_rig_state_.mode (mode); - // apply any offset & calibration - // we store the offset here for use in feedback from the rig, we - // cannot absolutely determine if the offset should apply but by - // simply picking an offset when the Rx frequency is set and - // sticking to it we get sane behaviour - current_offset_ = stations_.offset (f); - Q_EMIT frequency (apply_calibration (f + current_offset_), mode); - } + // apply any offset & calibration + // we store the offset here for use in feedback from the rig, we + // cannot absolutely determine if the offset should apply but by + // simply picking an offset when the Rx frequency is set and + // sticking to it we get sane behaviour + current_offset_ = stations_.offset (f); + cached_rig_state_.frequency (apply_calibration (f + current_offset_)); + + Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_); } void Configuration::impl::transceiver_tx_frequency (Frequency f) { - if (/* set_mode () || */ cached_rig_state_.tx_frequency () != f || !cached_rig_state_.compare_split (!!f)) + cached_rig_state_.online (true); // we want the rig online + cached_rig_state_.split (f); + cached_rig_state_.tx_frequency (f); + + // lookup offset for tx and apply calibration + if (f) { - cached_rig_state_.split (f); - cached_rig_state_.tx_frequency (f); - - // lookup offset and apply calibration if we are in split mode - if (cached_rig_state_.split ()) - { - // apply and offset and calibration - // we store the offset here for use in feedback from the - // rig, we cannot absolutely determine if the offset should - // apply but by simply picking an offset when the Rx - // frequency is set and sticking to it we get sane behaviour - current_offset_ = stations_.offset (f); - f = apply_calibration (f + current_offset_); - } - - - // Rationalise TX VFO mode if we ask for split and are - // responsible for mode. - Q_EMIT tx_frequency (f, cached_rig_state_.split () - && ui_->mode_group_box->isEnabled () - && data_mode_none != data_mode_); + // apply and offset and calibration + // we store the offset here for use in feedback from the + // rig, we cannot absolutely determine if the offset should + // apply but by simply picking an offset when the Rx + // frequency is set and sticking to it we get sane behaviour + current_tx_offset_ = stations_.offset (f); + cached_rig_state_.tx_frequency (apply_calibration (f + current_tx_offset_)); } + + Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_); } void Configuration::impl::transceiver_mode (MODE m) { - if (cached_rig_state_.mode () != m) - { - cached_rig_state_.mode (m); - - // Rationalise mode if we are responsible for it and in split mode. - Q_EMIT mode (m, cached_rig_state_.split () - && ui_->mode_group_box->isEnabled () - && data_mode_none != data_mode_); - } + cached_rig_state_.online (true); // we want the rig online + cached_rig_state_.mode (m); + Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_); } void Configuration::impl::transceiver_ptt (bool on) { + cached_rig_state_.online (true); // we want the rig online cached_rig_state_.ptt (on); - - // pass this on regardless of cache - Q_EMIT ptt (on); + Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_); } -void Configuration::impl::sync_transceiver (bool force_signal) +void Configuration::impl::sync_transceiver (bool /*force_signal*/) { // pass this on as cache must be ignored - Q_EMIT sync (force_signal); + // Q_EMIT sync (force_signal); } -void Configuration::impl::handle_transceiver_update (TransceiverState state) +void Configuration::impl::handle_transceiver_update (TransceiverState const& state, + unsigned sequence_number) { #if WSJT_TRACE_CAT - qDebug () << "Configuration::handle_transceiver_update: Transceiver State:" << state; + qDebug () << "Configuration::handle_transceiver_update: Transceiver State #:" << sequence_number << state; #endif + // only follow rig on some information, ignore other stuff + cached_rig_state_.online (state.online ()); + cached_rig_state_.frequency (state.frequency ()); + cached_rig_state_.mode (state.mode ()); + cached_rig_state_.split (state.split ()); + if (state.online ()) { ui_->test_PTT_push_button->setChecked (state.ptt ()); - TransceiverFactory::SplitMode split_mode_selected; if (isVisible ()) { ui_->test_CAT_push_button->setStyleSheet ("QPushButton {background-color: green;}"); @@ -2386,80 +2360,37 @@ void Configuration::impl::handle_transceiver_update (TransceiverState state) ui_->test_PTT_push_button->setEnabled ((TransceiverFactory::PTT_method_CAT == ptt_method && CAT_PTT_enabled) || TransceiverFactory::PTT_method_DTR == ptt_method || TransceiverFactory::PTT_method_RTS == ptt_method); - - - // Follow the setup choice. - split_mode_selected = static_cast (ui_->split_mode_button_group->checkedId ()); } - else - { - // Follow the rig unless configuration has been changed. - split_mode_selected = static_cast (rig_params_.split_mode); - - if (enforce_mode_and_split_) - { - if (TransceiverFactory::basic_transceiver_name_ != ui_->rig_combo_box->currentText () - && ((TransceiverFactory::split_mode_none != split_mode_selected) != state.split ())) - { - if (!setup_split_) - { - // Rig split mode isn't consistent with settings so - // change settings. - // - // For rigs that can't report split mode changes - // (e.g.Icom) this is going to confuse operators, but - // what can we do if they change the rig? - // auto split_mode = state.split () ? TransceiverFactory::split_mode_rig : TransceiverFactory::split_mode_none; - // rig_params_.split_mode = split_mode; - // ui_->split_mode_button_group->button (split_mode)->setChecked (true); - // split_mode_selected = split_mode; - setup_split_ = true; - required_tx_frequency_ = 0; - - // Q_EMIT self_->transceiver_failure (tr ("Rig - // split mode setting not consistent with WSJT-X - // settings. Changing WSJT-X settings for - // you.")); - if (cached_rig_state_.split () != state.split ()) - { - Q_EMIT self_->transceiver_failure (tr ("Rig split mode setting not consistent with WSJT-X settings.")); - } - } - } - } - } - - // One time rig setup split - if (setup_split_ && cached_rig_state_.split () != state.split ()) - { - Q_EMIT tx_frequency (TransceiverFactory::split_mode_none != split_mode_selected && cached_rig_state_.split () - ? (required_tx_frequency_ ? required_tx_frequency_ : state.tx_frequency ()) - : 0, true); - } - setup_split_ = false; - required_tx_frequency_ = 0; } else { close_rig (); } - // take off calibration & offset - state.frequency (remove_calibration (state.frequency ()) - current_offset_); - - if (state.tx_frequency ()) + // pass on to clients if current command is processed + if (sequence_number == transceiver_command_number_) { + TransceiverState reported_state {state}; // take off calibration & offset - state.tx_frequency (remove_calibration (state.tx_frequency ()) - current_offset_); + reported_state.frequency (remove_calibration (reported_state.frequency ()) - current_offset_); + + if (reported_state.tx_frequency ()) + { + // take off calibration & offset + reported_state.tx_frequency (remove_calibration (reported_state.tx_frequency ()) - current_tx_offset_); + } + + Q_EMIT self_->transceiver_update (reported_state); + } + else + { +#if WSJT_TRACE_CAT + qDebug () << "Configuration::handle_transceiver_update: skipping because of command #:" << transceiver_command_number_; +#endif } - - cached_rig_state_ = state; - - // pass on to clients - Q_EMIT self_->transceiver_update (cached_rig_state_); } -void Configuration::impl::handle_transceiver_failure (QString reason) +void Configuration::impl::handle_transceiver_failure (QString const& reason) { #if WSJT_TRACE_CAT qDebug () << "Configuration::handle_transceiver_failure: reason:" << reason; @@ -2487,7 +2418,7 @@ void Configuration::impl::close_rig () if (rig_active_) { ui_->test_CAT_push_button->setStyleSheet ("QPushButton {background-color: red;}"); - Q_EMIT stop_transceiver (reset_split_); + Q_EMIT stop_transceiver (); Q_FOREACH (auto const& connection, rig_connections_) { disconnect (connection); diff --git a/Configuration.hpp b/Configuration.hpp index 1f7a7fc8d..c9311e875 100644 --- a/Configuration.hpp +++ b/Configuration.hpp @@ -72,6 +72,7 @@ public: ~Configuration (); int exec (); + bool is_active () const; QDir temp_dir () const; QDir doc_dir () const; @@ -155,6 +156,15 @@ public: // open_if_closed parameter is passed as true. bool transceiver_online (bool open_if_closed = false); + // Frequency resolution of the rig + // + // 0 - 1Hz + // 1 - 10Hz rounded + // -1 - 10Hz truncated + // 2 - 100Hz rounded + // -2 - 100Hz truncated + int transceiver_resolution () const; + // Close down connection to rig. void transceiver_offline (); @@ -206,7 +216,7 @@ public: // // signals a change in one of the TransceiverState members - Q_SIGNAL void transceiver_update (Transceiver::TransceiverState) const; + Q_SIGNAL void transceiver_update (Transceiver::TransceiverState const&) const; // Signals a failure of a control rig CAT or PTT connection. // @@ -214,7 +224,7 @@ public: // connections are closed automatically. The connections can be // re-established with a call to transceiver_online(true) assuming // the fault condition has been rectified or is transient. - Q_SIGNAL void transceiver_failure (QString reason) const; + Q_SIGNAL void transceiver_failure (QString const& reason) const; private: class impl; diff --git a/Configuration.ui b/Configuration.ui index 87b927ebf..f7d084115 100644 --- a/Configuration.ui +++ b/Configuration.ui @@ -1045,6 +1045,9 @@ this setting allows you to select which audio input will be used + + <html><head/><body><p>Interval to poll rig for status. Set to zero for no polling. Longer intervals will mean that changes to the rig will take longer to be detected.</p><p>If you set this to zero then spots may be uploaded with an incorrect frequency.</p></body></html> + s @@ -1127,13 +1130,10 @@ radio interface behave as expected. Split Operation - - + + - None - - - true + Fake It split_mode_button_group @@ -1150,27 +1150,17 @@ radio interface behave as expected. - - + + - Fake It - - - split_mode_button_group - - - - - - - <html><head/><body><p>Reset split on program exit</p></body></html> - - - Reset split on exit + None true + + split_mode_button_group + @@ -1388,12 +1378,6 @@ both here. - - - 0 - 56 - - AzEl Directory @@ -1403,6 +1387,9 @@ both here. Location: + + azel_path_select_push_button + @@ -2476,8 +2463,8 @@ soundcard changes accept() - 254 - 554 + 272 + 606 157 @@ -2492,8 +2479,8 @@ soundcard changes reject() - 322 - 554 + 340 + 606 286 @@ -2508,7 +2495,7 @@ soundcard changes setFocus() - 566 + 404 62 diff --git a/DXLabSuiteCommanderTransceiver.cpp b/DXLabSuiteCommanderTransceiver.cpp index 7ef7bb4ce..4205c0ce1 100644 --- a/DXLabSuiteCommanderTransceiver.cpp +++ b/DXLabSuiteCommanderTransceiver.cpp @@ -39,8 +39,10 @@ void DXLabSuiteCommanderTransceiver::register_transceivers (TransceiverFactory:: (*registry)[commander_transceiver_name] = TransceiverFactory::Capabilities {id, TransceiverFactory::Capabilities::network, true}; } -DXLabSuiteCommanderTransceiver::DXLabSuiteCommanderTransceiver (std::unique_ptr wrapped, QString const& address, bool use_for_ptt, int poll_interval) - : PollingTransceiver {poll_interval} +DXLabSuiteCommanderTransceiver::DXLabSuiteCommanderTransceiver (std::unique_ptr wrapped, + QString const& address, bool use_for_ptt, + int poll_interval, QObject * parent) + : PollingTransceiver {poll_interval, parent} , wrapped_ {std::move (wrapped)} , use_for_ptt_ {use_for_ptt} , server_ {address} @@ -48,14 +50,10 @@ DXLabSuiteCommanderTransceiver::DXLabSuiteCommanderTransceiver (std::unique_ptr< { } -DXLabSuiteCommanderTransceiver::~DXLabSuiteCommanderTransceiver () +int DXLabSuiteCommanderTransceiver::do_start () { -} - -void DXLabSuiteCommanderTransceiver::do_start () -{ - TRACE_CAT ("starting"); - wrapped_->start (); + TRACE_CAT ("DXLabSuiteCommanderTransceiver", "starting"); + if (wrapped_) wrapped_->start (0); auto server_details = network_server_lookup (server_, 52002u, QHostAddress::LocalHost, QAbstractSocket::IPv4Protocol); @@ -67,11 +65,46 @@ void DXLabSuiteCommanderTransceiver::do_start () commander_->connectToHost (std::get<0> (server_details), std::get<1> (server_details)); if (!commander_->waitForConnected ()) { - TRACE_CAT ("failed to connect" << commander_->errorString ()); + TRACE_CAT ("DXLabSuiteCommanderTransceiver", "failed to connect" << commander_->errorString ()); throw error {tr ("Failed to connect to DX Lab Suite Commander\n") + commander_->errorString ()}; } - + + int resolution {0}; + auto reply = command_with_reply ("CmdGetFreq"); + if (0 == reply.indexOf ("') + 1)); + if (f && !(f % 10)) + { + auto test_frequency = f - f % 100 + 55; + auto f_string = frequency_to_string (test_frequency); + auto params = ("" + f_string).arg (f_string.size ()); + simple_command (("CmdSetFreq" + params).arg (params.size ())); + reply = command_with_reply ("CmdGetFreq"); + if (0 == reply.indexOf ("') + 1)); + switch (static_cast (new_frequency - test_frequency)) + { + case -5: resolution = -1; break; // 10Hz truncated + case 5: resolution = 1; break; // 10Hz rounded + case -55: resolution = -2; break; // 100Hz truncated + case 45: resolution = 2; break; // 100Hz rounded + } + f_string = frequency_to_string (f); + params = ("" + f_string).arg (f_string.size ()); + simple_command (("CmdSetFreq" + params).arg (params.size ())); + } + } + } + else + { + TRACE_CAT ("DXLabSuiteCommanderTransceiver", "get frequency unexpected response"); + throw error {tr ("DX Lab Suite Commander didn't respond correctly reading frequency")}; + } + poll (); + return resolution; } void DXLabSuiteCommanderTransceiver::do_stop () @@ -82,27 +115,30 @@ void DXLabSuiteCommanderTransceiver::do_stop () delete commander_, commander_ = nullptr; } - wrapped_->stop (); - TRACE_CAT ("stopped"); + if (wrapped_) wrapped_->stop (); + TRACE_CAT ("DXLabSuiteCommanderTransceiver", "stopped"); } void DXLabSuiteCommanderTransceiver::do_ptt (bool on) { - TRACE_CAT (on << state ()); + TRACE_CAT ("DXLabSuiteCommanderTransceiver", on << state ()); if (use_for_ptt_) { simple_command (on ? "CmdTX" : "CmdRX"); } else { - wrapped_->ptt (on); + Q_ASSERT (wrapped_); + TransceiverState new_state {wrapped_->state ()}; + new_state.ptt (on); + wrapped_->set (new_state, 0); } update_PTT (on); } -void DXLabSuiteCommanderTransceiver::do_frequency (Frequency f, MODE m) +void DXLabSuiteCommanderTransceiver::do_frequency (Frequency f, MODE m, bool /*no_ignore*/) { - TRACE_CAT (f << state ()); + TRACE_CAT ("DXLabSuiteCommanderTransceiver", f << state ()); auto f_string = frequency_to_string (f); if (UNK != m) { @@ -119,9 +155,9 @@ void DXLabSuiteCommanderTransceiver::do_frequency (Frequency f, MODE m) update_rx_frequency (f); } -void DXLabSuiteCommanderTransceiver::do_tx_frequency (Frequency tx, bool /* rationalise_mode */) +void DXLabSuiteCommanderTransceiver::do_tx_frequency (Frequency tx, bool /*no_ignore*/) { - TRACE_CAT (tx << state ()); + TRACE_CAT ("DXLabSuiteCommanderTransceiver", tx << state ()); if (tx) { auto f_string = frequency_to_string (tx); @@ -136,9 +172,9 @@ void DXLabSuiteCommanderTransceiver::do_tx_frequency (Frequency tx, bool /* rati update_other_frequency (tx); } -void DXLabSuiteCommanderTransceiver::do_mode (MODE m, bool /* rationalise */) +void DXLabSuiteCommanderTransceiver::do_mode (MODE m) { - TRACE_CAT (m << state ()); + TRACE_CAT ("DXLabSuiteCommanderTransceiver", m << state ()); auto m_string = map_mode (m); auto params = ("<1:%1>" + m_string).arg (m_string.size ()); simple_command (("CmdSetMode" + params).arg (params.size ())); @@ -168,7 +204,7 @@ void DXLabSuiteCommanderTransceiver::poll () } else { - TRACE_CAT_POLL ("get frequency unexpected response"); + TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "get frequency unexpected response"); throw error {tr ("DX Lab Suite Commander didn't respond correctly polling frequency")}; } @@ -189,7 +225,7 @@ void DXLabSuiteCommanderTransceiver::poll () } else { - TRACE_CAT_POLL ("get tx frequency unexpected response"); + TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "get tx frequency unexpected response"); throw error {tr ("DX Lab Suite Commander didn't respond correctly polling TX frequency")}; } } @@ -208,13 +244,13 @@ void DXLabSuiteCommanderTransceiver::poll () } else { - TRACE_CAT_POLL ("unexpected split state" << split); + TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "unexpected split state" << split); throw error {tr ("DX Lab Suite Commander sent an unrecognised split state: ") + split}; } } else { - TRACE_CAT_POLL ("get split mode unexpected response"); + TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "get split mode unexpected response"); throw error {tr ("DX Lab Suite Commander didn't respond correctly polling split status")}; } @@ -265,14 +301,14 @@ void DXLabSuiteCommanderTransceiver::poll () } else { - TRACE_CAT_POLL ("unexpected mode name" << mode); + TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "unexpected mode name" << mode); throw error {tr ("DX Lab Suite Commander sent an unrecognised mode: \"") + mode + '"'}; } update_mode (m); } else { - TRACE_CAT_POLL ("unexpected response"); + TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "unexpected response"); throw error {tr ("DX Lab Suite Commander didn't respond correctly polling mode")}; } } @@ -283,12 +319,12 @@ void DXLabSuiteCommanderTransceiver::simple_command (QString const& cmd, bool no if (!no_debug) { - TRACE_CAT (cmd); + TRACE_CAT ("DXLabSuiteCommanderTransceiver", cmd); } if (!write_to_port (cmd)) { - TRACE_CAT ("failed:" << commander_->errorString ()); + TRACE_CAT ("DXLabSuiteCommanderTransceiver", "failed:" << commander_->errorString ()); throw error {tr ("DX Lab Suite Commander send command failed\n") + commander_->errorString ()}; } } @@ -299,7 +335,7 @@ QString DXLabSuiteCommanderTransceiver::command_with_reply (QString const& cmd, if (!write_to_port (cmd)) { - TRACE_CAT ("failed to send command:" << commander_->errorString ()); + TRACE_CAT ("DXLabSuiteCommanderTransceiver", "failed to send command:" << commander_->errorString ()); throw error { tr ("DX Lab Suite Commander failed to send command \"%1\": %2\n") .arg (cmd) @@ -316,7 +352,7 @@ QString DXLabSuiteCommanderTransceiver::command_with_reply (QString const& cmd, replied = commander_->waitForReadyRead (); if (!replied && commander_->error () != commander_->SocketTimeoutError) { - TRACE_CAT (cmd << "failed to read reply:" << commander_->errorString ()); + TRACE_CAT ("DXLabSuiteCommanderTransceiver", cmd << "failed to read reply:" << commander_->errorString ()); throw error { tr ("DX Lab Suite Commander send command \"%1\" read reply failed: %2\n") .arg (cmd) @@ -327,7 +363,7 @@ QString DXLabSuiteCommanderTransceiver::command_with_reply (QString const& cmd, if (!replied) { - TRACE_CAT (cmd << "retries exhausted"); + TRACE_CAT ("DXLabSuiteCommanderTransceiver", cmd << "retries exhausted"); throw error { tr ("DX Lab Suite Commander retries exhausted sending command \"%1\"") .arg (cmd) @@ -343,7 +379,7 @@ QString DXLabSuiteCommanderTransceiver::command_with_reply (QString const& cmd, if (!no_debug) { - TRACE_CAT (cmd << "->" << result); + TRACE_CAT ("DXLabSuiteCommanderTransceiver", cmd << "->" << result); } return result; // converting raw UTF-8 bytes to QString diff --git a/DXLabSuiteCommanderTransceiver.hpp b/DXLabSuiteCommanderTransceiver.hpp index c039857bc..bd2c114c4 100644 --- a/DXLabSuiteCommanderTransceiver.hpp +++ b/DXLabSuiteCommanderTransceiver.hpp @@ -27,15 +27,16 @@ public: static void register_transceivers (TransceiverFactory::Transceivers *, int id); // takes ownership of wrapped Transceiver - explicit DXLabSuiteCommanderTransceiver (std::unique_ptr wrapped, QString const& address, bool use_for_ptt, int poll_interval); - ~DXLabSuiteCommanderTransceiver (); + explicit DXLabSuiteCommanderTransceiver (std::unique_ptr wrapped, + QString const& address, bool use_for_ptt, + int poll_interval, QObject * parent = nullptr); protected: - void do_start () override; + int do_start () override; void do_stop () override; - void do_frequency (Frequency, MODE) override; - void do_tx_frequency (Frequency, bool rationalise_mode) override; - void do_mode (MODE, bool rationalise) override; + void do_frequency (Frequency, MODE, bool no_ignore) override; + void do_tx_frequency (Frequency, bool no_ignore) override; + void do_mode (MODE) override; void do_ptt (bool on) override; void poll () override; @@ -47,7 +48,7 @@ private: QString frequency_to_string (Frequency) const; Frequency string_to_frequency (QString) const; - std::unique_ptr wrapped_; + std::unique_ptr wrapped_; // may be null bool use_for_ptt_; QString server_; QTcpSocket * commander_; diff --git a/EmulateSplitTransceiver.cpp b/EmulateSplitTransceiver.cpp index 8b2b9ee6d..7e84919e2 100644 --- a/EmulateSplitTransceiver.cpp +++ b/EmulateSplitTransceiver.cpp @@ -1,116 +1,65 @@ #include "EmulateSplitTransceiver.hpp" -EmulateSplitTransceiver::EmulateSplitTransceiver (std::unique_ptr wrapped) - : wrapped_ {std::move (wrapped)} - , frequency_ {0, 0} - , pre_tx_frequency_ {0} - , tx_ {false} +EmulateSplitTransceiver::EmulateSplitTransceiver (std::unique_ptr wrapped, QObject * parent) + : Transceiver {parent} + , wrapped_ {std::move (wrapped)} + , rx_frequency_ {0} + , tx_frequency_ {0} + , split_ {false} { // Connect update signal of wrapped Transceiver object instance to ours. connect (wrapped_.get (), &Transceiver::update, this, &EmulateSplitTransceiver::handle_update); - // Connect failure signal of wrapped Transceiver object to our - // parent failure signal. + // Connect other signals of wrapped Transceiver object to our + // parent matching signals. + connect (wrapped_.get (), &Transceiver::resolution, this, &Transceiver::resolution); + connect (wrapped_.get (), &Transceiver::finished, this, &Transceiver::finished); connect (wrapped_.get (), &Transceiver::failure, this, &Transceiver::failure); } -void EmulateSplitTransceiver::start () noexcept -{ - wrapped_->start (); - wrapped_->tx_frequency (0, false); -} - -void EmulateSplitTransceiver::frequency (Frequency rx, MODE m) noexcept +void EmulateSplitTransceiver::set (TransceiverState const& s, unsigned sequence_number) noexcept { #if WSJT_TRACE_CAT - qDebug () << "EmulateSplitTransceiver::frequency:" << rx << "mode:" << m; + qDebug () << "EmulateSplitTransceiver::set: state:" << s << "#:" << sequence_number; #endif + // save for use in updates + rx_frequency_ = s.frequency (); + tx_frequency_ = s.tx_frequency (); + split_ = s.split (); - // Save frequency parameters. - frequency_[0] = rx; - - // Set active frequency. - wrapped_->frequency (rx, m); + TransceiverState emulated_state {s}; + if (s.ptt () && split_) emulated_state.frequency (s.tx_frequency ()); + emulated_state.split (false); + emulated_state.tx_frequency (0); + wrapped_->set (emulated_state, sequence_number); } -void EmulateSplitTransceiver::tx_frequency (Frequency tx, bool /* rationalise_mode */) noexcept -{ -#if WSJT_TRACE_CAT - qDebug () << "EmulateSplitTransceiver::tx_frequency:" << tx; -#endif - - // Save frequency parameter. - frequency_[1] = tx; - - // Set active frequency. - wrapped_->frequency (frequency_[(tx_ && frequency_[1]) ? 1 : 0]); -} - -void EmulateSplitTransceiver::ptt (bool on) noexcept -{ -#if WSJT_TRACE_CAT - qDebug () << "EmulateSplitTransceiver::ptt:" << on; -#endif - - // Save TX state for future frequency change requests. - if (on) - { - // save the Rx frequency - pre_tx_frequency_ = frequency_[0]; - - // Switch to other frequency if we have one i.e. client wants - // split operation). - wrapped_->frequency (frequency_[frequency_[1] ? 1 : 0]); - - // Change TX state. - wrapped_->ptt (true); - } - else - { - // Change TX state. - wrapped_->ptt (false); - - // Switch to RX frequency. - wrapped_->frequency (pre_tx_frequency_); - pre_tx_frequency_ = 0; - } - tx_ = on; -} - -void EmulateSplitTransceiver::handle_update (TransceiverState state) +void EmulateSplitTransceiver::handle_update (TransceiverState const& state, + unsigned sequence_number) { #if WSJT_TRACE_CAT qDebug () << "EmulateSplitTransceiver::handle_update: from wrapped:" << state; #endif - // Change to reflect emulated state, we don't want to report the - // shifted frequency when transmitting. - if (pre_tx_frequency_) - { - state.frequency (pre_tx_frequency_); - } - else - { - // Follow the rig if in RX mode. - frequency_[0] = state.frequency (); - } - - // Always report the other frequency as the Tx frequency we will use. - state.tx_frequency (frequency_[1]); - if (state.split ()) { Q_EMIT failure (tr ("Emulated split mode requires rig to be in simplex mode")); } else { - state.split (true); // override rig state - + TransceiverState new_state {state}; + // Follow the rig if in RX mode. + if (state.ptt ()) new_state.frequency (rx_frequency_); + + // These are always what was requested in prior set state operation + new_state.tx_frequency (tx_frequency_); + new_state.split (split_); + #if WSJT_TRACE_CAT qDebug () << "EmulateSplitTransceiver::handle_update: signalling:" << state; #endif // signal emulated state - Q_EMIT update (state); + Q_EMIT update (new_state, sequence_number); } } diff --git a/EmulateSplitTransceiver.hpp b/EmulateSplitTransceiver.hpp index 1e35e7424..210a37b71 100644 --- a/EmulateSplitTransceiver.hpp +++ b/EmulateSplitTransceiver.hpp @@ -29,25 +29,23 @@ class EmulateSplitTransceiver final { public: // takes ownership of wrapped Transceiver - explicit EmulateSplitTransceiver (std::unique_ptr wrapped); + explicit EmulateSplitTransceiver (std::unique_ptr wrapped, + QObject * parent = nullptr); - void start () noexcept override; - void frequency (Frequency, MODE) noexcept override; - void tx_frequency (Frequency, bool rationalise_mode) noexcept override; - void ptt (bool on) noexcept override; + void set (TransceiverState const&, + unsigned sequence_number) noexcept override; // forward everything else to wrapped Transceiver - void stop (bool /* reset_split */) noexcept override {wrapped_->stop (false); Q_EMIT finished ();} - void mode (MODE m, bool /* rationalise */) noexcept override {wrapped_->mode (m, false);} - void sync (bool force_signal) noexcept override {wrapped_->sync (force_signal);} + void start (unsigned sequence_number) noexcept override {wrapped_->start (sequence_number);} + void stop () noexcept override {wrapped_->stop ();} private: - void handle_update (TransceiverState); + void handle_update (TransceiverState const&, unsigned seqeunce_number); std::unique_ptr wrapped_; - Frequency frequency_[2]; // [0] <- RX, [1] <- other - Frequency pre_tx_frequency_; // return to this on switching to Rx - bool tx_; + Frequency rx_frequency_; // requested Rx frequency + Frequency tx_frequency_; // requested Tx frequency + bool split_; // requested split state }; #endif diff --git a/HRDTransceiver.cpp b/HRDTransceiver.cpp index bf94541cb..a065b2c86 100644 --- a/HRDTransceiver.cpp +++ b/HRDTransceiver.cpp @@ -72,10 +72,13 @@ qint32 const HRDMessage::magic_2_value_ (0xABCD1234); HRDTransceiver::HRDTransceiver (std::unique_ptr wrapped , QString const& server , bool use_for_ptt - , int poll_interval) - : PollingTransceiver {poll_interval} + , int poll_interval + , bool set_rig_mode + , QObject * parent) + : PollingTransceiver {poll_interval, parent} , wrapped_ {std::move (wrapped)} , use_for_ptt_ {use_for_ptt} + , set_rig_mode_ {set_rig_mode} , server_ {server} , hrd_ {0} , protocol_ {none} @@ -102,14 +105,10 @@ HRDTransceiver::HRDTransceiver (std::unique_ptr wrapped { } -HRDTransceiver::~HRDTransceiver () +int HRDTransceiver::do_start () { -} - -void HRDTransceiver::do_start () -{ - TRACE_CAT ("starting"); - wrapped_->start (); + TRACE_CAT ("HRDTransceiver", "starting"); + if (wrapped_) wrapped_->start (0); auto server_details = network_server_lookup (server_, 7809u); if (!hrd_) @@ -119,7 +118,7 @@ void HRDTransceiver::do_start () hrd_->connectToHost (std::get<0> (server_details), std::get<1> (server_details)); if (!hrd_->waitForConnected ()) { - TRACE_CAT ("failed to connect:" << hrd_->errorString ()); + TRACE_CAT ("HRDTransceiver", "failed to connect:" << hrd_->errorString ()); throw error {tr ("Failed to connect to Ham Radio Deluxe\n") + hrd_->errorString ()}; } @@ -144,7 +143,7 @@ void HRDTransceiver::do_start () hrd_->connectToHost (std::get<0> (server_details), std::get<1> (server_details)); if (!hrd_->waitForConnected ()) { - TRACE_CAT ("failed to connect:" << hrd_->errorString ()); + TRACE_CAT ("HRDTransceiver", "failed to connect:" << hrd_->errorString ()); throw error {tr ("Failed to connect to Ham Radio Deluxe\n") + hrd_->errorString ()}; } @@ -161,14 +160,14 @@ void HRDTransceiver::do_start () auto id = send_command ("get id", false, false); auto version = send_command ("get version", false, false); - TRACE_CAT ("Id:" << id << "Version:" << version); + TRACE_CAT ("HRDTransceiver", "Id:" << id << "Version:" << version); HRD_info << "Id: " << id << "\n"; HRD_info << "Version: " << version << "\n"; auto radios = send_command ("get radios", false, false).trimmed ().split (',', QString::SkipEmptyParts); if (radios.isEmpty ()) { - TRACE_CAT ("no rig found"); + TRACE_CAT ("HRDTransceiver", "no rig found"); throw error {tr ("Ham Radio Deluxe: no rig found")}; } @@ -181,10 +180,10 @@ void HRDTransceiver::do_start () } #if WSJT_TRACE_CAT - TRACE_CAT ("radios:-"); + TRACE_CAT ("HRDTransceiver", "radios:-"); Q_FOREACH (auto const& radio, radios_) { - TRACE_CAT ("\t[" << std::get<0> (radio) << "] " << std::get<1> (radio)); + TRACE_CAT ("HRDTransceiver", "\t[" << std::get<0> (radio) << "] " << std::get<1> (radio)); } #endif @@ -192,36 +191,36 @@ void HRDTransceiver::do_start () HRD_info << "Current radio: " << current_radio_name << "\n"; if (current_radio_name.isEmpty ()) { - TRACE_CAT ("no rig found"); + TRACE_CAT ("HRDTransceiver", "no rig found"); throw error {tr ("Ham Radio Deluxe: no rig found")}; } vfo_count_ = send_command ("get vfo-count").toUInt (); HRD_info << "VFO count: " << vfo_count_ << "\n"; - TRACE_CAT ("vfo count:" << vfo_count_); + TRACE_CAT ("HRDTransceiver", "vfo count:" << vfo_count_); buttons_ = send_command ("get buttons").trimmed ().split (',', QString::SkipEmptyParts).replaceInStrings (" ", "~"); - TRACE_CAT ("HRD Buttons: " << buttons_); + TRACE_CAT ("HRDTransceiver", "HRD Buttons: " << buttons_); HRD_info << "Buttons: {" << buttons_.join (", ") << "}\n"; dropdown_names_ = send_command ("get dropdowns").trimmed ().split (',', QString::SkipEmptyParts); - TRACE_CAT ("Dropdowns:"); + TRACE_CAT ("HRDTransceiver", "Dropdowns:"); HRD_info << "Dropdowns:\n"; Q_FOREACH (auto const& dd, dropdown_names_) { auto selections = send_command ("get dropdown-list {" + dd + "}").trimmed ().split (',', QString::SkipEmptyParts); - TRACE_CAT ("\t" << dd << ": {" << selections.join (", ") << "}"); + TRACE_CAT ("HRDTransceiver", "\t" << dd << ": {" << selections.join (", ") << "}"); HRD_info << "\t" << dd << ": {" << selections.join (", ") << "}\n"; dropdowns_[dd] = selections; } slider_names_ = send_command ("get sliders").trimmed ().split (',', QString::SkipEmptyParts).replaceInStrings (" ", "~"); - TRACE_CAT ("Sliders:-"); + TRACE_CAT ("HRDTransceiver", "Sliders:-"); HRD_info << "Sliders:\n"; Q_FOREACH (auto const& s, slider_names_) { auto range = send_command ("get slider-range " + current_radio_name + " " + s).trimmed ().split (',', QString::SkipEmptyParts); - TRACE_CAT ("\t" << s << ": {" << range.join (", ") << "}"); + TRACE_CAT ("HRDTransceiver", "\t" << s << ": {" << range.join (", ") << "}"); HRD_info << "\t" << s << ": {" << range.join (", ") << "}\n"; sliders_[s] = range; } @@ -292,6 +291,24 @@ void HRDTransceiver::do_start () update_mode (lookup_mode (get_dropdown (mode_A_dropdown_), mode_A_map_)); } } + + int resolution {0}; + auto f = send_command ("get frequency").toUInt (); + if (f && !(f % 10)) + { + auto test_frequency = f - f % 100 + 55; + send_simple_command ("set frequency-hz " + QString::number (test_frequency)); + auto new_frequency = send_command ("get frequency").toUInt (); + switch (static_cast (new_frequency - test_frequency)) + { + case -5: resolution = -1; break; // 10Hz truncated + case 5: resolution = 1; break; // 10Hz rounded + case -55: resolution = -2; break; // 100Hz truncated + case 45: resolution = 2; break; // 100Hz rounded + } + send_simple_command ("set frequency-hz " + QString::number (f)); + } + return resolution; } void HRDTransceiver::do_stop () @@ -301,11 +318,8 @@ void HRDTransceiver::do_stop () hrd_->close (); } - if (wrapped_) - { - wrapped_->stop (); - } - TRACE_CAT ("stopped" << state () << "reversed" << reversed_); + if (wrapped_) wrapped_->stop (); + TRACE_CAT ("HRDTransceiver", "stopped" << state () << "reversed" << reversed_); } int HRDTransceiver::find_button (QRegExp const& re) const @@ -352,11 +366,11 @@ void HRDTransceiver::map_modes (int dropdown, ModeMap *map) map->push_back (std::forward_as_tuple (DIG_FM, find_dropdown_selection (dropdown, QRegExp ("^(PKT-FM|PKT|FM)$")))); #if WSJT_TRACE_CAT - TRACE_CAT ("for dropdown" << dropdown_names_[dropdown]); + TRACE_CAT ("HRDTransceiver", "for dropdown" << dropdown_names_[dropdown]); std::for_each (map->begin (), map->end (), [this, dropdown] (ModeMap::value_type const& item) { auto const& rhs = std::get<1> (item); - TRACE_CAT ('\t' << std::get<0> (item) << "<->" << (rhs.size () ? dropdowns_[dropdown_names_[dropdown]][rhs.front ()] : "None")); + TRACE_CAT ("HRDTransceiver", '\t' << std::get<0> (item) << "<->" << (rhs.size () ? dropdowns_[dropdown_names_[dropdown]][rhs.front ()] : "None")); }); #endif } @@ -419,14 +433,14 @@ void HRDTransceiver::set_dropdown (int dd, int value) } else { - TRACE_CAT ("item" << value << "not found in" << dd_name); + TRACE_CAT ("HRDTransceiver", "item" << value << "not found in" << dd_name); throw error {tr ("Ham Radio Deluxe: item not found in %1 dropdown list").arg (dd_name)}; } } void HRDTransceiver::do_ptt (bool on) { - TRACE_CAT (on); + TRACE_CAT ("HRDTransceiver", on); if (use_for_ptt_) { if (ptt_button_ >= 0) @@ -439,7 +453,10 @@ void HRDTransceiver::do_ptt (bool on) } else { - wrapped_->ptt (on); + Q_ASSERT (wrapped_); + TransceiverState new_state {wrapped_->state ()}; + new_state.ptt (on); + wrapped_->set (new_state, 0); } update_PTT (on); } @@ -456,17 +473,17 @@ void HRDTransceiver::set_button (int button_index, bool checked) } else { - TRACE_CAT ("invalid button"); + TRACE_CAT ("HRDTransceiver", "invalid button"); throw error {tr ("Ham Radio Deluxe: button not available")}; } } -void HRDTransceiver::do_frequency (Frequency f, MODE m) +void HRDTransceiver::do_frequency (Frequency f, MODE m, bool /*no_ignore*/) { - TRACE_CAT (f << "reversed" << reversed_); + TRACE_CAT ("HRDTransceiver", f << "reversed" << reversed_); if (UNK != m) { - do_mode (m, false); + do_mode (m); } auto fo_string = QString::number (f); if (vfo_count_ > 1 && reversed_) @@ -481,9 +498,9 @@ void HRDTransceiver::do_frequency (Frequency f, MODE m) update_rx_frequency (f); } -void HRDTransceiver::do_tx_frequency (Frequency tx, bool rationalise_mode) +void HRDTransceiver::do_tx_frequency (Frequency tx, bool /*no_ignore*/) { - TRACE_CAT (tx << "rationalize mode:" << rationalise_mode << "reversed" << reversed_); + TRACE_CAT ("HRDTransceiver", tx << "reversed" << reversed_); // re-check if reversed VFOs bool rx_A {true}; @@ -510,7 +527,7 @@ void HRDTransceiver::do_tx_frequency (Frequency tx, bool rationalise_mode) bool split {tx != 0}; if (split) { - if (rationalise_mode) + if (set_rig_mode_) { if (!reversed_ && mode_B_dropdown_ >= 0) { @@ -536,12 +553,13 @@ void HRDTransceiver::do_tx_frequency (Frequency tx, bool rationalise_mode) set_dropdown (mode_A_dropdown_, lookup_mode (state ().mode (), mode_A_map_)); set_dropdown (receiver_dropdown_, (reversed_ ? rx_B_selection_ : rx_A_selection_).front ()); } - else + else if (vfo_count_ > 1) { set_button (vfo_A_button_ >= 0 ? (reversed_ ? vfo_A_button_ : vfo_B_button_) : vfo_toggle_button_); set_dropdown (mode_A_dropdown_, lookup_mode (state ().mode (), mode_A_map_)); set_button (vfo_A_button_ >= 0 ? (reversed_ ? vfo_B_button_ : vfo_A_button_) : vfo_toggle_button_); } + // else Tx VFO mode gets set with frequency below } } @@ -565,12 +583,11 @@ void HRDTransceiver::do_tx_frequency (Frequency tx, bool rationalise_mode) // we rationalise the modes here as well as the frequencies set_button (vfo_B_button_ >= 0 ? vfo_B_button_ : vfo_toggle_button_); send_simple_command ("set frequency-hz " + fo_string); - if (rationalise_mode && mode_B_dropdown_ < 0) + if (set_rig_mode_ && mode_B_dropdown_ < 0) { // do this here rather than later so we only // toggle/switch VFOs once set_dropdown (mode_A_dropdown_, lookup_mode (state ().mode (), mode_A_map_)); - rationalise_mode = false; } set_button (vfo_A_button_ >= 0 ? vfo_A_button_ : vfo_toggle_button_); } @@ -637,9 +654,9 @@ void HRDTransceiver::do_tx_frequency (Frequency tx, bool rationalise_mode) update_split (split); } -void HRDTransceiver::do_mode (MODE mode, bool rationalise) +void HRDTransceiver::do_mode (MODE mode) { - TRACE_CAT (mode); + TRACE_CAT ("HRDTransceiver", mode); if (reversed_ && mode_B_dropdown_ >= 0) { set_dropdown (mode_B_dropdown_, lookup_mode (mode, mode_B_map_)); @@ -648,7 +665,7 @@ void HRDTransceiver::do_mode (MODE mode, bool rationalise) { set_dropdown (mode_A_dropdown_, lookup_mode (mode, mode_A_map_)); } - if (rationalise && state ().split ()) // rationalise mode if split + if (set_rig_mode_ && state ().split ()) // rationalise mode if split { if (reversed_) { @@ -672,12 +689,15 @@ void HRDTransceiver::do_mode (MODE mode, bool rationalise) set_dropdown (mode_A_dropdown_, lookup_mode (mode, mode_A_map_)); set_dropdown (receiver_dropdown_, rx_B_selection_.front ()); } - else + else if (vfo_count_ > 1) { set_button (vfo_A_button_ >= 0 ? vfo_A_button_ : vfo_toggle_button_); set_dropdown (mode_A_dropdown_, lookup_mode (mode, mode_A_map_)); set_button (vfo_B_button_ >= 0 ? vfo_B_button_ : vfo_toggle_button_); } + // else Tx VFO mode gets set when Tx VFO frequency is + // set + if ( tx_A_button_ >= 0) { set_button (tx_A_button_); @@ -706,12 +726,15 @@ void HRDTransceiver::do_mode (MODE mode, bool rationalise) set_dropdown (mode_A_dropdown_, lookup_mode (mode, mode_A_map_)); set_dropdown (receiver_dropdown_, rx_A_selection_.front ()); } - else + else if (vfo_count_ > 1) { set_button (vfo_B_button_ >= 0 ? vfo_B_button_ : vfo_toggle_button_); set_dropdown (mode_A_dropdown_, lookup_mode (mode, mode_A_map_)); set_button (vfo_A_button_ >= 0 ? vfo_A_button_ : vfo_toggle_button_); } + // else Tx VFO mode gets set when Tx VFO frequency is + // set + if ( tx_B_button_ >= 0) { set_button (tx_B_button_); @@ -732,7 +755,7 @@ bool HRDTransceiver::is_button_checked (int button_index, bool no_debug) auto reply = send_command ("get button-select " + buttons_.value (button_index), no_debug); if ("1" != reply && "0" != reply) { - TRACE_CAT ("bad response"); + TRACE_CAT ("HRDTransceiver", "bad response"); throw error {tr ("Ham Radio Deluxe didn't respond as expected")}; } return "1" == reply; @@ -859,7 +882,7 @@ QString HRDTransceiver::send_command (QString const& cmd, bool no_debug, bool pr }); if (radio_iter == radios_.end ()) { - TRACE_CAT ("rig disappeared or changed"); + TRACE_CAT ("HRDTransceiver", "rig disappeared or changed"); throw error {tr ("Ham Radio Deluxe: rig has disappeared or changed")}; } @@ -873,7 +896,7 @@ QString HRDTransceiver::send_command (QString const& cmd, bool no_debug, bool pr if (QTcpSocket::ConnectedState != hrd_->state ()) { - TRACE_CAT (cmd << "failed" << hrd_->errorString ()); + TRACE_CAT ("HRDTransceiver", cmd << "failed" << hrd_->errorString ()); throw error { tr ("Ham Radio Deluxe send command \"%1\" failed %2\n") .arg (cmd) @@ -886,7 +909,7 @@ QString HRDTransceiver::send_command (QString const& cmd, bool no_debug, bool pr auto message = ((prepend_context ? context + cmd : cmd) + "\r").toLocal8Bit (); if (!write_to_port (message.constData (), message.size ())) { - TRACE_CAT ("failed to write command" << cmd << "to HRD"); + TRACE_CAT ("HRDTransceiver", "failed to write command" << cmd << "to HRD"); throw error { tr ("Ham Radio Deluxe: failed to write command \"%1\"") .arg (cmd) @@ -899,7 +922,7 @@ QString HRDTransceiver::send_command (QString const& cmd, bool no_debug, bool pr QScopedPointer message {new (string) HRDMessage}; if (!write_to_port (reinterpret_cast (message.data ()), message->size_)) { - TRACE_CAT ("failed to write command" << cmd << "to HRD"); + TRACE_CAT ("HRDTransceiver", "failed to write command" << cmd << "to HRD"); throw error { tr ("Ham Radio Deluxe: failed to write command \"%1\"") .arg (cmd) @@ -916,7 +939,7 @@ QString HRDTransceiver::send_command (QString const& cmd, bool no_debug, bool pr HRDMessage const * reply {new (buffer) HRDMessage}; if (reply->magic_1_value_ != reply->magic_1_ && reply->magic_2_value_ != reply->magic_2_) { - TRACE_CAT (cmd << "invalid reply"); + TRACE_CAT ("HRDTransceiver", cmd << "invalid reply"); throw error { tr ("Ham Radio Deluxe sent an invalid reply to our command \"%1\"") .arg (cmd) @@ -928,7 +951,7 @@ QString HRDTransceiver::send_command (QString const& cmd, bool no_debug, bool pr { if (!no_debug) { - TRACE_CAT (cmd << "reading more reply data"); + TRACE_CAT ("HRDTransceiver", cmd << "reading more reply data"); } buffer += read_reply (cmd); reply = new (buffer) HRDMessage; @@ -938,7 +961,7 @@ QString HRDTransceiver::send_command (QString const& cmd, bool no_debug, bool pr } if (!no_debug) { - TRACE_CAT (cmd << " ->" << result); + TRACE_CAT ("HRDTransceiver", cmd << " ->" << result); } return result; } @@ -970,7 +993,7 @@ QByteArray HRDTransceiver::read_reply (QString const& cmd) replied = hrd_->waitForReadyRead (); if (!replied && hrd_->error () != hrd_->SocketTimeoutError) { - TRACE_CAT (cmd << "failed to reply" << hrd_->errorString ()); + TRACE_CAT ("HRDTransceiver", cmd << "failed to reply" << hrd_->errorString ()); throw error { tr ("Ham Radio Deluxe failed to reply to command \"%1\" %2\n") .arg (cmd) @@ -980,7 +1003,7 @@ QByteArray HRDTransceiver::read_reply (QString const& cmd) } if (!replied) { - TRACE_CAT (cmd << "retries exhausted"); + TRACE_CAT ("HRDTransceiver", cmd << "retries exhausted"); throw error { tr ("Ham Radio Deluxe retries exhausted sending command \"%1\"") .arg (cmd) @@ -993,7 +1016,7 @@ void HRDTransceiver::send_simple_command (QString const& command, bool no_debug) { if ("OK" != send_command (command, no_debug)) { - TRACE_CAT (command << "unexpected response"); + TRACE_CAT ("HRDTransceiver", command << "unexpected response"); throw error { tr ("Ham Radio Deluxe didn't respond to command \"%1\" as expected") .arg (command) diff --git a/HRDTransceiver.hpp b/HRDTransceiver.hpp index 87a9a439b..86d393030 100644 --- a/HRDTransceiver.hpp +++ b/HRDTransceiver.hpp @@ -34,16 +34,17 @@ public: explicit HRDTransceiver (std::unique_ptr wrapped , QString const& server , bool use_for_ptt - , int poll_interval); - ~HRDTransceiver (); + , int poll_interval + , bool set_rig_mode + , QObject * parent = nullptr); protected: // Implement the TransceiverBase interface. - void do_start () override; + int do_start () override; void do_stop () override; - void do_frequency (Frequency, MODE) override; - void do_tx_frequency (Frequency, bool rationalise_mode) override; - void do_mode (MODE, bool rationalise) override; + void do_frequency (Frequency, MODE, bool no_ignore) override; + void do_tx_frequency (Frequency, bool no_ignore) override; + void do_mode (MODE) override; void do_ptt (bool on) override; // Implement the PollingTransceiver interface. @@ -74,10 +75,12 @@ private: // An alternate TransceiverBase instance that can be used to drive // PTT if required. - std::unique_ptr wrapped_; + std::unique_ptr wrapped_; // may be null bool use_for_ptt_; // Use HRD for PTT. + bool set_rig_mode_; // Set VFO mode when required + QString server_; // The TCP/IP addrress and port for // the HRD server. diff --git a/HamlibTransceiver.cpp b/HamlibTransceiver.cpp index cc9f26281..fe94e1ed4 100644 --- a/HamlibTransceiver.cpp +++ b/HamlibTransceiver.cpp @@ -169,10 +169,13 @@ void HamlibTransceiver::RIGDeleter::cleanup (RIG * rig) } } -HamlibTransceiver::HamlibTransceiver (TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port) - : PollingTransceiver {0} +HamlibTransceiver::HamlibTransceiver (TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port, + QObject * parent) + : PollingTransceiver {0, parent} , rig_ {rig_init (RIG_MODEL_DUMMY)} + , set_rig_mode_ {false} , back_ptt_port_ {false} + , one_VFO_ {false} , is_dummy_ {true} , reversed_ {false} , mode_query_works_ {true} @@ -218,10 +221,13 @@ HamlibTransceiver::HamlibTransceiver (TransceiverFactory::PTTMethod ptt_type, QS } } -HamlibTransceiver::HamlibTransceiver (int model_number, TransceiverFactory::ParameterPack const& params) - : PollingTransceiver {params.poll_interval} +HamlibTransceiver::HamlibTransceiver (int model_number, TransceiverFactory::ParameterPack const& params, + QObject * parent) + : PollingTransceiver {params.poll_interval, parent} , rig_ {rig_init (model_number)} + , set_rig_mode_ {params.set_rig_mode} , back_ptt_port_ {TransceiverFactory::TX_audio_source_rear == params.audio_source} + , one_VFO_ {false} , is_dummy_ {RIG_MODEL_DUMMY == model_number} , reversed_ {false} , mode_query_works_ {rig_ && rig_->caps->get_mode} @@ -249,44 +255,44 @@ HamlibTransceiver::HamlibTransceiver (int model_number, TransceiverFactory::Para , "hamlib_settings.json"); if (!settings_file_name.isEmpty ()) { - QFile 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)}; - } - if (!settings_doc.isObject ()) - { - throw error {tr ("Hamlib settings file error: top level must be a JSON object")}; - } - auto const& settings = settings_doc.object (); + QFile 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)}; + } + 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 ()); - } - } - } + // + // 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 (RIG_MODEL_DUMMY != model_number) + if (!is_dummy_) { switch (rig_->caps->port_type) { @@ -354,8 +360,7 @@ HamlibTransceiver::HamlibTransceiver (int model_number, TransceiverFactory::Para case TransceiverFactory::PTT_method_RTS: if (!params.ptt_port.isEmpty () && params.ptt_port != "None" - && (RIG_MODEL_DUMMY == model_number - || params.ptt_port != params.serial_port)) + && (is_dummy_ || params.ptt_port != params.serial_port)) { #if defined (WIN32) set_conf ("ptt_pathname", ("\\\\.\\" + params.ptt_port).toLatin1 ().data ()); @@ -381,22 +386,20 @@ HamlibTransceiver::HamlibTransceiver (int model_number, TransceiverFactory::Para // rig_set_freq_callback (rig_.data (), &frequency_change_callback, this); } -HamlibTransceiver::~HamlibTransceiver () -{ -} - void HamlibTransceiver::error_check (int ret_code, QString const& doing) const { if (RIG_OK != ret_code) { - TRACE_CAT_POLL ("error:" << rigerror (ret_code)); + TRACE_CAT_POLL ("HamlibTransceiver", "error:" << rigerror (ret_code)); throw error {tr ("Hamlib error: %1 while %2").arg (rigerror (ret_code)).arg (doing)}; } } -void HamlibTransceiver::do_start () +int HamlibTransceiver::do_start () { - TRACE_CAT (QString::fromLatin1 (rig_->caps->mfg_name).trimmed () << QString::fromLatin1 (rig_->caps->model_name).trimmed ()); + 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")); @@ -407,6 +410,12 @@ void HamlibTransceiver::do_start () 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 { @@ -430,38 +439,38 @@ void HamlibTransceiver::do_start () // 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")); - TRACE_CAT ("current frequency =" << 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 ("current mode =" << rig_strrmode (m) << "bw =" << w); + TRACE_CAT ("HamlibTransceiver", "current mode =" << rig_strrmode (m) << "bw =" << w); if (!rig_->caps->set_vfo) { - TRACE_CAT ("rig_vfo_op TOGGLE"); + 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 ("rig_set_vfo to other VFO"); + TRACE_CAT ("HamlibTransceiver", "rig_set_vfo to other VFO"); error_check (rig_set_vfo (rig_.data (), rig_->state.vfo_list & RIG_VFO_B ? RIG_VFO_B : RIG_VFO_SUB), tr ("setting current VFO")); } error_check (rig_get_freq (rig_.data (), RIG_VFO_CURR, &f2), tr ("getting other VFO frequency")); - TRACE_CAT ("rig_get_freq other frequency =" << 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 ("rig_get_mode other mode =" << rig_strrmode (mb) << "bw =" << wb); + TRACE_CAT ("HamlibTransceiver", "rig_get_mode other mode =" << rig_strrmode (mb) << "bw =" << wb); update_other_frequency (f2); if (!rig_->caps->set_vfo) { - TRACE_CAT ("rig_vfo_op TOGGLE"); + 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 ("rig_set_vfo A/MAIN"); + 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")); } @@ -472,15 +481,15 @@ void HamlibTransceiver::do_start () else { error_check (rig_get_freq (rig_.data (), RIG_VFO_CURR, &f1), tr ("getting frequency")); - TRACE_CAT ("rig_get_freq frequency =" << 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 ("rig_get_mode mode =" << rig_strrmode (m) << "bw =" << w); + TRACE_CAT ("HamlibTransceiver", "rig_get_mode mode =" << rig_strrmode (m) << "bw =" << w); update_rx_frequency (f1); } - // TRACE_CAT ("rig_set_split_vfo split off"); + // 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); } @@ -491,7 +500,7 @@ void HamlibTransceiver::do_start () 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 ("rig_get_vfo current VFO = " << rig_strvfo (v)); + TRACE_CAT ("HamlibTransceiver", "rig_get_vfo current VFO = " << rig_strvfo (v)); } reversed_ = RIG_VFO_B == v; @@ -500,7 +509,7 @@ void HamlibTransceiver::do_start () { if (RIG_OK == rig_get_mode (rig_.data (), RIG_VFO_CURR, &m, &w)) { - TRACE_CAT ("rig_get_mode current mode =" << rig_strrmode (m) << "bw =" << w); + TRACE_CAT ("HamlibTransceiver", "rig_get_mode current mode =" << rig_strrmode (m) << "bw =" << w); } else { @@ -508,7 +517,7 @@ void HamlibTransceiver::do_start () // 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_POLL ("rig_get_mode can't do on this rig"); + TRACE_CAT_POLL ("HamlibTransceiver", "rig_get_mode can't do on this rig"); } } } @@ -530,9 +539,30 @@ void HamlibTransceiver::do_start () } } + int resolution {0}; + freq_t current_frequency; + error_check (rig_get_freq (rig_.data (), RIG_VFO_CURR, ¤t_frequency), tr ("getting current VFO 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")); + switch (static_cast (new_frequency - test_frequency)) + { + case -5: resolution = -1; break; // 10Hz truncated + case 5: resolution = 1; break; // 10Hz rounded + case -55: resolution = -2; break; // 100Hz truncated + case 45: resolution = 2; break; // 100Hz rounded + } + error_check (rig_set_freq (rig_.data (), RIG_VFO_CURR, current_frequency), tr ("setting frequency")); + } + poll (); - TRACE_CAT ("exit" << state () << "reversed =" << reversed_); + TRACE_CAT ("HamlibTransceiver", "exit" << state () << "reversed =" << reversed_ << "resolution = " << resolution); + return resolution; } void HamlibTransceiver::do_stop () @@ -551,77 +581,92 @@ void HamlibTransceiver::do_stop () rig_close (rig_.data ()); } - TRACE_CAT ("state:" << state () << "reversed =" << reversed_); + TRACE_CAT ("HamlibTransceiver", "state:" << state () << "reversed =" << reversed_); } -auto HamlibTransceiver::get_vfos () const -> std::tuple +auto HamlibTransceiver::get_vfos (bool for_split) const -> std::tuple { 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 ("rig_get_vfo VFO = " << rig_strvfo (v)); + TRACE_CAT ("HamlibTransceiver", "rig_get_vfo VFO = " << rig_strvfo (v)); reversed_ = RIG_VFO_B == v; } - else if (rig_->caps->set_vfo && rig_->caps->set_split_vfo) + else if (!for_split && 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 ("rig_set_vfo VFO = A/MAIN"); + 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 both 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_) + 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 ("reversing VFOs"); + TRACE_CAT ("HamlibTransceiver", "reversing VFOs"); std::swap (rx_vfo, tx_vfo); } - TRACE_CAT ("RX VFO = " << rig_strvfo (rx_vfo) << " TX VFO = " << rig_strvfo (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) +void HamlibTransceiver::do_frequency (Frequency f, MODE m, bool no_ignore) { - TRACE_CAT (f << "mode:" << m << "reversed:" << reversed_); + TRACE_CAT ("HamlibTransceiver", f << "mode:" << m << "reversed:" << reversed_); - // 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")); - - if (mode_query_works_ && UNK != m) + // only change when receiving or simplex or direct VFO addressing + // unavailable or forced + if (!state ().ptt () || !state ().split () || !one_VFO_ || no_ignore) { - do_mode (m, false); - - // for the 2nd time because a mode change may have caused a - // frequency change + // 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); - // for the second time because some rigs change mode according - // to frequency such as the TS-2000 auto mode setting - do_mode (m, false); + 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_NORMAL), 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_NORMAL), tr ("setting current VFO mode")); + } + update_mode (m); + } } - - update_rx_frequency (f); } -void HamlibTransceiver::do_tx_frequency (Frequency tx, bool rationalise_mode) +void HamlibTransceiver::do_tx_frequency (Frequency tx, bool no_ignore) { - TRACE_CAT (tx << "rationalise mode:" << rationalise_mode << "reversed:" << reversed_); + 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; - update_split (tx); - auto vfos = get_vfos (); + auto vfos = get_vfos (tx); // auto rx_vfo = std::get<0> (vfos); // or use RIG_VFO_CURR auto tx_vfo = std::get<1> (vfos); @@ -640,7 +685,7 @@ void HamlibTransceiver::do_tx_frequency (Frequency tx, bool rationalise_mode) // 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 ("rig_set_split_vfo split =" << 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)) { @@ -652,85 +697,126 @@ void HamlibTransceiver::do_tx_frequency (Frequency tx, bool rationalise_mode) // specific rig. error_check (rc, tr ("setting/unsetting split mode")); } - tickle_hamlib_ = false; + update_split (tx); } - hamlib_tx_vfo_fixup fixup (rig_.data (), tx_vfo); - // do this before setting the mode because changing band may - // recall the last mode used on the target band - error_check (rig_set_split_freq (rig_.data (), RIG_VFO_CURR, tx), tr ("setting split TX frequency")); - - if (rationalise_mode) + // just change current when transmitting with single VFO + // addressing + if (state ().ptt () && one_VFO_) { - rmode_t current_mode; - pbwidth_t current_width; + 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_get_split_mode (rig_.data (), RIG_VFO_CURR, ¤t_mode, ¤t_width), tr ("getting mode of split TX VFO")); - TRACE_CAT ("rig_get_split_mode mode = " << rig_strrmode (current_mode) << "bw =" << current_width); + error_check (rig_set_freq (rig_.data (), RIG_VFO_CURR, tx), tr ("setting frequency")); - auto new_mode = map_mode (state ().mode ()); - if (new_mode != current_mode) + if (set_rig_mode_ && mode_query_works_) { - TRACE_CAT ("rig_set_split_mode mode = " << rig_strrmode (new_mode)); - error_check (rig_set_split_mode (rig_.data (), RIG_VFO_CURR, new_mode, RIG_PASSBAND_NORMAL), tr ("setting split TX VFO mode")); + rmode_t current_mode; + pbwidth_t current_width; + auto new_mode = map_mode (state ().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); - // do this again as setting the mode may change the frequency + 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_NORMAL), 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 (set_rig_mode_) + { + auto new_mode = map_mode (state ().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_NORMAL), 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); } } - - // 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 ("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)) + else { - // 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")); + // 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); } - - update_other_frequency (tx); } } -void HamlibTransceiver::do_mode (MODE mode, bool rationalise) +void HamlibTransceiver::do_mode (MODE mode) { - TRACE_CAT (mode << "rationalise:" << rationalise); + TRACE_CAT ("HamlibTransceiver", mode); - auto vfos = get_vfos (); + 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; - - error_check (rig_get_mode (rig_.data (), RIG_VFO_CURR, ¤t_mode, ¤t_width), tr ("getting current VFO mode")); - TRACE_CAT ("rig_get_mode mode = " << rig_strrmode (current_mode) << "bw =" << current_width); - auto new_mode = map_mode (mode); - if (new_mode != current_mode) + + // only change when receiving or simplex if direct VFO addressing unavailable + if (!(state ().ptt () && state ().split () && one_VFO_)) { - TRACE_CAT ("rig_set_mode mode = " << rig_strrmode (new_mode)); - error_check (rig_set_mode (rig_.data (), RIG_VFO_CURR, new_mode, RIG_PASSBAND_NORMAL), tr ("setting current VFO mode")); - } - - if (!is_dummy_ && state ().split () && rationalise) - { - error_check (rig_get_split_mode (rig_.data (), RIG_VFO_CURR, ¤t_mode, ¤t_width), tr ("getting split TX VFO mode")); - TRACE_CAT ("rig_get_split_mode mode = " << rig_strrmode (current_mode) << "bw =" << current_width); + 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 ("rig_set_split_mode mode = " << rig_strrmode (new_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_NORMAL), 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_NORMAL), 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_NORMAL), tr ("setting split TX VFO mode")); } @@ -757,17 +843,22 @@ void HamlibTransceiver::poll () { 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 ("VFO =" << rig_strvfo (v)); + TRACE_CAT_POLL ("HamlibTransceiver", "VFO =" << rig_strvfo (v)); reversed_ = RIG_VFO_B == v; } - error_check (rig_get_freq (rig_.data (), RIG_VFO_CURR, &f), tr ("getting current VFO frequency")); - TRACE_CAT_POLL ("rig_get_freq frequency =" << f); - update_rx_frequency (f); + // only read 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")); + 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))) + && (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 @@ -779,12 +870,14 @@ void HamlibTransceiver::poll () , 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 current VFO frequency")); - TRACE_CAT_POLL ("rig_get_freq other VFO =" << f); + , &f), tr ("getting other VFO frequency")); + TRACE_CAT_POLL ("HamlibTransceiver", "rig_get_freq other VFO =" << f); update_other_frequency (f); } - if (mode_query_works_) + // 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 @@ -795,12 +888,12 @@ void HamlibTransceiver::poll () auto rc = rig_get_mode (rig_.data (), RIG_VFO_CURR, &m, &w); if (RIG_OK == rc) { - TRACE_CAT_POLL ("rig_get_mode mode =" << rig_strrmode (m) << "bw =" << w); + TRACE_CAT_POLL ("HamlibTransceiver", "rig_get_mode mode =" << rig_strrmode (m) << "bw =" << w); update_mode (map_mode (m)); } else { - TRACE_CAT_POLL ("rig_get_mode mode failed with rc:" << rc << "ignoring"); + TRACE_CAT_POLL ("HamlibTransceiver", "rig_get_mode mode failed with rc:" << rc << "ignoring"); } } @@ -811,7 +904,7 @@ void HamlibTransceiver::poll () auto rc = rig_get_split_vfo (rig_.data (), RIG_VFO_CURR, &s, &v); if (-RIG_OK == rc && RIG_SPLIT_ON == s) { - TRACE_CAT_POLL ("rig_get_split_vfo split = " << s << " VFO = " << rig_strvfo (v)); + TRACE_CAT_POLL ("HamlibTransceiver", "rig_get_split_vfo split = " << s << " VFO = " << rig_strvfo (v)); update_split (true); // if (RIG_VFO_A == v) // { @@ -820,14 +913,14 @@ void HamlibTransceiver::poll () } else if (-RIG_OK == rc) // not split { - TRACE_CAT_POLL ("rig_get_split_vfo split = " << s << " VFO = " << rig_strvfo (v)); + 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 ("rig_get_split_vfo can't do on this rig"); + 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; } @@ -842,7 +935,7 @@ void HamlibTransceiver::poll () // support command { error_check (rc, tr ("getting PTT state")); - TRACE_CAT_POLL ("rig_get_ptt PTT =" << p); + TRACE_CAT_POLL ("HamlibTransceiver", "rig_get_ptt PTT =" << p); update_PTT (!(RIG_PTT_OFF == p)); } } @@ -864,12 +957,12 @@ void HamlibTransceiver::poll () void HamlibTransceiver::do_ptt (bool on) { - TRACE_CAT (on << state () << "reversed =" << reversed_); + TRACE_CAT ("HamlibTransceiver", on << state () << "reversed =" << reversed_); if (on) { if (RIG_PTT_NONE != rig_->state.pttport.type.ptt) { - TRACE_CAT ("rig_set_ptt PTT = true"); + 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")); @@ -879,7 +972,7 @@ void HamlibTransceiver::do_ptt (bool on) { if (RIG_PTT_NONE != rig_->state.pttport.type.ptt) { - TRACE_CAT ("rig_set_ptt PTT = false"); + 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")); } } diff --git a/HamlibTransceiver.hpp b/HamlibTransceiver.hpp index d89527f22..4b452956e 100644 --- a/HamlibTransceiver.hpp +++ b/HamlibTransceiver.hpp @@ -26,16 +26,17 @@ class HamlibTransceiver final public: static void register_transceivers (TransceiverFactory::Transceivers *); - explicit HamlibTransceiver (int model_number, TransceiverFactory::ParameterPack const&); - explicit HamlibTransceiver (TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port); - ~HamlibTransceiver (); + explicit HamlibTransceiver (int model_number, TransceiverFactory::ParameterPack const&, + QObject * parent = nullptr); + explicit HamlibTransceiver (TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port, + QObject * parent = nullptr); private: - void do_start () override; + int do_start () override; void do_stop () override; - void do_frequency (Frequency, MODE) override; - void do_tx_frequency (Frequency, bool rationalise_mode) override; - void do_mode (MODE, bool rationalise) override; + void do_frequency (Frequency, MODE, bool no_ignore) override; + void do_tx_frequency (Frequency, bool no_ignore) override; + void do_mode (MODE) override; void do_ptt (bool) override; void poll () override; @@ -45,12 +46,14 @@ class HamlibTransceiver final 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 () const; + std::tuple get_vfos (bool for_split) const; struct RIGDeleter {static void cleanup (RIG *);}; QScopedPointer rig_; + bool set_rig_mode_; bool back_ptt_port_; + bool one_VFO_; bool is_dummy_; // these are saved on destruction so we can start new instances diff --git a/LiveFrequencyValidator.cpp b/LiveFrequencyValidator.cpp index eb9670657..48b683f35 100644 --- a/LiveFrequencyValidator.cpp +++ b/LiveFrequencyValidator.cpp @@ -13,20 +13,25 @@ LiveFrequencyValidator::LiveFrequencyValidator (QComboBox * combo_box , Bands const * bands , FrequencyList const * frequencies + , Frequency const * nominal_frequency , QWidget * parent) : QRegExpValidator { QRegExp { // frequency in MHz or band bands->data (QModelIndex {}).toString () // out of band string + QString {R"(|((\d{0,6}(\)"} // or up to 6 digits + QLocale {}.decimalPoint () // (followed by decimal separator - + R"(\d{0,2})?)([Mm]{1,2}|([Cc][Mm])))|(\d{0,4}(\)" // followed by up to 2 digits and either 'm' or 'cm' or 'mm' (case insensitive)) + + R"(\d{0,2})?)([Mm]{1,2}|([Cc][Mm])))|(\d{0,6}(\)" // followed by up to 2 digits and either 'm' or 'cm' or 'mm' (case insensitive)) + QLocale {}.decimalPoint () // or a decimal separator - + R"(\d{0,6})?))" // followed by up to 6 digits + + R"(\d{0,6})?)|(\d{0,3}(\)" // followed by up to 6 + // digits or a decimal number + + QLocale {}.decimalPoint () // or a decimal separator + + R"(\d{0,6})?[Kk]))" // followed by a 'k' or 'K' } , parent } , bands_ {bands} , frequencies_ {frequencies} + , nominal_frequency_ {nominal_frequency} , combo_box_ {combo_box} { } @@ -65,6 +70,14 @@ void LiveFrequencyValidator::fixup (QString& input) const input = QString {}; } } + else if (input.contains (QChar {'k'}, Qt::CaseInsensitive)) + { + // kHz in current MHz input + auto f = Radio::frequency (input.remove (QChar {'k'}, Qt::CaseInsensitive), 3); + f += *nominal_frequency_ / 1000000u * 1000000u; + input = bands_->find (f); + Q_EMIT valid (f); + } else { // frequency input diff --git a/LiveFrequencyValidator.hpp b/LiveFrequencyValidator.hpp index d4d6cc696..5fadf8d08 100644 --- a/LiveFrequencyValidator.hpp +++ b/LiveFrequencyValidator.hpp @@ -1,52 +1,56 @@ -#ifndef LIVE_FREQUENCY_VALIDATOR_HPP__ -#define LIVE_FREQUENCY_VALIDATOR_HPP__ - -#include -#include - -#include "Radio.hpp" - -class Bands; -class FrequencyList; -class QComboBox; -class QWidget; - -// -// Class LiveFrequencyValidator -// -// QLineEdit validator that controls input to an editable -// QComboBox where the user can enter a valid band or a valid -// frequency in megahetz. -// -// Collabrations -// -// Implements the QRegExpValidator interface. Validates input -// from the supplied QComboBox as either a valid frequency in -// megahertz or a valid band as defined by the supplied column of -// the supplied QAbstractItemModel. -// -class LiveFrequencyValidator final - : public QRegExpValidator -{ - Q_OBJECT; - -public: - using Frequency = Radio::Frequency; - - LiveFrequencyValidator (QComboBox * combo_box // associated combo box - , Bands const * bands // bands model - , FrequencyList const * frequencies // working frequencies model - , QWidget * parent = nullptr); - - State validate (QString& input, int& pos) const override; - void fixup (QString& input) const override; - - Q_SIGNAL void valid (Frequency) const; - -private: - Bands const * bands_; - FrequencyList const * frequencies_; - QComboBox * combo_box_; -}; - -#endif +#ifndef LIVE_FREQUENCY_VALIDATOR_HPP__ +#define LIVE_FREQUENCY_VALIDATOR_HPP__ + +#include +#include + +#include "Radio.hpp" + +class Bands; +class FrequencyList; +class QComboBox; +class QWidget; + +// +// Class LiveFrequencyValidator +// +// QLineEdit validator that controls input to an editable +// QComboBox where the user can enter a valid band or a valid +// frequency in megahetz. +// +// Collabrations +// +// Implements the QRegExpValidator interface. Validates input +// from the supplied QComboBox as either a valid frequency in +// megahertz or a valid band as defined by the supplied column of +// the supplied QAbstractItemModel. +// +class LiveFrequencyValidator final + : public QRegExpValidator +{ + Q_OBJECT; + +public: + using Frequency = Radio::Frequency; + + LiveFrequencyValidator (QComboBox * combo_box // associated combo box + , Bands const * bands // bands model + , FrequencyList const * frequencies // working + // frequencies + // model + , Frequency const * nominal_frequency + , QWidget * parent = nullptr); + + State validate (QString& input, int& pos) const override; + void fixup (QString& input) const override; + + Q_SIGNAL void valid (Frequency) const; + +private: + Bands const * bands_; + FrequencyList const * frequencies_; + Frequency const * nominal_frequency_; + QComboBox * combo_box_; +}; + +#endif diff --git a/OmniRigTransceiver.cpp b/OmniRigTransceiver.cpp index 4621d5f98..91fb145a1 100644 --- a/OmniRigTransceiver.cpp +++ b/OmniRigTransceiver.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "qt_helpers.hpP" @@ -48,7 +49,7 @@ auto OmniRigTransceiver::map_mode (OmniRig::RigParamX param) -> MODE { return FM; } - TRACE_CAT ("unrecognized mode"); + TRACE_CAT ("OmniRigTransceiver", "unrecognized mode"); throw_qstring (tr ("OmniRig: unrecognized mode")); return UNK; } @@ -70,7 +71,7 @@ OmniRig::RigParamX OmniRigTransceiver::map_mode (MODE mode) case DIG_FM: return OmniRig::PM_FM; default: break; } - return OmniRig::PM_SSB_U; // quieten compiler grumble + return OmniRig::PM_SSB_U; // quieten compiler grumble } void OmniRigTransceiver::register_transceivers (TransceiverFactory::Transceivers * registry, int id1, int id2) @@ -78,23 +79,26 @@ void OmniRigTransceiver::register_transceivers (TransceiverFactory::Transceivers (*registry)[OmniRig_transceiver_one_name] = TransceiverFactory::Capabilities { id1 , TransceiverFactory::Capabilities::none // COM isn't serial or network - , true // does PTT - , false // doesn't select mic/data (use OmniRig config file) - , true // can remote control RTS nd DTR - , true // asynchronous interface + , true // does PTT + , false // doesn't select mic/data (use OmniRig config file) + , true // can remote control RTS nd DTR + , true // asynchronous interface }; (*registry)[OmniRig_transceiver_two_name] = TransceiverFactory::Capabilities { id2 , TransceiverFactory::Capabilities::none // COM isn't serial or network - , true // does PTT - , false // doesn't select mic/data (use OmniRig config file) - , true // can remote control RTS nd DTR - , true // asynchronous interface + , true // does PTT + , false // doesn't select mic/data (use OmniRig config file) + , true // can remote control RTS nd DTR + , true // asynchronous interface }; } -OmniRigTransceiver::OmniRigTransceiver (std::unique_ptr wrapped, RigNumber n, TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port) - : wrapped_ {std::move (wrapped)} +OmniRigTransceiver::OmniRigTransceiver (std::unique_ptr wrapped, + RigNumber n, TransceiverFactory::PTTMethod ptt_type, + QString const& ptt_port, QObject * parent) + : TransceiverBase {parent} + , wrapped_ {std::move (wrapped)} , use_for_ptt_ {TransceiverFactory::PTT_method_CAT == ptt_type || ("CAT" == ptt_port && (TransceiverFactory::PTT_method_RTS == ptt_type || TransceiverFactory::PTT_method_DTR == ptt_type))} , ptt_type_ {ptt_type} , rig_number_ {n} @@ -105,21 +109,17 @@ OmniRigTransceiver::OmniRigTransceiver (std::unique_ptr wrapped { } -OmniRigTransceiver::~OmniRigTransceiver () +int OmniRigTransceiver::do_start () { -} - -void OmniRigTransceiver::do_start () -{ - TRACE_CAT ("starting"); - wrapped_->start (); + TRACE_CAT ("OmniRigTransceiver", "starting"); + if (wrapped_) wrapped_->start (0); CoInitializeEx (nullptr, 0 /*COINIT_APARTMENTTHREADED*/); // required because Qt only does this for GUI thread omni_rig_.reset (new OmniRig::OmniRigX {this}); if (omni_rig_->isNull ()) { - TRACE_CAT ("failed to start COM server"); + TRACE_CAT ("OmniRigTransceiver", "failed to start COM server"); throw_qstring (tr ("Failed to start OmniRig COM server")); } @@ -135,7 +135,7 @@ void OmniRigTransceiver::do_start () , SIGNAL (CustomReply (int, QVariant const&, QVariant const&)) , this, SLOT (handle_custom_reply (int, QVariant const&, QVariant const&))); - TRACE_CAT ("OmniRig s/w version:" << QString::number (omni_rig_->SoftwareVersion ()).toLocal8Bit () + TRACE_CAT ("OmniRigTransceiver", "OmniRig s/w version:" << QString::number (omni_rig_->SoftwareVersion ()).toLocal8Bit () << "i/f version:" << QString::number (omni_rig_->InterfaceVersion ()).toLocal8Bit ()); // fetch the interface of the RigX CoClass and instantiate a proxy object @@ -155,18 +155,19 @@ void OmniRigTransceiver::do_start () Q_ASSERT (port_); Q_ASSERT (!port_->isNull ()); + TRACE_CAT ("OmniRigTransceiver", "OmniRig RTS state:" << port_->Rts ()); - // if (!port_->Lock ()) // try to take exclusive use of the OmniRig serial port for PTT - // { - // throw_qstring (tr ("Failed to get exclusive use of %1" from OmniRig").arg (ptt_type)); - // } + if (!port_->Lock ()) // try to take exclusive use of the OmniRig serial port for PTT + { + TRACE_CAT ("OmniRigTransceiver", "Failed to get exclusive use of serial port for PTT from OmniRig"); + } // start off so we don't accidentally key the radio if (TransceiverFactory::PTT_method_DTR == ptt_type_) { port_->SetDtr (false); } - else // RTS + else // RTS { port_->SetRts (false); } @@ -175,66 +176,81 @@ void OmniRigTransceiver::do_start () readable_params_ = rig_->ReadableParams (); writable_params_ = rig_->WriteableParams (); - TRACE_CAT (QString {"OmniRig initial rig type: %1 readable params = 0x%2 writable params = 0x%3 for rig %4"} + TRACE_CAT ("OmniRigTransceiver", QString {"OmniRig initial rig type: %1 readable params = 0x%2 writable params = 0x%3 for rig %4"} .arg (rig_->RigType ()) .arg (readable_params_, 8, 16, QChar ('0')) .arg (writable_params_, 8, 16, QChar ('0')) .arg (rig_number_).toLocal8Bit ()); - TRACE_CAT ("OmniRig status:" << rig_->StatusStr ()); - - init_rig (); + for (unsigned tries {0}; tries < 10; ++tries) + { + QThread::msleep (100); // wait until OmniRig polls the rig + auto f = rig_->GetRxFrequency (); + int resolution {0}; + if (f) + { + if (f % 10) return resolution; // 1Hz resolution + auto test_frequency = f - f % 100 + 55; + if (OmniRig::PM_FREQ & writable_params_) + { + rig_->SetFreq (test_frequency); + } + else if (reversed_ && (OmniRig::PM_FREQB & writable_params_)) + { + rig_->SetFreqB (test_frequency); + } + else if (!reversed_ && (OmniRig::PM_FREQA & writable_params_)) + { + rig_->SetFreqA (test_frequency); + } + else + { + throw_qstring (tr ("OmniRig: don't know how to set rig frequency")); + } + switch (rig_->GetRxFrequency () - test_frequency) + { + case -5: resolution = -1; break; // 10Hz truncated + case 5: resolution = 1; break; // 10Hz rounded + case -55: resolution = -2; break; // 100Hz truncated + case 45: resolution = 2; break; // 100Hz rounded + } + if (OmniRig::PM_FREQ & writable_params_) + { + rig_->SetFreq (f); + } + else if (reversed_ && (OmniRig::PM_FREQB & writable_params_)) + { + rig_->SetFreqB (f); + } + else if (!reversed_ && (OmniRig::PM_FREQA & writable_params_)) + { + rig_->SetFreqA (f); + } + update_rx_frequency (f); + return resolution; + } + } + throw_qstring (tr ("OmniRig: Initialization timed out")); + return 0; // keep compiler happy } void OmniRigTransceiver::do_stop () { + QThread::msleep (200); // leave some time for pending + // commands at the server end if (port_) { - // port_->Unlock (); // release serial port + port_->Unlock (); // release serial port port_->clear (); } - rig_->clear (); - omni_rig_->clear (); + if (rig_) rig_->clear (); + if (omni_rig_) omni_rig_->clear (); CoUninitialize (); - wrapped_->stop (); - TRACE_CAT ("stopped"); + if (wrapped_) wrapped_->stop (); + TRACE_CAT ("OmniRigTransceiver", "stopped"); } -void OmniRigTransceiver::online_check () -{ - if (OmniRig::ST_ONLINE != rig_->Status ()) - { - offline ("OmniRig rig went offline for more than 5 seconds"); - } - else - { - init_rig (); - } -} - -void OmniRigTransceiver::init_rig () -{ - if (OmniRig::ST_ONLINE != rig_->Status ()) - { - QTimer::singleShot (5000, this, SLOT (online_check ())); - } - else - { - update_rx_frequency (rig_->GetRxFrequency ()); - if (state ().split ()) - { - TRACE_CAT ("set split"); - rig_->SetSplitMode (state ().frequency (), state ().tx_frequency ()); - } - else - { - TRACE_CAT ("set simplex"); - rig_->SetSimplexMode (state ().frequency ()); - } - } -} - -void OmniRigTransceiver::do_sync (bool force_signal) +void OmniRigTransceiver::do_sync (bool force_signal, bool /*no_poll*/) { // nothing much we can do here, we just have to let OmniRig do its // stuff and its first poll should send us and update that will @@ -247,13 +263,13 @@ void OmniRigTransceiver::do_sync (bool force_signal) void OmniRigTransceiver::handle_COM_exception (int code, QString source, QString desc, QString help) { - TRACE_CAT (QString::number (code) + " at " + source + ": " + desc + " (" + help + ')'); + TRACE_CAT ("OmniRigTransceiver", QString::number (code) + " at " + source + ": " + desc + " (" + help + ')'); throw_qstring (tr ("OmniRig COM/OLE error: %1 at %2: %3 (%4)").arg (QString::number (code)).arg (source). arg (desc). arg (help)); } void OmniRigTransceiver::handle_visible_change () { - TRACE_CAT ("visibility change: visibility =" << omni_rig_->DialogVisible ()); + TRACE_CAT ("OmniRigTransceiver", "visibility change: visibility =" << omni_rig_->DialogVisible ()); } void OmniRigTransceiver::handle_rig_type_change (int rig_number) @@ -262,7 +278,7 @@ void OmniRigTransceiver::handle_rig_type_change (int rig_number) { readable_params_ = rig_->ReadableParams (); writable_params_ = rig_->WriteableParams (); - TRACE_CAT (QString {"OmniRig rig type change to: %1 readable params = 0x%2 writable params = 0x%3 for rig %4"} + TRACE_CAT ("OmniRigTransceiver", QString {"OmniRig rig type change to: %1 readable params = 0x%2 writable params = 0x%3 for rig %4"} .arg (rig_->RigType ()) .arg (readable_params_, 8, 16, QChar ('0')) .arg (writable_params_, 8, 16, QChar ('0')) @@ -275,11 +291,15 @@ void OmniRigTransceiver::handle_status_change (int rig_number) { if (rig_number_ == rig_number) { - TRACE_CAT (QString {"OmniRig status change: new status for rig %1 = "}.arg (rig_number).toLocal8Bit () << rig_->StatusStr ().toLocal8Bit ()); + TRACE_CAT ("OmniRigTransceiver", QString {"OmniRig status change: new status for rig %1 = "}.arg (rig_number).toLocal8Bit () << rig_->StatusStr ().toLocal8Bit ()); if (OmniRig::ST_ONLINE != rig_->Status ()) { QTimer::singleShot (5000, this, SLOT (online_check ())); } + else + { + TRACE_CAT ("OmniRigTransceiver", "OmniRig frequency:" << rig_->GetRxFrequency ()); + } } } @@ -287,16 +307,16 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params) { if (rig_number_ == rig_number) { - TRACE_CAT (QString {"OmniRig params change: params = 0x%1 for rig %2"} + TRACE_CAT ("OmniRigTransceiver", QString {"OmniRig params change: params = 0x%1 for rig %2"} .arg (params, 8, 16, QChar ('0')) .arg (rig_number).toLocal8Bit () << "state before:" << state ()); // starting_ = false; TransceiverState old_state {state ()}; auto need_frequency = false; - // state_.online = true; // sometimes we don't get an initial - // // OmniRig::ST_ONLINE status change - // // event + // state_.online = true; // sometimes we don't get an initial + // // OmniRig::ST_ONLINE status change + // // event if (params & OmniRig::PM_VFOAA) { update_split (false); @@ -481,7 +501,7 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params) update_complete (); send_update_signal_ = false; } - TRACE_CAT ("OmniRig params change: state after:" << state ()); + TRACE_CAT ("OmniRigTransceiver", "OmniRig params change: state after:" << state ()); } } @@ -492,19 +512,19 @@ void OmniRigTransceiver::handle_custom_reply (int rig_number, QVariant const& co if (rig_number_ == rig_number) { - TRACE_CAT ("OmniRig custom command" << command.toString ().toLocal8Bit () + TRACE_CAT ("OmniRigTransceiver", "custom command" << command.toString ().toLocal8Bit () << "with reply" << reply.toString ().toLocal8Bit () << QString ("for rig %1").arg (rig_number).toLocal8Bit ()); - TRACE_CAT ("OmniRig rig number:" << rig_number_ << ':' << state ()); + TRACE_CAT ("OmniRigTransceiver", "rig number:" << rig_number_ << ':' << state ()); } } void OmniRigTransceiver::do_ptt (bool on) { - TRACE_CAT (on << state ()); + TRACE_CAT ("OmniRigTransceiver", on << state ()); if (use_for_ptt_ && TransceiverFactory::PTT_method_CAT == ptt_type_) { - TRACE_CAT ("set PTT"); + TRACE_CAT ("OmniRigTransceiver", "set PTT"); rig_->SetTx (on ? OmniRig::PM_TX : OmniRig::PM_RX); } else @@ -513,19 +533,22 @@ void OmniRigTransceiver::do_ptt (bool on) { if (TransceiverFactory::PTT_method_RTS == ptt_type_) { - TRACE_CAT ("set RTS"); + TRACE_CAT ("OmniRigTransceiver", "set RTS"); port_->SetRts (on); } - else // "DTR" + else // "DTR" { - TRACE_CAT ("set DTR"); + TRACE_CAT ("OmniRigTransceiver", "set DTR"); port_->SetDtr (on); } } else { - TRACE_CAT ("set PTT using basic transceiver"); - wrapped_->ptt (on); + TRACE_CAT ("OmniRigTransceiver", "set PTT using basic transceiver"); + Q_ASSERT (wrapped_); + TransceiverState new_state {wrapped_->state ()}; + new_state.ptt (on); + wrapped_->set (new_state, 0); } if (state ().ptt () != on) { @@ -536,12 +559,12 @@ void OmniRigTransceiver::do_ptt (bool on) } } -void OmniRigTransceiver::do_frequency (Frequency f, MODE m) +void OmniRigTransceiver::do_frequency (Frequency f, MODE m, bool /*no_ignore*/) { - TRACE_CAT (f << state ()); + TRACE_CAT ("OmniRigTransceiver", f << state ()); if (UNK != m) { - do_mode (m, false); + do_mode (m); } if (OmniRig::PM_FREQ & writable_params_) { @@ -564,20 +587,20 @@ void OmniRigTransceiver::do_frequency (Frequency f, MODE m) } } -void OmniRigTransceiver::do_tx_frequency (Frequency tx, bool /* rationalise_mode */) +void OmniRigTransceiver::do_tx_frequency (Frequency tx, bool /*no_ignore*/) { - TRACE_CAT (tx << state ()); + TRACE_CAT ("OmniRigTransceiver", tx << state ()); bool split {tx != 0}; if (split) { - TRACE_CAT ("set SPLIT mode on"); + TRACE_CAT ("OmniRigTransceiver", "set SPLIT mode on"); rig_->SetSplitMode (state ().frequency (), tx); update_other_frequency (tx); update_split (true); } else { - TRACE_CAT ("set SPLIT mode off"); + TRACE_CAT ("OmniRigTransceiver", "set SPLIT mode off"); rig_->SetSimplexMode (state ().frequency ()); update_split (false); } @@ -592,8 +615,8 @@ void OmniRigTransceiver::do_tx_frequency (Frequency tx, bool /* rationalise_mode } if (!((OmniRig::PM_VFOAB | OmniRig::PM_VFOBA | OmniRig::PM_SPLITON) & readable_params_)) { - TRACE_CAT ("setting SPLIT manually"); - update_split (split); // we can't read it so just set and + TRACE_CAT ("OmniRigTransceiver", "setting SPLIT manually"); + update_split (split); // we can't read it so just set and // hope op doesn't change it notify = true; } @@ -603,9 +626,9 @@ void OmniRigTransceiver::do_tx_frequency (Frequency tx, bool /* rationalise_mode } } -void OmniRigTransceiver::do_mode (MODE mode, bool /* rationalise */) +void OmniRigTransceiver::do_mode (MODE mode) { - TRACE_CAT (mode << state ()); + TRACE_CAT ("OmniRigTransceiver", mode << state ()); // TODO: G4WJS OmniRig doesn't seem to have any capability of tracking/setting VFO B mode auto mapped = map_mode (mode); if (mapped & writable_params_) diff --git a/OmniRigTransceiver.hpp b/OmniRigTransceiver.hpp index d5c41a9b2..dc5b0dae9 100644 --- a/OmniRigTransceiver.hpp +++ b/OmniRigTransceiver.hpp @@ -30,19 +30,17 @@ public: enum RigNumber {One = 1, Two}; // takes ownership of wrapped Transceiver - explicit OmniRigTransceiver (std::unique_ptr wrapped, RigNumber, TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port); - ~OmniRigTransceiver (); + explicit OmniRigTransceiver (std::unique_ptr wrapped, RigNumber, TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port, QObject * parent = nullptr); - void do_start () override; + int do_start () override; void do_stop () override; - void do_frequency (Frequency, MODE) override; - void do_tx_frequency (Frequency, bool rationalise_mode) override; - void do_mode (MODE, bool rationalise) override; + void do_frequency (Frequency, MODE, bool no_ignore) override; + void do_tx_frequency (Frequency, bool no_ignore) override; + void do_mode (MODE) override; void do_ptt (bool on) override; - void do_sync (bool force_signal) override; + void do_sync (bool force_signal, bool no_poll) override; private: - Q_SLOT void online_check (); Q_SLOT void handle_COM_exception (int, QString, QString, QString); Q_SLOT void handle_visible_change (); Q_SLOT void handle_rig_type_change (int rig_number); @@ -50,12 +48,10 @@ private: Q_SLOT void handle_params_change (int rig_number, int params); Q_SLOT void handle_custom_reply (int, QVariant const& command, QVariant const& reply); - void init_rig (); - static MODE map_mode (OmniRig::RigParamX param); static OmniRig::RigParamX map_mode (MODE mode); - std::unique_ptr wrapped_; + std::unique_ptr wrapped_; // may be null bool use_for_ptt_; TransceiverFactory::PTTMethod ptt_type_; QScopedPointer omni_rig_; diff --git a/PollingTransceiver.cpp b/PollingTransceiver.cpp index 04da65ef6..ede371e48 100644 --- a/PollingTransceiver.cpp +++ b/PollingTransceiver.cpp @@ -13,8 +13,9 @@ namespace unsigned const polls_to_stabilize {3}; } -PollingTransceiver::PollingTransceiver (int poll_interval) - : interval_ {poll_interval * 1000} +PollingTransceiver::PollingTransceiver (int poll_interval, QObject * parent) + : TransceiverBase {parent} + , interval_ {poll_interval * 1000} , poll_timer_ {nullptr} , retries_ {0} { @@ -26,12 +27,19 @@ void PollingTransceiver::start_timer () { if (!poll_timer_) { - poll_timer_ = new QTimer {this}; // pass ownership to QObject which handles destruction for us + poll_timer_ = new QTimer {this}; // pass ownership to + // QObject which handles + // destruction for us - connect (poll_timer_, &QTimer::timeout, this, &PollingTransceiver::handle_timeout); + connect (poll_timer_, &QTimer::timeout, this, + &PollingTransceiver::handle_timeout); } poll_timer_->start (interval_); } + else + { + stop_timer (); + } } void PollingTransceiver::stop_timer () @@ -77,7 +85,7 @@ void PollingTransceiver::do_post_frequency (Frequency f, MODE m) } } -void PollingTransceiver::do_post_tx_frequency (Frequency f, bool /* rationalize */) +void PollingTransceiver::do_post_tx_frequency (Frequency f) { if (next_state_.tx_frequency () != f) { @@ -89,7 +97,7 @@ void PollingTransceiver::do_post_tx_frequency (Frequency f, bool /* rationalize } } -void PollingTransceiver::do_post_mode (MODE m, bool /*rationalize_mode*/) +void PollingTransceiver::do_post_mode (MODE m) { // we don't ever expect mode to goto to unknown if (m != UNK && next_state_.mode () != m) @@ -106,7 +114,8 @@ void PollingTransceiver::do_post_ptt (bool p) { // update expected state with new PTT and set poll count next_state_.ptt (p); - retries_ = 0; // fast feedback on PTT + retries_ = polls_to_stabilize; + //retries_ = 0; // fast feedback on PTT } } @@ -120,10 +129,9 @@ bool PollingTransceiver::do_pre_update () return true; } -void PollingTransceiver::do_sync (bool force_signal) +void PollingTransceiver::do_sync (bool force_signal, bool no_poll) { - poll (); // tell sub-classes to update our - // state + if (!no_poll) poll (); // tell sub-classes to update our state // Signal new state if it is directly requested or, what we expected // or, hasn't become what we expected after polls_to_stabilize @@ -142,9 +150,9 @@ void PollingTransceiver::do_sync (bool force_signal) } else if (force_signal || state () != last_signalled_state_) { - // here is the normal passive polling path - // either our client has requested a state update regardless of change - // or sate has changed asynchronously + // here is the normal passive polling path either our client has + // requested a state update regardless of change or state has + // changed asynchronously force_signal = true; } @@ -154,7 +162,7 @@ void PollingTransceiver::do_sync (bool force_signal) retries_ = 0; next_state_ = state (); last_signalled_state_ = state (); - update_complete (); + update_complete (true); } } @@ -166,7 +174,7 @@ void PollingTransceiver::handle_timeout () // inform our parent of the failure via the offline() message try { - do_sync (false); + do_sync (); } catch (std::exception const& e) { diff --git a/PollingTransceiver.hpp b/PollingTransceiver.hpp index 2ae523d14..b81d49283 100644 --- a/PollingTransceiver.hpp +++ b/PollingTransceiver.hpp @@ -35,10 +35,11 @@ class PollingTransceiver Q_OBJECT; // for translation context protected: - explicit PollingTransceiver (int poll_interval); // in seconds + explicit PollingTransceiver (int poll_interval, // in seconds + QObject * parent); protected: - void do_sync (bool force_signal) override final; + void do_sync (bool force_signal = false, bool no_poll = false) override final; // Sub-classes implement this and fetch what they can from the rig // in a non-intrusive manner. @@ -46,9 +47,9 @@ protected: void do_post_start () override final; void do_post_stop () override final; - void do_post_frequency (Frequency, MODE = UNK) override final; - void do_post_tx_frequency (Frequency, bool rationalize = true) override final; - void do_post_mode (MODE, bool rationalize = true) override final; + void do_post_frequency (Frequency, MODE) override final; + void do_post_tx_frequency (Frequency) override final; + void do_post_mode (MODE) override final; void do_post_ptt (bool = true) override final; bool do_pre_update () override final; diff --git a/Transceiver.cpp b/Transceiver.cpp index 31a29be83..a74e2a65a 100644 --- a/Transceiver.cpp +++ b/Transceiver.cpp @@ -10,8 +10,8 @@ QDebug operator << (QDebug d, Transceiver::TransceiverState const& s) { d.nospace () << "Transceiver::TransceiverState(online: " << (s.online_ ? "yes" : "no") - << " Frequency {" << s.frequency_[0] << "Hz, " << s.frequency_[1] << "Hz} " << s.mode_ - << "; SPLIT: " << (Transceiver::TransceiverState::on == s.split_ ? "on" : Transceiver::TransceiverState::off == s.split_ ? "off" : "unknown") + << " Frequency {" << s.rx_frequency_ << "Hz, " << s.tx_frequency_ << "Hz} " << s.mode_ + << "; SPLIT: " << (Transceiver::TransceiverState::Split::on == s.split_ ? "on" : Transceiver::TransceiverState::Split::off == s.split_ ? "off" : "unknown") << "; PTT: " << (s.ptt_ ? "on" : "off") << ')'; return d.space (); @@ -26,8 +26,8 @@ ENUM_CONVERSION_OPS_IMPL (Transceiver, MODE); bool operator != (Transceiver::TransceiverState const& lhs, Transceiver::TransceiverState const& rhs) { return lhs.online_ != rhs.online_ - || lhs.frequency_[0] != rhs.frequency_[0] - || lhs.frequency_[1] != rhs.frequency_[1] + || lhs.rx_frequency_ != rhs.rx_frequency_ + || lhs.tx_frequency_ != rhs.tx_frequency_ || lhs.mode_ != rhs.mode_ || lhs.split_ != rhs.split_ || lhs.ptt_ != rhs.ptt_; diff --git a/Transceiver.hpp b/Transceiver.hpp index f84215693..65b190825 100644 --- a/Transceiver.hpp +++ b/Transceiver.hpp @@ -16,9 +16,9 @@ class QString; // // Responsibilities // -// Provides Qt slots to set the frequency, mode and PTT of some -// transceiver. They are Qt slots so that they may be invoked across -// a thread boundary. +// Provides a Qt slot to set the frequency, mode and PTT of some +// transceiver. This is a Qt slot so that it may be invoked across a +// thread boundary. // // Provides a synchronisation Qt slot which should be implemented in // sub-classes in such a way that normal operation of the rig is not @@ -58,14 +58,10 @@ public: using Frequency = Radio::Frequency; protected: - Transceiver () - { - } + Transceiver (QObject * parent) : QObject {parent} {} public: - virtual ~Transceiver () - { - } + virtual ~Transceiver () {} enum MODE {UNK, CW, CW_R, USB, LSB, FSK, FSK_R, DIG_U, DIG_L, AM, FM, DIG_FM}; Q_ENUM (MODE) @@ -79,33 +75,34 @@ public: public: TransceiverState () : online_ {false} - , frequency_ {0, 0} + , rx_frequency_ {0} + , tx_frequency_ {0} , mode_ {UNK} - , split_ {unknown} + , split_ {Split::unknown} , ptt_ {false} { } bool online () const {return online_;} - Frequency frequency () const {return frequency_[0];} - Frequency tx_frequency () const {return frequency_[1];} - bool split () const {return on == split_;} - bool compare_split (bool with) const {return split_ == (with ? on : off);} + Frequency frequency () const {return rx_frequency_;} + Frequency tx_frequency () const {return tx_frequency_;} + bool split () const {return Split::on == split_;} MODE mode () const {return mode_;} bool ptt () const {return ptt_;} void online (bool state) {online_ = state;} - void frequency (Frequency f) {frequency_[0] = f;} - void tx_frequency (Frequency f) {frequency_[1] = f;} - void split (bool state) {split_ = state ? on : off;} + void frequency (Frequency f) {rx_frequency_ = f;} + void tx_frequency (Frequency f) {tx_frequency_ = f;} + void split (bool state) {split_ = state ? Split::on : Split::off;} void mode (MODE m) {mode_ = m;} void ptt (bool state) {ptt_ = state;} private: bool online_; - Frequency frequency_[2]; // [0] -> Rx; [1] -> Other + Frequency rx_frequency_; + Frequency tx_frequency_; // 0 means use Rx MODE mode_; - enum {unknown, off, on} split_; + enum class Split {unknown, off, on} split_; bool ptt_; // Don't forget to update the debug print and != operator if you // add more members here @@ -115,39 +112,42 @@ public: }; // - // The following slots and signals are expected to all run in the - // same thread which is not necessarily the main GUI thread. It is - // up to the client of the Transceiver class to organise the + // The following slots and signals are expected to all run in the + // same thread which is not necessarily the main GUI thread. It is + // up to the client of the Transceiver class to organise the // allocation to a thread and the lifetime of the object instances. // + // Apply state changes to the rig. The sequence_number parameter + // will be included in any status updates generated after this + // transaction is processed. The sequence number may be used to + // ignore any status updates until the results of this transaction + // have been processed thus avoiding any unwanted "ping-pong" due to + // signals crossing in transit. + Q_SLOT virtual void set (Transceiver::TransceiverState const&, + unsigned sequence_number) noexcept = 0; + // Connect and disconnect. - Q_SLOT virtual void start () noexcept = 0; - Q_SLOT virtual void stop (bool reset_split = false) noexcept = 0; + Q_SLOT virtual void start (unsigned sequence_number) noexcept = 0; + Q_SLOT virtual void stop () noexcept = 0; - // Set frequency in Hertz. - Q_SLOT virtual void frequency (Frequency, MODE = UNK) noexcept = 0; - - // Setting a non-zero TX frequency means split operation, the value - // zero means simplex operation. // - // Rationalise_mode means ensure TX uses same mode as RX. - Q_SLOT virtual void tx_frequency (Frequency tx = 0, bool rationalise_mode = true) noexcept = 0; - - // Set mode. - // Rationalise means ensure TX uses same mode as RX. - Q_SLOT virtual void mode (MODE, bool rationalise = true) noexcept = 0; - - // Set/unset PTT. - Q_SLOT virtual void ptt (bool = true) noexcept = 0; - - // Attempt to re-synchronise or query state. - // Force_signal guarantees a update or failure signal. - Q_SLOT virtual void sync (bool force_signal = false) noexcept = 0; - // asynchronous status updates - Q_SIGNAL void update (Transceiver::TransceiverState) const; - Q_SIGNAL void failure (QString reason) const; + // + + // 0 - 1Hz + // 1 - 10Hz rounded + // -1 - 10Hz truncated + // 2 - 100Hz rounded + // -2 - 100Hz truncated + Q_SIGNAL void resolution (int); + + // rig state changed + Q_SIGNAL void update (Transceiver::TransceiverState const&, + unsigned sequence_number) const; + + // something went wrong - not recoverable, start new instance + Q_SIGNAL void failure (QString const& reason) const; // Ready to be destroyed. Q_SIGNAL void finished () const; diff --git a/TransceiverBase.cpp b/TransceiverBase.cpp index fc1d5f09a..7b5bcc09c 100644 --- a/TransceiverBase.cpp +++ b/TransceiverBase.cpp @@ -14,29 +14,15 @@ namespace auto const unexpected = TransceiverBase::tr ("Unexpected rig error"); } -void TransceiverBase::start () noexcept +void TransceiverBase::start (unsigned sequence_number) noexcept { QString message; try { - if (state_.online ()) - { - try - { - // try and ensure PTT isn't left set - do_ptt (false); - do_post_ptt (false); - } - catch (...) - { - // don't care about exceptions - } - do_stop (); - do_post_stop (); - } - do_start (); - do_post_start (); - state_.online (true); + may_update u {this, true}; + shutdown (); + startup (); + last_sequence_number_ = sequence_number; } catch (std::exception const& e) { @@ -52,33 +38,138 @@ void TransceiverBase::start () noexcept } } -void TransceiverBase::stop (bool reset_split) noexcept +void TransceiverBase::set (TransceiverState const& s, + unsigned sequence_number) noexcept +{ + TRACE_CAT ("TransceiverBase", "#:" << sequence_number << s); + + QString message; + try + { + may_update u {this, true}; + bool was_online {requested_.online ()}; + if (!s.online () && was_online) + { + shutdown (); + } + else if (s.online () && !was_online) + { + shutdown (); + startup (); + } + if (requested_.online ()) + { + bool ptt_on {false}; + bool ptt_off {false}; + if (s.ptt () != requested_.ptt ()) + { + ptt_on = s.ptt (); + ptt_off = !s.ptt (); + } + if (ptt_off) + { + do_ptt (false); + do_post_ptt (false); + QThread::msleep (100); // some rigs cannot process CAT + // commands while switching from + // Tx to Rx + } + if ((s.frequency () != requested_.frequency () // and QSY + || (s.mode () != UNK && s.mode () != requested_.mode ())) // or mode change + || ptt_off) // or just returned to rx + { + do_frequency (s.frequency (), s.mode (), ptt_off); + do_post_frequency (s.frequency (), s.mode ()); + + // record what actually changed + requested_.frequency (actual_.frequency ()); + requested_.mode (actual_.mode ()); + } + if (!s.tx_frequency () || s.tx_frequency () > 10000) // ignore bogus startup values + { + if ((s.tx_frequency () != requested_.tx_frequency () // and QSY + || (s.mode () != UNK && s.mode () != requested_.mode ())) // or mode change + // || s.split () != requested_.split ())) // or split change + || ptt_on) // or about to tx + { + do_tx_frequency (s.tx_frequency (), ptt_on); + do_post_tx_frequency (s.tx_frequency ()); + + // record what actually changed + requested_.tx_frequency (actual_.tx_frequency ()); + requested_.split (actual_.split ()); + } + } + if (ptt_on) + { + do_ptt (true); + do_post_ptt (true); + QThread::msleep (100); // some rigs cannot process CAT + // commands while switching from + // Rx to Tx + } + + // record what actually changed + requested_.ptt (actual_.ptt ()); + } + last_sequence_number_ = sequence_number; + } + catch (std::exception const& e) + { + message = e.what (); + } + catch (...) + { + message = unexpected; + } + if (!message.isEmpty ()) + { + offline (message); + } +} + +void TransceiverBase::startup () +{ + Q_EMIT resolution (do_start ()); + do_post_start (); + actual_.online (true); + requested_.online (true); +} + +void TransceiverBase::shutdown () +{ + may_update u {this}; + if (requested_.online ()) + { + try + { + // try and ensure PTT isn't left set + do_ptt (false); + do_post_ptt (false); + if (requested_.split ()) + { + // try and reset split mode + do_tx_frequency (0, true); + do_post_tx_frequency (0); + } + } + catch (...) + { + // don't care about exceptions + } + } + do_stop (); + do_post_stop (); + actual_.online (false); + requested_.online (false); +} + +void TransceiverBase::stop () noexcept { QString message; try { - if (state_.online ()) - { - try - { - // try and ensure PTT isn't left set - do_ptt (false); - do_post_ptt (false); - if (reset_split) - { - // try and reset split mode - do_tx_frequency (0, false); - do_post_tx_frequency (0, false); - } - } - catch (...) - { - // don't care about exceptions - } - } - do_stop (); - do_post_stop (); - state_.online (false); + shutdown (); } catch (std::exception const& e) { @@ -98,206 +189,82 @@ void TransceiverBase::stop (bool reset_split) noexcept } } -void TransceiverBase::frequency (Frequency f, MODE m) noexcept -{ - QString message; - try - { - if (state_.online ()) - { - do_frequency (f, m); - do_post_frequency (f, m); - } - } - catch (std::exception const& e) - { - message = e.what (); - } - catch (...) - { - message = unexpected; - } - if (!message.isEmpty ()) - { - offline (message); - } -} - -void TransceiverBase::tx_frequency (Frequency tx, bool rationalise_mode) noexcept -{ - QString message; - try - { - if (state_.online ()) - { - do_tx_frequency (tx, rationalise_mode); - do_post_tx_frequency (tx, rationalise_mode); - } - } - catch (std::exception const& e) - { - message = e.what (); - } - catch (...) - { - message = unexpected; - } - if (!message.isEmpty ()) - { - offline (message); - } -} - -void TransceiverBase::mode (MODE m, bool rationalise) noexcept -{ - QString message; - try - { - if (state_.online ()) - { - do_mode (m, rationalise); - do_post_mode (m, rationalise); - } - } - catch (std::exception const& e) - { - message = e.what (); - } - catch (...) - { - message = unexpected; - } - if (!message.isEmpty ()) - { - offline (message); - } -} - -void TransceiverBase::ptt (bool on) noexcept -{ - QString message; - try - { - if (state_.online ()) - { - do_ptt (on); - do_post_ptt (on); - } - } - catch (std::exception const& e) - { - message = e.what (); - } - catch (...) - { - message = unexpected; - } - if (!message.isEmpty ()) - { - offline (message); - } -} - -void TransceiverBase::sync (bool force_signal) noexcept -{ - QString message; - try - { - if (state_.online ()) - { - do_sync (force_signal); - } - } - catch (std::exception const& e) - { - message = e.what (); - } - catch (...) - { - message = unexpected; - } - if (!message.isEmpty ()) - { - offline (message); - } -} - void TransceiverBase::update_rx_frequency (Frequency rx) { - state_.frequency (rx); + actual_.frequency (rx); + requested_.frequency (rx); // track rig changes } void TransceiverBase::update_other_frequency (Frequency tx) { - state_.tx_frequency (tx); + actual_.tx_frequency (tx); } void TransceiverBase::update_split (bool state) { - state_.split (state); + actual_.split (state); } void TransceiverBase::update_mode (MODE m) { - state_.mode (m); + actual_.mode (m); } void TransceiverBase::update_PTT (bool state) { - auto prior = state_.ptt (); - state_.ptt (state); - if (state != prior) - { - // always signal PTT changes because some MainWindow logic - // depends on it - update_complete (); - } + actual_.ptt (state); } -void TransceiverBase::updated () - { - if (do_pre_update ()) - { - Q_EMIT update (state_); - } - } - -void TransceiverBase::update_complete () +bool TransceiverBase::maybe_low_resolution (Radio::Frequency low_res, + Radio::Frequency high_res) { - // Use a timer to ensure that the calling function completes before - // the Transceiver::update signal is triggered. - QTimer::singleShot (0, this, SLOT (updated ())); + if (resolution_ != Resolution::truncate + && low_res == (high_res + 5) / 10 * 10) // rounded to 10's + { + resolution_ = Resolution::round; + return true; + } + if (resolution_ != Resolution::round + && low_res == high_res / 10 * 10) // truncated to 10's + { + resolution_ = Resolution::truncate; + return true; + } + + if (resolution_ != Resolution::truncate + && low_res == (high_res + 50) / 100 * 100) // rounded to 100's + { + resolution_ = Resolution::round; + return true; + } + if (resolution_ != Resolution::round + && low_res == high_res / 100 * 100) // truncated to 100's + { + resolution_ = Resolution::truncate; + return true; + } + + return false; +} + +void TransceiverBase::update_complete (bool force_signal) +{ + if ((do_pre_update () && actual_ != last_) || force_signal) + { + Q_EMIT update (actual_, last_sequence_number_); + last_ = actual_; + } } void TransceiverBase::offline (QString const& reason) { - QString message; + Q_EMIT failure (reason); try { - if (state_.online ()) - { - try - { - // try and ensure PTT isn't left set - do_ptt (false); - do_post_ptt (false); - } - catch (...) - { - // don't care about exceptions - } - } - do_stop (); - do_post_stop (); - state_.online (false); - } - catch (std::exception const& e) - { - message = e.what (); + shutdown (); } catch (...) { - message = unexpected; + // don't care } - Q_EMIT failure (reason + '\n' + message); } diff --git a/TransceiverBase.hpp b/TransceiverBase.hpp index 578c012ac..4b579e4a6 100644 --- a/TransceiverBase.hpp +++ b/TransceiverBase.hpp @@ -60,20 +60,29 @@ class TransceiverBase { Q_OBJECT; +private: + enum class Resolution {accurate, round, truncate}; + protected: - TransceiverBase () = default; + TransceiverBase (QObject * parent) + : Transceiver {parent} + , resolution_ {Resolution::accurate} + , last_sequence_number_ {0} + {} public: // // Implement the Transceiver abstract interface. // - void start () noexcept override final; - void stop (bool reset_split = false) noexcept override final; - void frequency (Frequency rx, MODE = UNK) noexcept override final; - void tx_frequency (Frequency tx, bool rationalise_mode) noexcept override final; - void mode (MODE, bool rationalise) noexcept override final; - void ptt (bool) noexcept override final; - void sync (bool force_signal) noexcept override final; + void start (unsigned sequence_number) noexcept override final; + void set (TransceiverState const&, + unsigned sequence_number) noexcept override final; + void stop () noexcept override final; + + // + // Query operations + // + TransceiverState const& state () const {return actual_;} protected: // @@ -82,32 +91,32 @@ protected: struct error : public std::runtime_error { - error (char const * const msg) : std::runtime_error (msg) {} - error (QString const& msg) : std::runtime_error (msg.toStdString ()) {} + explicit error (char const * const msg) : std::runtime_error (msg) {} + explicit error (QString const& msg) : std::runtime_error (msg.toStdString ()) {} }; // Template methods that sub classes implement to do what they need to do. // // These methods may throw exceptions to signal errors. - virtual void do_start () = 0; + virtual int do_start () = 0; // returns resolution, See Transceiver::resolution virtual void do_post_start () {} virtual void do_stop () = 0; virtual void do_post_stop () {} - virtual void do_frequency (Frequency rx, MODE = UNK) = 0; - virtual void do_post_frequency (Frequency, MODE = UNK) {} + virtual void do_frequency (Frequency, MODE, bool no_ignore) = 0; + virtual void do_post_frequency (Frequency, MODE) {} - virtual void do_tx_frequency (Frequency tx = 0u, bool rationalise_mode = false) = 0; - virtual void do_post_tx_frequency (Frequency = 0u, bool /* rationalise_mode */ = false) {} + virtual void do_tx_frequency (Frequency, bool no_ignore) = 0; + virtual void do_post_tx_frequency (Frequency) {} - virtual void do_mode (MODE, bool rationalise = true) = 0; - virtual void do_post_mode (MODE, bool /* rationalise */ = true) {} + virtual void do_mode (MODE) = 0; + virtual void do_post_mode (MODE) {} virtual void do_ptt (bool = true) = 0; virtual void do_post_ptt (bool = true) {} - virtual void do_sync (bool force_signal = false) = 0; + virtual void do_sync (bool force_signal = false, bool no_poll = false) = 0; virtual bool do_pre_update () {return true;} @@ -119,31 +128,48 @@ protected: void update_PTT (bool = true); // Calling this eventually triggers the Transceiver::update(State) signal. - void update_complete (); + void update_complete (bool force_signal = false); // sub class may asynchronously take the rig offline by calling this void offline (QString const& reason); - // and query state with this one - TransceiverState const& state () const {return state_;} - private: - Q_SLOT void updated (); + void startup (); + void shutdown (); + bool maybe_low_resolution (Frequency low_res, Frequency high_res); - TransceiverState state_; + // use this convenience class to notify in update methods + class may_update + { + public: + explicit may_update (TransceiverBase * self, bool force_signal = false) + : self_ {self} + , force_signal_ {force_signal} + {} + ~may_update () {self_->update_complete (force_signal_);} + private: + TransceiverBase * self_; + bool force_signal_; + }; + + TransceiverState requested_; + TransceiverState actual_; + TransceiverState last_; + Resolution resolution_; // rig accuracy + unsigned last_sequence_number_; // from set state operation }; // some trace macros #if WSJT_TRACE_CAT -#define TRACE_CAT(MSG) qDebug () << __PRETTY_FUNCTION__ << MSG +#define TRACE_CAT(FAC, MSG) qDebug () << QString {"%1::%2:"}.arg ((FAC)).arg (__func__) << MSG #else -#define TRACE_CAT(MSG) +#define TRACE_CAT(FAC, MSG) #endif #if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS -#define TRACE_CAT_POLL(MSG) qDebug () << __PRETTY_FUNCTION__ << MSG +#define TRACE_CAT_POLL(FAC, MSG) qDebug () << QString {"%1::%2:"}.arg ((FAC)).arg (__func__) << MSG #else -#define TRACE_CAT_POLL(MSG) +#define TRACE_CAT_POLL(FAC, MSG) #endif #endif diff --git a/TransceiverFactory.cpp b/TransceiverFactory.cpp index 19b92b9c5..d69eaa493 100644 --- a/TransceiverFactory.cpp +++ b/TransceiverFactory.cpp @@ -82,36 +82,44 @@ std::unique_ptr TransceiverFactory::create (ParameterPack const& pa { case CommanderId: { - // we start with a dummy HamlibTransceiver object instance that can support direct PTT - std::unique_ptr basic_transceiver {new HamlibTransceiver {params.ptt_type, params.ptt_port}}; - if (target_thread) + std::unique_ptr basic_transceiver; + if (PTT_method_CAT != params.ptt_type) { - basic_transceiver.get ()->moveToThread (target_thread); + // we start with a dummy HamlibTransceiver object instance that can support direct PTT + basic_transceiver.reset (new HamlibTransceiver {params.ptt_type, params.ptt_port}); + if (target_thread) + { + basic_transceiver.get ()->moveToThread (target_thread); + } } // wrap the basic Transceiver object instance with a decorator object that talks to DX Lab Suite Commander result.reset (new DXLabSuiteCommanderTransceiver {std::move (basic_transceiver), params.network_port, PTT_method_CAT == params.ptt_type, params.poll_interval}); if (target_thread) { - result.get ()->moveToThread (target_thread); + result->moveToThread (target_thread); } } break; case HRDId: { - // we start with a dummy HamlibTransceiver object instance that can support direct PTT - std::unique_ptr basic_transceiver {new HamlibTransceiver {params.ptt_type, params.ptt_port}}; - if (target_thread) + std::unique_ptr basic_transceiver; + if (PTT_method_CAT != params.ptt_type) { - basic_transceiver.get ()->moveToThread (target_thread); + // we start with a dummy HamlibTransceiver object instance that can support direct PTT + basic_transceiver.reset (new HamlibTransceiver {params.ptt_type, params.ptt_port}); + if (target_thread) + { + basic_transceiver.get ()->moveToThread (target_thread); + } } // wrap the basic Transceiver object instance with a decorator object that talks to ham Radio Deluxe - result.reset (new HRDTransceiver {std::move (basic_transceiver), params.network_port, PTT_method_CAT == params.ptt_type, params.poll_interval}); + result.reset (new HRDTransceiver {std::move (basic_transceiver), params.network_port, PTT_method_CAT == params.ptt_type, params.poll_interval, params.set_rig_mode}); if (target_thread) { - result.get ()->moveToThread (target_thread); + result->moveToThread (target_thread); } } break; @@ -119,36 +127,44 @@ std::unique_ptr TransceiverFactory::create (ParameterPack const& pa #if defined (WIN32) case OmniRigOneId: { - // we start with a dummy HamlibTransceiver object instance that can support direct PTT - std::unique_ptr basic_transceiver {new HamlibTransceiver {params.ptt_type, params.ptt_port}}; - if (target_thread) + std::unique_ptr basic_transceiver; + if (PTT_method_CAT != params.ptt_type && "CAT" != params.ptt_port) { - basic_transceiver.get ()->moveToThread (target_thread); + // we start with a dummy HamlibTransceiver object instance that can support direct PTT + basic_transceiver.reset (new HamlibTransceiver {params.ptt_type, params.ptt_port}); + if (target_thread) + { + basic_transceiver.get ()->moveToThread (target_thread); + } } // wrap the basic Transceiver object instance with a decorator object that talks to OmniRig rig one result.reset (new OmniRigTransceiver {std::move (basic_transceiver), OmniRigTransceiver::One, params.ptt_type, params.ptt_port}); if (target_thread) { - result.get ()->moveToThread (target_thread); + result->moveToThread (target_thread); } } break; case OmniRigTwoId: { - // we start with a dummy HamlibTransceiver object instance that can support direct PTT - std::unique_ptr basic_transceiver {new HamlibTransceiver {params.ptt_type, params.ptt_port}}; - if (target_thread) + std::unique_ptr basic_transceiver; + if (PTT_method_CAT != params.ptt_type && "CAT" != params.ptt_port) { - basic_transceiver.get ()->moveToThread (target_thread); + // we start with a dummy HamlibTransceiver object instance that can support direct PTT + basic_transceiver.reset (new HamlibTransceiver {params.ptt_type, params.ptt_port}); + if (target_thread) + { + basic_transceiver.get ()->moveToThread (target_thread); + } } // wrap the basic Transceiver object instance with a decorator object that talks to OmniRig rig two result.reset (new OmniRigTransceiver {std::move (basic_transceiver), OmniRigTransceiver::Two, params.ptt_type, params.ptt_port}); if (target_thread) { - result.get ()->moveToThread (target_thread); + result->moveToThread (target_thread); } } break; @@ -158,7 +174,7 @@ std::unique_ptr TransceiverFactory::create (ParameterPack const& pa result.reset (new HamlibTransceiver {supported_transceivers ()[params.rig_name].model_number_, params}); if (target_thread) { - result.get ()->moveToThread (target_thread); + result->moveToThread (target_thread); } break; } @@ -169,7 +185,7 @@ std::unique_ptr TransceiverFactory::create (ParameterPack const& pa result.reset (new EmulateSplitTransceiver {std::move (result)}); if (target_thread) { - result.get ()->moveToThread (target_thread); + result->moveToThread (target_thread); } } diff --git a/TransceiverFactory.hpp b/TransceiverFactory.hpp index ec1b16ea6..251a3056b 100644 --- a/TransceiverFactory.hpp +++ b/TransceiverFactory.hpp @@ -104,17 +104,18 @@ public: StopBits stop_bits; Handshake handshake; bool force_dtr; - bool dtr_high; // to power interface + bool dtr_high; // to power interface bool force_rts; - bool rts_high; // to power interface - PTTMethod ptt_type; // "CAT" | "DTR" | "RTS" | "VOX" - TXAudioSource audio_source; // some rigs allow audio routing - // to Mic/Data connector - SplitMode split_mode; // how to support split TX mode - QString ptt_port; // serial port device name or special - // value "CAT" - int poll_interval; // in seconds for interfaces that - // require polling for state changes + bool rts_high; // to power interface + PTTMethod ptt_type; // "CAT" | "DTR" | "RTS" | "VOX" + TXAudioSource audio_source; // some rigs allow audio routing + // to Mic/Data connector + bool set_rig_mode; // handle rig mode when required + SplitMode split_mode; // how to support split TX mode + QString ptt_port; // serial port device name or special + // value "CAT" + int poll_interval; // in seconds for interfaces that + // require polling for state changes bool operator == (ParameterPack const& rhs) const { @@ -132,9 +133,11 @@ public: && rhs.rts_high == rts_high && rhs.ptt_type == ptt_type && rhs.audio_source == audio_source + && rhs.set_rig_mode == set_rig_mode && rhs.split_mode == split_mode && rhs.ptt_port == ptt_port - && rhs.poll_interval == poll_interval; + && rhs.poll_interval == poll_interval + ; } }; diff --git a/astro.cpp b/astro.cpp index 35b1759f2..cec038bd6 100644 --- a/astro.cpp +++ b/astro.cpp @@ -20,30 +20,37 @@ #include "ui_astro.h" #include "moc_astro.cpp" + +extern "C" { + void astrosub_(int* nyear, int* month, int* nday, double* uth, double* freqMoon, + const char* mygrid, const char* hisgrid, double* azsun, + double* elsun, double* azmoon, double* elmoon, double* azmoondx, + double* elmoondx, int* ntsky, int* ndop, int* ndop00, + double* ramoon, double* decmoon, double* dgrd, double* poloffset, + double* xnr, double* techo, double* width1, double* width2, + bool* bTx, const char* AzElFileName, const char* jpleph, + int len1, int len2, int len3, int len4); +} + Astro::Astro(QSettings * settings, Configuration const * configuration, QWidget * parent) - : QWidget {parent, Qt::Tool | Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowMinimizeButtonHint | Qt::MSWindowsFixedSizeDialogHint} + : QDialog {parent, Qt::WindowTitleHint} , settings_ {settings} , configuration_ {configuration} , ui_ {new Ui::Astro} - , m_bRxAudioTrack {false} - , m_bTxAudioTrack {false} - , m_bTrackVFO {true} , m_DopplerMethod {0} - , m_kHz {0} - , m_Hz {0} - , m_stepHz {1} { ui_->setupUi (this); setWindowTitle (QApplication::applicationName () + " - " + tr ("Astronomical Data")); setStyleSheet ("QWidget {background: white;}"); connect (ui_->cbDopplerTracking, &QAbstractButton::toggled, ui_->doppler_widget, &QWidget::setVisible); - connect (ui_->cbDopplerTracking, &QAbstractButton::toggled, this, &Astro::doppler_tracking_toggled); read_settings (); ui_->text_label->clear (); } Astro::~Astro () { + ui_->cbDopplerTracking->setChecked (false); + Q_EMIT tracking_update (); if (isVisible ()) write_settings (); } @@ -56,40 +63,30 @@ void Astro::closeEvent (QCloseEvent * e) void Astro::read_settings () { SettingsGroup g (settings_, "Astro"); - ui_->cbDopplerTracking->setChecked (settings_->value ("DopplerTracking",false).toBool ()); ui_->doppler_widget->setVisible (ui_->cbDopplerTracking->isChecked ()); m_DopplerMethod=settings_->value("DopplerMethod",0).toInt(); - if(m_DopplerMethod==0) ui_->rbNoDoppler->setChecked(true); - if(m_DopplerMethod==1) ui_->rbFullTrack->setChecked(true); - if(m_DopplerMethod==2) ui_->rbConstFreqOnMoon->setChecked(true); - m_stepHz=settings_->value("StepHz",1).toInt(); - if(m_stepHz==1) ui_->rb1Hz->setChecked(true); - if(m_stepHz==10) ui_->rb10Hz->setChecked(true); - if(m_stepHz==100) ui_->rb100Hz->setChecked(true); - m_kHz=settings_->value("kHzAdd",100).toInt(); - ui_->kHzSpinBox->setValue(m_kHz); - m_bRxAudioTrack=settings_->value("RxAudioTrack",false).toBool(); - m_bTxAudioTrack=settings_->value("TxAudioTrack",false).toBool(); - ui_->cbTxAudioTrack->setChecked(m_bTxAudioTrack); + switch (m_DopplerMethod) + { + case 0: ui_->rbNoDoppler->setChecked (true); break; + case 1: ui_->rbFullTrack->setChecked (true); break; + case 2: ui_->rbConstFreqOnMoon->setChecked (true); break; + case 3: ui_->rbRxOnly->setChecked (true); break; + } move (settings_->value ("window/pos", pos ()).toPoint ()); } void Astro::write_settings () { SettingsGroup g (settings_, "Astro"); - settings_->setValue ("DopplerTracking", ui_->cbDopplerTracking->isChecked ()); + //settings_->setValue ("DopplerTracking", ui_->cbDopplerTracking->isChecked ()); settings_->setValue ("DopplerMethod",m_DopplerMethod); - settings_->setValue ("StepHz",m_stepHz); - settings_->setValue ("kHzAdd",m_kHz); - settings_->setValue ("RxAudioTrack",m_bRxAudioTrack); - settings_->setValue ("TxAudioTrack",m_bTxAudioTrack); settings_->setValue ("window/pos", pos ()); } auto Astro::astroUpdate(QDateTime const& t, QString const& mygrid, QString const& hisgrid, Frequency freq, - bool dx_is_self, bool bTx) -> FrequencyDelta + bool dx_is_self, bool bTx, bool no_tx_QSY, int TR_period) -> Correction { - Frequency freq_moon {freq + 1000 * m_kHz + m_Hz}; + Frequency freq_moon {freq}; double azsun,elsun,azmoon,elmoon,azmoondx,elmoondx; double ramoon,decmoon,dgrd,poloffset,xnr,techo,width1,width2; int ntsky; @@ -148,94 +145,125 @@ auto Astro::astroUpdate(QDateTime const& t, QString const& mygrid, QString const } ui_->text_label->setText(message); - FrequencyDelta astro_correction {0}; + Correction correction; if (ui_->cbDopplerTracking->isChecked ()) { switch (m_DopplerMethod) { - case 1: - // All Doppler correction done here; DX station stays at nominal dial frequency. - if(dx_is_self) { - astro_correction = m_stepHz*qRound(double(ndop00)/m_stepHz); - } else { - astro_correction = m_stepHz*qRound(double(ndop)/m_stepHz); - } + case 1: // All Doppler correction done here; DX station stays at nominal dial frequency. + case 3: // Both stations do full correction on Rx and none on Tx + correction.rx = dx_is_self ? ndop00 : ndop; break; case 2: // Doppler correction to constant frequency on Moon - astro_correction = m_stepHz*qRound(double(ndop00/2.0)/m_stepHz); + correction.rx = ndop00 / 2; break; } - if (bTx) { - astro_correction = 1000 * m_kHz + m_Hz - astro_correction; - } else { - if(dx_is_self && m_DopplerMethod==1) { - astro_correction = 1000*m_kHz + m_Hz; - } else { - astro_correction += 1000*m_kHz + m_Hz; + if (3 != m_DopplerMethod) correction.tx = -correction.rx; + if(dx_is_self && m_DopplerMethod == 1) correction.rx = 0; + + if (no_tx_QSY && 3 != m_DopplerMethod && 0 != m_DopplerMethod) + { + // calculate a single correction for transmit half way through + // the period as a compromise for rigs that can't CAT QSY + // while transmitting + // + // use a base time of (secs-since-epoch + 2) so as to be sure + // we do the next period if we calculate just before it starts + auto sec_since_epoch = t.toMSecsSinceEpoch () / 1000 + 2; + auto target_sec = sec_since_epoch - sec_since_epoch % TR_period + TR_period / 2; + auto target_date_time = QDateTime::fromMSecsSinceEpoch (target_sec * 1000); + QString date {target_date_time.date().toString("yyyy MMM dd").trimmed ()}; + QString utc {target_date_time.time().toString().trimmed ()}; + int nyear {target_date_time.date().year()}; + int month {target_date_time.date().month()}; + int nday {target_date_time.date().day()}; + int nhr {target_date_time.time().hour()}; + int nmin {target_date_time.time().minute()}; + double sec {target_date_time.time().second() + 0.001*target_date_time.time().msec()}; + double uth {nhr + nmin/60.0 + sec/3600.0}; + astrosub_(&nyear, &month, &nday, &uth, &freq8, mygrid.toLatin1().constData(), + hisgrid.toLatin1().constData(), &azsun, &elsun, &azmoon, &elmoon, + &azmoondx, &elmoondx, &ntsky, &ndop, &ndop00, &ramoon, &decmoon, + &dgrd, &poloffset, &xnr, &techo, &width1, &width2, &bTx, + "", jpleph.toLatin1().constData(), 6, 6, + 0, jpleph.length()); + FrequencyDelta offset {0}; + switch (m_DopplerMethod) + { + case 1: + // All Doppler correction done here; DX station stays at nominal dial frequency. + offset = dx_is_self ? ndop00 : ndop; + break; + + case 2: + // Doppler correction to constant frequency on Moon + offset = ndop00 / 2; + break; + } + correction.tx = -offset; } - } } - return astro_correction; + return correction; +} + +void Astro::check_split () +{ + if (doppler_tracking () && !configuration_->split_mode ()) + { + QMessageBox::warning (this, "Doppler Tracking", + "Split operating is required for Doppler tracking"); + ui_->rbNoDoppler->click (); + } } void Astro::on_rbFullTrack_clicked() { m_DopplerMethod = 1; + check_split (); + Q_EMIT tracking_update (); +} + +void Astro::on_rbRxOnly_clicked() +{ + m_DopplerMethod = 3; + check_split (); + Q_EMIT tracking_update (); } void Astro::on_rbConstFreqOnMoon_clicked() { m_DopplerMethod = 2; + check_split (); + Q_EMIT tracking_update (); } void Astro::on_rbNoDoppler_clicked() { m_DopplerMethod = 0; -} - -void Astro::on_rb1Hz_clicked() -{ - m_stepHz=1; -} - -void Astro::on_rb10Hz_clicked() -{ - m_stepHz=10; -} - -void Astro::on_rb100Hz_clicked() -{ - m_stepHz=100; -} - -void Astro::on_cbTxAudioTrack_toggled(bool b) -{ - m_bTxAudioTrack=b; -} - -void Astro::on_kHzSpinBox_valueChanged(int n) -{ - m_kHz=n; -} - -void Astro::on_HzSpinBox_valueChanged(int n) -{ - m_Hz=n; + Q_EMIT tracking_update (); } bool Astro::doppler_tracking () const { - return ui_->cbDopplerTracking->isChecked (); + return ui_->cbDopplerTracking->isChecked () && m_DopplerMethod; } -bool Astro::trackVFO() +void Astro::on_cbDopplerTracking_toggled(bool) { - return m_bTrackVFO; + check_split (); + Q_EMIT tracking_update (); } -void Astro::on_cbTrackVFO_toggled(bool b) +void Astro::nominal_frequency (Frequency rx, Frequency tx) { - m_bTrackVFO=b; + ui_->sked_frequency_label->setText (Radio::pretty_frequency_MHz_string (rx)); + ui_->sked_tx_frequency_label->setText (Radio::pretty_frequency_MHz_string (tx)); +} + +void Astro::hideEvent (QHideEvent * e) +{ + Q_EMIT tracking_update (); + QWidget::hideEvent (e); } diff --git a/astro.h b/astro.h index f12821c6c..b72e31efb 100644 --- a/astro.h +++ b/astro.h @@ -2,7 +2,8 @@ #ifndef ASTRO_H #define ASTRO_H -#include +#include + #include "Radio.hpp" class QSettings; @@ -12,63 +13,75 @@ namespace Ui { } class Astro final - : public QWidget + : public QDialog { Q_OBJECT; +public: using Frequency = Radio::Frequency; using FrequencyDelta = Radio::FrequencyDelta; -public: explicit Astro(QSettings * settings, Configuration const *, QWidget * parent = nullptr); ~Astro (); - FrequencyDelta astroUpdate(QDateTime const& t, QString const& mygrid, QString const& hisgrid, Frequency frequency, - bool dx_is_self, bool bTx); + + struct Correction + { + Correction () + : rx {0} + , tx {0} + {} + Correction (Correction const&) = default; + Correction& operator = (Correction const&) = default; + + FrequencyDelta rx; + FrequencyDelta tx; + }; + Correction astroUpdate(QDateTime const& t, + QString const& mygrid, + QString const& hisgrid, + Frequency frequency, + bool dx_is_self, + bool bTx, + bool no_tx_QSY, + int TR_period); + bool doppler_tracking () const; - bool trackVFO(); - Q_SIGNAL void doppler_tracking_toggled (bool); + Q_SLOT void nominal_frequency (Frequency rx, Frequency tx); + Q_SIGNAL void tracking_update () const; protected: + void hideEvent (QHideEvent *) override; void closeEvent (QCloseEvent *) override; private slots: void on_rbConstFreqOnMoon_clicked(); void on_rbFullTrack_clicked(); + void on_rbRxOnly_clicked(); void on_rbNoDoppler_clicked(); - void on_rb1Hz_clicked(); - void on_rb10Hz_clicked(); - void on_rb100Hz_clicked(); - void on_cbTxAudioTrack_toggled(bool b); - void on_kHzSpinBox_valueChanged(int n); - void on_HzSpinBox_valueChanged(int n); - void on_cbTrackVFO_toggled(bool b); + void on_cbDopplerTracking_toggled(bool); private: void read_settings (); void write_settings (); + void check_split (); QSettings * settings_; Configuration const * configuration_; Ui::Astro * ui_; - bool m_bRxAudioTrack; - bool m_bTxAudioTrack; - bool m_bTrackVFO; qint32 m_DopplerMethod; - qint32 m_kHz; - qint32 m_Hz; - qint32 m_stepHz; }; -extern "C" { - void astrosub_(int* nyear, int* month, int* nday, double* uth, double* freqMoon, - const char* mygrid, const char* hisgrid, double* azsun, - double* elsun, double* azmoon, double* elmoon, double* azmoondx, - double* elmoondx, int* ntsky, int* ndop, int* ndop00, - double* ramoon, double* decmoon, double* dgrd, double* poloffset, - double* xnr, double* techo, double* width1, double* width2, - bool* bTx, const char* AzElFileName, const char* jpleph, - int len1, int len2, int len3, int len4); +inline +bool operator == (Astro::Correction const& lhs, Astro::Correction const& rhs) +{ + return lhs.rx == rhs.rx && lhs.tx == rhs.tx; +} + +inline +bool operator != (Astro::Correction const& lhs, Astro::Correction const& rhs) +{ + return !(lhs == rhs); } #endif // ASTRO_H diff --git a/astro.ui b/astro.ui index cca0ac655..410f613cf 100644 --- a/astro.ui +++ b/astro.ui @@ -1,243 +1,246 @@ - - - Astro - - - - 0 - 0 - 440 - 406 - - - - - QLayout::SetFixedSize - - - - - * { - font-weight: normal; -} - - - - - - Frequency above nominal band edge - - - - - - Qt::AlignCenter - - - kHz - - - 999 - - - 200 - - - - - - - Qt::AlignCenter - - - Hz - - - -2000 - - - 2000 - - - 100 - - - - - - - - - - Doppler tracking - - - - - - Full Doppler to DX Grid - - - true - - - - - - - Constant frequency on Moon - - - false - - - - - - - None - - - false - - - - - - - - - - Transceiver step size - - - - - - 1 Hz - - - true - - - - - - - 10 Hz - - - - - - - 100 Hz - - - - - - - - - - true - - - Enable - - - - - - true - - - Track VFOs - - - true - - - - - - - false - - - Track Tx audio - - - - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - - - - - - - 0 - 0 - - - - * { - font-family: Courier; - font-size: 12pt; - font-weight: bold; -} - - - QFrame::Sunken - - - Astro Data - - - Qt::AlignCenter - - - 6 - - - - - - - - - - - - Doppler tracking - - - - - - - - - - - - + + + Astro + + + + 0 + 0 + 359 + 287 + + + + + 0 + 0 + + + + + QLayout::SetFixedSize + + + + + * { + font-weight: normal; +} + + + + + + Doppler tracking + + + + + + <html><head/><body><p>One station does all Doppler shift correction, their QSO partner receives and transmits on the sked frequency.</p><p>If the rig does not accept CAT QSY commands while transmitting a single correction is applied for the whole transmit period.</p></body></html> + + + Full Doppler to DX Grid + + + true + + + + + + + <html><head/><body><p>Both stations do full correction to their QSO partner's grid square during receive, no correction is applied on transmit.</p><p>This mode facilitates accurate Doppler shift correction when one or both stations have a rig that does not accept CAT QSY commands while transmitting.</p></body></html> + + + Receive only + + + + + + + <html><head/><body><p>Both stations correct for Doppler shift such that they would be heard on the moon at the sked frequency.</p><p>If the rig does not accept CAT QSY commands while transmitting a single correction is applied for the whole transmit period.</p></body></html> + + + Constant frequency on Moon + + + false + + + + + + + <html><head/><body><p>No Doppler shift correction is applied. This may be used when the QSO partner does full Doppler correction to your grid square.</p></body></html> + + + None + + + false + + + + + + + + + + true + + + Sked frequency + + + + + + * { + font-family: Courier; + font-size: 12pt; + font-weight: bold; +} + + + 0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + * { + font-family: Courier; + font-size: 12pt; + font-weight: bold; +} + + + 0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + * { + font-family: Courier; + font-size: 12pt; + font-weight: bold; +} + + + Rx: + + + + + + + * { + font-family: Courier; + font-size: 12pt; + font-weight: bold; +} + + + Tx: + + + + + + + <html><head/><body><p>Press and hold the CTRL key to adjust the sked frequency manually with the rig's VFO dial or enter directly into the band edit.</p></body></html> + + + Qt::AutoText + + + Qt::AlignCenter + + + true + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + + + + + 0 + 0 + + + + * { + font-family: Courier; + font-size: 12pt; + font-weight: bold; +} + + + QFrame::Sunken + + + Astro Data + + + Qt::AlignCenter + + + 6 + + + + + + + + + + + + Doppler tracking + + + + + + + + + + + + diff --git a/mainwindow.cpp b/mainwindow.cpp index b66447301..05e60eb48 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -31,7 +31,6 @@ #include "fastplot.h" #include "fastgraph.h" #include "about.h" -#include "astro.h" #include "messageaveraging.h" #include "widegraph.h" #include "sleep.h" @@ -90,7 +89,7 @@ extern "C" { int fftwf_export_wisdom_to_filename(const char *); void wspr_downsample_(short int d2[], int* k); - void savec2_(char* fname, int* m_TRseconds, double* m_dialFreq, int len1); + void savec2_(char* fname, int* TR_seconds, double* dial_freq, int len1); void avecho_( short id2[], int* dop, int* nfrit, int* nqual, float* f1, float* level, float* sigdb, float* snr, float* dfreq, @@ -150,11 +149,14 @@ MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdme m_echoGraph (new EchoGraph(settings)), m_fastGraph (new FastGraph(settings)), m_logDlg (new LogQSO (program_title (), settings, this)), - m_dialFreq {std::numeric_limits::max ()}, + m_lastDialFreq {0}, + //m_dialFreq {std::numeric_limits::max ()}, m_detector {new Detector {RX_SAMPLE_RATE, NTMAX, 6912 / 2, downSampleFactor}}, m_soundInput {new SoundInput}, m_modulator {new Modulator {TX_SAMPLE_RATE, NTMAX}}, m_soundOutput {new SoundOutput}, + m_freqNominal {0}, + m_freqTxNominal {0}, m_XIT {0}, m_pctx {0}, m_diskData {false}, @@ -385,8 +387,11 @@ MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdme ui->bandComboBox->view ()->setMinimumWidth (ui->bandComboBox->view ()->sizeHintForColumn (FrequencyList::frequency_mhz_column)); // Enable live band combo box entry validation and action. - auto band_validator = new LiveFrequencyValidator {ui->bandComboBox, m_config.bands(), - m_config.frequencies(), this}; + auto band_validator = new LiveFrequencyValidator {ui->bandComboBox + , m_config.bands () + , m_config.frequencies () + , &m_freqNominal + , this}; ui->bandComboBox->setValidator (band_validator); // Hook up signals. @@ -517,7 +522,7 @@ MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdme m_TRperiodFast=-1; m_nTx73=0; m_freqCQ=0; - m_dialFreq0=0; + m_callingFrequency=0; QString t1[28]={"1 uW","2 uW","5 uW","10 uW","20 uW","50 uW","100 uW","200 uW","500 uW", "1 mW","2 mW","5 mW","10 mW","20 mW","50 mW","100 mW","200 mW","500 mW", "1 W","2 W","5 W","10 W","20 W","50 W","100 W","200 W","500 W","1 kW"}; @@ -632,7 +637,7 @@ MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdme ui->label_10->setStyleSheet("QLabel{background-color: #aabec8}"); // this must be done before initializing the mode as some modes need - // to turn off split om the rig e.g. WSPR + // to turn off split on the rig e.g. WSPR m_config.transceiver_online (true); bool b=m_config.enable_VHF_features() and (m_mode=="JT4" or m_mode=="JT65" or @@ -649,9 +654,6 @@ MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdme if(m_mode=="ISCAT") on_actionISCAT_triggered(); if(m_mode=="JTMSK") on_actionJTMSK_triggered(); - // this can only be done after mode is initialized otherwise split - // on rig may be incorrect selected - on_monitorButton_clicked (!m_config.monitor_off_at_startup ()); if(m_mode=="Echo") monitor(false); //Don't auto-start Monitor in Echo mode. m_ntx=1; @@ -689,6 +691,7 @@ MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdme //--------------------------------------------------- MainWindow destructor MainWindow::~MainWindow() { + m_astroWidget.reset (); QString fname {QDir::toNativeSeparators(m_dataDir.absoluteFilePath ("wsjtx_wisdom.dat"))}; QByteArray cfname=fname.toLocal8Bit(); fftwf_export_wisdom_to_filename(cfname); @@ -765,7 +768,7 @@ void MainWindow::readSettings() m_settings->endGroup(); // do this outside of settings group because it uses groups internally - if (displayAstro) on_actionAstronomical_data_triggered (); + ui->actionAstronomical_data->setChecked (displayAstro); if (displayMsgAvg) on_actionMessage_averaging_triggered(); m_settings->beginGroup("Common"); @@ -911,7 +914,7 @@ void MainWindow::dataSink(qint64 frames) } if(ihsym==3*m_hsymStop/4) { - m_dialFreqRxWSPR=m_dialFreq; + m_dialFreqRxWSPR=m_freqNominal; } if(ihsym == m_hsymStop) { @@ -942,7 +945,7 @@ void MainWindow::dataSink(qint64 frames) } return; } - if( m_dialFreqRxWSPR==0) m_dialFreqRxWSPR=m_dialFreq; + if( m_dialFreqRxWSPR==0) m_dialFreqRxWSPR=m_freqNominal; m_dataAvailable=true; dec_data.params.npts8=(ihsym*m_nsps)/16; dec_data.params.newdat=1; @@ -971,7 +974,7 @@ void MainWindow::dataSink(qint64 frames) strcpy(c2name,m_c2name.toLatin1()); int nsec=120; int nbfo=1500; - double f0m1500=m_dialFreq/1000000.0 + nbfo - 1500; + double f0m1500=m_freqNominal/1000000.0 + nbfo - 1500; savec2_(c2name,&nsec,&f0m1500,len1); } } @@ -997,7 +1000,7 @@ void MainWindow::dataSink(qint64 frames) cmnd=t3.mid(0,i1+7) + t3.mid(i1+7); ui->DecodeButton->setChecked (true); p1.start(QDir::toNativeSeparators(cmnd)); - m_messageClient->status_update (m_dialFreq, m_mode, m_hisCall, + m_messageClient->status_update (m_freqNominal, m_mode, m_hisCall, QString::number (ui->rptSpinBox->value ()), m_modeTx, ui->autoButton->isChecked (), m_transmitting, (m_decoderBusy = true)); @@ -1020,7 +1023,7 @@ void MainWindow::save_wave_file (QString const& name, short const * data, int se .arg (QString {m_mode.contains ('J') && !m_mode.contains ('+') ? QString {", Sub Mode="} + QChar {'A' + m_nSubMode} : QString {}}) - .arg (Radio::frequency_MHz_string (m_dialFreq)) + .arg (Radio::frequency_MHz_string (m_freqNominal)) .arg (QString {!m_mode.contains ("WSPR") ? QString {", DXCall=%1, DXGrid=%2"} .arg (m_hisCall) .arg (m_hisGrid).toLocal8Bit () : ""}); @@ -1155,11 +1158,8 @@ void MainWindow::on_actionSettings_triggered() //Setup Dialog VHF_controls_visible(b); } + m_config.transceiver_online (true); if(!m_bFastMode) setXIT (ui->TxFreqSpinBox->value ()); - if (m_config.transceiver_online ()) - { - Q_EMIT m_config.transceiver_frequency (m_dialFreq); - } if(m_config.single_decode() or m_mode=="JT4") { ui->label_6->setText("Single-Period Decodes"); ui->label_7->setText("Average Decodes"); @@ -1178,21 +1178,18 @@ void MainWindow::on_monitorButton_clicked (bool checked) if (checked && !prior) { - Frequency operating_frequency {m_dialFreq}; if (m_config.monitor_last_used ()) { // put rig back where it was when last in control - operating_frequency = m_lastMonitoredFrequency; - Q_EMIT m_config.transceiver_frequency (operating_frequency); - } - qsy (operating_frequency); - if (m_config.monitor_last_used ()) - { + m_freqNominal = m_lastMonitoredFrequency; + m_freqTxNominal = m_freqNominal; + if (m_astroWidget) m_astroWidget->nominal_frequency (m_freqNominal, m_freqTxNominal); + setRig (); setXIT (ui->TxFreqSpinBox->value ()); } } -//Get Configuration in/out of strict split and mode checking + //Get Configuration in/out of strict split and mode checking Q_EMIT m_config.sync_transceiver (true, checked); } else @@ -1221,7 +1218,7 @@ void MainWindow::on_actionAbout_triggered() //Display "About" void MainWindow::on_autoButton_clicked (bool checked) { m_auto = checked; - m_messageClient->status_update (m_dialFreq, m_mode, m_hisCall, + m_messageClient->status_update (m_freqNominal, m_mode, m_hisCall, QString::number (ui->rptSpinBox->value ()), m_modeTx, ui->autoButton->isChecked (), m_transmitting, m_decoderBusy); @@ -1345,49 +1342,21 @@ void MainWindow::bumpFqso(int n) //bumpFqso() } } -void MainWindow::qsy (Frequency f) -{ - if (!m_transmitting) - { - if (m_monitoring) - { - m_lastMonitoredFrequency = f; - } -// if (m_dialFreq != f) - if (m_dialFreq != f and (m_mode!="JTMSK" or !ui->cbCQRx->isChecked())) - { - m_dialFreq = f; - m_repeatMsg=0; - m_secBandChanged=QDateTime::currentMSecsSinceEpoch()/1000; - if(m_dialFreq/1000000 < 30 and m_mode.mid(0,4)!="WSPR") { -// Write freq changes to ALL.TXT only below 30 MHz. - QFile f2 {m_dataDir.absoluteFilePath ("ALL.TXT")}; - if (f2.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) { - QTextStream out(&f2); - out << QDateTime::currentDateTimeUtc().toString("yyyy-MMM-dd hh:mm") - << " " << (m_dialFreq / 1.e6) << " MHz " << m_mode << endl; - f2.close(); - } else { - msgBox("Cannot open \"" + f2.fileName () + "\" for append:" + f2.errorString ()); - } - } - - if (m_config.spot_to_psk_reporter ()) { - pskSetLocal (); - } - displayDialFrequency (); - statusChanged(); - m_wideGraph->setDialFreq(m_dialFreq / 1.e6); - } - } -} - void MainWindow::displayDialFrequency () { + Frequency dial_frequency {m_rigState.ptt () && m_rigState.split () ? + m_rigState.tx_frequency () : m_rigState.frequency ()}; + // lookup band - auto const& band_name = m_config.bands ()->find (m_dialFreq); - ui->bandComboBox->setCurrentText (band_name); - m_wideGraph->setRxBand (band_name); + auto const& band_name = m_config.bands ()->find (dial_frequency); + if (m_lastBand != band_name) + { + // only change this when necessary as we get called a lot and it + // would trash any user input to the band combo box line edit + ui->bandComboBox->setCurrentText (band_name); + m_wideGraph->setRxBand (band_name); + m_lastBand = band_name; + } // search working frequencies for one we are within 10kHz of (1 Mhz // of on VHF and up) @@ -1399,29 +1368,24 @@ void MainWindow::displayDialFrequency () // ensure that we can use unsigned Radio::Frequency since we // potentially use the full 64-bit unsigned range. auto const& working_frequency = item.frequency_; - auto const& offset = m_dialFreq > working_frequency ? m_dialFreq - working_frequency : working_frequency - m_dialFreq; + auto const& offset = dial_frequency > working_frequency ? + dial_frequency - working_frequency : + working_frequency - dial_frequency; if (offset < min_offset) { - m_freqNominal = working_frequency; - min_offset = offset; + min_offset = offset; } - } - if (min_offset < 10000u or (m_config.enable_VHF_features() and - min_offset < 1000000u)) { + } + if (min_offset < 10000u || (m_config.enable_VHF_features() && min_offset < 1000000u)) { valid = true; } update_dynamic_property (ui->labDialFreq, "oob", !valid); - if(m_dialFreq==18446744073709551615u) { - ui->labDialFreq->setText (Radio::pretty_frequency_MHz_string (default_frequency)); - } else { - ui->labDialFreq->setText (Radio::pretty_frequency_MHz_string (m_dialFreq)); - - } + ui->labDialFreq->setText (Radio::pretty_frequency_MHz_string (dial_frequency)); } void MainWindow::statusChanged() { - m_messageClient->status_update (m_dialFreq, m_mode, m_hisCall, + m_messageClient->status_update (m_freqNominal, m_mode, m_hisCall, QString::number (ui->rptSpinBox->value ()), m_modeTx, ui->autoButton->isChecked (), m_transmitting, m_decoderBusy); @@ -1429,7 +1393,8 @@ void MainWindow::statusChanged() QFile f {m_config.temp_dir ().absoluteFilePath ("wsjtx_status.txt")}; if(f.open(QFile::WriteOnly | QIODevice::Text)) { QTextStream out(&f); - out << (m_dialFreq / 1.e6) << ";" << m_mode << ";" << m_hisCall << ";" + out << qSetRealNumberPrecision (12) << (m_freqNominal / 1.e6) + << ";" << m_mode << ";" << m_hisCall << ";" << ui->rptSpinBox->value() << ";" << m_modeTx << endl; f.close(); } else { @@ -1546,20 +1511,29 @@ void MainWindow::on_actionFast_Graph_triggered() m_fastGraph->show(); } -void MainWindow::on_actionAstronomical_data_triggered() +void MainWindow::on_actionAstronomical_data_toggled (bool checked) { - if (!m_astroWidget) + if (checked) { m_astroWidget.reset (new Astro {m_settings, &m_config}); // hook up termination signal connect (this, &MainWindow::finished, m_astroWidget.data (), &Astro::close); - connect (m_astroWidget.data (), &Astro::doppler_tracking_toggled - , this, &MainWindow::DopplerTracking_toggled); + connect (m_astroWidget.data (), &Astro::tracking_update, [this] { + m_astroCorrection = {}; + setRig (); + setXIT (ui->TxFreqSpinBox->value ()); + displayDialFrequency (); + }); + m_astroWidget->showNormal(); + m_astroWidget->raise (); + m_astroWidget->activateWindow (); + m_astroWidget->nominal_frequency (m_freqNominal, m_freqTxNominal); + } + else + { + m_astroWidget.reset (); } - m_astroWidget->showNormal(); - m_astroWidget->raise (); - m_astroWidget->activateWindow (); } void MainWindow::on_actionMessage_averaging_triggered() @@ -1947,7 +1921,8 @@ void::MainWindow::fast_decode_done() QTextStream out(&f); if(m_RxLog==1) { out << QDateTime::currentDateTimeUtc().toString("yyyy-MMM-dd hh:mm") - << " " << (m_dialFreq / 1.e6) << " MHz " << m_mode << endl; + << " " << qSetRealNumberPrecision (12) << (m_freqNominal / 1.e6) << " MHz " + << m_mode << endl; m_RxLog=0; } int n=message.length(); @@ -1973,7 +1948,7 @@ void::MainWindow::fast_decode_done() decodedtext.deCallAndGrid(/*out*/deCall,grid); int audioFrequency = decodedtext.frequencyOffset(); int snr = decodedtext.snr(); - Frequency frequency = m_dialFreq + audioFrequency; + Frequency frequency = m_freqNominal + audioFrequency; pskSetLocal(); if(gridOK(grid)) // qDebug() << "To PSKreporter:" << deCall << grid << frequency << msgmode << snr; @@ -2045,7 +2020,8 @@ void MainWindow::readFromStdout() //readFromStdout QTextStream out(&f); if(m_RxLog==1) { out << QDateTime::currentDateTimeUtc().toString("yyyy-MMM-dd hh:mm") - << " " << (m_dialFreq / 1.e6) << " MHz " << m_mode << endl; + << " " << qSetRealNumberPrecision (12) << (m_freqNominal / 1.e6) << " MHz " + << m_mode << endl; m_RxLog=0; } int n=t.length(); @@ -2060,7 +2036,7 @@ void MainWindow::readFromStdout() //readFromStdout QString band; if (QDateTime::currentMSecsSinceEpoch() / 1000 - m_secBandChanged > 50) { - band = ' ' + m_config.bands ()->find (m_dialFreq); + band = ' ' + m_config.bands ()->find (m_freqNominal); } ui->decodedTextBrowser->insertLineSpacer (band.rightJustified (40, '-')); m_blankLine = false; @@ -2120,7 +2096,7 @@ void MainWindow::readFromStdout() //readFromStdout decodedtext.deCallAndGrid(/*out*/deCall,grid); int audioFrequency = decodedtext.frequencyOffset(); int snr = decodedtext.snr(); - Frequency frequency = m_dialFreq + audioFrequency; + Frequency frequency = m_freqNominal + audioFrequency; pskSetLocal (); if(gridOK(grid)) psk_Reporter->addRemoteStation(deCall,grid,QString::number(frequency),msgmode, @@ -2211,7 +2187,7 @@ void MainWindow::decodeBusy(bool b) //decodeBusy() ui->actionOpen_next_in_directory->setEnabled(!b); ui->actionDecode_remaining_files_in_directory->setEnabled(!b); - m_messageClient->status_update (m_dialFreq, m_mode, m_hisCall, + m_messageClient->status_update (m_freqNominal, m_mode, m_hisCall, QString::number (ui->rptSpinBox->value ()), m_modeTx, ui->autoButton->isChecked (), m_transmitting, m_decoderBusy); @@ -2296,7 +2272,7 @@ void MainWindow::guiUpdate() fmod(tsec,m_TRperiod)<(1.0 + 85.0*m_nsps/12000.0)) m_bTxTime=true; // Don't transmit another mode in the 30 m WSPR sub-band - Frequency onAirFreq = m_dialFreq + ui->TxFreqSpinBox->value(); + Frequency onAirFreq = m_freqNominal + ui->TxFreqSpinBox->value(); if ((onAirFreq > 10139900 and onAirFreq < 10140320) and m_mode.mid(0,4)!="WSPR") { m_bTxTime=false; @@ -2316,28 +2292,22 @@ void MainWindow::guiUpdate() if(g_iptt==0 and ((m_bTxTime and fTR<99) or m_tune )) { //### Allow late starts icw[0]=m_ncw; g_iptt = 1; - setXIT (ui->TxFreqSpinBox->value ()); //Ensure correct offset + setRig (); + setXIT (ui->TxFreqSpinBox->value ()); // If "CQ nnn ..." feature is active, set the proper Tx frequency - if(m_config.offsetRxFreq() and ui->cbCQRx->isChecked()) { - if(m_ntx==6) { - m_dialFreqTx = m_dialFreq0; - } else { - int MHz=int(m_dialFreq/1000000); - m_dialFreqTx=1000000*MHz + 1000*m_freqCQ; + if(m_config.offsetRxFreq() && ui->cbCQRx->isChecked() + && (m_monitoring || m_transmitting) + && m_config.transceiver_online () + && m_config.split_mode ()) + { + // All conditions are met, reset the transceiver Tx frequency: + Frequency tx_frequency {6 == m_ntx ? + m_callingFrequency : + m_freqTxNominal / 1000000 * 1000000 + 1000 * m_freqCQ + m_XIT}; + Q_EMIT m_config.transceiver_tx_frequency (tx_frequency); } - if (m_monitoring || m_transmitting) { - if (m_config.transceiver_online ()) { - if (m_config.split_mode ()) { - // All conditions are met, reset the transceiver dial frequency: -// ui->labDialFreq->setText (Radio::pretty_frequency_MHz_string (m_dialFreq)); - Q_EMIT m_config.transceiver_tx_frequency (m_dialFreqTx); - } - } - } - } - Q_EMIT m_config.transceiver_ptt (true); //Assert the PTT ptt1Timer->start(200); //Sequencer delay } @@ -2350,7 +2320,7 @@ void MainWindow::guiUpdate() m_rxDone=false; } if(m_transmitting) { - WSPR_history(m_dialFreq,-1); + WSPR_history(m_freqNominal,-1); m_bTxTime=false; //Time to stop a WSPR transmission m_btxok=false; } @@ -2439,7 +2409,8 @@ void MainWindow::guiUpdate() { QTextStream out(&f); out << QDateTime::currentDateTimeUtc().toString("hhmm") - << " Transmitting " << (m_dialFreq / 1.e6) << " MHz " << m_modeTx + << " Transmitting " << qSetRealNumberPrecision (12) << (m_freqNominal / 1.e6) + << " MHz " << m_modeTx << ": " << m_currentMessage << endl; f.close(); } @@ -2536,7 +2507,8 @@ void MainWindow::guiUpdate() if (f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) { QTextStream out(&f); out << QDateTime::currentDateTimeUtc().toString("hhmm") - << " Transmitting " << (m_dialFreq / 1.e6) << " MHz " << m_modeTx + << " Transmitting " << qSetRealNumberPrecision (12) << (m_freqNominal / 1.e6) << " MHz " + << m_modeTx << ": " << m_currentMessage << endl; f.close(); } else { @@ -2552,7 +2524,7 @@ void MainWindow::guiUpdate() m_transmitting = true; transmitDisplay (true); - m_messageClient->status_update (m_dialFreq, m_mode, m_hisCall, + m_messageClient->status_update (m_freqNominal, m_mode, m_hisCall, QString::number (ui->rptSpinBox->value ()), m_modeTx, ui->autoButton->isChecked (), m_transmitting, m_decoderBusy); @@ -2583,8 +2555,8 @@ void MainWindow::guiUpdate() } } - QDateTime t = QDateTime::currentDateTimeUtc(); - astroCalculations (t, (m_astroWidget && m_astroWidget->doppler_tracking())); + astroUpdate (); + if(m_transmitting) { char s[37]; sprintf(s,"Tx: %s",msgsent); @@ -2616,6 +2588,7 @@ void MainWindow::guiUpdate() tx_status_label->setText(""); } + QDateTime t = QDateTime::currentDateTimeUtc(); QString utc = t.date().toString("yyyy MMM dd") + "\n " + t.time().toString() + " "; ui->labUTC->setText(utc); @@ -2623,6 +2596,7 @@ void MainWindow::guiUpdate() ui->signal_meter_widget->setValue(0); } m_sec0=nsec; + displayDialFrequency (); } iptt0=g_iptt; btxok0=m_btxok; @@ -2647,7 +2621,7 @@ void MainWindow::startTx2() if(m_mode.mid(0,4)=="WSPR" and !m_tune) { if (m_config.TX_messages ()) { t = " Transmitting " + m_mode + " ----------------------- " + - m_config.bands ()->find (m_dialFreq); + m_config.bands ()->find (m_freqNominal); t=WSPR_hhmm(0) + ' ' + t.rightJustified (66, '-'); ui->decodedTextBrowser->appendText(t); } @@ -2656,7 +2630,7 @@ void MainWindow::startTx2() if (f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) { QTextStream out(&f); out << QDateTime::currentDateTimeUtc().toString("yyMMdd hhmm") - << " Transmitting " << (m_dialFreq / 1.e6) << " MHz: " + << " Transmitting " << qSetRealNumberPrecision (12) << (m_freqNominal / 1.e6) << " MHz: " << m_currentMessage << " " + m_mode << endl; f.close(); } else { @@ -2676,7 +2650,7 @@ void MainWindow::stopTx() tx_status_label->setText(""); ptt0Timer->start(200); //Sequencer delay monitor (true); - m_messageClient->status_update (m_dialFreq, m_mode, m_hisCall, + m_messageClient->status_update (m_freqNominal, m_mode, m_hisCall, QString::number (ui->rptSpinBox->value ()), m_modeTx, ui->autoButton->isChecked (), m_transmitting, m_decoderBusy); @@ -2701,14 +2675,17 @@ void MainWindow::stopTx2() m_ntr=0; } if(m_config.offsetRxFreq() and ui->cbCQRx->isChecked()) { -// Q_EMIT m_config.transceiver_frequency(m_dialFreq); +// Q_EMIT m_config.transceiver_frequency(m_freqNominal); RxQSYTimer->start(50); } } void MainWindow::RxQSY() { - Q_EMIT m_config.transceiver_frequency(m_dialFreq); + // this appears to be a null directive + if (m_config.transceiver_online ()) { + Q_EMIT m_config.transceiver_frequency(m_freqNominal); + } } void MainWindow::ba2msg(QByteArray ba, char message[]) //ba2msg() @@ -2830,21 +2807,21 @@ void MainWindow::processMessage(QString const& messages, int position, bool ctrl if(ok and kHz>=0 and kHz<=999) { t2a=t2a.mid(0,i1+4) + t2a.mid(i1+8,-1); if (m_config.transceiver_online ()) { - int MHz=int(m_dialFreq/1000000); - m_dialFreq = 1000000*MHz + 1000*kHz; //QSY Freq for answering CQ nnn + Frequency frequency {m_freqNominal / 1000000 * 1000000 + 1000*kHz}; //QSY Freq for answering CQ nnn QString t; - t.sprintf("QSY %7.3f",0.000001*m_dialFreq); + t.sprintf("QSY %7.3f", frequency / 10e6); ui->decodedTextBrowser2->displayQSY(t); - ui->labDialFreq->setText (Radio::pretty_frequency_MHz_string (m_dialFreq)); - Q_EMIT m_config.transceiver_frequency (m_dialFreq); + // ui->labDialFreq->setText (Radio::pretty_frequency_MHz_string (frequency)); + if (m_config.transceiver_online ()) { + Q_EMIT m_config.transceiver_frequency (frequency); + } - if (m_monitoring || m_transmitting) { - if (m_config.transceiver_online ()) { - if (m_config.split_mode ()) { - // All conditions are met, reset the transceiver dial frequency: - Q_EMIT m_config.transceiver_tx_frequency(m_dialFreq); - } - } + if ((m_monitoring || m_transmitting) + && m_config.transceiver_online () + && m_config.split_mode ()) + { + // All conditions are met, reset the transmit dial frequency: + Q_EMIT m_config.transceiver_tx_frequency(frequency); } } @@ -3477,7 +3454,7 @@ void MainWindow::on_logQSOButton_clicked() //Log QSO button m_dateTimeQSO=QDateTime::currentDateTimeUtc(); m_logDlg->initLogQSO (m_hisCall, m_hisGrid, m_modeTx, m_rptSent, m_rptRcvd, - m_dateTimeQSO, m_dialFreq + ui->TxFreqSpinBox->value(), + m_dateTimeQSO, m_freqNominal + ui->TxFreqSpinBox->value(), m_config.my_callsign(), m_config.my_grid(), m_noSuffix, m_config.log_as_RTTY(), m_config.report_in_comments()); } @@ -3489,7 +3466,7 @@ void MainWindow::acceptQSO2(QDateTime const& QSO_date, QString const& call, QStr , QString const& name) { QString date = m_dateTimeQSO.toString("yyyyMMdd"); - m_logBook.addAsWorked (m_hisCall, m_config.bands ()->find (m_dialFreq), m_modeTx, date); + m_logBook.addAsWorked (m_hisCall, m_config.bands ()->find (m_freqNominal), m_modeTx, date); m_messageClient->qso_logged (QSO_date, call, grid, dial_freq, mode, rpt_sent, rpt_received, tx_power, comments, name); @@ -3510,6 +3487,7 @@ void MainWindow::on_actionJT9_triggered() } m_bFast9=ui->cbFast9->isChecked(); m_bFastMode=m_bFast9; + WSPR_config(false); switch_mode (Modes::JT9); if(m_modeTx!="JT9") on_pbTxMode_clicked(); statusChanged(); @@ -3538,7 +3516,6 @@ void MainWindow::on_actionJT9_triggered() ui->cbEME->setVisible(true); ui->sbSubmode->setVisible(true); ui->sbSubmode->setMaximum(7); - WSPR_config(false); fast_config(m_bFastMode); if(m_bFast9) { m_TRperiod=ui->sbTR->cleanText().toInt(); @@ -3569,6 +3546,7 @@ void MainWindow::on_actionJTMSK_triggered() m_mode="JTMSK"; m_modeTx="JTMSK"; ui->actionJTMSK->setChecked(true); + WSPR_config(false); switch_mode (Modes::JTMSK); statusChanged(); m_nsps=6; @@ -3583,7 +3561,6 @@ void MainWindow::on_actionJTMSK_triggered() ui->cbShMsgs->setVisible(true); ui->cbTx6->setVisible(false); ui->sbSubmode->setVisible(false); - WSPR_config(false); m_bFastMode=true; m_bFast9=true; fast_config(m_bFastMode); @@ -3614,6 +3591,7 @@ void MainWindow::on_actionJT65_triggered() on_pbTxMode_clicked(); } m_mode="JT65"; + WSPR_config(false); switch_mode (Modes::JT65); if(m_modeTx!="JT65") on_pbTxMode_clicked(); statusChanged(); @@ -3639,7 +3617,6 @@ void MainWindow::on_actionJT65_triggered() m_bFast9=false; VHF_controls_visible(bVHF); ui->cbFast9->setVisible(false); - WSPR_config(false); fast_config(false); ui->sbSubmode->setMaximum(2); if(bVHF) { @@ -3660,6 +3637,7 @@ void MainWindow::on_actionJT65_triggered() void MainWindow::on_actionJT9_JT65_triggered() { m_mode="JT9+JT65"; + WSPR_config(false); switch_mode (Modes::JT65); if(m_modeTx != "JT65") { ui->pbTxMode->setText("Tx JT9 @"); @@ -3685,7 +3663,6 @@ void MainWindow::on_actionJT9_JT65_triggered() m_bFastMode=false; m_bFast9=false; VHF_controls_visible(false); - WSPR_config(false); fast_config(false); ui->sbSubmode->setValue(0); ui->sbTR->setValue(0); @@ -3697,6 +3674,7 @@ void MainWindow::on_actionJT9_JT65_triggered() void MainWindow::on_actionJT4_triggered() { m_mode="JT4"; + WSPR_config(false); switch_mode (Modes::JT4); m_modeTx="JT4"; mode_label->setStyleSheet("QLabel{background-color: #cc99ff}"); @@ -3718,7 +3696,6 @@ void MainWindow::on_actionJT4_triggered() m_bFast9=false; bool bVHF=m_config.enable_VHF_features(); VHF_controls_visible(bVHF); - WSPR_config(false); fast_config(false); ui->cbFast9->setVisible(false); ui->cbShMsgs->setVisible(true); @@ -3741,6 +3718,7 @@ void MainWindow::on_actionJT4_triggered() void MainWindow::on_actionWSPR_2_triggered() { m_mode="WSPR-2"; + WSPR_config(true); switch_mode (Modes::WSPR); m_modeTx="WSPR-2"; //### not needed ?? ### statusChanged(); @@ -3760,7 +3738,6 @@ void MainWindow::on_actionWSPR_2_triggered() m_wideGraph->setModeTx(m_modeTx); m_bFastMode=false; m_bFast9=false; - WSPR_config(true); fast_config(false); ui->TxFreqSpinBox->setValue(ui->WSPRfreqSpinBox->value()); } @@ -3792,11 +3769,12 @@ void MainWindow::on_actionEcho_triggered() ui->TxFreqSpinBox->setEnabled (false); statusChanged(); if(!m_echoGraph->isVisible()) m_echoGraph->show(); - on_actionAstronomical_data_triggered (); + if (!ui->actionAstronomical_data->isChecked ()) { + ui->actionAstronomical_data->setChecked (true); + } m_bFastMode=false; m_bFast9=false; VHF_controls_visible(false); - WSPR_config(true); //Make some irrelevant controls invisible fast_config(false); ui->decodedTextLabel->setText(" UTC N Level Sig DF Width Q"); auto_tx_label->setText(""); @@ -3814,6 +3792,7 @@ void MainWindow::on_actionISCAT_triggered() m_nsps=6912; //For symspec only m_hsymStop=103; m_toneSpacing=11025.0/256.0; + WSPR_config(false); switch_mode(Modes::ISCAT); m_wideGraph->setMode(m_mode); m_wideGraph->setModeTx(m_modeTx); @@ -3823,7 +3802,6 @@ void MainWindow::on_actionISCAT_triggered() mode_label->setStyleSheet("QLabel{background-color: #ff9933}"); mode_label->setText(m_mode); VHF_controls_visible(true); - WSPR_config(false); fast_config(true); ui->pbTxMode->setVisible(false); ui->cbFast9->setVisible(false); @@ -3849,9 +3827,8 @@ void MainWindow::on_actionISCAT_triggered() void MainWindow::switch_mode (Mode mode) { - auto f = m_dialFreq; m_config.frequencies ()->filter (mode); - auto const& row = m_config.frequencies ()->best_working_frequency (f); + auto const& row = m_config.frequencies ()->best_working_frequency (m_freqNominal); if (row >= 0) { ui->bandComboBox->setCurrentIndex (row); on_bandComboBox_activated (row); @@ -3882,7 +3859,9 @@ void MainWindow::WSPR_config(bool b) ui->decodedTextLabel->setText( "UTC dB DT Freq Drift Call Grid dBm Dist"); auto_tx_label->setText(""); - Q_EMIT m_config.transceiver_tx_frequency (0); // turn off split + if (m_config.transceiver_online ()) { + Q_EMIT m_config.transceiver_tx_frequency (0); // turn off split + } m_bSimplex = true; } else { ui->decodedTextLabel->setText("UTC dB DT Freq Message"); @@ -4013,11 +3992,11 @@ void MainWindow::on_bandComboBox_currentIndexChanged (int index) { auto const& frequencies = m_config.frequencies (); auto const& source_index = frequencies->mapToSource (frequencies->index (index, FrequencyList::frequency_column)); - Frequency frequency {m_dialFreq}; + Frequency frequency {m_freqNominal}; if (source_index.isValid ()) { frequency = frequencies->frequency_list ()[source_index.row ()].frequency_; - m_dialFreq0=frequency; + m_callingFrequency = frequency; } // Lookup band @@ -4039,7 +4018,7 @@ void MainWindow::on_bandComboBox_activated (int index) { auto const& frequencies = m_config.frequencies (); auto const& source_index = frequencies->mapToSource (frequencies->index (index, FrequencyList::frequency_column)); - Frequency frequency {m_dialFreq}; + Frequency frequency {m_freqNominal}; if (source_index.isValid ()) { frequency = frequencies->frequency_list ()[source_index.row ()].frequency_; @@ -4052,15 +4031,33 @@ void MainWindow::on_bandComboBox_activated (int index) void MainWindow::band_changed (Frequency f) { if (m_bandEdited) { - if (!m_mode.startsWith ("WSPR")) { - ui->stopTxButton->click (); // halt any transmission - auto_tx_mode (false); // disable auto Tx + if (!m_mode.startsWith ("WSPR")) { // band hopping preserves auto Tx + if (f + m_wideGraph->nStartFreq () > m_freqNominal + ui->TxFreqSpinBox->value () + || f + m_wideGraph->nStartFreq () + m_wideGraph->fSpan () <= + m_freqNominal + ui->TxFreqSpinBox->value ()) { + qDebug () << "start f:" << m_wideGraph->nStartFreq () << "span:" << m_wideGraph->fSpan () << "DF:" << ui->TxFreqSpinBox->value (); + // disable auto Tx if "blind" QSY outside of waterfall + ui->stopTxButton->click (); // halt any transmission + auto_tx_mode (false); // disable auto Tx + } + else { + // adjust DF:s + int shift = f - m_freqNominal; + ui->RxFreqSpinBox->setValue (ui->RxFreqSpinBox->value () - shift); + if (!m_mode.startsWith ("JT4")) // JT4 uses fixed 1000Hz Tx DF + { + ui->TxFreqSpinBox->setValue (ui->TxFreqSpinBox->value () - shift); + } + } } + m_lastBand.clear (); m_bandEdited = false; psk_Reporter->sendReport(); // Upload any queued spots before changing band if (!m_transmitting) monitor (true); - Q_EMIT m_config.transceiver_frequency (f); - qsy (f); + m_freqNominal = f; + m_freqTxNominal = m_freqNominal; + if (m_astroWidget) m_astroWidget->nominal_frequency (m_freqNominal, m_freqTxNominal); + setRig (); setXIT (ui->TxFreqSpinBox->value ()); m_repeatMsg = 0; // reset Tx watchdog } @@ -4233,7 +4230,7 @@ void MainWindow::rigOpen () ui->readFreq->setText (""); ui->readFreq->setEnabled (true); m_config.transceiver_online (true); - Q_EMIT m_config.sync_transceiver (true); + Q_EMIT m_config.sync_transceiver (true, true); } void MainWindow::on_pbR2T_clicked() @@ -4256,7 +4253,7 @@ void MainWindow::on_readFreq_clicked() if (m_config.transceiver_online (true)) { - Q_EMIT m_config.sync_transceiver (true); + Q_EMIT m_config.sync_transceiver (true, true); } } @@ -4273,8 +4270,10 @@ void MainWindow::on_pbTxMode_clicked() statusChanged(); } -void MainWindow::setXIT(int n) +void MainWindow::setXIT(int n, Frequency base) { + if (m_transmitting && !m_config.tx_QSY_allowed ()) return; + if (!base) base = m_freqNominal; m_XIT = 0; if (!m_bSimplex) { // m_bSimplex is false, so we can use split mode if requested @@ -4283,14 +4282,16 @@ void MainWindow::setXIT(int n) m_XIT=(n/500)*500 - 1500; } - if (m_monitoring || m_transmitting) { - if (m_config.transceiver_online ()) { - if (m_config.split_mode ()) { - // All conditions are met, reset the transceiver dial frequency: - Q_EMIT m_config.transceiver_tx_frequency (m_dialFreq + m_XIT); - } + if ((m_monitoring || m_transmitting) + && m_config.transceiver_online () + && m_config.split_mode ()) + { + // All conditions are met, reset the transceiver Tx dial + // frequency + m_freqTxNominal = base + m_XIT; + if (m_astroWidget) m_astroWidget->nominal_frequency (m_freqNominal, m_freqTxNominal); + Q_EMIT m_config.transceiver_tx_frequency (m_freqTxNominal + m_astroCorrection.tx); } - } } //Now set the audio Tx freq Q_EMIT transmitFrequency (ui->TxFreqSpinBox->value () - m_XIT); @@ -4309,6 +4310,17 @@ void MainWindow::setFreq4(int rxFreq, int txFreq) if (ui->TxFreqSpinBox->isEnabled ()) { ui->TxFreqSpinBox->setValue(txFreq); } + else if (m_mode=="JT4" + && m_freqNominal >= 432000000u + && (Qt::ControlModifier & QApplication::keyboardModifiers ())) { + auto temp = ui->TxFreqSpinBox->value (); + m_freqNominal += txFreq - temp; + m_freqTxNominal += txFreq - temp; + ui->RxFreqSpinBox->setValue (temp); + if (m_astroWidget) m_astroWidget->nominal_frequency (m_freqNominal, m_freqTxNominal); + setRig (); + setXIT (ui->TxFreqSpinBox->value ()); + } } } @@ -4319,20 +4331,75 @@ void MainWindow::on_cbTxLock_clicked(bool checked) if(m_lockTxFreq) on_pbR2T_clicked(); } -void MainWindow::handle_transceiver_update (Transceiver::TransceiverState s) +void MainWindow::handle_transceiver_update (Transceiver::TransceiverState const& s) { - transmitDisplay (s.ptt ()); - if ((s.frequency () - m_dialFreq) || s.split () != m_splitMode) + // qDebug () << "MainWindow::handle_transceiver_update:" << s; + Transceiver::TransceiverState old_state {m_rigState}; + //transmitDisplay (s.ptt ()); + m_rigState = s; + if (old_state.online () == false && s.online () == true) + { + // initializing + on_monitorButton_clicked (!m_config.monitor_off_at_startup ()); + } + if (s.frequency () != old_state.frequency () || s.split () != m_splitMode) { m_splitMode = s.split (); - qsy (s.frequency ()); + if (!s.ptt ()) //!m_transmitting) + { + auto temp = m_freqNominal; + m_freqNominal = s.frequency () - m_astroCorrection.rx; + if (temp != m_freqNominal) + { + m_freqTxNominal = m_freqNominal; + } + + if (m_monitoring) + { + m_lastMonitoredFrequency = m_freqNominal; + } + if (m_lastDialFreq != m_freqNominal and (m_mode!="JTMSK" or !ui->cbCQRx->isChecked())) + { + m_lastDialFreq = m_freqNominal; + m_repeatMsg=0; + m_secBandChanged=QDateTime::currentMSecsSinceEpoch()/1000; + if(s.frequency () < 30000000u && m_mode.mid(0,4)!="WSPR") { + // Write freq changes to ALL.TXT only below 30 MHz. + QFile f2 {m_dataDir.absoluteFilePath ("ALL.TXT")}; + if (f2.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) { + QTextStream out(&f2); + out << QDateTime::currentDateTimeUtc().toString("yyyy-MMM-dd hh:mm") + << " " << qSetRealNumberPrecision (12) << (m_freqNominal / 1.e6) << " MHz " + << m_mode << endl; + f2.close(); + } else { + msgBox("Cannot open \"" + f2.fileName () + "\" for append:" + f2.errorString ()); + } + } + + if (m_config.spot_to_psk_reporter ()) { + pskSetLocal (); + } + statusChanged(); + m_wideGraph->setDialFreq(m_freqNominal / 1.e6); + } + } + else + { + m_freqTxNominal = s.split () ? s.tx_frequency () - m_astroCorrection.tx : s.frequency (); + } + if (m_astroWidget) m_astroWidget->nominal_frequency (m_freqNominal, m_freqTxNominal); } + // ensure frequency display is correct + if (m_astroWidget && old_state.ptt () != s.ptt ()) setRig (); + + displayDialFrequency (); update_dynamic_property (ui->readFreq, "state", "ok"); ui->readFreq->setEnabled (false); ui->readFreq->setText (s.split () ? "S" : ""); } -void MainWindow::handle_transceiver_failure (QString reason) +void MainWindow::handle_transceiver_failure (QString const& reason) { update_dynamic_property (ui->readFreq, "state", "error"); ui->readFreq->setEnabled (true); @@ -4600,7 +4667,6 @@ void MainWindow::pskSetLocal () void MainWindow::transmitDisplay (bool transmitting) { - if (transmitting == m_transmitting) { if (transmitting) { ui->signal_meter_widget->setValue(0); @@ -4609,14 +4675,14 @@ void MainWindow::transmitDisplay (bool transmitting) } auto QSY_allowed = !transmitting or m_config.tx_QSY_allowed () or - !m_config.split_mode (); + !m_config.split_mode (); if (ui->cbTxLock->isChecked ()) { ui->RxFreqSpinBox->setEnabled (QSY_allowed); ui->pbT2R->setEnabled (QSY_allowed); } - if(m_mode=="JT4" and (m_dialFreq/1000000 >= 432)) { -// if(m_mode=="JT4") { + if(m_mode=="JT4" && m_freqNominal >= 432000000u) { + // if(m_mode=="JT4") { ui->TxFreqSpinBox->setValue(1000); ui->TxFreqSpinBox->setEnabled (false); ui->cbTxLock->setChecked(false); @@ -4627,17 +4693,17 @@ void MainWindow::transmitDisplay (bool transmitting) ui->cbTxLock->setEnabled (QSY_allowed); } - // the following are always disallowed in transmit + // the following are always disallowed in transmit ui->menuMode->setEnabled (!transmitting); - ui->bandComboBox->setEnabled (!transmitting); - if (!transmitting) { - if ("JT9+JT65" == m_mode) { - // allow mode switch in Rx when in dual mode - ui->pbTxMode->setEnabled (true); - } - } else { - ui->pbTxMode->setEnabled (false); + //ui->bandComboBox->setEnabled (!transmitting); + if (!transmitting) { + if ("JT9+JT65" == m_mode) { + // allow mode switch in Rx when in dual mode + ui->pbTxMode->setEnabled (true); } + } else { + ui->pbTxMode->setEnabled (false); + } } } @@ -4951,7 +5017,7 @@ void MainWindow::p1ReadFromStdout() //p1readFromStdout m_RxLog=0; m_startAnother=m_loopall; m_blankLine=true; - m_messageClient->status_update (m_dialFreq, m_mode, m_hisCall, + m_messageClient->status_update (m_freqNominal, m_mode, m_hisCall, QString::number (ui->rptSpinBox->value ()), m_modeTx, ui->autoButton->isChecked (), m_transmitting, (m_decoderBusy = false)); @@ -5145,11 +5211,11 @@ void MainWindow::WSPR_scheduling () { m_WSPR_tx_next = false; if (ui->band_hopping_group_box->isChecked ()) { - // qDebug () << "hop data: period:" << hop_data.period_name_ - // << "frequencies index:" << hop_data.frequencies_index_ - // << "tune:" << hop_data.tune_required_ - // << "tx:" << hop_data.tx_next_; auto hop_data = m_WSPR_band_hopping.next_hop (m_auto); + qDebug () << "hop data: period:" << hop_data.period_name_ + << "frequencies index:" << hop_data.frequencies_index_ + << "tune:" << hop_data.tune_required_ + << "tx:" << hop_data.tx_next_; m_WSPR_tx_next = hop_data.tx_next_; if (hop_data.frequencies_index_ >= 0) { // new band ui->bandComboBox->setCurrentIndex (hop_data.frequencies_index_); @@ -5162,7 +5228,7 @@ void MainWindow::WSPR_scheduling () QFile f {path}; if (f.exists ()) { m_cmnd = QDir::toNativeSeparators (f.fileName ()) + ' ' + - m_config.bands ()->find (m_dialFreq).remove ('m'); + m_config.bands ()->find (m_freqNominal).remove ('m'); } } if(m_cmnd!="") p3.start(m_cmnd); // Execute user's hardware controller @@ -5184,34 +5250,73 @@ void MainWindow::WSPR_scheduling () } } -void MainWindow::DopplerTracking_toggled (bool enabled) +void MainWindow::astroUpdate () { - if (!enabled) { - // do one astro update and last adjustment - astroCalculations (QDateTime::currentDateTimeUtc(), true); - } + if (m_astroWidget) + { + auto correction = m_astroWidget->astroUpdate(QDateTime::currentDateTimeUtc (), + m_config.my_grid(), m_hisGrid, + m_freqNominal, + "Echo" == m_mode, m_transmitting, + !m_config.tx_QSY_allowed (), m_TRperiod); + // no Doppler correction while CTRL pressed allows manual tuning + if (Qt::ControlModifier & QApplication::queryKeyboardModifiers ()) return; + + // no Doppler correction in Tx if rig can't do it + if (m_transmitting && !m_config.tx_QSY_allowed ()) return; + if (!m_astroWidget->doppler_tracking ()) return; + if ((m_monitoring || m_transmitting) + // no Doppler correction below 6m + && m_freqNominal >= 50000000 + && m_config.split_mode ()) + { + // adjust for rig resolution + if (m_config.transceiver_resolution () > 1) + { + correction.rx = (correction.rx + 50) / 100 * 100; + correction.tx = (correction.tx + 50) / 100 * 100; + } + else if (m_config.transceiver_resolution () > 0) + { + correction.rx = (correction.rx + 5) / 10 * 10; + correction.tx = (correction.tx + 5) / 10 * 10; + } + else if (m_config.transceiver_resolution () < -1) + { + correction.rx = correction.rx / 100 * 100; + correction.tx = correction.tx / 100 * 100; + } + else if (m_config.transceiver_resolution () < 0) + { + correction.rx = correction.rx / 10 * 10; + correction.tx = correction.tx / 10 * 10; + } + m_astroCorrection = correction; + } + else + { + m_astroCorrection = {}; + } + + setRig (); + } } -void MainWindow::astroCalculations (QDateTime const& time, bool adjust) +void MainWindow::setRig () { - if (m_astroWidget) { - auto astro_correction = m_astroWidget->astroUpdate(time, m_config.my_grid(), - m_hisGrid, m_dialFreq, "Echo" == m_mode, m_transmitting); - // Adjust frequency only if requested, allowed by mode, and freq > 50MHz. - if (adjust && !m_bSimplex && (m_dialFreq >= 50000000) && - m_astroWidget->trackVFO()) { - if(m_transmitting) { - m_dialFreqTx = m_freqNominal + astro_correction; - ui->labDialFreq->setText (Radio::pretty_frequency_MHz_string (m_dialFreqTx)); - Q_EMIT m_config.transceiver_tx_frequency (m_dialFreqTx); - } else { - m_dialFreq = m_freqNominal + astro_correction; - ui->labDialFreq->setText (Radio::pretty_frequency_MHz_string (m_dialFreq)); - Q_EMIT m_config.transceiver_frequency(m_dialFreq); - } + if(m_transmitting && !m_config.tx_QSY_allowed ()) return; + if ((m_monitoring || m_transmitting) + && m_config.transceiver_online ()) + { + if(m_transmitting && m_config.split_mode ()) + { + Q_EMIT m_config.transceiver_tx_frequency (m_freqTxNominal + m_astroCorrection.tx); + } + else + { + Q_EMIT m_config.transceiver_frequency (m_freqNominal + m_astroCorrection.rx); + } } -// qDebug() << "A" << 0.000001*m_dialFreq << 0.000001*m_dialFreqTx << astro_correction; - } } void MainWindow::fastPick(int x0, int x1, int y) @@ -5254,13 +5359,11 @@ void MainWindow::on_cbCQRx_toggled(bool b) void MainWindow::CQRxFreq() { - if(m_config.offsetRxFreq() and ui->cbCQRx->isChecked()) { - int MHz=int(m_dialFreq/1000000); - m_dialFreq = 1000000*MHz + 1000*m_freqCQ; - } else { - m_dialFreq = m_dialFreq0; + Frequency rx_frequency {m_config.offsetRxFreq () && ui->cbCQRx->isChecked () ? + m_freqNominal / 1000000 * 1000000 + 1000 * m_freqCQ : + m_callingFrequency}; + if (m_config.transceiver_online ()) { + Q_EMIT m_config.transceiver_frequency (rx_frequency); } - ui->labDialFreq->setText (Radio::pretty_frequency_MHz_string (m_dialFreq)); - Q_EMIT m_config.transceiver_frequency(m_dialFreq); } diff --git a/mainwindow.h b/mainwindow.h index bbdc3369b..76b1ba933 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -30,6 +30,7 @@ #include "logbook/logbook.h" #include "decodedtext.h" #include "commons.h" +#include "astro.h" #define NUM_JT4_SYMBOLS 206 //(72+31)*2, embedded sync #define NUM_JT65_SYMBOLS 126 //63 data + 63 sync @@ -59,7 +60,6 @@ class FastGraph; class WideGraph; class LogQSO; class Transceiver; -class Astro; class MessageAveraging; class MessageClient; class QTime; @@ -78,6 +78,7 @@ class MainWindow : public QMainWindow public: using Frequency = Radio::Frequency; + using FrequencyDelta = Radio::FrequencyDelta; using Mode = Modes::Mode; // Multiple instances: call MainWindow() with *thekey @@ -103,7 +104,7 @@ public slots: void p1ReadFromStdout(); void p1ReadFromStderr(); void p1Error(QProcess::ProcessError); - void setXIT(int n); + void setXIT(int n, Frequency base = 0u); void setFreq4(int rxFreq, int txFreq); void msgAvgDecode2(); void fastPick(int x0, int x1, int y); @@ -199,9 +200,9 @@ private slots: void on_cbTxLock_clicked(bool checked); void on_outAttenuation_valueChanged (int); void rigOpen (); - void handle_transceiver_update (Transceiver::TransceiverState); - void handle_transceiver_failure (QString reason); - void on_actionAstronomical_data_triggered(); + void handle_transceiver_update (Transceiver::TransceiverState const&); + void handle_transceiver_failure (QString const& reason); + void on_actionAstronomical_data_toggled (bool); void on_actionShort_list_of_add_on_prefixes_and_suffixes_triggered(); void getpfx(); void band_changed (Frequency); @@ -238,7 +239,6 @@ private slots: void on_pbTxNext_clicked(bool b); void on_actionEcho_Graph_triggered(); void on_actionEcho_triggered(); - void DopplerTracking_toggled (bool); void on_actionISCAT_triggered(); void on_actionFast_Graph_triggered(); void fast_decode_done(); @@ -274,6 +274,8 @@ private: Q_SIGNAL void toggleShorthand () const; private: + void astroUpdate (); + QDir m_dataDir; QString m_revision; bool m_multiple; @@ -298,9 +300,11 @@ private: QScopedPointer m_mouseCmnds; QScopedPointer m_msgAvgWidget; - Frequency m_dialFreq; - Frequency m_dialFreq0; - Frequency m_dialFreqRxWSPR; + Transceiver::TransceiverState m_rigState; + Frequency m_lastDialFreq; + QString m_lastBand; + Frequency m_callingFrequency; + Frequency m_dialFreqRxWSPR; // best guess at WSPR QRG Detector * m_detector; SoundInput * m_soundInput; @@ -311,9 +315,9 @@ private: qint64 m_msErase; qint64 m_secBandChanged; qint64 m_freqMoon; - qint64 m_freqNominal; - qint64 m_dialFreqTx; - qint64 m_dialFreqRx; + Frequency m_freqNominal; + Frequency m_freqTxNominal; + Astro::Correction m_astroCorrection; double m_s6; double m_tRemaining; @@ -536,7 +540,6 @@ private: void msgtype(QString t, QLineEdit* tx); void stub(); void statusChanged(); - void qsy(Frequency f); bool gridOK(QString g); bool shortList(QString callsign); void transmit (double snr = 99.); @@ -552,7 +555,7 @@ private: void enable_DXCC_entity (bool on); void switch_mode (Mode); void WSPR_scheduling (); - void astroCalculations (QDateTime const&, bool adjust); + void setRig (); void WSPR_history(Frequency dialFreq, int ndecodes); QString WSPR_hhmm(int n); void fast_config(bool b); diff --git a/mainwindow.ui b/mainwindow.ui index e712ecc29..c456045e9 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -1,2900 +1,2903 @@ - - - MainWindow - - - - 0 - 0 - 786 - 475 - - - - WSJT-X by K1JT - - - - - - - - - - - - 3 - - - 1 - - - - - - 500 - 16777215 - - - - - 10 - 50 - false - - - - Band Activity - - - Qt::AlignCenter - - - - - - - QFrame::Plain - - - Qt::Horizontal - - - - - - - - 300 - 20 - - - - - 600 - 20 - - - - - - - - - 252 - 252 - 252 - - - - - - - 170 - 170 - 170 - - - - - - - - - 252 - 252 - 252 - - - - - - - 170 - 170 - 170 - - - - - - - - - 170 - 170 - 170 - - - - - - - 170 - 170 - 170 - - - - - - - - true - - - UTC dB DT Freq Dr - - - Qt::PlainText - - - 5 - - - - - - - QFrame::Plain - - - Qt::Horizontal - - - - - - - - 300 - 20 - - - - - 600 - 20 - - - - - - - - - 252 - 252 - 252 - - - - - - - 170 - 170 - 170 - - - - - - - - - 252 - 252 - 252 - - - - - - - 170 - 170 - 170 - - - - - - - - - 170 - 170 - 170 - - - - - - - 170 - 170 - 170 - - - - - - - - true - - - UTC dB DT Freq Dr - - - Qt::PlainText - - - 5 - - - - - - - true - - - - 0 - 10 - - - - - 200 - 100 - - - - Qt::ScrollBarAlwaysOn - - - 0 - - - Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - - 500 - 16777215 - - - - - 10 - 50 - false - - - - Rx Frequency - - - Qt::AlignCenter - - - - - - - - 0 - 10 - - - - - 200 - 100 - - - - QFrame::Panel - - - Qt::ScrollBarAlwaysOn - - - Qt::ScrollBarAsNeeded - - - 0 - - - Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - - - - - - 50 - 0 - - - - Enter this QSO in ADIF log - - - Log &QSO - - - - - - - - 50 - 0 - - - - Stop monitoring - - - &Stop - - - - - - - - 50 - 0 - - - - Start monitoring - - - QPushButton:checked { - background-color: #00ff00; - border-style: outset; - border-width: 1px; - border-radius: 5px; - border-color: black; - min-width: 5em; - padding: 3px; -} - - - &Monitor - - - true - - - false - - - - - - - - 50 - 0 - - - - Erase QSO Frequency window. Double-click for both windows - - - &Erase - - - - - - - true - - - Clear Avg - - - - - - - - 50 - 0 - - - - <html><head/><body><p>Decode most recent Rx period at QSO Frequency</p></body></html> - - - QPushButton:checked { - background-color: cyan; - border-style: outset; - border-width: 1px; - border-radius: 5px; - border-color: black; - min-width: 5em; - padding: 3px; -} - - - &Decode - - - true - - - - - - - - 50 - 0 - - - - Toggle Tx Enable On/Off - - - QPushButton:checked { - background-color: red; - border-style: outset; - border-width: 1px; - border-radius: 5px; - border-color: black; - min-width: 5em; - padding: 3px; -} - - - E&nable Tx - - - true - - - - - - - - 50 - 0 - - - - Stop transmitting immediately - - - &Halt Tx - - - - - - - <html><head/><body><p>Transmit a pure tone</p></body></html> - - - QPushButton:checked { - background-color: red; - border-style: outset; - border-width: 1px; - border-radius: 5px; - border-color: black; - min-width: 5em; - padding: 3px; -} - - - &Tune - - - true - - - - - - - - - - - - 0 - 0 - - - - USB dial frequency - - - QLabel { - font-family: MS Shell Dlg 2; - font-size: 16pt; - color : yellow; - background-color : black; -} -QLabel[oob="true"] { - background-color: red; -} - - - - 14.078 000 - - - Qt::AlignCenter - - - 5 - - - - - - - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - Tx frequency tracks Rx frequency - - - Lock Tx=Rx - - - - - - - - 0 - 0 - - - - Set Tx frequency to Rx Frequency - - - Tx ← Rx - - - - - - - Check to Tx in even minutes, uncheck for odd minutes - - - Tx even/1st - - - - - - - 5 - - - - - <html><head/><body><p>Signal report (dB)</p></body></html> - - - Report - - - -50 - - - 49 - - - -15 - - - - - - - - - Audio Rx frequency - - - Qt::AlignCenter - - - Hz - - - Rx - - - 200 - - - 5000 - - - 1500 - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Audio Tx frequency - - - Qt::AlignCenter - - - Hz - - - Tx - - - 200 - - - 5000 - - - 1500 - - - - - - - false - - - Toggle Tx mode - - - Tx JT9 @ - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - 0 - 0 - - - - Set Rx frequency to Tx Frequency - - - Rx ← Tx - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Check for JT9 fast modes - - - Fast - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 60 - 16777215 - - - - Check to generate JT4 or JT65 shorthand messages. - - - Sh - - - - - - - false - - - Check to generate "@1250 (SEND MSGS)" in Tx6. - - - Tx6 - - - - - - - - 16777215 - 16777215 - - - - Check to add 2.5 s to expected propagation delay. - - - EME delay - - - - - - - - - - - Synchorizing threshold - - - Qt::AlignCenter - - - Sync - - - -1 - - - 10 - - - 1 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - false - - - CQ Rx - - - 1 - - - 999 - - - 285 - - - - - - - true - - - - 20 - 16777215 - - - - - - - - - - - - - Frequency tolerance (Hz) - - - Qt::AlignCenter - - - F Tol - - - 21 - - - 27 - - - - - - - Tx/Rx sequence length - - - Qt::AlignCenter - - - s - - - T/R - - - 11 - - - 14 - - - 14 - - - - - - - Submode controls tone spacing (A is narrowest) - - - Qt::AlignCenter - - - Submode - - - 0 - - - 7 - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 5 - 0 - - - - - - - - - - - - - QTabWidget::West - - - QTabWidget::Triangular - - - 1 - - - - 1 - - - - 6 - - - 6 - - - 6 - - - 6 - - - - - - 2 - 0 - - - - Generate standard messages for minimal QSO - - - Generate Std Msgs - - - - - - - Queue up the next Tx message - - - Next - - - - - - - Switch to this Tx message NOW - - - Now - - - Qt::AlignCenter - - - - - - - - 2 - 0 - - - - - - - - - - - Send this message in next Tx interval - - - - - - Ctrl+1 - - - false - - - buttonGroup - - - - - - - Switch to this Tx message NOW - - - Qt::LeftToRight - - - Tx &1 - - - Alt+1 - - - - - - - - 2 - 0 - - - - - - - - Send this message in next Tx interval - - - - - - Ctrl+2 - - - buttonGroup - - - - - - - Switch to this Tx message NOW - - - Tx &2 - - - Alt+2 - - - - - - - - 2 - 0 - - - - - - - - Send this message in next Tx interval - - - - - - Ctrl+3 - - - buttonGroup - - - - - - - Switch to this Tx message NOW - - - Tx &3 - - - Alt+3 - - - - - - - - 2 - 0 - - - - - - - - Send this message in next Tx interval - - - - - - Ctrl+4 - - - buttonGroup - - - - - - - Switch to this Tx message NOW - - - Tx &4 - - - Alt+4 - - - - - - - - 2 - 0 - - - - Enter a free text message (maximum 13 characters) -or select a predefined macro from the dropdown list. -Press ENTER to add the current text to the predefined -list. The list can be maintained in Settings (F2). - - - true - - - QComboBox::InsertAtBottom - - - - - - - Send this message in next Tx interval - - - - - - Ctrl+5 - - - buttonGroup - - - - - - - Switch to this Tx message NOW - - - Tx &5 - - - Alt+5 - - - - - - - - 2 - 0 - - - - - - - - - - - Send this message in next Tx interval - - - - - - Ctrl+6 - - - true - - - buttonGroup - - - - - - - Switch to this Tx message NOW - - - Tx &6 - - - Alt+6 - - - - - - - - 2 - - - - 6 - - - 6 - - - 6 - - - 6 - - - - - 2 - - - - - - 2 - 0 - - - - - 0 - 30 - - - - - 16777215 - 30 - - - - Calling CQ - - - Qt::AlignCenter - - - - - - - - 2 - 0 - - - - Generate a CQ message - - - CQ - - - - - - - - 2 - 0 - - - - Generate message with RRR - - - RRR - - - - - - - - 2 - 0 - - - - Generate message with report - - - dB - - - - - - - - 2 - 0 - - - - - 0 - 30 - - - - - 16777215 - 30 - - - - Answering CQ - - - Qt::AlignCenter - - - - - - - - 2 - 0 - - - - Generate message for replying to a CQ - - - Grid - - - - - - - - 2 - 0 - - - - Generate message with R+report - - - R+dB - - - - - - - - 2 - 0 - - - - Generate message with 73 - - - 73 - - - - - - - - - - - - 3 - 0 - - - - - - - - - 1 - 0 - - - - - 0 - 26 - - - - Send this standard (generated) message - - - Gen msg - - - true - - - - - - - - - - - - 3 - 0 - - - - - 150 - 0 - - - - Enter a free text message (maximum 13 characters) -or select a predefined macro from the dropdown list. -Press ENTER to add the current text to the predefined -list. The list can be maintained in Settings (F2). - - - true - - - QComboBox::InsertAtBottom - - - - - - - - 1 - 0 - - - - Send this free-text message (max 13 characters) - - - Free msg - - - - - - - - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Band Hopping - - - true - - - - - - Choose bands and times of day for band-hopping. - - - Schedule ... - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Upload decoded messages to WPRnet.org. - - - Upload spots - - - - - - - Qt::AlignCenter - - - Hz - - - Tx - - - 1400 - - - 1600 - - - 1500 - - - - - - - Transmit during the next 2-minute sequence. - - - QPushButton:checked { - background-color: red; - border-style: outset; - border-width: 1px; - border-radius: 5px; - border-color: black; - min-width: 5em; - padding: 3px; -} - - - Tx Next - - - true - - - - - - - Set Tx power in dBm (dB above 1 mW) as part of your WSPR message. - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Percentage of 2-minute sequences devoted to transmitting. - - - Qt::AlignCenter - - - % - - - Tx Pct - - - 100 - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - - - - - 252 - 252 - 252 - - - - - - - 159 - 175 - 213 - - - - - - - - - 252 - 252 - 252 - - - - - - - 159 - 175 - 213 - - - - - - - - - 159 - 175 - 213 - - - - - - - 159 - 175 - 213 - - - - - - - - true - - - DX Call - - - Qt::AlignCenter - - - 5 - - - 2 - - - - - - - - 0 - 0 - - - - - - - - - 252 - 252 - 252 - - - - - - - 159 - 175 - 213 - - - - - - - - - 252 - 252 - 252 - - - - - - - 159 - 175 - 213 - - - - - - - - - 159 - 175 - 213 - - - - - - - 159 - 175 - 213 - - - - - - - - true - - - DX Grid - - - Qt::AlignCenter - - - 5 - - - 2 - - - - - - - Callsign of station to be worked - - - - - - Qt::AlignCenter - - - - - - - Locator of station to be worked - - - - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - true - - - - - - Qt::AlignCenter - - - 4 - - - - - - - - 0 - 0 - - - - - - - Qt::AlignCenter - - - - - - - Search for callsign in database - - - &Lookup - - - - - - - Add callsign and locator to database - - - Add - - - - - - - - - - Digital gain for audio input - - - -50 - - - 50 - - - 20 - - - QSlider::TicksBelow - - - 10 - - - - - - - - 0 - 0 - - - - QFrame::Panel - - - QFrame::Sunken - - - - - - - Adjust Tx audio level - - - 450 - - - 0 - - - Qt::Vertical - - - true - - - true - - - QSlider::TicksBelow - - - 50 - - - - - - - - 0 - 0 - - - - QLabel { - font-family: MS Shell Dlg 2; - font-size: 16pt; - background-color : black; - color : yellow; -} - - - QFrame::StyledPanel - - - QFrame::Sunken - - - 2 - - - 0 - - - <html><head/><body><p align="center"> 2015 Jun 17 </p><p align="center"> 01:23:45 </p></body></html> - - - Qt::AlignCenter - - - 5 - - - - - - - Pwr - - - - - - - false - - - <html><head/><body><p>If orange or red there has been a rig control failure, click to reset and read the dial frequency.</p></body></html> - - - QPushButton { - font-family: helvetica; - font-size: 9pt; - font-weight: bold; - background-color: white; - color: black; - border-style: solid; - border-width:1px; - border-radius:10px; - border-color: gray; - max-width:20px; - max-height:20px; - min-width:20px; - min-height:20px; -} -QPushButton[state="error"] { - background-color: red; -} -QPushButton[state="warning"] { - background-color: orange; -} -QPushButton[state="ok"] { - background-color: #00ff00; -} - - - ? - - - - - - - Select operating band or frequency in MHz - - - true - - - QComboBox::NoInsert - - - QComboBox::AdjustToMinimumContentsLength - - - - - - - - - - - - - 0 - 0 - 786 - 21 - - - - - File - - - - - - - - - - - - - - - - - - - View - - - - - - - - - - Decode - - - - - - - - - - - Save - - - - - - - - Help - - - - - - - - - - - - - - Mode - - - - - - - - - - - - - - - - - - - - - - - Exit - - - QAction::QuitRole - - - - - false - - - Configuration - - - F2 - - - - - About WSJT-X - - - Ctrl+F1 - - - - - Waterfall - - - - - Open - - - Ctrl+O - - - - - Open next in directory - - - F6 - - - - - Decode remaining files in directory - - - Shift+F6 - - - - - Delete all *.wav && *.c2 files in SaveDir - - - - - true - - - false - - - Fast - - - - - true - - - true - - - None - - - - - true - - - Save all - - - - - Online User Guide - - - F1 - - - - - Keyboard shortcuts - - - F3 - - - - - Special mouse commands - - - F5 - - - - - true - - - true - - - JT9 - - - - - true - - - true - - - Save decoded - - - - - true - - - false - - - Normal - - - - - true - - - true - - - Deep - - - - - true - - - Monitor OFF at startup - - - - - Erase ALL.TXT - - - - - Erase wsjtx_log.adi - - - - - true - - - Convert mode to RTTY for logging - - - - - true - - - Log dB reports to Comments - - - - - true - - - Prompt me to log QSO - - - - - true - - - Blank line between decoding periods - - - - - true - - - Clear DX Call and Grid after logging - - - - - true - - - Display distance in miles - - - - - true - - - Double-click on call sets Tx Enable - - - F7 - - - - - true - - - Tx disabled after sending 73 - - - - - true - - - Runaway Tx watchdog - - - - - true - - - Allow multiple instances - - - - - true - - - Tx freq locked to Rx freq - - - - - true - - - JT65 - - - - - true - - - JT9+JT65 - - - - - true - - - Tx messages to Rx Frequency window - - - - - true - - - Gray1 - - - - - true - - - Show DXCC entity and worked B4 status - - - - - Astronomical data - - - - - Short list of add-on prefixes and suffixes - - - - - Settings... - - - F2 - - - - - Local User Guide - - - - - Open log directory - - - - - true - - - JT4 - - - - - Message averaging - - - F7 - - - - - true - - - Enable averaging - - - - - true - - - Enable deep search - - - - - true - - - WSPR-2 - - - - - true - - - false - - - WSPR-15 - - - - - Echo Graph - - - F8 - - - - - true - - - Echo - - - EME Echo mode - - - - - true - - - ISCAT - - - - - Fast Graph - - - F9 - - - - - Save reference spectrum - - - - - true - - - JTMSK - - - - - &Download Samples ... - - - <html><head/><body><p>Download sample audio files demonstrating the various modes.</p></body></html> - - - - - - - DisplayText - QTextEdit -
displaytext.h
-
- - LettersSpinBox - QSpinBox -
LettersSpinBox.hpp
-
- - SignalMeter - QFrame -
signalmeter.h
- 1 -
-
- - logQSOButton - stopButton - monitorButton - EraseButton - DecodeButton - autoButton - stopTxButton - tuneButton - bandComboBox - inGain - txFirstCheckBox - TxFreqSpinBox - rptSpinBox - genStdMsgsPushButton - tx1 - tx2 - tx3 - tx4 - tx6 - txrb1 - txrb2 - txrb3 - txrb4 - txrb5 - txrb6 - txb1 - txb2 - txb3 - txb4 - txb5 - txb6 - genMsg - pbAnswerCaller - rbFreeText - pbSendRRR - pbAnswerCQ - pbSendReport - pbSend73 - rbGenMsg - pbCallCQ - - - - - - - true - - - -
+ + + MainWindow + + + + 0 + 0 + 786 + 475 + + + + WSJT-X by K1JT + + + + + + + + + + + + 3 + + + 1 + + + + + + 500 + 16777215 + + + + + 10 + 50 + false + + + + Band Activity + + + Qt::AlignCenter + + + + + + + QFrame::Plain + + + Qt::Horizontal + + + + + + + + 300 + 20 + + + + + 600 + 20 + + + + + + + + + 252 + 252 + 252 + + + + + + + 170 + 170 + 170 + + + + + + + + + 252 + 252 + 252 + + + + + + + 170 + 170 + 170 + + + + + + + + + 170 + 170 + 170 + + + + + + + 170 + 170 + 170 + + + + + + + + true + + + UTC dB DT Freq Dr + + + Qt::PlainText + + + 5 + + + + + + + QFrame::Plain + + + Qt::Horizontal + + + + + + + + 300 + 20 + + + + + 600 + 20 + + + + + + + + + 252 + 252 + 252 + + + + + + + 170 + 170 + 170 + + + + + + + + + 252 + 252 + 252 + + + + + + + 170 + 170 + 170 + + + + + + + + + 170 + 170 + 170 + + + + + + + 170 + 170 + 170 + + + + + + + + true + + + UTC dB DT Freq Dr + + + Qt::PlainText + + + 5 + + + + + + + true + + + + 0 + 10 + + + + + 200 + 100 + + + + Qt::ScrollBarAlwaysOn + + + 0 + + + Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + 500 + 16777215 + + + + + 10 + 50 + false + + + + Rx Frequency + + + Qt::AlignCenter + + + + + + + + 0 + 10 + + + + + 200 + 100 + + + + QFrame::Panel + + + Qt::ScrollBarAlwaysOn + + + Qt::ScrollBarAsNeeded + + + 0 + + + Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + + + + 50 + 0 + + + + Enter this QSO in ADIF log + + + Log &QSO + + + + + + + + 50 + 0 + + + + Stop monitoring + + + &Stop + + + + + + + + 50 + 0 + + + + Start monitoring + + + QPushButton:checked { + background-color: #00ff00; + border-style: outset; + border-width: 1px; + border-radius: 5px; + border-color: black; + min-width: 5em; + padding: 3px; +} + + + &Monitor + + + true + + + false + + + + + + + + 50 + 0 + + + + Erase QSO Frequency window. Double-click for both windows + + + &Erase + + + + + + + true + + + Clear Avg + + + + + + + + 50 + 0 + + + + <html><head/><body><p>Decode most recent Rx period at QSO Frequency</p></body></html> + + + QPushButton:checked { + background-color: cyan; + border-style: outset; + border-width: 1px; + border-radius: 5px; + border-color: black; + min-width: 5em; + padding: 3px; +} + + + &Decode + + + true + + + + + + + + 50 + 0 + + + + Toggle Tx Enable On/Off + + + QPushButton:checked { + background-color: red; + border-style: outset; + border-width: 1px; + border-radius: 5px; + border-color: black; + min-width: 5em; + padding: 3px; +} + + + E&nable Tx + + + true + + + + + + + + 50 + 0 + + + + Stop transmitting immediately + + + &Halt Tx + + + + + + + <html><head/><body><p>Transmit a pure tone</p></body></html> + + + QPushButton:checked { + background-color: red; + border-style: outset; + border-width: 1px; + border-radius: 5px; + border-color: black; + min-width: 5em; + padding: 3px; +} + + + &Tune + + + true + + + + + + + + + + + + 0 + 0 + + + + USB dial frequency + + + QLabel { + font-family: MS Shell Dlg 2; + font-size: 16pt; + color : yellow; + background-color : black; +} +QLabel[oob="true"] { + background-color: red; +} + + + + 14.078 000 + + + Qt::AlignCenter + + + 5 + + + + + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Tx frequency tracks Rx frequency + + + Lock Tx=Rx + + + + + + + + 0 + 0 + + + + Set Tx frequency to Rx Frequency + + + Tx ← Rx + + + + + + + Check to Tx in even minutes, uncheck for odd minutes + + + Tx even/1st + + + + + + + 5 + + + + + <html><head/><body><p>Signal report (dB)</p></body></html> + + + Report + + + -50 + + + 49 + + + -15 + + + + + + + + + Audio Rx frequency + + + Qt::AlignCenter + + + Hz + + + Rx + + + 200 + + + 5000 + + + 1500 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Audio Tx frequency + + + Qt::AlignCenter + + + Hz + + + Tx + + + 200 + + + 5000 + + + 1500 + + + + + + + false + + + Toggle Tx mode + + + Tx JT9 @ + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + Set Rx frequency to Tx Frequency + + + Rx ← Tx + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Check for JT9 fast modes + + + Fast + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 60 + 16777215 + + + + Check to generate JT4 or JT65 shorthand messages. + + + Sh + + + + + + + false + + + Check to generate "@1250 (SEND MSGS)" in Tx6. + + + Tx6 + + + + + + + + 16777215 + 16777215 + + + + Check to add 2.5 s to expected propagation delay. + + + EME delay + + + + + + + + + + + Synchorizing threshold + + + Qt::AlignCenter + + + Sync + + + -1 + + + 10 + + + 1 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + CQ Rx + + + 1 + + + 999 + + + 285 + + + + + + + true + + + + 20 + 16777215 + + + + + + + + + + + + + Frequency tolerance (Hz) + + + Qt::AlignCenter + + + F Tol + + + 21 + + + 27 + + + + + + + Tx/Rx sequence length + + + Qt::AlignCenter + + + s + + + T/R + + + 11 + + + 14 + + + 14 + + + + + + + Submode controls tone spacing (A is narrowest) + + + Qt::AlignCenter + + + Submode + + + 0 + + + 7 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 0 + + + + + + + + + + + + + QTabWidget::West + + + QTabWidget::Triangular + + + 1 + + + + 1 + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 2 + 0 + + + + Generate standard messages for minimal QSO + + + Generate Std Msgs + + + + + + + Queue up the next Tx message + + + Next + + + + + + + Switch to this Tx message NOW + + + Now + + + Qt::AlignCenter + + + + + + + + 2 + 0 + + + + + + + + + + + Send this message in next Tx interval + + + + + + Ctrl+1 + + + false + + + buttonGroup + + + + + + + Switch to this Tx message NOW + + + Qt::LeftToRight + + + Tx &1 + + + Alt+1 + + + + + + + + 2 + 0 + + + + + + + + Send this message in next Tx interval + + + + + + Ctrl+2 + + + buttonGroup + + + + + + + Switch to this Tx message NOW + + + Tx &2 + + + Alt+2 + + + + + + + + 2 + 0 + + + + + + + + Send this message in next Tx interval + + + + + + Ctrl+3 + + + buttonGroup + + + + + + + Switch to this Tx message NOW + + + Tx &3 + + + Alt+3 + + + + + + + + 2 + 0 + + + + + + + + Send this message in next Tx interval + + + + + + Ctrl+4 + + + buttonGroup + + + + + + + Switch to this Tx message NOW + + + Tx &4 + + + Alt+4 + + + + + + + + 2 + 0 + + + + Enter a free text message (maximum 13 characters) +or select a predefined macro from the dropdown list. +Press ENTER to add the current text to the predefined +list. The list can be maintained in Settings (F2). + + + true + + + QComboBox::InsertAtBottom + + + + + + + Send this message in next Tx interval + + + + + + Ctrl+5 + + + buttonGroup + + + + + + + Switch to this Tx message NOW + + + Tx &5 + + + Alt+5 + + + + + + + + 2 + 0 + + + + + + + + + + + Send this message in next Tx interval + + + + + + Ctrl+6 + + + true + + + buttonGroup + + + + + + + Switch to this Tx message NOW + + + Tx &6 + + + Alt+6 + + + + + + + + 2 + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + 2 + + + + + + 2 + 0 + + + + + 0 + 30 + + + + + 16777215 + 30 + + + + Calling CQ + + + Qt::AlignCenter + + + + + + + + 2 + 0 + + + + Generate a CQ message + + + CQ + + + + + + + + 2 + 0 + + + + Generate message with RRR + + + RRR + + + + + + + + 2 + 0 + + + + Generate message with report + + + dB + + + + + + + + 2 + 0 + + + + + 0 + 30 + + + + + 16777215 + 30 + + + + Answering CQ + + + Qt::AlignCenter + + + + + + + + 2 + 0 + + + + Generate message for replying to a CQ + + + Grid + + + + + + + + 2 + 0 + + + + Generate message with R+report + + + R+dB + + + + + + + + 2 + 0 + + + + Generate message with 73 + + + 73 + + + + + + + + + + + + 3 + 0 + + + + + + + + + 1 + 0 + + + + + 0 + 26 + + + + Send this standard (generated) message + + + Gen msg + + + true + + + + + + + + + + + + 3 + 0 + + + + + 150 + 0 + + + + Enter a free text message (maximum 13 characters) +or select a predefined macro from the dropdown list. +Press ENTER to add the current text to the predefined +list. The list can be maintained in Settings (F2). + + + true + + + QComboBox::InsertAtBottom + + + + + + + + 1 + 0 + + + + Send this free-text message (max 13 characters) + + + Free msg + + + + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Band Hopping + + + true + + + + + + Choose bands and times of day for band-hopping. + + + Schedule ... + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Upload decoded messages to WPRnet.org. + + + Upload spots + + + + + + + Qt::AlignCenter + + + Hz + + + Tx + + + 1400 + + + 1600 + + + 1500 + + + + + + + Transmit during the next 2-minute sequence. + + + QPushButton:checked { + background-color: red; + border-style: outset; + border-width: 1px; + border-radius: 5px; + border-color: black; + min-width: 5em; + padding: 3px; +} + + + Tx Next + + + true + + + + + + + Set Tx power in dBm (dB above 1 mW) as part of your WSPR message. + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Percentage of 2-minute sequences devoted to transmitting. + + + Qt::AlignCenter + + + % + + + Tx Pct + + + 100 + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + + 252 + 252 + 252 + + + + + + + 159 + 175 + 213 + + + + + + + + + 252 + 252 + 252 + + + + + + + 159 + 175 + 213 + + + + + + + + + 159 + 175 + 213 + + + + + + + 159 + 175 + 213 + + + + + + + + true + + + DX Call + + + Qt::AlignCenter + + + 5 + + + 2 + + + + + + + + 0 + 0 + + + + + + + + + 252 + 252 + 252 + + + + + + + 159 + 175 + 213 + + + + + + + + + 252 + 252 + 252 + + + + + + + 159 + 175 + 213 + + + + + + + + + 159 + 175 + 213 + + + + + + + 159 + 175 + 213 + + + + + + + + true + + + DX Grid + + + Qt::AlignCenter + + + 5 + + + 2 + + + + + + + Callsign of station to be worked + + + + + + Qt::AlignCenter + + + + + + + Locator of station to be worked + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + true + + + + + + Qt::AlignCenter + + + 4 + + + + + + + + 0 + 0 + + + + + + + Qt::AlignCenter + + + + + + + Search for callsign in database + + + &Lookup + + + + + + + Add callsign and locator to database + + + Add + + + + + + + + + + Digital gain for audio input + + + -50 + + + 50 + + + 20 + + + QSlider::TicksBelow + + + 10 + + + + + + + + 0 + 0 + + + + QFrame::Panel + + + QFrame::Sunken + + + + + + + Adjust Tx audio level + + + 450 + + + 0 + + + Qt::Vertical + + + true + + + true + + + QSlider::TicksBelow + + + 50 + + + + + + + + 0 + 0 + + + + QLabel { + font-family: MS Shell Dlg 2; + font-size: 16pt; + background-color : black; + color : yellow; +} + + + QFrame::StyledPanel + + + QFrame::Sunken + + + 2 + + + 0 + + + <html><head/><body><p align="center"> 2015 Jun 17 </p><p align="center"> 01:23:45 </p></body></html> + + + Qt::AlignCenter + + + 5 + + + + + + + Pwr + + + + + + + false + + + <html><head/><body><p>If orange or red there has been a rig control failure, click to reset and read the dial frequency.</p></body></html> + + + QPushButton { + font-family: helvetica; + font-size: 9pt; + font-weight: bold; + background-color: white; + color: black; + border-style: solid; + border-width:1px; + border-radius:10px; + border-color: gray; + max-width:20px; + max-height:20px; + min-width:20px; + min-height:20px; +} +QPushButton[state="error"] { + background-color: red; +} +QPushButton[state="warning"] { + background-color: orange; +} +QPushButton[state="ok"] { + background-color: #00ff00; +} + + + ? + + + + + + + Select operating band or frequency in MHz + + + true + + + QComboBox::NoInsert + + + QComboBox::AdjustToMinimumContentsLength + + + + + + + + + + + + + 0 + 0 + 786 + 21 + + + + + File + + + + + + + + + + + + + + + + + + + View + + + + + + + + + + Decode + + + + + + + + + + + Save + + + + + + + + Help + + + + + + + + + + + + + + Mode + + + + + + + + + + + + + + + + + + + + + + + Exit + + + QAction::QuitRole + + + + + false + + + Configuration + + + F2 + + + + + About WSJT-X + + + Ctrl+F1 + + + + + Waterfall + + + + + Open + + + Ctrl+O + + + + + Open next in directory + + + F6 + + + + + Decode remaining files in directory + + + Shift+F6 + + + + + Delete all *.wav && *.c2 files in SaveDir + + + + + true + + + false + + + Fast + + + + + true + + + true + + + None + + + + + true + + + Save all + + + + + Online User Guide + + + F1 + + + + + Keyboard shortcuts + + + F3 + + + + + Special mouse commands + + + F5 + + + + + true + + + true + + + JT9 + + + + + true + + + true + + + Save decoded + + + + + true + + + false + + + Normal + + + + + true + + + true + + + Deep + + + + + true + + + Monitor OFF at startup + + + + + Erase ALL.TXT + + + + + Erase wsjtx_log.adi + + + + + true + + + Convert mode to RTTY for logging + + + + + true + + + Log dB reports to Comments + + + + + true + + + Prompt me to log QSO + + + + + true + + + Blank line between decoding periods + + + + + true + + + Clear DX Call and Grid after logging + + + + + true + + + Display distance in miles + + + + + true + + + Double-click on call sets Tx Enable + + + F7 + + + + + true + + + Tx disabled after sending 73 + + + + + true + + + Runaway Tx watchdog + + + + + true + + + Allow multiple instances + + + + + true + + + Tx freq locked to Rx freq + + + + + true + + + JT65 + + + + + true + + + JT9+JT65 + + + + + true + + + Tx messages to Rx Frequency window + + + + + true + + + Gray1 + + + + + true + + + Show DXCC entity and worked B4 status + + + + + true + + + Astronomical data + + + + + Short list of add-on prefixes and suffixes + + + + + Settings... + + + F2 + + + + + Local User Guide + + + + + Open log directory + + + + + true + + + JT4 + + + + + Message averaging + + + F7 + + + + + true + + + Enable averaging + + + + + true + + + Enable deep search + + + + + true + + + WSPR-2 + + + + + true + + + false + + + WSPR-15 + + + + + Echo Graph + + + F8 + + + + + true + + + Echo + + + EME Echo mode + + + + + true + + + ISCAT + + + + + Fast Graph + + + F9 + + + + + Save reference spectrum + + + + + true + + + JTMSK + + + + + &Download Samples ... + + + <html><head/><body><p>Download sample audio files demonstrating the various modes.</p></body></html> + + + + + + + DisplayText + QTextEdit +
displaytext.h
+
+ + LettersSpinBox + QSpinBox +
LettersSpinBox.hpp
+
+ + SignalMeter + QFrame +
signalmeter.h
+ 1 +
+
+ + logQSOButton + stopButton + monitorButton + EraseButton + DecodeButton + autoButton + stopTxButton + tuneButton + bandComboBox + inGain + txFirstCheckBox + TxFreqSpinBox + rptSpinBox + genStdMsgsPushButton + tx1 + tx2 + tx3 + tx4 + tx6 + txrb1 + txrb2 + txrb3 + txrb4 + txrb5 + txrb6 + txb1 + txb2 + txb3 + txb4 + txb5 + txb6 + genMsg + pbAnswerCaller + rbFreeText + pbSendRRR + pbAnswerCQ + pbSendReport + pbSend73 + rbGenMsg + pbCallCQ + + + + + + + true + + + +