Rig control overhaul to implement generic Doppler shift tracking

The  concept of  a nominal  receive  and transmit  frequency has  been
introduced. This is  used as a base frequency  for Doppler correction,
frequency setting  and reporting. The  start up frequency is  now zero
which is  updated by the first  rig control status report.  This needs
more  work to  accommodate  calling frequency  plus working  frequency
operation as is used for random MS operation etc..

The  main  window  frequency  display  now  shows  the  transmit  dial
frequency while transmitting.

The mode changing logic sequence has been changed such that the rig is
correctly put  into and  taken out  of split mode  as required  by the
target mode.  This also  avoids the "other"  VFO having  its frequency
changed when  entering a mode that  does not use split  operating like
WSPR.

The main window  band combo box edit  may now be used to  input an kHz
offset  from the  current MHz  dial  frequency. This  is intended  for
setting  a sked  or working  frequency on  the VHF  and up  bands. For
example the working frequency for 23cms  might be set to 1296MHz and a
working  frequency of  1296.3MHz would  be selected  by selecting  the
23cms band  with the combo box  drop down list and  then entering 300k
into the band combo box edit widget.

When using JT4 modes a CTRL+Click on the waterfall adjusts the nominal
frequency such  that the frequency  clicked on  becomes the Tx  and Rx
frequency using  the fixed 1000Hz  DF that  JT4 modes use.   This will
probably be extended to all QSO modes when used in VHF & up mode. This
assumes that 1000Hz is an optimal DF  for both Tx and Rx and therefore
one  can "net"  to an  off frequency,  but visible  on the  waterfall,
caller with one click.

Improvements to OmniRig  rig control including use of  the serial port
control lines RTS or DTR, on the  CAT serial port used by OmniRig, for
PTT control.

Incrementing transaction sequence numbers added to messages to and from
the rig control  thread. This enables round trip status  to be tracked
and associated with a request. For  example a command that might cause
several  asynchronous  status  updates  can  now  be  tracked  in  the
originating thread such  that it is clear which updates  are caused by
executing the  request. This in turn  allows updates to be  held until
the request is complete i.e. the  state is consistent with the results
of the request.

Messages  to the  rig control  thread are  now posted  as a  new state
(Transceiver::TransceiverState) object. The  rig control thread tracks
requests and  actions any differences  between the prior  requests and
the new state.

The rig  control thread is now  stored on the  heap so that it  can be
closed down  and released as needed.  Along with this the  rig control
close  down  semantics  are  better defined  avoiding  some  potential
deadlock situations.

If the rig  is placed into split  mode it will be  reverted to simplex
mode when the rig connection is closed.

When  using direct  rig control  via Hamlib,  rigs that  have A/B  VFO
arrangements and  no method to query  the current VFO like  many Icoms
and  the Yaesu  FT-817/857/897(D)  series now  have smarted  frequency
updating requiring no  VFO changes when changing  the frequency.  This
is particularly  important when doing  Tx Doppler correction  to avoid
glitches.

The implementation  of emulated  split operating  mode ("Fake  It") is
simplified and improved.

A dummy  Hamlib transceiver for PTT  control on a separate  port is no
long instantiated if CAT or VOX PTT control is selected.

The resolution and  any rounding of the rig CAT  frequency set and get
commands is determined automatically  upon opening the rig connection.
This is needed to determine the  rate of frequency updates for Doppler
tracking. It also allows the rig to be more accurately controlled.

Frequency  calibration is  calculated separately  for the  receive and
transmit frequencies.

Whether  the  rig  modulation  mode  should be  controlled  is  now  a
constructor  argument rather  than  being passed  with individual  rig
control requests.

Doppler  shift  correction  is   considerably  enhanced  with  simpler
controls and much  better rig control.  A new mode  of tracking called
"receive only" is introduced for those with rigs that cannot be QSY:ed
via  CAT  when transmitting.   Such  rigs  have a  Doppler  correction
calculated  for the  middle of  the next  transmit period  just before
transmission starts. While  using Doppler tracking it  is now possible
to adjust the  sked frequency either using the new  kHz offset feature
of the main  window band combo box  or by directly tuning  the rig VFO
knob while holding down the CTRL key.

The astronomical data window that includes Doppler tracking control is
now opened  and closed using a  checkable menu item to  avoid it being
accidentally closed.

Debug  configuration  rig  control  diagnostic  messages  now  have  a
facility argument for clearer and more standardized trace messages.

git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@6590 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
This commit is contained in:
Bill Somerville 2016-04-06 17:11:58 +00:00
parent d864e7f4cb
commit 2cfbb15b4f
29 changed files with 4837 additions and 4593 deletions

View File

@ -372,7 +372,7 @@ private:
void set_application_font (QFont const&); void set_application_font (QFont const&);
void initialize_models (); void initialize_models ();
bool open_rig (); bool open_rig (bool force = false);
//bool set_mode (); //bool set_mode ();
void close_rig (); void close_rig ();
TransceiverFactory::ParameterPack gather_rig_data (); TransceiverFactory::ParameterPack gather_rig_data ();
@ -416,8 +416,8 @@ private:
Q_SLOT void insert_frequency (); Q_SLOT void insert_frequency ();
Q_SLOT void delete_stations (); Q_SLOT void delete_stations ();
Q_SLOT void insert_station (); Q_SLOT void insert_station ();
Q_SLOT void handle_transceiver_update (TransceiverState); Q_SLOT void handle_transceiver_update (TransceiverState const&, unsigned sequence_number);
Q_SLOT void handle_transceiver_failure (QString reason); Q_SLOT void handle_transceiver_failure (QString const& reason);
Q_SLOT void on_pbCQmsg_clicked(); Q_SLOT void on_pbCQmsg_clicked();
Q_SLOT void on_pbMyCall_clicked(); Q_SLOT void on_pbMyCall_clicked();
Q_SLOT void on_pbTxMsg_clicked(); Q_SLOT void on_pbTxMsg_clicked();
@ -425,17 +425,14 @@ private:
Q_SLOT void on_pbNewCall_clicked(); Q_SLOT void on_pbNewCall_clicked();
// typenames used as arguments must match registered type names :( // typenames used as arguments must match registered type names :(
Q_SIGNAL void start_transceiver () const; Q_SIGNAL void start_transceiver (unsigned seqeunce_number) const;
Q_SIGNAL void stop_transceiver (bool reset_split) const; Q_SIGNAL void set_transceiver (Transceiver::TransceiverState const&,
Q_SIGNAL void frequency (Frequency rx, Transceiver::MODE) const; unsigned sequence_number) const;
Q_SIGNAL void tx_frequency (Frequency tx, bool rationalize_mode) const; Q_SIGNAL void stop_transceiver () const;
Q_SIGNAL void mode (Transceiver::MODE, bool rationalize) const;
Q_SIGNAL void ptt (bool) const;
Q_SIGNAL void sync (bool force_signal) const;
Configuration * const self_; // back pointer to public interface Configuration * const self_; // back pointer to public interface
QThread transceiver_thread_; QThread * transceiver_thread_;
TransceiverFactory transceiver_factory_; TransceiverFactory transceiver_factory_;
QList<QMetaObject::Connection> rig_connections_; QList<QMetaObject::Connection> rig_connections_;
@ -473,6 +470,7 @@ private:
StationList stations_; StationList stations_;
StationList next_stations_; StationList next_stations_;
FrequencyDelta current_offset_; FrequencyDelta current_offset_;
FrequencyDelta current_tx_offset_;
QAction * frequency_delete_action_; QAction * frequency_delete_action_;
QAction * frequency_insert_action_; QAction * frequency_insert_action_;
@ -489,24 +487,10 @@ private:
bool have_rig_; bool have_rig_;
bool rig_changed_; bool rig_changed_;
TransceiverState cached_rig_state_; TransceiverState cached_rig_state_;
int rig_resolution_; // see Transceiver::resolution signal
double frequency_calibration_intercept_; double frequency_calibration_intercept_;
double frequency_calibration_slope_ppm_; double frequency_calibration_slope_ppm_;
unsigned transceiver_command_number_;
// 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_;
// configuration fields that we publish // configuration fields that we publish
QString my_callsign_; 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_;} QDir Configuration::temp_dir () const {return m_->temp_dir_;}
int Configuration::exec () {return m_->exec ();} 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_;} QAudioDeviceInfo const& Configuration::audio_input_device () const {return m_->audio_input_device_;}
AudioDevice::Channel Configuration::audio_input_channel () const {return m_->audio_input_channel_;} 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); return m_->have_rig (open_if_closed);
} }
int Configuration::transceiver_resolution () const
{
return m_->rig_resolution_;
}
void Configuration::transceiver_offline () void Configuration::transceiver_offline ()
{ {
#if WSJT_TRACE_CAT #if WSJT_TRACE_CAT
qDebug () << "Configuration::transceiver_offline:" << m_->cached_rig_state_; qDebug () << "Configuration::transceiver_offline:" << m_->cached_rig_state_;
#endif #endif
return m_->close_rig (); m_->close_rig ();
} }
void Configuration::transceiver_frequency (Frequency f) 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_; qDebug () << "Configuration::transceiver_tx_frequency:" << f << m_->cached_rig_state_;
#endif #endif
m_->setup_split_ = true;
m_->required_tx_frequency_ = f;
m_->transceiver_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_; qDebug () << "Configuration::sync_transceiver: force signal:" << force_signal << "enforce_mode_and_split:" << enforce_mode_and_split << m_->cached_rig_state_;
#endif #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); m_->sync_transceiver (force_signal);
if (!enforce_mode_and_split)
{
m_->transceiver_tx_frequency (0);
}
} }
Configuration::impl::impl (Configuration * self, QSettings * settings, QWidget * parent) Configuration::impl::impl (Configuration * self, QSettings * settings, QWidget * parent)
@ -738,15 +727,14 @@ Configuration::impl::impl (Configuration * self, QSettings * settings, QWidget *
, stations_ {&bands_} , stations_ {&bands_}
, next_stations_ {&bands_} , next_stations_ {&bands_}
, current_offset_ {0} , current_offset_ {0}
, current_tx_offset_ {0}
, frequency_dialog_ {new FrequencyDialog {&modes_, this}} , frequency_dialog_ {new FrequencyDialog {&modes_, this}}
, station_dialog_ {new StationDialog {&next_stations_, &bands_, this}} , station_dialog_ {new StationDialog {&next_stations_, &bands_, this}}
, rig_active_ {false} , rig_active_ {false}
, have_rig_ {false} , have_rig_ {false}
, rig_changed_ {false} , rig_changed_ {false}
// , ptt_state_ {false} , rig_resolution_ {0}
, setup_split_ {false} , transceiver_command_number_ {0}
, required_tx_frequency_ {0}
, enforce_mode_and_split_ {false}
, degrade_ {0.} // initialize to zero each run, not , degrade_ {0.} // initialize to zero each run, not
// saved in settings // saved in settings
, default_audio_input_device_selected_ {false} , default_audio_input_device_selected_ {false}
@ -1018,18 +1006,15 @@ Configuration::impl::impl (Configuration * self, QSettings * settings, QWidget *
enumerate_rigs (); enumerate_rigs ();
initialize_models (); initialize_models ();
transceiver_thread_.start (); transceiver_thread_ = new QThread {this};
transceiver_thread_->start ();
} }
Configuration::impl::~impl () Configuration::impl::~impl ()
{ {
transceiver_thread_->quit ();
transceiver_thread_->wait ();
write_settings (); write_settings ();
close_rig ();
transceiver_thread_.quit ();
transceiver_thread_.wait ();
temp_dir_.removeRecursively (); // clean up temp files 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_->rig_combo_box->setCurrentText (rig_params_.rig_name);
ui_->TX_mode_button_group->button (data_mode_)->setChecked (true); ui_->TX_mode_button_group->button (data_mode_)->setChecked (true);
ui_->split_mode_button_group->button (rig_params_.split_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_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_data_bits_button_group->button (rig_params_.data_bits)->setChecked (true);
ui_->CAT_stop_bits_button_group->button (rig_params_.stop_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 (); EMEonly_ = settings_->value("EMEonly",false).toBool ();
offsetRxFreq_ = settings_->value("OffsetRx",false).toBool(); offsetRxFreq_ = settings_->value("OffsetRx",false).toBool();
rig_params_.poll_interval = settings_->value ("Polling", 0).toInt (); 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<TransceiverFactory::SplitMode> (); rig_params_.split_mode = settings_->value ("SplitMode", QVariant::fromValue (TransceiverFactory::split_mode_none)).value<TransceiverFactory::SplitMode> ();
reset_split_ = settings_->value ("ResetSplitOnExit", true).toBool ();
udp_server_name_ = settings_->value ("UDPServer", "127.0.0.1").toString (); udp_server_name_ = settings_->value ("UDPServer", "127.0.0.1").toString ();
udp_server_port_ = settings_->value ("UDPServerPort", 2237).toUInt (); udp_server_port_ = settings_->value ("UDPServerPort", 2237).toUInt ();
accept_udp_requests_ = settings_->value ("AcceptUDPRequests", false).toBool (); 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 ("TXAudioSource", QVariant::fromValue (rig_params_.audio_source));
settings_->setValue ("Polling", rig_params_.poll_interval); settings_->setValue ("Polling", rig_params_.poll_interval);
settings_->setValue ("SplitMode", QVariant::fromValue (rig_params_.split_mode)); settings_->setValue ("SplitMode", QVariant::fromValue (rig_params_.split_mode));
settings_->setValue ("ResetSplitOnExit", reset_split_);
settings_->setValue ("VHFUHF", enable_VHF_features_); settings_->setValue ("VHFUHF", enable_VHF_features_);
settings_->setValue ("Decode52", decode_at_52s_); settings_->setValue ("Decode52", decode_at_52s_);
settings_->setValue ("SingleDecode", single_decode_); settings_->setValue ("SingleDecode", single_decode_);
@ -1644,6 +1627,7 @@ TransceiverFactory::ParameterPack Configuration::impl::gather_rig_data ()
result.ptt_type = static_cast<TransceiverFactory::PTTMethod> (ui_->PTT_method_button_group->checkedId ()); result.ptt_type = static_cast<TransceiverFactory::PTTMethod> (ui_->PTT_method_button_group->checkedId ());
result.ptt_port = ui_->PTT_port_combo_box->currentText (); result.ptt_port = ui_->PTT_port_combo_box->currentText ();
result.audio_source = static_cast<TransceiverFactory::TXAudioSource> (ui_->TX_audio_source_button_group->checkedId ()); result.audio_source = static_cast<TransceiverFactory::TXAudioSource> (ui_->TX_audio_source_button_group->checkedId ());
result.set_rig_mode = static_cast<DataMode> (ui_->TX_mode_button_group->checkedId ()) != data_mode_none;
result.split_mode = static_cast<TransceiverFactory::SplitMode> (ui_->split_mode_button_group->checkedId ()); result.split_mode = static_cast<TransceiverFactory::SplitMode> (ui_->split_mode_button_group->checkedId ());
return result; return result;
} }
@ -1815,7 +1799,6 @@ void Configuration::impl::accept ()
NDxG_ = ui_->cbNDxG->isChecked (); NDxG_ = ui_->cbNDxG->isChecked ();
NN_ = ui_->cbNN->isChecked (); NN_ = ui_->cbNN->isChecked ();
EMEonly_ = ui_->cbEMEonly->isChecked (); EMEonly_ = ui_->cbEMEonly->isChecked ();
reset_split_ = ui_->reset_split_check_box->isChecked ();
offsetRxFreq_ = ui_->offset_Rx_freq_check_box->isChecked(); offsetRxFreq_ = ui_->offset_Rx_freq_check_box->isChecked();
frequency_calibration_intercept_ = ui_->calibration_intercept_spin_box->value (); 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 */) void Configuration::impl::on_split_mode_button_group_buttonClicked (int /* id */)
{ {
setup_split_ = true;
required_tx_frequency_ = 0;
set_rig_invariants (); set_rig_invariants ();
} }
@ -2016,9 +1997,9 @@ void Configuration::impl::on_test_CAT_push_button_clicked ()
} }
ui_->test_CAT_push_button->setStyleSheet ({}); ui_->test_CAT_push_button->setStyleSheet ({});
if (open_rig ()) if (open_rig (true))
{ {
Q_EMIT sync (true); //Q_EMIT sync (true);
} }
set_rig_invariants (); set_rig_invariants ();
@ -2218,52 +2199,58 @@ bool Configuration::impl::have_rig (bool open_if_closed)
return rig_active_; return rig_active_;
} }
bool Configuration::impl::open_rig () bool Configuration::impl::open_rig (bool force)
{ {
auto result = false; auto result = false;
auto const rig_data = gather_rig_data (); 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 try
{ {
close_rig (); close_rig ();
// create a new Transceiver object // 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 // hook up Configuration transceiver control signals to Transceiver slots
// //
// these connections cross the thread boundary // these connections cross the thread boundary
rig_connections_ << connect (this, &Configuration::impl::frequency, rig.get (), &Transceiver::frequency); rig_connections_ << connect (this, &Configuration::impl::set_transceiver,
rig_connections_ << connect (this, &Configuration::impl::tx_frequency, rig.get (), &Transceiver::tx_frequency); rig.get (), &Transceiver::set);
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);
// hook up Transceiver signals to Configuration signals // hook up Transceiver signals to Configuration signals
// //
// these connections cross the thread boundary // these connections cross the thread boundary
connect (rig.get (), &Transceiver::update, this, &Configuration::impl::handle_transceiver_update); rig_connections_ << connect (rig.get (), &Transceiver::resolution, this, [=] (int resolution) {
connect (rig.get (), &Transceiver::failure, this, &Configuration::impl::handle_transceiver_failure); 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 // setup thread safe startup and close down semantics
rig_connections_ << connect (this, &Configuration::impl::start_transceiver, rig.get (), &Transceiver::start); 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 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 // must be queued connection to avoid premature
// since finished signal is going to be emitted from the object // self-immolation since finished signal is going to be
// that will get destroyed in its own stop slot i.e. a same // emitted from the object that will get destroyed in its
// thread signal to slot connection which by default will be // own stop slot i.e. a same thread signal to slot
// reduced to a method function call. // connection which by default will be reduced to a method
// function call.
connect (p, &Transceiver::finished, p, &Transceiver::deleteLater, Qt::QueuedConnection); connect (p, &Transceiver::finished, p, &Transceiver::deleteLater, Qt::QueuedConnection);
ui_->test_CAT_push_button->setStyleSheet ({}); ui_->test_CAT_push_button->setStyleSheet ({});
rig_active_ = true; 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; result = true;
} }
catch (std::exception const& e) catch (std::exception const& e)
@ -2284,98 +2271,85 @@ bool Configuration::impl::open_rig ()
void Configuration::impl::transceiver_frequency (Frequency f) void Configuration::impl::transceiver_frequency (Frequency f)
{ {
Transceiver::MODE mode {Transceiver::UNK}; Transceiver::MODE mode {Transceiver::UNK};
if (ui_->mode_group_box->isEnabled ()) switch (data_mode_)
{ {
switch (static_cast<DataMode> (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_USB: mode = Transceiver::USB; break; case data_mode_none: break;
case data_mode_data: mode = Transceiver::DIG_U; break;
case data_mode_none: break;
}
} }
if (cached_rig_state_.frequency () != f cached_rig_state_.online (true); // we want the rig online
|| (mode != Transceiver::UNK && mode != cached_rig_state_.mode ())) cached_rig_state_.mode (mode);
{
cached_rig_state_.frequency (f);
cached_rig_state_.mode (mode);
// apply any offset & calibration // apply any offset & calibration
// we store the offset here for use in feedback from the rig, we // we store the offset here for use in feedback from the rig, we
// cannot absolutely determine if the offset should apply but by // cannot absolutely determine if the offset should apply but by
// simply picking an offset when the Rx frequency is set and // simply picking an offset when the Rx frequency is set and
// sticking to it we get sane behaviour // sticking to it we get sane behaviour
current_offset_ = stations_.offset (f); current_offset_ = stations_.offset (f);
Q_EMIT frequency (apply_calibration (f + current_offset_), mode); 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) 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); // apply and offset and calibration
cached_rig_state_.tx_frequency (f); // we store the offset here for use in feedback from the
// rig, we cannot absolutely determine if the offset should
// lookup offset and apply calibration if we are in split mode // apply but by simply picking an offset when the Rx
if (cached_rig_state_.split ()) // frequency is set and sticking to it we get sane behaviour
{ current_tx_offset_ = stations_.offset (f);
// apply and offset and calibration cached_rig_state_.tx_frequency (apply_calibration (f + current_tx_offset_));
// 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_);
} }
Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_);
} }
void Configuration::impl::transceiver_mode (MODE m) void Configuration::impl::transceiver_mode (MODE m)
{ {
if (cached_rig_state_.mode () != m) cached_rig_state_.online (true); // we want the rig online
{ cached_rig_state_.mode (m);
cached_rig_state_.mode (m); Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_);
// 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_);
}
} }
void Configuration::impl::transceiver_ptt (bool on) void Configuration::impl::transceiver_ptt (bool on)
{ {
cached_rig_state_.online (true); // we want the rig online
cached_rig_state_.ptt (on); cached_rig_state_.ptt (on);
Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_);
// pass this on regardless of cache
Q_EMIT ptt (on);
} }
void Configuration::impl::sync_transceiver (bool force_signal) void Configuration::impl::sync_transceiver (bool /*force_signal*/)
{ {
// pass this on as cache must be ignored // 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 #if WSJT_TRACE_CAT
qDebug () << "Configuration::handle_transceiver_update: Transceiver State:" << state; qDebug () << "Configuration::handle_transceiver_update: Transceiver State #:" << sequence_number << state;
#endif #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 ()) if (state.online ())
{ {
ui_->test_PTT_push_button->setChecked (state.ptt ()); ui_->test_PTT_push_button->setChecked (state.ptt ());
TransceiverFactory::SplitMode split_mode_selected;
if (isVisible ()) if (isVisible ())
{ {
ui_->test_CAT_push_button->setStyleSheet ("QPushButton {background-color: green;}"); 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) ui_->test_PTT_push_button->setEnabled ((TransceiverFactory::PTT_method_CAT == ptt_method && CAT_PTT_enabled)
|| TransceiverFactory::PTT_method_DTR == ptt_method || TransceiverFactory::PTT_method_DTR == ptt_method
|| TransceiverFactory::PTT_method_RTS == ptt_method); || TransceiverFactory::PTT_method_RTS == ptt_method);
// Follow the setup choice.
split_mode_selected = static_cast<TransceiverFactory::SplitMode> (ui_->split_mode_button_group->checkedId ());
} }
else
{
// Follow the rig unless configuration has been changed.
split_mode_selected = static_cast<TransceiverFactory::SplitMode> (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 else
{ {
close_rig (); close_rig ();
} }
// take off calibration & offset // pass on to clients if current command is processed
state.frequency (remove_calibration (state.frequency ()) - current_offset_); if (sequence_number == transceiver_command_number_)
if (state.tx_frequency ())
{ {
TransceiverState reported_state {state};
// take off calibration & offset // 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 #if WSJT_TRACE_CAT
qDebug () << "Configuration::handle_transceiver_failure: reason:" << reason; qDebug () << "Configuration::handle_transceiver_failure: reason:" << reason;
@ -2487,7 +2418,7 @@ void Configuration::impl::close_rig ()
if (rig_active_) if (rig_active_)
{ {
ui_->test_CAT_push_button->setStyleSheet ("QPushButton {background-color: red;}"); 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_) Q_FOREACH (auto const& connection, rig_connections_)
{ {
disconnect (connection); disconnect (connection);

View File

@ -72,6 +72,7 @@ public:
~Configuration (); ~Configuration ();
int exec (); int exec ();
bool is_active () const;
QDir temp_dir () const; QDir temp_dir () const;
QDir doc_dir () const; QDir doc_dir () const;
@ -155,6 +156,15 @@ public:
// open_if_closed parameter is passed as true. // open_if_closed parameter is passed as true.
bool transceiver_online (bool open_if_closed = false); 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. // Close down connection to rig.
void transceiver_offline (); void transceiver_offline ();
@ -206,7 +216,7 @@ public:
// //
// signals a change in one of the TransceiverState members // 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. // Signals a failure of a control rig CAT or PTT connection.
// //
@ -214,7 +224,7 @@ public:
// connections are closed automatically. The connections can be // connections are closed automatically. The connections can be
// re-established with a call to transceiver_online(true) assuming // re-established with a call to transceiver_online(true) assuming
// the fault condition has been rectified or is transient. // 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: private:
class impl; class impl;

View File

@ -1045,6 +1045,9 @@ this setting allows you to select which audio input will be used
</item> </item>
<item> <item>
<widget class="QSpinBox" name="CAT_poll_interval_spin_box"> <widget class="QSpinBox" name="CAT_poll_interval_spin_box">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;p&gt;If you set this to zero then spots may be uploaded with an incorrect frequency.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="suffix"> <property name="suffix">
<string> s</string> <string> s</string>
</property> </property>
@ -1127,13 +1130,10 @@ radio interface behave as expected.</string>
<string>Split Operation</string> <string>Split Operation</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout_12"> <layout class="QGridLayout" name="gridLayout_12">
<item row="0" column="0"> <item row="0" column="2">
<widget class="QRadioButton" name="split_none_radio_button"> <widget class="QRadioButton" name="split_emulate_radio_button">
<property name="text"> <property name="text">
<string>None</string> <string>Fake It</string>
</property>
<property name="checked">
<bool>true</bool>
</property> </property>
<attribute name="buttonGroup"> <attribute name="buttonGroup">
<string notr="true">split_mode_button_group</string> <string notr="true">split_mode_button_group</string>
@ -1150,27 +1150,17 @@ radio interface behave as expected.</string>
</attribute> </attribute>
</widget> </widget>
</item> </item>
<item row="0" column="2"> <item row="0" column="0">
<widget class="QRadioButton" name="split_emulate_radio_button"> <widget class="QRadioButton" name="split_none_radio_button">
<property name="text"> <property name="text">
<string>Fake It</string> <string>None</string>
</property>
<attribute name="buttonGroup">
<string notr="true">split_mode_button_group</string>
</attribute>
</widget>
</item>
<item row="1" column="0" colspan="3">
<widget class="QCheckBox" name="reset_split_check_box">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Reset split on program exit&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Reset split on exit</string>
</property> </property>
<property name="checked"> <property name="checked">
<bool>true</bool> <bool>true</bool>
</property> </property>
<attribute name="buttonGroup">
<string notr="true">split_mode_button_group</string>
</attribute>
</widget> </widget>
</item> </item>
</layout> </layout>
@ -1388,12 +1378,6 @@ both here.</string>
</item> </item>
<item row="3" column="0"> <item row="3" column="0">
<widget class="QGroupBox" name="azel_path_group_box"> <widget class="QGroupBox" name="azel_path_group_box">
<property name="minimumSize">
<size>
<width>0</width>
<height>56</height>
</size>
</property>
<property name="title"> <property name="title">
<string>AzEl Directory</string> <string>AzEl Directory</string>
</property> </property>
@ -1403,6 +1387,9 @@ both here.</string>
<property name="text"> <property name="text">
<string>Location:</string> <string>Location:</string>
</property> </property>
<property name="buddy">
<cstring>azel_path_select_push_button</cstring>
</property>
</widget> </widget>
</item> </item>
<item> <item>
@ -2476,8 +2463,8 @@ soundcard changes</string>
<slot>accept()</slot> <slot>accept()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>254</x> <x>272</x>
<y>554</y> <y>606</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>157</x> <x>157</x>
@ -2492,8 +2479,8 @@ soundcard changes</string>
<slot>reject()</slot> <slot>reject()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>322</x> <x>340</x>
<y>554</y> <y>606</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>286</x> <x>286</x>
@ -2508,7 +2495,7 @@ soundcard changes</string>
<slot>setFocus()</slot> <slot>setFocus()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>566</x> <x>404</x>
<y>62</y> <y>62</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">

View File

@ -39,8 +39,10 @@ void DXLabSuiteCommanderTransceiver::register_transceivers (TransceiverFactory::
(*registry)[commander_transceiver_name] = TransceiverFactory::Capabilities {id, TransceiverFactory::Capabilities::network, true}; (*registry)[commander_transceiver_name] = TransceiverFactory::Capabilities {id, TransceiverFactory::Capabilities::network, true};
} }
DXLabSuiteCommanderTransceiver::DXLabSuiteCommanderTransceiver (std::unique_ptr<TransceiverBase> wrapped, QString const& address, bool use_for_ptt, int poll_interval) DXLabSuiteCommanderTransceiver::DXLabSuiteCommanderTransceiver (std::unique_ptr<TransceiverBase> wrapped,
: PollingTransceiver {poll_interval} QString const& address, bool use_for_ptt,
int poll_interval, QObject * parent)
: PollingTransceiver {poll_interval, parent}
, wrapped_ {std::move (wrapped)} , wrapped_ {std::move (wrapped)}
, use_for_ptt_ {use_for_ptt} , use_for_ptt_ {use_for_ptt}
, server_ {address} , server_ {address}
@ -48,14 +50,10 @@ DXLabSuiteCommanderTransceiver::DXLabSuiteCommanderTransceiver (std::unique_ptr<
{ {
} }
DXLabSuiteCommanderTransceiver::~DXLabSuiteCommanderTransceiver () int DXLabSuiteCommanderTransceiver::do_start ()
{ {
} TRACE_CAT ("DXLabSuiteCommanderTransceiver", "starting");
if (wrapped_) wrapped_->start (0);
void DXLabSuiteCommanderTransceiver::do_start ()
{
TRACE_CAT ("starting");
wrapped_->start ();
auto server_details = network_server_lookup (server_, 52002u, QHostAddress::LocalHost, QAbstractSocket::IPv4Protocol); 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)); commander_->connectToHost (std::get<0> (server_details), std::get<1> (server_details));
if (!commander_->waitForConnected ()) 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 ()}; throw error {tr ("Failed to connect to DX Lab Suite Commander\n") + commander_->errorString ()};
} }
int resolution {0};
auto reply = command_with_reply ("<command:10>CmdGetFreq<parameters:0>");
if (0 == reply.indexOf ("<CmdFreq:"))
{
auto f = string_to_frequency (reply.mid (reply.indexOf ('>') + 1));
if (f && !(f % 10))
{
auto test_frequency = f - f % 100 + 55;
auto f_string = frequency_to_string (test_frequency);
auto params = ("<xcvrfreq:%1>" + f_string).arg (f_string.size ());
simple_command (("<command:10>CmdSetFreq<parameters:%1>" + params).arg (params.size ()));
reply = command_with_reply ("<command:10>CmdGetFreq<parameters:0>");
if (0 == reply.indexOf ("<CmdFreq:"))
{
auto new_frequency = string_to_frequency (reply.mid (reply.indexOf ('>') + 1));
switch (static_cast<Radio::FrequencyDelta> (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 = ("<xcvrfreq:%1>" + f_string).arg (f_string.size ());
simple_command (("<command:10>CmdSetFreq<parameters:%1>" + 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 (); poll ();
return resolution;
} }
void DXLabSuiteCommanderTransceiver::do_stop () void DXLabSuiteCommanderTransceiver::do_stop ()
@ -82,27 +115,30 @@ void DXLabSuiteCommanderTransceiver::do_stop ()
delete commander_, commander_ = nullptr; delete commander_, commander_ = nullptr;
} }
wrapped_->stop (); if (wrapped_) wrapped_->stop ();
TRACE_CAT ("stopped"); TRACE_CAT ("DXLabSuiteCommanderTransceiver", "stopped");
} }
void DXLabSuiteCommanderTransceiver::do_ptt (bool on) void DXLabSuiteCommanderTransceiver::do_ptt (bool on)
{ {
TRACE_CAT (on << state ()); TRACE_CAT ("DXLabSuiteCommanderTransceiver", on << state ());
if (use_for_ptt_) if (use_for_ptt_)
{ {
simple_command (on ? "<command:5>CmdTX<parameters:0>" : "<command:5>CmdRX<parameters:0>"); simple_command (on ? "<command:5>CmdTX<parameters:0>" : "<command:5>CmdRX<parameters:0>");
} }
else else
{ {
wrapped_->ptt (on); Q_ASSERT (wrapped_);
TransceiverState new_state {wrapped_->state ()};
new_state.ptt (on);
wrapped_->set (new_state, 0);
} }
update_PTT (on); 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); auto f_string = frequency_to_string (f);
if (UNK != m) if (UNK != m)
{ {
@ -119,9 +155,9 @@ void DXLabSuiteCommanderTransceiver::do_frequency (Frequency f, MODE m)
update_rx_frequency (f); 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) if (tx)
{ {
auto f_string = frequency_to_string (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); 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 m_string = map_mode (m);
auto params = ("<1:%1>" + m_string).arg (m_string.size ()); auto params = ("<1:%1>" + m_string).arg (m_string.size ());
simple_command (("<command:10>CmdSetMode<parameters:%1>" + params).arg (params.size ())); simple_command (("<command:10>CmdSetMode<parameters:%1>" + params).arg (params.size ()));
@ -168,7 +204,7 @@ void DXLabSuiteCommanderTransceiver::poll ()
} }
else 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")}; throw error {tr ("DX Lab Suite Commander didn't respond correctly polling frequency")};
} }
@ -189,7 +225,7 @@ void DXLabSuiteCommanderTransceiver::poll ()
} }
else 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")}; throw error {tr ("DX Lab Suite Commander didn't respond correctly polling TX frequency")};
} }
} }
@ -208,13 +244,13 @@ void DXLabSuiteCommanderTransceiver::poll ()
} }
else 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}; throw error {tr ("DX Lab Suite Commander sent an unrecognised split state: ") + split};
} }
} }
else 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")}; throw error {tr ("DX Lab Suite Commander didn't respond correctly polling split status")};
} }
@ -265,14 +301,14 @@ void DXLabSuiteCommanderTransceiver::poll ()
} }
else 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 + '"'}; throw error {tr ("DX Lab Suite Commander sent an unrecognised mode: \"") + mode + '"'};
} }
update_mode (m); update_mode (m);
} }
else 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")}; 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) if (!no_debug)
{ {
TRACE_CAT (cmd); TRACE_CAT ("DXLabSuiteCommanderTransceiver", cmd);
} }
if (!write_to_port (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 ()}; 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)) if (!write_to_port (cmd))
{ {
TRACE_CAT ("failed to send command:" << commander_->errorString ()); TRACE_CAT ("DXLabSuiteCommanderTransceiver", "failed to send command:" << commander_->errorString ());
throw error { throw error {
tr ("DX Lab Suite Commander failed to send command \"%1\": %2\n") tr ("DX Lab Suite Commander failed to send command \"%1\": %2\n")
.arg (cmd) .arg (cmd)
@ -316,7 +352,7 @@ QString DXLabSuiteCommanderTransceiver::command_with_reply (QString const& cmd,
replied = commander_->waitForReadyRead (); replied = commander_->waitForReadyRead ();
if (!replied && commander_->error () != commander_->SocketTimeoutError) 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 { throw error {
tr ("DX Lab Suite Commander send command \"%1\" read reply failed: %2\n") tr ("DX Lab Suite Commander send command \"%1\" read reply failed: %2\n")
.arg (cmd) .arg (cmd)
@ -327,7 +363,7 @@ QString DXLabSuiteCommanderTransceiver::command_with_reply (QString const& cmd,
if (!replied) if (!replied)
{ {
TRACE_CAT (cmd << "retries exhausted"); TRACE_CAT ("DXLabSuiteCommanderTransceiver", cmd << "retries exhausted");
throw error { throw error {
tr ("DX Lab Suite Commander retries exhausted sending command \"%1\"") tr ("DX Lab Suite Commander retries exhausted sending command \"%1\"")
.arg (cmd) .arg (cmd)
@ -343,7 +379,7 @@ QString DXLabSuiteCommanderTransceiver::command_with_reply (QString const& cmd,
if (!no_debug) if (!no_debug)
{ {
TRACE_CAT (cmd << "->" << result); TRACE_CAT ("DXLabSuiteCommanderTransceiver", cmd << "->" << result);
} }
return result; // converting raw UTF-8 bytes to QString return result; // converting raw UTF-8 bytes to QString

View File

@ -27,15 +27,16 @@ public:
static void register_transceivers (TransceiverFactory::Transceivers *, int id); static void register_transceivers (TransceiverFactory::Transceivers *, int id);
// takes ownership of wrapped Transceiver // takes ownership of wrapped Transceiver
explicit DXLabSuiteCommanderTransceiver (std::unique_ptr<TransceiverBase> wrapped, QString const& address, bool use_for_ptt, int poll_interval); explicit DXLabSuiteCommanderTransceiver (std::unique_ptr<TransceiverBase> wrapped,
~DXLabSuiteCommanderTransceiver (); QString const& address, bool use_for_ptt,
int poll_interval, QObject * parent = nullptr);
protected: protected:
void do_start () override; int do_start () override;
void do_stop () override; void do_stop () override;
void do_frequency (Frequency, MODE) override; void do_frequency (Frequency, MODE, bool no_ignore) override;
void do_tx_frequency (Frequency, bool rationalise_mode) override; void do_tx_frequency (Frequency, bool no_ignore) override;
void do_mode (MODE, bool rationalise) override; void do_mode (MODE) override;
void do_ptt (bool on) override; void do_ptt (bool on) override;
void poll () override; void poll () override;
@ -47,7 +48,7 @@ private:
QString frequency_to_string (Frequency) const; QString frequency_to_string (Frequency) const;
Frequency string_to_frequency (QString) const; Frequency string_to_frequency (QString) const;
std::unique_ptr<TransceiverBase> wrapped_; std::unique_ptr<TransceiverBase> wrapped_; // may be null
bool use_for_ptt_; bool use_for_ptt_;
QString server_; QString server_;
QTcpSocket * commander_; QTcpSocket * commander_;

View File

@ -1,116 +1,65 @@
#include "EmulateSplitTransceiver.hpp" #include "EmulateSplitTransceiver.hpp"
EmulateSplitTransceiver::EmulateSplitTransceiver (std::unique_ptr<Transceiver> wrapped) EmulateSplitTransceiver::EmulateSplitTransceiver (std::unique_ptr<Transceiver> wrapped, QObject * parent)
: wrapped_ {std::move (wrapped)} : Transceiver {parent}
, frequency_ {0, 0} , wrapped_ {std::move (wrapped)}
, pre_tx_frequency_ {0} , rx_frequency_ {0}
, tx_ {false} , tx_frequency_ {0}
, split_ {false}
{ {
// Connect update signal of wrapped Transceiver object instance to ours. // Connect update signal of wrapped Transceiver object instance to ours.
connect (wrapped_.get (), &Transceiver::update, this, &EmulateSplitTransceiver::handle_update); connect (wrapped_.get (), &Transceiver::update, this, &EmulateSplitTransceiver::handle_update);
// Connect failure signal of wrapped Transceiver object to our // Connect other signals of wrapped Transceiver object to our
// parent failure signal. // 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); connect (wrapped_.get (), &Transceiver::failure, this, &Transceiver::failure);
} }
void EmulateSplitTransceiver::start () noexcept void EmulateSplitTransceiver::set (TransceiverState const& s, unsigned sequence_number) noexcept
{
wrapped_->start ();
wrapped_->tx_frequency (0, false);
}
void EmulateSplitTransceiver::frequency (Frequency rx, MODE m) noexcept
{ {
#if WSJT_TRACE_CAT #if WSJT_TRACE_CAT
qDebug () << "EmulateSplitTransceiver::frequency:" << rx << "mode:" << m; qDebug () << "EmulateSplitTransceiver::set: state:" << s << "#:" << sequence_number;
#endif #endif
// save for use in updates
rx_frequency_ = s.frequency ();
tx_frequency_ = s.tx_frequency ();
split_ = s.split ();
// Save frequency parameters. TransceiverState emulated_state {s};
frequency_[0] = rx; if (s.ptt () && split_) emulated_state.frequency (s.tx_frequency ());
emulated_state.split (false);
// Set active frequency. emulated_state.tx_frequency (0);
wrapped_->frequency (rx, m); wrapped_->set (emulated_state, sequence_number);
} }
void EmulateSplitTransceiver::tx_frequency (Frequency tx, bool /* rationalise_mode */) noexcept void EmulateSplitTransceiver::handle_update (TransceiverState const& state,
{ unsigned sequence_number)
#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)
{ {
#if WSJT_TRACE_CAT #if WSJT_TRACE_CAT
qDebug () << "EmulateSplitTransceiver::handle_update: from wrapped:" << state; qDebug () << "EmulateSplitTransceiver::handle_update: from wrapped:" << state;
#endif #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 ()) if (state.split ())
{ {
Q_EMIT failure (tr ("Emulated split mode requires rig to be in simplex mode")); Q_EMIT failure (tr ("Emulated split mode requires rig to be in simplex mode"));
} }
else 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 #if WSJT_TRACE_CAT
qDebug () << "EmulateSplitTransceiver::handle_update: signalling:" << state; qDebug () << "EmulateSplitTransceiver::handle_update: signalling:" << state;
#endif #endif
// signal emulated state // signal emulated state
Q_EMIT update (state); Q_EMIT update (new_state, sequence_number);
} }
} }

View File

@ -29,25 +29,23 @@ class EmulateSplitTransceiver final
{ {
public: public:
// takes ownership of wrapped Transceiver // takes ownership of wrapped Transceiver
explicit EmulateSplitTransceiver (std::unique_ptr<Transceiver> wrapped); explicit EmulateSplitTransceiver (std::unique_ptr<Transceiver> wrapped,
QObject * parent = nullptr);
void start () noexcept override; void set (TransceiverState const&,
void frequency (Frequency, MODE) noexcept override; unsigned sequence_number) noexcept override;
void tx_frequency (Frequency, bool rationalise_mode) noexcept override;
void ptt (bool on) noexcept override;
// forward everything else to wrapped Transceiver // forward everything else to wrapped Transceiver
void stop (bool /* reset_split */) noexcept override {wrapped_->stop (false); Q_EMIT finished ();} void start (unsigned sequence_number) noexcept override {wrapped_->start (sequence_number);}
void mode (MODE m, bool /* rationalise */) noexcept override {wrapped_->mode (m, false);} void stop () noexcept override {wrapped_->stop ();}
void sync (bool force_signal) noexcept override {wrapped_->sync (force_signal);}
private: private:
void handle_update (TransceiverState); void handle_update (TransceiverState const&, unsigned seqeunce_number);
std::unique_ptr<Transceiver> wrapped_; std::unique_ptr<Transceiver> wrapped_;
Frequency frequency_[2]; // [0] <- RX, [1] <- other Frequency rx_frequency_; // requested Rx frequency
Frequency pre_tx_frequency_; // return to this on switching to Rx Frequency tx_frequency_; // requested Tx frequency
bool tx_; bool split_; // requested split state
}; };
#endif #endif

View File

@ -72,10 +72,13 @@ qint32 const HRDMessage::magic_2_value_ (0xABCD1234);
HRDTransceiver::HRDTransceiver (std::unique_ptr<TransceiverBase> wrapped HRDTransceiver::HRDTransceiver (std::unique_ptr<TransceiverBase> wrapped
, QString const& server , QString const& server
, bool use_for_ptt , bool use_for_ptt
, int poll_interval) , int poll_interval
: PollingTransceiver {poll_interval} , bool set_rig_mode
, QObject * parent)
: PollingTransceiver {poll_interval, parent}
, wrapped_ {std::move (wrapped)} , wrapped_ {std::move (wrapped)}
, use_for_ptt_ {use_for_ptt} , use_for_ptt_ {use_for_ptt}
, set_rig_mode_ {set_rig_mode}
, server_ {server} , server_ {server}
, hrd_ {0} , hrd_ {0}
, protocol_ {none} , protocol_ {none}
@ -102,14 +105,10 @@ HRDTransceiver::HRDTransceiver (std::unique_ptr<TransceiverBase> wrapped
{ {
} }
HRDTransceiver::~HRDTransceiver () int HRDTransceiver::do_start ()
{ {
} TRACE_CAT ("HRDTransceiver", "starting");
if (wrapped_) wrapped_->start (0);
void HRDTransceiver::do_start ()
{
TRACE_CAT ("starting");
wrapped_->start ();
auto server_details = network_server_lookup (server_, 7809u); auto server_details = network_server_lookup (server_, 7809u);
if (!hrd_) if (!hrd_)
@ -119,7 +118,7 @@ void HRDTransceiver::do_start ()
hrd_->connectToHost (std::get<0> (server_details), std::get<1> (server_details)); hrd_->connectToHost (std::get<0> (server_details), std::get<1> (server_details));
if (!hrd_->waitForConnected ()) 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 ()}; 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)); hrd_->connectToHost (std::get<0> (server_details), std::get<1> (server_details));
if (!hrd_->waitForConnected ()) 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 ()}; 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 id = send_command ("get id", false, false);
auto version = send_command ("get version", 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 << "Id: " << id << "\n";
HRD_info << "Version: " << version << "\n"; HRD_info << "Version: " << version << "\n";
auto radios = send_command ("get radios", false, false).trimmed ().split (',', QString::SkipEmptyParts); auto radios = send_command ("get radios", false, false).trimmed ().split (',', QString::SkipEmptyParts);
if (radios.isEmpty ()) if (radios.isEmpty ())
{ {
TRACE_CAT ("no rig found"); TRACE_CAT ("HRDTransceiver", "no rig found");
throw error {tr ("Ham Radio Deluxe: no rig found")}; throw error {tr ("Ham Radio Deluxe: no rig found")};
} }
@ -181,10 +180,10 @@ void HRDTransceiver::do_start ()
} }
#if WSJT_TRACE_CAT #if WSJT_TRACE_CAT
TRACE_CAT ("radios:-"); TRACE_CAT ("HRDTransceiver", "radios:-");
Q_FOREACH (auto const& radio, 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 #endif
@ -192,36 +191,36 @@ void HRDTransceiver::do_start ()
HRD_info << "Current radio: " << current_radio_name << "\n"; HRD_info << "Current radio: " << current_radio_name << "\n";
if (current_radio_name.isEmpty ()) 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")}; throw error {tr ("Ham Radio Deluxe: no rig found")};
} }
vfo_count_ = send_command ("get vfo-count").toUInt (); vfo_count_ = send_command ("get vfo-count").toUInt ();
HRD_info << "VFO count: " << vfo_count_ << "\n"; 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 (" ", "~"); 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"; HRD_info << "Buttons: {" << buttons_.join (", ") << "}\n";
dropdown_names_ = send_command ("get dropdowns").trimmed ().split (',', QString::SkipEmptyParts); dropdown_names_ = send_command ("get dropdowns").trimmed ().split (',', QString::SkipEmptyParts);
TRACE_CAT ("Dropdowns:"); TRACE_CAT ("HRDTransceiver", "Dropdowns:");
HRD_info << "Dropdowns:\n"; HRD_info << "Dropdowns:\n";
Q_FOREACH (auto const& dd, dropdown_names_) Q_FOREACH (auto const& dd, dropdown_names_)
{ {
auto selections = send_command ("get dropdown-list {" + dd + "}").trimmed ().split (',', QString::SkipEmptyParts); 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"; HRD_info << "\t" << dd << ": {" << selections.join (", ") << "}\n";
dropdowns_[dd] = selections; dropdowns_[dd] = selections;
} }
slider_names_ = send_command ("get sliders").trimmed ().split (',', QString::SkipEmptyParts).replaceInStrings (" ", "~"); slider_names_ = send_command ("get sliders").trimmed ().split (',', QString::SkipEmptyParts).replaceInStrings (" ", "~");
TRACE_CAT ("Sliders:-"); TRACE_CAT ("HRDTransceiver", "Sliders:-");
HRD_info << "Sliders:\n"; HRD_info << "Sliders:\n";
Q_FOREACH (auto const& s, slider_names_) Q_FOREACH (auto const& s, slider_names_)
{ {
auto range = send_command ("get slider-range " + current_radio_name + " " + s).trimmed ().split (',', QString::SkipEmptyParts); 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"; HRD_info << "\t" << s << ": {" << range.join (", ") << "}\n";
sliders_[s] = range; sliders_[s] = range;
} }
@ -292,6 +291,24 @@ void HRDTransceiver::do_start ()
update_mode (lookup_mode (get_dropdown (mode_A_dropdown_), mode_A_map_)); 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<Radio::FrequencyDelta> (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 () void HRDTransceiver::do_stop ()
@ -301,11 +318,8 @@ void HRDTransceiver::do_stop ()
hrd_->close (); hrd_->close ();
} }
if (wrapped_) if (wrapped_) wrapped_->stop ();
{ TRACE_CAT ("HRDTransceiver", "stopped" << state () << "reversed" << reversed_);
wrapped_->stop ();
}
TRACE_CAT ("stopped" << state () << "reversed" << reversed_);
} }
int HRDTransceiver::find_button (QRegExp const& re) const 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)$")))); map->push_back (std::forward_as_tuple (DIG_FM, find_dropdown_selection (dropdown, QRegExp ("^(PKT-FM|PKT|FM)$"))));
#if WSJT_TRACE_CAT #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) std::for_each (map->begin (), map->end (), [this, dropdown] (ModeMap::value_type const& item)
{ {
auto const& rhs = std::get<1> (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 #endif
} }
@ -419,14 +433,14 @@ void HRDTransceiver::set_dropdown (int dd, int value)
} }
else 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)}; throw error {tr ("Ham Radio Deluxe: item not found in %1 dropdown list").arg (dd_name)};
} }
} }
void HRDTransceiver::do_ptt (bool on) void HRDTransceiver::do_ptt (bool on)
{ {
TRACE_CAT (on); TRACE_CAT ("HRDTransceiver", on);
if (use_for_ptt_) if (use_for_ptt_)
{ {
if (ptt_button_ >= 0) if (ptt_button_ >= 0)
@ -439,7 +453,10 @@ void HRDTransceiver::do_ptt (bool on)
} }
else else
{ {
wrapped_->ptt (on); Q_ASSERT (wrapped_);
TransceiverState new_state {wrapped_->state ()};
new_state.ptt (on);
wrapped_->set (new_state, 0);
} }
update_PTT (on); update_PTT (on);
} }
@ -456,17 +473,17 @@ void HRDTransceiver::set_button (int button_index, bool checked)
} }
else else
{ {
TRACE_CAT ("invalid button"); TRACE_CAT ("HRDTransceiver", "invalid button");
throw error {tr ("Ham Radio Deluxe: button not available")}; 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) if (UNK != m)
{ {
do_mode (m, false); do_mode (m);
} }
auto fo_string = QString::number (f); auto fo_string = QString::number (f);
if (vfo_count_ > 1 && reversed_) if (vfo_count_ > 1 && reversed_)
@ -481,9 +498,9 @@ void HRDTransceiver::do_frequency (Frequency f, MODE m)
update_rx_frequency (f); 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 // re-check if reversed VFOs
bool rx_A {true}; bool rx_A {true};
@ -510,7 +527,7 @@ void HRDTransceiver::do_tx_frequency (Frequency tx, bool rationalise_mode)
bool split {tx != 0}; bool split {tx != 0};
if (split) if (split)
{ {
if (rationalise_mode) if (set_rig_mode_)
{ {
if (!reversed_ && mode_B_dropdown_ >= 0) 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 (mode_A_dropdown_, lookup_mode (state ().mode (), mode_A_map_));
set_dropdown (receiver_dropdown_, (reversed_ ? rx_B_selection_ : rx_A_selection_).front ()); 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_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_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_); 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 // we rationalise the modes here as well as the frequencies
set_button (vfo_B_button_ >= 0 ? vfo_B_button_ : vfo_toggle_button_); set_button (vfo_B_button_ >= 0 ? vfo_B_button_ : vfo_toggle_button_);
send_simple_command ("set frequency-hz " + fo_string); 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 // do this here rather than later so we only
// toggle/switch VFOs once // toggle/switch VFOs once
set_dropdown (mode_A_dropdown_, lookup_mode (state ().mode (), mode_A_map_)); 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_); 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); 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) if (reversed_ && mode_B_dropdown_ >= 0)
{ {
set_dropdown (mode_B_dropdown_, lookup_mode (mode, mode_B_map_)); 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_)); 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_) 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 (mode_A_dropdown_, lookup_mode (mode, mode_A_map_));
set_dropdown (receiver_dropdown_, rx_B_selection_.front ()); 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_button (vfo_A_button_ >= 0 ? vfo_A_button_ : vfo_toggle_button_);
set_dropdown (mode_A_dropdown_, lookup_mode (mode, mode_A_map_)); set_dropdown (mode_A_dropdown_, lookup_mode (mode, mode_A_map_));
set_button (vfo_B_button_ >= 0 ? vfo_B_button_ : vfo_toggle_button_); 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) if ( tx_A_button_ >= 0)
{ {
set_button (tx_A_button_); 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 (mode_A_dropdown_, lookup_mode (mode, mode_A_map_));
set_dropdown (receiver_dropdown_, rx_A_selection_.front ()); 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_button (vfo_B_button_ >= 0 ? vfo_B_button_ : vfo_toggle_button_);
set_dropdown (mode_A_dropdown_, lookup_mode (mode, mode_A_map_)); set_dropdown (mode_A_dropdown_, lookup_mode (mode, mode_A_map_));
set_button (vfo_A_button_ >= 0 ? vfo_A_button_ : vfo_toggle_button_); 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) if ( tx_B_button_ >= 0)
{ {
set_button (tx_B_button_); 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); auto reply = send_command ("get button-select " + buttons_.value (button_index), no_debug);
if ("1" != reply && "0" != reply) 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")}; throw error {tr ("Ham Radio Deluxe didn't respond as expected")};
} }
return "1" == reply; return "1" == reply;
@ -859,7 +882,7 @@ QString HRDTransceiver::send_command (QString const& cmd, bool no_debug, bool pr
}); });
if (radio_iter == radios_.end ()) 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")}; 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 ()) if (QTcpSocket::ConnectedState != hrd_->state ())
{ {
TRACE_CAT (cmd << "failed" << hrd_->errorString ()); TRACE_CAT ("HRDTransceiver", cmd << "failed" << hrd_->errorString ());
throw error { throw error {
tr ("Ham Radio Deluxe send command \"%1\" failed %2\n") tr ("Ham Radio Deluxe send command \"%1\" failed %2\n")
.arg (cmd) .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 (); auto message = ((prepend_context ? context + cmd : cmd) + "\r").toLocal8Bit ();
if (!write_to_port (message.constData (), message.size ())) 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 { throw error {
tr ("Ham Radio Deluxe: failed to write command \"%1\"") tr ("Ham Radio Deluxe: failed to write command \"%1\"")
.arg (cmd) .arg (cmd)
@ -899,7 +922,7 @@ QString HRDTransceiver::send_command (QString const& cmd, bool no_debug, bool pr
QScopedPointer<HRDMessage> message {new (string) HRDMessage}; QScopedPointer<HRDMessage> message {new (string) HRDMessage};
if (!write_to_port (reinterpret_cast<char const *> (message.data ()), message->size_)) if (!write_to_port (reinterpret_cast<char const *> (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 { throw error {
tr ("Ham Radio Deluxe: failed to write command \"%1\"") tr ("Ham Radio Deluxe: failed to write command \"%1\"")
.arg (cmd) .arg (cmd)
@ -916,7 +939,7 @@ QString HRDTransceiver::send_command (QString const& cmd, bool no_debug, bool pr
HRDMessage const * reply {new (buffer) HRDMessage}; HRDMessage const * reply {new (buffer) HRDMessage};
if (reply->magic_1_value_ != reply->magic_1_ && reply->magic_2_value_ != reply->magic_2_) 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 { throw error {
tr ("Ham Radio Deluxe sent an invalid reply to our command \"%1\"") tr ("Ham Radio Deluxe sent an invalid reply to our command \"%1\"")
.arg (cmd) .arg (cmd)
@ -928,7 +951,7 @@ QString HRDTransceiver::send_command (QString const& cmd, bool no_debug, bool pr
{ {
if (!no_debug) if (!no_debug)
{ {
TRACE_CAT (cmd << "reading more reply data"); TRACE_CAT ("HRDTransceiver", cmd << "reading more reply data");
} }
buffer += read_reply (cmd); buffer += read_reply (cmd);
reply = new (buffer) HRDMessage; reply = new (buffer) HRDMessage;
@ -938,7 +961,7 @@ QString HRDTransceiver::send_command (QString const& cmd, bool no_debug, bool pr
} }
if (!no_debug) if (!no_debug)
{ {
TRACE_CAT (cmd << " ->" << result); TRACE_CAT ("HRDTransceiver", cmd << " ->" << result);
} }
return result; return result;
} }
@ -970,7 +993,7 @@ QByteArray HRDTransceiver::read_reply (QString const& cmd)
replied = hrd_->waitForReadyRead (); replied = hrd_->waitForReadyRead ();
if (!replied && hrd_->error () != hrd_->SocketTimeoutError) 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 { throw error {
tr ("Ham Radio Deluxe failed to reply to command \"%1\" %2\n") tr ("Ham Radio Deluxe failed to reply to command \"%1\" %2\n")
.arg (cmd) .arg (cmd)
@ -980,7 +1003,7 @@ QByteArray HRDTransceiver::read_reply (QString const& cmd)
} }
if (!replied) if (!replied)
{ {
TRACE_CAT (cmd << "retries exhausted"); TRACE_CAT ("HRDTransceiver", cmd << "retries exhausted");
throw error { throw error {
tr ("Ham Radio Deluxe retries exhausted sending command \"%1\"") tr ("Ham Radio Deluxe retries exhausted sending command \"%1\"")
.arg (cmd) .arg (cmd)
@ -993,7 +1016,7 @@ void HRDTransceiver::send_simple_command (QString const& command, bool no_debug)
{ {
if ("OK" != send_command (command, no_debug)) if ("OK" != send_command (command, no_debug))
{ {
TRACE_CAT (command << "unexpected response"); TRACE_CAT ("HRDTransceiver", command << "unexpected response");
throw error { throw error {
tr ("Ham Radio Deluxe didn't respond to command \"%1\" as expected") tr ("Ham Radio Deluxe didn't respond to command \"%1\" as expected")
.arg (command) .arg (command)

View File

@ -34,16 +34,17 @@ public:
explicit HRDTransceiver (std::unique_ptr<TransceiverBase> wrapped explicit HRDTransceiver (std::unique_ptr<TransceiverBase> wrapped
, QString const& server , QString const& server
, bool use_for_ptt , bool use_for_ptt
, int poll_interval); , int poll_interval
~HRDTransceiver (); , bool set_rig_mode
, QObject * parent = nullptr);
protected: protected:
// Implement the TransceiverBase interface. // Implement the TransceiverBase interface.
void do_start () override; int do_start () override;
void do_stop () override; void do_stop () override;
void do_frequency (Frequency, MODE) override; void do_frequency (Frequency, MODE, bool no_ignore) override;
void do_tx_frequency (Frequency, bool rationalise_mode) override; void do_tx_frequency (Frequency, bool no_ignore) override;
void do_mode (MODE, bool rationalise) override; void do_mode (MODE) override;
void do_ptt (bool on) override; void do_ptt (bool on) override;
// Implement the PollingTransceiver interface. // Implement the PollingTransceiver interface.
@ -74,10 +75,12 @@ private:
// An alternate TransceiverBase instance that can be used to drive // An alternate TransceiverBase instance that can be used to drive
// PTT if required. // PTT if required.
std::unique_ptr<TransceiverBase> wrapped_; std::unique_ptr<TransceiverBase> wrapped_; // may be null
bool use_for_ptt_; // Use HRD for PTT. 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 QString server_; // The TCP/IP addrress and port for
// the HRD server. // the HRD server.

View File

@ -169,10 +169,13 @@ void HamlibTransceiver::RIGDeleter::cleanup (RIG * rig)
} }
} }
HamlibTransceiver::HamlibTransceiver (TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port) HamlibTransceiver::HamlibTransceiver (TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port,
: PollingTransceiver {0} QObject * parent)
: PollingTransceiver {0, parent}
, rig_ {rig_init (RIG_MODEL_DUMMY)} , rig_ {rig_init (RIG_MODEL_DUMMY)}
, set_rig_mode_ {false}
, back_ptt_port_ {false} , back_ptt_port_ {false}
, one_VFO_ {false}
, is_dummy_ {true} , is_dummy_ {true}
, reversed_ {false} , reversed_ {false}
, mode_query_works_ {true} , mode_query_works_ {true}
@ -218,10 +221,13 @@ HamlibTransceiver::HamlibTransceiver (TransceiverFactory::PTTMethod ptt_type, QS
} }
} }
HamlibTransceiver::HamlibTransceiver (int model_number, TransceiverFactory::ParameterPack const& params) HamlibTransceiver::HamlibTransceiver (int model_number, TransceiverFactory::ParameterPack const& params,
: PollingTransceiver {params.poll_interval} QObject * parent)
: PollingTransceiver {params.poll_interval, parent}
, rig_ {rig_init (model_number)} , rig_ {rig_init (model_number)}
, set_rig_mode_ {params.set_rig_mode}
, back_ptt_port_ {TransceiverFactory::TX_audio_source_rear == params.audio_source} , back_ptt_port_ {TransceiverFactory::TX_audio_source_rear == params.audio_source}
, one_VFO_ {false}
, is_dummy_ {RIG_MODEL_DUMMY == model_number} , is_dummy_ {RIG_MODEL_DUMMY == model_number}
, reversed_ {false} , reversed_ {false}
, mode_query_works_ {rig_ && rig_->caps->get_mode} , mode_query_works_ {rig_ && rig_->caps->get_mode}
@ -249,44 +255,44 @@ HamlibTransceiver::HamlibTransceiver (int model_number, TransceiverFactory::Para
, "hamlib_settings.json"); , "hamlib_settings.json");
if (!settings_file_name.isEmpty ()) if (!settings_file_name.isEmpty ())
{ {
QFile settings_file {settings_file_name}; QFile settings_file {settings_file_name};
if (settings_file.open (QFile::ReadOnly)) if (settings_file.open (QFile::ReadOnly))
{ {
QJsonParseError status; QJsonParseError status;
auto settings_doc = QJsonDocument::fromJson (settings_file.readAll (), &status); auto settings_doc = QJsonDocument::fromJson (settings_file.readAll (), &status);
if (status.error) if (status.error)
{ {
throw error {tr ("Hamlib settings file error: %1 at character offset %2") throw error {tr ("Hamlib settings file error: %1 at character offset %2")
.arg (status.errorString ()).arg (status.offset)}; .arg (status.errorString ()).arg (status.offset)};
} }
if (!settings_doc.isObject ()) if (!settings_doc.isObject ())
{ {
throw error {tr ("Hamlib settings file error: top level must be a JSON object")}; throw error {tr ("Hamlib settings file error: top level must be a JSON object")};
} }
auto const& settings = settings_doc.object (); auto const& settings = settings_doc.object ();
// //
// configuration settings // configuration settings
// //
auto const& config = settings["config"]; auto const& config = settings["config"];
if (!config.isUndefined ()) if (!config.isUndefined ())
{ {
if (!config.isObject ()) if (!config.isObject ())
{ {
throw error {tr ("Hamlib settings file error: config must be a JSON object")}; throw error {tr ("Hamlib settings file error: config must be a JSON object")};
} }
auto const& config_list = config.toObject (); auto const& config_list = config.toObject ();
for (auto item = config_list.constBegin (); item != config_list.constEnd (); ++item) for (auto item = config_list.constBegin (); item != config_list.constEnd (); ++item)
{ {
set_conf (item.key ().toLocal8Bit ().constData () set_conf (item.key ().toLocal8Bit ().constData ()
, (*item).toVariant ().toString ().toLocal8Bit ().constData ()); , (*item).toVariant ().toString ().toLocal8Bit ().constData ());
} }
} }
} }
} }
} }
if (RIG_MODEL_DUMMY != model_number) if (!is_dummy_)
{ {
switch (rig_->caps->port_type) switch (rig_->caps->port_type)
{ {
@ -354,8 +360,7 @@ HamlibTransceiver::HamlibTransceiver (int model_number, TransceiverFactory::Para
case TransceiverFactory::PTT_method_RTS: case TransceiverFactory::PTT_method_RTS:
if (!params.ptt_port.isEmpty () if (!params.ptt_port.isEmpty ()
&& params.ptt_port != "None" && params.ptt_port != "None"
&& (RIG_MODEL_DUMMY == model_number && (is_dummy_ || params.ptt_port != params.serial_port))
|| params.ptt_port != params.serial_port))
{ {
#if defined (WIN32) #if defined (WIN32)
set_conf ("ptt_pathname", ("\\\\.\\" + params.ptt_port).toLatin1 ().data ()); 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); // rig_set_freq_callback (rig_.data (), &frequency_change_callback, this);
} }
HamlibTransceiver::~HamlibTransceiver ()
{
}
void HamlibTransceiver::error_check (int ret_code, QString const& doing) const void HamlibTransceiver::error_check (int ret_code, QString const& doing) const
{ {
if (RIG_OK != ret_code) 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)}; 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")); 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) if (-RIG_ENAVAIL == rc || -RIG_ENIMPL == rc)
{ {
get_vfo_works_ = false; 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 else
{ {
@ -430,38 +439,38 @@ void HamlibTransceiver::do_start ()
// assume it is as when we started by setting at open time right // assume it is as when we started by setting at open time right
// here. We also gather/set other initial state. // here. We also gather/set other initial state.
error_check (rig_get_freq (rig_.data (), RIG_VFO_CURR, &f1), tr ("getting current frequency")); 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")); 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) 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")); error_check (rig_vfo_op (rig_.data (), RIG_VFO_CURR, RIG_OP_TOGGLE), tr ("exchanging VFOs"));
} }
else 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_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")); 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")); 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); update_other_frequency (f2);
if (!rig_->caps->set_vfo) 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")); error_check (rig_vfo_op (rig_.data (), RIG_VFO_CURR, RIG_OP_TOGGLE), tr ("exchanging VFOs"));
} }
else 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")); 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 else
{ {
error_check (rig_get_freq (rig_.data (), RIG_VFO_CURR, &f1), tr ("getting frequency")); 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")); 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); 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")); // error_check (rig_set_split_vfo (rig_.data (), RIG_VFO_CURR, RIG_SPLIT_OFF, RIG_VFO_CURR), tr ("setting split off"));
// update_split (false); // update_split (false);
} }
@ -491,7 +500,7 @@ void HamlibTransceiver::do_start ()
if (get_vfo_works_ && rig_->caps->get_vfo) 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 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; 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)) 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 else
{ {
@ -508,7 +517,7 @@ void HamlibTransceiver::do_start ()
// Some rigs (HDSDR) don't have a working way of // Some rigs (HDSDR) don't have a working way of
// reporting MODE so we give up on mode queries - // reporting MODE so we give up on mode queries -
// sets will still cause an error // 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, &current_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<Radio::FrequencyDelta> (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 (); poll ();
TRACE_CAT ("exit" << state () << "reversed =" << reversed_); TRACE_CAT ("HamlibTransceiver", "exit" << state () << "reversed =" << reversed_ << "resolution = " << resolution);
return resolution;
} }
void HamlibTransceiver::do_stop () void HamlibTransceiver::do_stop ()
@ -551,77 +581,92 @@ void HamlibTransceiver::do_stop ()
rig_close (rig_.data ()); rig_close (rig_.data ());
} }
TRACE_CAT ("state:" << state () << "reversed =" << reversed_); TRACE_CAT ("HamlibTransceiver", "state:" << state () << "reversed =" << reversed_);
} }
auto HamlibTransceiver::get_vfos () const -> std::tuple<vfo_t, vfo_t> auto HamlibTransceiver::get_vfos (bool for_split) const -> std::tuple<vfo_t, vfo_t>
{ {
if (get_vfo_works_ && rig_->caps->get_vfo) if (get_vfo_works_ && rig_->caps->get_vfo)
{ {
vfo_t v; vfo_t v;
error_check (rig_get_vfo (rig_.data (), &v), tr ("getting current VFO")); // has side effect of establishing current VFO inside hamlib 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; 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 // use VFO A/MAIN for main frequency and B/SUB for Tx
// frequency if split since these type of radios can only // frequency if split since these type of radios can only
// support this way around // 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")); 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 // 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 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) ? (rig_->state.vfo_list & RIG_VFO_B ? RIG_VFO_B : RIG_VFO_SUB)
: rx_vfo; : rx_vfo;
if (reversed_) if (reversed_)
{ {
TRACE_CAT ("reversing VFOs"); TRACE_CAT ("HamlibTransceiver", "reversing VFOs");
std::swap (rx_vfo, tx_vfo); 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); 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 // only change when receiving or simplex or direct VFO addressing
// set // unavailable or forced
error_check (rig_set_freq (rig_.data (), RIG_VFO_CURR, f), tr ("setting frequency")); if (!state ().ptt () || !state ().split () || !one_VFO_ || no_ignore)
if (mode_query_works_ && UNK != m)
{ {
do_mode (m, false); // for the 1st time as a band change may cause a recalled mode to be
// set
// 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")); 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 if (mode_query_works_ && UNK != m)
// to frequency such as the TS-2000 auto mode setting {
do_mode (m, false); rmode_t current_mode;
pbwidth_t current_width;
auto new_mode = map_mode (m);
error_check (rig_get_mode (rig_.data (), RIG_VFO_CURR, &current_mode, &current_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 if (WSJT_RIG_NONE_CAN_SPLIT || !is_dummy_) // split is meaningless if you can't see it
{ {
auto split = tx ? RIG_SPLIT_ON : RIG_SPLIT_OFF; auto split = tx ? RIG_SPLIT_ON : RIG_SPLIT_OFF;
update_split (tx); auto vfos = get_vfos (tx);
auto vfos = get_vfos ();
// auto rx_vfo = std::get<0> (vfos); // or use RIG_VFO_CURR // auto rx_vfo = std::get<0> (vfos); // or use RIG_VFO_CURR
auto tx_vfo = std::get<1> (vfos); 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 // much we can do since the Hamlib Library needs this
// call at least once to establish the Tx VFO. Best we // call at least once to establish the Tx VFO. Best we
// can do is only do this once per session. // 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); auto rc = rig_set_split_vfo (rig_.data (), RIG_VFO_CURR, split, tx_vfo);
if (tx || (-RIG_ENAVAIL != rc && -RIG_ENIMPL != rc)) if (tx || (-RIG_ENAVAIL != rc && -RIG_ENIMPL != rc))
{ {
@ -652,85 +697,126 @@ void HamlibTransceiver::do_tx_frequency (Frequency tx, bool rationalise_mode)
// specific rig. // specific rig.
error_check (rc, tr ("setting/unsetting split mode")); error_check (rc, tr ("setting/unsetting split mode"));
} }
tickle_hamlib_ = false; tickle_hamlib_ = false;
update_split (tx);
} }
hamlib_tx_vfo_fixup fixup (rig_.data (), tx_vfo); // just change current when transmitting with single VFO
// do this before setting the mode because changing band may // addressing
// recall the last mode used on the target band if (state ().ptt () && one_VFO_)
error_check (rig_set_split_freq (rig_.data (), RIG_VFO_CURR, tx), tr ("setting split TX frequency"));
if (rationalise_mode)
{ {
rmode_t current_mode; TRACE_CAT ("HamlibTransceiver", "rig_set_split_vfo split =" << split);
pbwidth_t current_width; 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, &current_mode, &current_width), tr ("getting mode of split TX VFO")); error_check (rig_set_freq (rig_.data (), RIG_VFO_CURR, tx), tr ("setting frequency"));
TRACE_CAT ("rig_get_split_mode mode = " << rig_strrmode (current_mode) << "bw =" << current_width);
auto new_mode = map_mode (state ().mode ()); if (set_rig_mode_ && mode_query_works_)
if (new_mode != current_mode)
{ {
TRACE_CAT ("rig_set_split_mode mode = " << rig_strrmode (new_mode)); rmode_t current_mode;
error_check (rig_set_split_mode (rig_.data (), RIG_VFO_CURR, new_mode, RIG_PASSBAND_NORMAL), tr ("setting split TX VFO mode")); pbwidth_t current_width;
auto new_mode = map_mode (state ().mode ());
error_check (rig_get_mode (rig_.data (), RIG_VFO_CURR, &current_mode, &current_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")); error_check (rig_set_split_freq (rig_.data (), RIG_VFO_CURR, tx), tr ("setting split TX frequency"));
} }
// Enable split last since some rigs (Kenwood for one) come out
// of split when you switch RX VFO (to set split mode above for
// example). Also the Elecraft K3 will refuse to go to split
// with certain VFO A/B mode combinations.
TRACE_CAT ("HamlibTransceiver", "rig_set_split_vfo split =" << split);
error_check (rig_set_split_vfo (rig_.data (), RIG_VFO_CURR, split, tx_vfo), tr ("setting split mode"));
update_other_frequency (tx);
update_split (tx);
} }
} }
else
// 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))
{ {
// On rigs that can't have split controlled only throw an // Disable split
// exception when an error other than command not accepted TRACE_CAT ("HamlibTransceiver", "rig_set_split_vfo split =" << split);
// is returned when trying to leave split mode. This allows auto rc = rig_set_split_vfo (rig_.data (), RIG_VFO_CURR, split, tx_vfo);
// fake split mode and non-split mode to work without error if (tx || (-RIG_ENAVAIL != rc && -RIG_ENIMPL != rc))
// on such rigs without having to know anything about the {
// specific rig. // On rigs that can't have split controlled only throw an
error_check (rc, tr ("setting/unsetting split mode")); // 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 rx_vfo = std::get<0> (vfos);
auto tx_vfo = std::get<1> (vfos); auto tx_vfo = std::get<1> (vfos);
rmode_t current_mode; rmode_t current_mode;
pbwidth_t current_width; pbwidth_t current_width;
error_check (rig_get_mode (rig_.data (), RIG_VFO_CURR, &current_mode, &current_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); 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_get_mode (rig_.data (), RIG_VFO_CURR, &current_mode, &current_width), tr ("getting current VFO mode"));
error_check (rig_set_mode (rig_.data (), RIG_VFO_CURR, new_mode, RIG_PASSBAND_NORMAL), tr ("setting current VFO mode")); TRACE_CAT ("HamlibTransceiver", "rig_get_mode mode = " << rig_strrmode (current_mode) << "bw =" << current_width);
}
if (!is_dummy_ && state ().split () && rationalise)
{
error_check (rig_get_split_mode (rig_.data (), RIG_VFO_CURR, &current_mode, &current_width), tr ("getting split TX VFO mode"));
TRACE_CAT ("rig_get_split_mode mode = " << rig_strrmode (current_mode) << "bw =" << current_width);
if (new_mode != current_mode) 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, &current_mode, &current_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, &current_mode, &current_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); 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")); 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; vfo_t v;
error_check (rig_get_vfo (rig_.data (), &v), tr ("getting current VFO")); // has side effect of establishing current VFO inside hamlib 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; reversed_ = RIG_VFO_B == v;
} }
error_check (rig_get_freq (rig_.data (), RIG_VFO_CURR, &f), tr ("getting current VFO frequency")); // only read when receiving or simplex
TRACE_CAT_POLL ("rig_get_freq frequency =" << f); if (!state ().ptt () || !state ().split ())
update_rx_frequency (f); {
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_) if ((WSJT_RIG_NONE_CAN_SPLIT || !is_dummy_)
&& state ().split () && 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 // only read "other" VFO if in split, this allows rigs like
// FlexRadio to work in Kenwood TS-2000 mode despite them // FlexRadio to work in Kenwood TS-2000 mode despite them
@ -779,12 +870,14 @@ void HamlibTransceiver::poll ()
, reversed_ , reversed_
? (rig_->state.vfo_list & RIG_VFO_A ? RIG_VFO_A : RIG_VFO_MAIN) ? (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) : (rig_->state.vfo_list & RIG_VFO_B ? RIG_VFO_B : RIG_VFO_SUB)
, &f), tr ("getting current VFO frequency")); , &f), tr ("getting other VFO frequency"));
TRACE_CAT_POLL ("rig_get_freq other VFO =" << f); TRACE_CAT_POLL ("HamlibTransceiver", "rig_get_freq other VFO =" << f);
update_other_frequency (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 // We have to ignore errors here because Yaesu FTdx... rigs can
// report the wrong mode when transmitting split with different // 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); auto rc = rig_get_mode (rig_.data (), RIG_VFO_CURR, &m, &w);
if (RIG_OK == rc) 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)); update_mode (map_mode (m));
} }
else 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); auto rc = rig_get_split_vfo (rig_.data (), RIG_VFO_CURR, &s, &v);
if (-RIG_OK == rc && RIG_SPLIT_ON == s) 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); update_split (true);
// if (RIG_VFO_A == v) // if (RIG_VFO_A == v)
// { // {
@ -820,14 +913,14 @@ void HamlibTransceiver::poll ()
} }
else if (-RIG_OK == rc) // not split 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); update_split (false);
} }
else else
{ {
// Some rigs (Icom) don't have a way of reporting SPLIT // Some rigs (Icom) don't have a way of reporting SPLIT
// mode // 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 // just report how we see it based on prior commands
split_query_works_ = false; split_query_works_ = false;
} }
@ -842,7 +935,7 @@ void HamlibTransceiver::poll ()
// support command // support command
{ {
error_check (rc, tr ("getting PTT state")); 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)); update_PTT (!(RIG_PTT_OFF == p));
} }
} }
@ -864,12 +957,12 @@ void HamlibTransceiver::poll ()
void HamlibTransceiver::do_ptt (bool on) void HamlibTransceiver::do_ptt (bool on)
{ {
TRACE_CAT (on << state () << "reversed =" << reversed_); TRACE_CAT ("HamlibTransceiver", on << state () << "reversed =" << reversed_);
if (on) if (on)
{ {
if (RIG_PTT_NONE != rig_->state.pttport.type.ptt) 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 error_check (rig_set_ptt (rig_.data (), RIG_VFO_CURR
, RIG_PTT_RIG_MICDATA == rig_->caps->ptt_type && back_ptt_port_ , RIG_PTT_RIG_MICDATA == rig_->caps->ptt_type && back_ptt_port_
? RIG_PTT_ON_DATA : RIG_PTT_ON), tr ("setting PTT on")); ? 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) 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")); error_check (rig_set_ptt (rig_.data (), RIG_VFO_CURR, RIG_PTT_OFF), tr ("setting PTT off"));
} }
} }

View File

@ -26,16 +26,17 @@ class HamlibTransceiver final
public: public:
static void register_transceivers (TransceiverFactory::Transceivers *); static void register_transceivers (TransceiverFactory::Transceivers *);
explicit HamlibTransceiver (int model_number, TransceiverFactory::ParameterPack const&); explicit HamlibTransceiver (int model_number, TransceiverFactory::ParameterPack const&,
explicit HamlibTransceiver (TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port); QObject * parent = nullptr);
~HamlibTransceiver (); explicit HamlibTransceiver (TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port,
QObject * parent = nullptr);
private: private:
void do_start () override; int do_start () override;
void do_stop () override; void do_stop () override;
void do_frequency (Frequency, MODE) override; void do_frequency (Frequency, MODE, bool no_ignore) override;
void do_tx_frequency (Frequency, bool rationalise_mode) override; void do_tx_frequency (Frequency, bool no_ignore) override;
void do_mode (MODE, bool rationalise) override; void do_mode (MODE) override;
void do_ptt (bool) override; void do_ptt (bool) override;
void poll () override; void poll () override;
@ -45,12 +46,14 @@ class HamlibTransceiver final
QByteArray get_conf (char const * item); QByteArray get_conf (char const * item);
Transceiver::MODE map_mode (rmode_t) const; Transceiver::MODE map_mode (rmode_t) const;
rmode_t map_mode (Transceiver::MODE mode) const; rmode_t map_mode (Transceiver::MODE mode) const;
std::tuple<vfo_t, vfo_t> get_vfos () const; std::tuple<vfo_t, vfo_t> get_vfos (bool for_split) const;
struct RIGDeleter {static void cleanup (RIG *);}; struct RIGDeleter {static void cleanup (RIG *);};
QScopedPointer<RIG, RIGDeleter> rig_; QScopedPointer<RIG, RIGDeleter> rig_;
bool set_rig_mode_;
bool back_ptt_port_; bool back_ptt_port_;
bool one_VFO_;
bool is_dummy_; bool is_dummy_;
// these are saved on destruction so we can start new instances // these are saved on destruction so we can start new instances

View File

@ -13,20 +13,25 @@
LiveFrequencyValidator::LiveFrequencyValidator (QComboBox * combo_box LiveFrequencyValidator::LiveFrequencyValidator (QComboBox * combo_box
, Bands const * bands , Bands const * bands
, FrequencyList const * frequencies , FrequencyList const * frequencies
, Frequency const * nominal_frequency
, QWidget * parent) , QWidget * parent)
: QRegExpValidator { : QRegExpValidator {
QRegExp { // frequency in MHz or band QRegExp { // frequency in MHz or band
bands->data (QModelIndex {}).toString () // out of band string bands->data (QModelIndex {}).toString () // out of band string
+ QString {R"(|((\d{0,6}(\)"} // or up to 6 digits + QString {R"(|((\d{0,6}(\)"} // or up to 6 digits
+ QLocale {}.decimalPoint () // (followed by decimal separator + 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 + 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 , parent
} }
, bands_ {bands} , bands_ {bands}
, frequencies_ {frequencies} , frequencies_ {frequencies}
, nominal_frequency_ {nominal_frequency}
, combo_box_ {combo_box} , combo_box_ {combo_box}
{ {
} }
@ -65,6 +70,14 @@ void LiveFrequencyValidator::fixup (QString& input) const
input = QString {}; 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 else
{ {
// frequency input // frequency input

View File

@ -1,52 +1,56 @@
#ifndef LIVE_FREQUENCY_VALIDATOR_HPP__ #ifndef LIVE_FREQUENCY_VALIDATOR_HPP__
#define LIVE_FREQUENCY_VALIDATOR_HPP__ #define LIVE_FREQUENCY_VALIDATOR_HPP__
#include <QObject> #include <QObject>
#include <QRegExpValidator> #include <QRegExpValidator>
#include "Radio.hpp" #include "Radio.hpp"
class Bands; class Bands;
class FrequencyList; class FrequencyList;
class QComboBox; class QComboBox;
class QWidget; class QWidget;
// //
// Class LiveFrequencyValidator // Class LiveFrequencyValidator
// //
// QLineEdit validator that controls input to an editable // QLineEdit validator that controls input to an editable
// QComboBox where the user can enter a valid band or a valid // QComboBox where the user can enter a valid band or a valid
// frequency in megahetz. // frequency in megahetz.
// //
// Collabrations // Collabrations
// //
// Implements the QRegExpValidator interface. Validates input // Implements the QRegExpValidator interface. Validates input
// from the supplied QComboBox as either a valid frequency in // from the supplied QComboBox as either a valid frequency in
// megahertz or a valid band as defined by the supplied column of // megahertz or a valid band as defined by the supplied column of
// the supplied QAbstractItemModel. // the supplied QAbstractItemModel.
// //
class LiveFrequencyValidator final class LiveFrequencyValidator final
: public QRegExpValidator : public QRegExpValidator
{ {
Q_OBJECT; Q_OBJECT;
public: public:
using Frequency = Radio::Frequency; using Frequency = Radio::Frequency;
LiveFrequencyValidator (QComboBox * combo_box // associated combo box LiveFrequencyValidator (QComboBox * combo_box // associated combo box
, Bands const * bands // bands model , Bands const * bands // bands model
, FrequencyList const * frequencies // working frequencies model , FrequencyList const * frequencies // working
, QWidget * parent = nullptr); // frequencies
// model
State validate (QString& input, int& pos) const override; , Frequency const * nominal_frequency
void fixup (QString& input) const override; , QWidget * parent = nullptr);
Q_SIGNAL void valid (Frequency) const; State validate (QString& input, int& pos) const override;
void fixup (QString& input) const override;
private:
Bands const * bands_; Q_SIGNAL void valid (Frequency) const;
FrequencyList const * frequencies_;
QComboBox * combo_box_; private:
}; Bands const * bands_;
FrequencyList const * frequencies_;
#endif Frequency const * nominal_frequency_;
QComboBox * combo_box_;
};
#endif

View File

@ -3,6 +3,7 @@
#include <QTimer> #include <QTimer>
#include <QDebug> #include <QDebug>
#include <objbase.h> #include <objbase.h>
#include <QThread>
#include "qt_helpers.hpP" #include "qt_helpers.hpP"
@ -48,7 +49,7 @@ auto OmniRigTransceiver::map_mode (OmniRig::RigParamX param) -> MODE
{ {
return FM; return FM;
} }
TRACE_CAT ("unrecognized mode"); TRACE_CAT ("OmniRigTransceiver", "unrecognized mode");
throw_qstring (tr ("OmniRig: unrecognized mode")); throw_qstring (tr ("OmniRig: unrecognized mode"));
return UNK; return UNK;
} }
@ -70,7 +71,7 @@ OmniRig::RigParamX OmniRigTransceiver::map_mode (MODE mode)
case DIG_FM: return OmniRig::PM_FM; case DIG_FM: return OmniRig::PM_FM;
default: break; 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) 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 { (*registry)[OmniRig_transceiver_one_name] = TransceiverFactory::Capabilities {
id1 id1
, TransceiverFactory::Capabilities::none // COM isn't serial or network , TransceiverFactory::Capabilities::none // COM isn't serial or network
, true // does PTT , true // does PTT
, false // doesn't select mic/data (use OmniRig config file) , false // doesn't select mic/data (use OmniRig config file)
, true // can remote control RTS nd DTR , true // can remote control RTS nd DTR
, true // asynchronous interface , true // asynchronous interface
}; };
(*registry)[OmniRig_transceiver_two_name] = TransceiverFactory::Capabilities { (*registry)[OmniRig_transceiver_two_name] = TransceiverFactory::Capabilities {
id2 id2
, TransceiverFactory::Capabilities::none // COM isn't serial or network , TransceiverFactory::Capabilities::none // COM isn't serial or network
, true // does PTT , true // does PTT
, false // doesn't select mic/data (use OmniRig config file) , false // doesn't select mic/data (use OmniRig config file)
, true // can remote control RTS nd DTR , true // can remote control RTS nd DTR
, true // asynchronous interface , true // asynchronous interface
}; };
} }
OmniRigTransceiver::OmniRigTransceiver (std::unique_ptr<TransceiverBase> wrapped, RigNumber n, TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port) OmniRigTransceiver::OmniRigTransceiver (std::unique_ptr<TransceiverBase> wrapped,
: wrapped_ {std::move (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))} , 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} , ptt_type_ {ptt_type}
, rig_number_ {n} , rig_number_ {n}
@ -105,21 +109,17 @@ OmniRigTransceiver::OmniRigTransceiver (std::unique_ptr<TransceiverBase> wrapped
{ {
} }
OmniRigTransceiver::~OmniRigTransceiver () int OmniRigTransceiver::do_start ()
{ {
} TRACE_CAT ("OmniRigTransceiver", "starting");
if (wrapped_) wrapped_->start (0);
void OmniRigTransceiver::do_start ()
{
TRACE_CAT ("starting");
wrapped_->start ();
CoInitializeEx (nullptr, 0 /*COINIT_APARTMENTTHREADED*/); // required because Qt only does this for GUI thread CoInitializeEx (nullptr, 0 /*COINIT_APARTMENTTHREADED*/); // required because Qt only does this for GUI thread
omni_rig_.reset (new OmniRig::OmniRigX {this}); omni_rig_.reset (new OmniRig::OmniRigX {this});
if (omni_rig_->isNull ()) 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")); throw_qstring (tr ("Failed to start OmniRig COM server"));
} }
@ -135,7 +135,7 @@ void OmniRigTransceiver::do_start ()
, SIGNAL (CustomReply (int, QVariant const&, QVariant const&)) , SIGNAL (CustomReply (int, QVariant const&, QVariant const&))
, this, SLOT (handle_custom_reply (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 ()); << "i/f version:" << QString::number (omni_rig_->InterfaceVersion ()).toLocal8Bit ());
// fetch the interface of the RigX CoClass and instantiate a proxy object // 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_);
Q_ASSERT (!port_->isNull ()); 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 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)); 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 // start off so we don't accidentally key the radio
if (TransceiverFactory::PTT_method_DTR == ptt_type_) if (TransceiverFactory::PTT_method_DTR == ptt_type_)
{ {
port_->SetDtr (false); port_->SetDtr (false);
} }
else // RTS else // RTS
{ {
port_->SetRts (false); port_->SetRts (false);
} }
@ -175,66 +176,81 @@ void OmniRigTransceiver::do_start ()
readable_params_ = rig_->ReadableParams (); readable_params_ = rig_->ReadableParams ();
writable_params_ = rig_->WriteableParams (); 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 (rig_->RigType ())
.arg (readable_params_, 8, 16, QChar ('0')) .arg (readable_params_, 8, 16, QChar ('0'))
.arg (writable_params_, 8, 16, QChar ('0')) .arg (writable_params_, 8, 16, QChar ('0'))
.arg (rig_number_).toLocal8Bit ()); .arg (rig_number_).toLocal8Bit ());
TRACE_CAT ("OmniRig status:" << rig_->StatusStr ()); for (unsigned tries {0}; tries < 10; ++tries)
{
init_rig (); 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 () void OmniRigTransceiver::do_stop ()
{ {
QThread::msleep (200); // leave some time for pending
// commands at the server end
if (port_) if (port_)
{ {
// port_->Unlock (); // release serial port port_->Unlock (); // release serial port
port_->clear (); port_->clear ();
} }
rig_->clear (); if (rig_) rig_->clear ();
omni_rig_->clear (); if (omni_rig_) omni_rig_->clear ();
CoUninitialize (); CoUninitialize ();
wrapped_->stop (); if (wrapped_) wrapped_->stop ();
TRACE_CAT ("stopped"); TRACE_CAT ("OmniRigTransceiver", "stopped");
} }
void OmniRigTransceiver::online_check () void OmniRigTransceiver::do_sync (bool force_signal, bool /*no_poll*/)
{
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)
{ {
// nothing much we can do here, we just have to let OmniRig do its // 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 // 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) 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)); 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 () 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) 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 (); readable_params_ = rig_->ReadableParams ();
writable_params_ = rig_->WriteableParams (); 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 (rig_->RigType ())
.arg (readable_params_, 8, 16, QChar ('0')) .arg (readable_params_, 8, 16, QChar ('0'))
.arg (writable_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) 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 ()) if (OmniRig::ST_ONLINE != rig_->Status ())
{ {
QTimer::singleShot (5000, this, SLOT (online_check ())); 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) 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 (params, 8, 16, QChar ('0'))
.arg (rig_number).toLocal8Bit () .arg (rig_number).toLocal8Bit ()
<< "state before:" << state ()); << "state before:" << state ());
// starting_ = false; // starting_ = false;
TransceiverState old_state {state ()}; TransceiverState old_state {state ()};
auto need_frequency = false; auto need_frequency = false;
// state_.online = true; // sometimes we don't get an initial // state_.online = true; // sometimes we don't get an initial
// // OmniRig::ST_ONLINE status change // // OmniRig::ST_ONLINE status change
// // event // // event
if (params & OmniRig::PM_VFOAA) if (params & OmniRig::PM_VFOAA)
{ {
update_split (false); update_split (false);
@ -481,7 +501,7 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params)
update_complete (); update_complete ();
send_update_signal_ = false; 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) 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 () << "with reply" << reply.toString ().toLocal8Bit ()
<< QString ("for rig %1").arg (rig_number).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) 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_) 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); rig_->SetTx (on ? OmniRig::PM_TX : OmniRig::PM_RX);
} }
else else
@ -513,19 +533,22 @@ void OmniRigTransceiver::do_ptt (bool on)
{ {
if (TransceiverFactory::PTT_method_RTS == ptt_type_) if (TransceiverFactory::PTT_method_RTS == ptt_type_)
{ {
TRACE_CAT ("set RTS"); TRACE_CAT ("OmniRigTransceiver", "set RTS");
port_->SetRts (on); port_->SetRts (on);
} }
else // "DTR" else // "DTR"
{ {
TRACE_CAT ("set DTR"); TRACE_CAT ("OmniRigTransceiver", "set DTR");
port_->SetDtr (on); port_->SetDtr (on);
} }
} }
else else
{ {
TRACE_CAT ("set PTT using basic transceiver"); TRACE_CAT ("OmniRigTransceiver", "set PTT using basic transceiver");
wrapped_->ptt (on); Q_ASSERT (wrapped_);
TransceiverState new_state {wrapped_->state ()};
new_state.ptt (on);
wrapped_->set (new_state, 0);
} }
if (state ().ptt () != on) 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) if (UNK != m)
{ {
do_mode (m, false); do_mode (m);
} }
if (OmniRig::PM_FREQ & writable_params_) 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}; bool split {tx != 0};
if (split) if (split)
{ {
TRACE_CAT ("set SPLIT mode on"); TRACE_CAT ("OmniRigTransceiver", "set SPLIT mode on");
rig_->SetSplitMode (state ().frequency (), tx); rig_->SetSplitMode (state ().frequency (), tx);
update_other_frequency (tx); update_other_frequency (tx);
update_split (true); update_split (true);
} }
else else
{ {
TRACE_CAT ("set SPLIT mode off"); TRACE_CAT ("OmniRigTransceiver", "set SPLIT mode off");
rig_->SetSimplexMode (state ().frequency ()); rig_->SetSimplexMode (state ().frequency ());
update_split (false); 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_)) if (!((OmniRig::PM_VFOAB | OmniRig::PM_VFOBA | OmniRig::PM_SPLITON) & readable_params_))
{ {
TRACE_CAT ("setting SPLIT manually"); TRACE_CAT ("OmniRigTransceiver", "setting SPLIT manually");
update_split (split); // we can't read it so just set and update_split (split); // we can't read it so just set and
// hope op doesn't change it // hope op doesn't change it
notify = true; 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 // TODO: G4WJS OmniRig doesn't seem to have any capability of tracking/setting VFO B mode
auto mapped = map_mode (mode); auto mapped = map_mode (mode);
if (mapped & writable_params_) if (mapped & writable_params_)

View File

@ -30,19 +30,17 @@ public:
enum RigNumber {One = 1, Two}; enum RigNumber {One = 1, Two};
// takes ownership of wrapped Transceiver // takes ownership of wrapped Transceiver
explicit OmniRigTransceiver (std::unique_ptr<TransceiverBase> wrapped, RigNumber, TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port); explicit OmniRigTransceiver (std::unique_ptr<TransceiverBase> wrapped, RigNumber, TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port, QObject * parent = nullptr);
~OmniRigTransceiver ();
void do_start () override; int do_start () override;
void do_stop () override; void do_stop () override;
void do_frequency (Frequency, MODE) override; void do_frequency (Frequency, MODE, bool no_ignore) override;
void do_tx_frequency (Frequency, bool rationalise_mode) override; void do_tx_frequency (Frequency, bool no_ignore) override;
void do_mode (MODE, bool rationalise) override; void do_mode (MODE) override;
void do_ptt (bool on) 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: private:
Q_SLOT void online_check ();
Q_SLOT void handle_COM_exception (int, QString, QString, QString); Q_SLOT void handle_COM_exception (int, QString, QString, QString);
Q_SLOT void handle_visible_change (); Q_SLOT void handle_visible_change ();
Q_SLOT void handle_rig_type_change (int rig_number); 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_params_change (int rig_number, int params);
Q_SLOT void handle_custom_reply (int, QVariant const& command, QVariant const& reply); Q_SLOT void handle_custom_reply (int, QVariant const& command, QVariant const& reply);
void init_rig ();
static MODE map_mode (OmniRig::RigParamX param); static MODE map_mode (OmniRig::RigParamX param);
static OmniRig::RigParamX map_mode (MODE mode); static OmniRig::RigParamX map_mode (MODE mode);
std::unique_ptr<TransceiverBase> wrapped_; std::unique_ptr<TransceiverBase> wrapped_; // may be null
bool use_for_ptt_; bool use_for_ptt_;
TransceiverFactory::PTTMethod ptt_type_; TransceiverFactory::PTTMethod ptt_type_;
QScopedPointer<OmniRig::OmniRigX> omni_rig_; QScopedPointer<OmniRig::OmniRigX> omni_rig_;

View File

@ -13,8 +13,9 @@ namespace
unsigned const polls_to_stabilize {3}; unsigned const polls_to_stabilize {3};
} }
PollingTransceiver::PollingTransceiver (int poll_interval) PollingTransceiver::PollingTransceiver (int poll_interval, QObject * parent)
: interval_ {poll_interval * 1000} : TransceiverBase {parent}
, interval_ {poll_interval * 1000}
, poll_timer_ {nullptr} , poll_timer_ {nullptr}
, retries_ {0} , retries_ {0}
{ {
@ -26,12 +27,19 @@ void PollingTransceiver::start_timer ()
{ {
if (!poll_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_); poll_timer_->start (interval_);
} }
else
{
stop_timer ();
}
} }
void PollingTransceiver::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) 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 // we don't ever expect mode to goto to unknown
if (m != UNK && next_state_.mode () != m) 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 // update expected state with new PTT and set poll count
next_state_.ptt (p); 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; 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 if (!no_poll) poll (); // tell sub-classes to update our state
// state
// Signal new state if it is directly requested or, what we expected // Signal new state if it is directly requested or, what we expected
// or, hasn't become what we expected after polls_to_stabilize // 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_) else if (force_signal || state () != last_signalled_state_)
{ {
// here is the normal passive polling path // here is the normal passive polling path either our client has
// either our client has requested a state update regardless of change // requested a state update regardless of change or state has
// or sate has changed asynchronously // changed asynchronously
force_signal = true; force_signal = true;
} }
@ -154,7 +162,7 @@ void PollingTransceiver::do_sync (bool force_signal)
retries_ = 0; retries_ = 0;
next_state_ = state (); next_state_ = state ();
last_signalled_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 // inform our parent of the failure via the offline() message
try try
{ {
do_sync (false); do_sync ();
} }
catch (std::exception const& e) catch (std::exception const& e)
{ {

View File

@ -35,10 +35,11 @@ class PollingTransceiver
Q_OBJECT; // for translation context Q_OBJECT; // for translation context
protected: protected:
explicit PollingTransceiver (int poll_interval); // in seconds explicit PollingTransceiver (int poll_interval, // in seconds
QObject * parent);
protected: 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 // Sub-classes implement this and fetch what they can from the rig
// in a non-intrusive manner. // in a non-intrusive manner.
@ -46,9 +47,9 @@ protected:
void do_post_start () override final; void do_post_start () override final;
void do_post_stop () override final; void do_post_stop () override final;
void do_post_frequency (Frequency, MODE = UNK) override final; void do_post_frequency (Frequency, MODE) override final;
void do_post_tx_frequency (Frequency, bool rationalize = true) override final; void do_post_tx_frequency (Frequency) override final;
void do_post_mode (MODE, bool rationalize = true) override final; void do_post_mode (MODE) override final;
void do_post_ptt (bool = true) override final; void do_post_ptt (bool = true) override final;
bool do_pre_update () override final; bool do_pre_update () override final;

View File

@ -10,8 +10,8 @@ QDebug operator << (QDebug d, Transceiver::TransceiverState const& s)
{ {
d.nospace () d.nospace ()
<< "Transceiver::TransceiverState(online: " << (s.online_ ? "yes" : "no") << "Transceiver::TransceiverState(online: " << (s.online_ ? "yes" : "no")
<< " Frequency {" << s.frequency_[0] << "Hz, " << s.frequency_[1] << "Hz} " << s.mode_ << " Frequency {" << s.rx_frequency_ << "Hz, " << s.tx_frequency_ << "Hz} " << s.mode_
<< "; SPLIT: " << (Transceiver::TransceiverState::on == s.split_ ? "on" : Transceiver::TransceiverState::off == s.split_ ? "off" : "unknown") << "; SPLIT: " << (Transceiver::TransceiverState::Split::on == s.split_ ? "on" : Transceiver::TransceiverState::Split::off == s.split_ ? "off" : "unknown")
<< "; PTT: " << (s.ptt_ ? "on" : "off") << "; PTT: " << (s.ptt_ ? "on" : "off")
<< ')'; << ')';
return d.space (); return d.space ();
@ -26,8 +26,8 @@ ENUM_CONVERSION_OPS_IMPL (Transceiver, MODE);
bool operator != (Transceiver::TransceiverState const& lhs, Transceiver::TransceiverState const& rhs) bool operator != (Transceiver::TransceiverState const& lhs, Transceiver::TransceiverState const& rhs)
{ {
return lhs.online_ != rhs.online_ return lhs.online_ != rhs.online_
|| lhs.frequency_[0] != rhs.frequency_[0] || lhs.rx_frequency_ != rhs.rx_frequency_
|| lhs.frequency_[1] != rhs.frequency_[1] || lhs.tx_frequency_ != rhs.tx_frequency_
|| lhs.mode_ != rhs.mode_ || lhs.mode_ != rhs.mode_
|| lhs.split_ != rhs.split_ || lhs.split_ != rhs.split_
|| lhs.ptt_ != rhs.ptt_; || lhs.ptt_ != rhs.ptt_;

View File

@ -16,9 +16,9 @@ class QString;
// //
// Responsibilities // Responsibilities
// //
// Provides Qt slots to set the frequency, mode and PTT of some // Provides a Qt slot to set the frequency, mode and PTT of some
// transceiver. They are Qt slots so that they may be invoked across // transceiver. This is a Qt slot so that it may be invoked across a
// a thread boundary. // thread boundary.
// //
// Provides a synchronisation Qt slot which should be implemented in // Provides a synchronisation Qt slot which should be implemented in
// sub-classes in such a way that normal operation of the rig is not // sub-classes in such a way that normal operation of the rig is not
@ -58,14 +58,10 @@ public:
using Frequency = Radio::Frequency; using Frequency = Radio::Frequency;
protected: protected:
Transceiver () Transceiver (QObject * parent) : QObject {parent} {}
{
}
public: public:
virtual ~Transceiver () virtual ~Transceiver () {}
{
}
enum MODE {UNK, CW, CW_R, USB, LSB, FSK, FSK_R, DIG_U, DIG_L, AM, FM, DIG_FM}; enum MODE {UNK, CW, CW_R, USB, LSB, FSK, FSK_R, DIG_U, DIG_L, AM, FM, DIG_FM};
Q_ENUM (MODE) Q_ENUM (MODE)
@ -79,33 +75,34 @@ public:
public: public:
TransceiverState () TransceiverState ()
: online_ {false} : online_ {false}
, frequency_ {0, 0} , rx_frequency_ {0}
, tx_frequency_ {0}
, mode_ {UNK} , mode_ {UNK}
, split_ {unknown} , split_ {Split::unknown}
, ptt_ {false} , ptt_ {false}
{ {
} }
bool online () const {return online_;} bool online () const {return online_;}
Frequency frequency () const {return frequency_[0];} Frequency frequency () const {return rx_frequency_;}
Frequency tx_frequency () const {return frequency_[1];} Frequency tx_frequency () const {return tx_frequency_;}
bool split () const {return on == split_;} bool split () const {return Split::on == split_;}
bool compare_split (bool with) const {return split_ == (with ? on : off);}
MODE mode () const {return mode_;} MODE mode () const {return mode_;}
bool ptt () const {return ptt_;} bool ptt () const {return ptt_;}
void online (bool state) {online_ = state;} void online (bool state) {online_ = state;}
void frequency (Frequency f) {frequency_[0] = f;} void frequency (Frequency f) {rx_frequency_ = f;}
void tx_frequency (Frequency f) {frequency_[1] = f;} void tx_frequency (Frequency f) {tx_frequency_ = f;}
void split (bool state) {split_ = state ? on : off;} void split (bool state) {split_ = state ? Split::on : Split::off;}
void mode (MODE m) {mode_ = m;} void mode (MODE m) {mode_ = m;}
void ptt (bool state) {ptt_ = state;} void ptt (bool state) {ptt_ = state;}
private: private:
bool online_; bool online_;
Frequency frequency_[2]; // [0] -> Rx; [1] -> Other Frequency rx_frequency_;
Frequency tx_frequency_; // 0 means use Rx
MODE mode_; MODE mode_;
enum {unknown, off, on} split_; enum class Split {unknown, off, on} split_;
bool ptt_; bool ptt_;
// Don't forget to update the debug print and != operator if you // Don't forget to update the debug print and != operator if you
// add more members here // add more members here
@ -115,39 +112,42 @@ public:
}; };
// //
// The following slots and signals are expected to all run in 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 // same thread which is not necessarily the main GUI thread. It is
// up to the client of the Transceiver class to organise the // up to the client of the Transceiver class to organise the
// allocation to a thread and the lifetime of the object instances. // 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. // Connect and disconnect.
Q_SLOT virtual void start () noexcept = 0; Q_SLOT virtual void start (unsigned sequence_number) noexcept = 0;
Q_SLOT virtual void stop (bool reset_split = false) 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 // 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. // Ready to be destroyed.
Q_SIGNAL void finished () const; Q_SIGNAL void finished () const;

View File

@ -14,29 +14,15 @@ namespace
auto const unexpected = TransceiverBase::tr ("Unexpected rig error"); auto const unexpected = TransceiverBase::tr ("Unexpected rig error");
} }
void TransceiverBase::start () noexcept void TransceiverBase::start (unsigned sequence_number) noexcept
{ {
QString message; QString message;
try try
{ {
if (state_.online ()) may_update u {this, true};
{ shutdown ();
try startup ();
{ last_sequence_number_ = sequence_number;
// 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);
} }
catch (std::exception const& e) 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; QString message;
try try
{ {
if (state_.online ()) shutdown ();
{
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);
} }
catch (std::exception const& e) 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) 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) void TransceiverBase::update_other_frequency (Frequency tx)
{ {
state_.tx_frequency (tx); actual_.tx_frequency (tx);
} }
void TransceiverBase::update_split (bool state) void TransceiverBase::update_split (bool state)
{ {
state_.split (state); actual_.split (state);
} }
void TransceiverBase::update_mode (MODE m) void TransceiverBase::update_mode (MODE m)
{ {
state_.mode (m); actual_.mode (m);
} }
void TransceiverBase::update_PTT (bool state) void TransceiverBase::update_PTT (bool state)
{ {
auto prior = state_.ptt (); actual_.ptt (state);
state_.ptt (state);
if (state != prior)
{
// always signal PTT changes because some MainWindow logic
// depends on it
update_complete ();
}
} }
void TransceiverBase::updated () bool TransceiverBase::maybe_low_resolution (Radio::Frequency low_res,
{ Radio::Frequency high_res)
if (do_pre_update ())
{
Q_EMIT update (state_);
}
}
void TransceiverBase::update_complete ()
{ {
// Use a timer to ensure that the calling function completes before if (resolution_ != Resolution::truncate
// the Transceiver::update signal is triggered. && low_res == (high_res + 5) / 10 * 10) // rounded to 10's
QTimer::singleShot (0, this, SLOT (updated ())); {
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) void TransceiverBase::offline (QString const& reason)
{ {
QString message; Q_EMIT failure (reason);
try try
{ {
if (state_.online ()) shutdown ();
{
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 ();
} }
catch (...) catch (...)
{ {
message = unexpected; // don't care
} }
Q_EMIT failure (reason + '\n' + message);
} }

View File

@ -60,20 +60,29 @@ class TransceiverBase
{ {
Q_OBJECT; Q_OBJECT;
private:
enum class Resolution {accurate, round, truncate};
protected: protected:
TransceiverBase () = default; TransceiverBase (QObject * parent)
: Transceiver {parent}
, resolution_ {Resolution::accurate}
, last_sequence_number_ {0}
{}
public: public:
// //
// Implement the Transceiver abstract interface. // Implement the Transceiver abstract interface.
// //
void start () noexcept override final; void start (unsigned sequence_number) noexcept override final;
void stop (bool reset_split = false) noexcept override final; void set (TransceiverState const&,
void frequency (Frequency rx, MODE = UNK) noexcept override final; unsigned sequence_number) noexcept override final;
void tx_frequency (Frequency tx, bool rationalise_mode) noexcept override final; void stop () 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; // Query operations
//
TransceiverState const& state () const {return actual_;}
protected: protected:
// //
@ -82,32 +91,32 @@ protected:
struct error struct error
: public std::runtime_error : public std::runtime_error
{ {
error (char const * const msg) : std::runtime_error (msg) {} explicit error (char const * const msg) : std::runtime_error (msg) {}
error (QString const& msg) : std::runtime_error (msg.toStdString ()) {} explicit error (QString const& msg) : std::runtime_error (msg.toStdString ()) {}
}; };
// Template methods that sub classes implement to do what they need to do. // Template methods that sub classes implement to do what they need to do.
// //
// These methods may throw exceptions to signal errors. // 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_post_start () {}
virtual void do_stop () = 0; virtual void do_stop () = 0;
virtual void do_post_stop () {} virtual void do_post_stop () {}
virtual void do_frequency (Frequency rx, MODE = UNK) = 0; virtual void do_frequency (Frequency, MODE, bool no_ignore) = 0;
virtual void do_post_frequency (Frequency, MODE = UNK) {} virtual void do_post_frequency (Frequency, MODE) {}
virtual void do_tx_frequency (Frequency tx = 0u, bool rationalise_mode = false) = 0; virtual void do_tx_frequency (Frequency, bool no_ignore) = 0;
virtual void do_post_tx_frequency (Frequency = 0u, bool /* rationalise_mode */ = false) {} virtual void do_post_tx_frequency (Frequency) {}
virtual void do_mode (MODE, bool rationalise = true) = 0; virtual void do_mode (MODE) = 0;
virtual void do_post_mode (MODE, bool /* rationalise */ = true) {} virtual void do_post_mode (MODE) {}
virtual void do_ptt (bool = true) = 0; virtual void do_ptt (bool = true) = 0;
virtual void do_post_ptt (bool = true) {} 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;} virtual bool do_pre_update () {return true;}
@ -119,31 +128,48 @@ protected:
void update_PTT (bool = true); void update_PTT (bool = true);
// Calling this eventually triggers the Transceiver::update(State) signal. // 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 // sub class may asynchronously take the rig offline by calling this
void offline (QString const& reason); void offline (QString const& reason);
// and query state with this one
TransceiverState const& state () const {return state_;}
private: 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 // some trace macros
#if WSJT_TRACE_CAT #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 #else
#define TRACE_CAT(MSG) #define TRACE_CAT(FAC, MSG)
#endif #endif
#if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS #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 #else
#define TRACE_CAT_POLL(MSG) #define TRACE_CAT_POLL(FAC, MSG)
#endif #endif
#endif #endif

View File

@ -82,36 +82,44 @@ std::unique_ptr<Transceiver> TransceiverFactory::create (ParameterPack const& pa
{ {
case CommanderId: case CommanderId:
{ {
// we start with a dummy HamlibTransceiver object instance that can support direct PTT std::unique_ptr<TransceiverBase> basic_transceiver;
std::unique_ptr<TransceiverBase> basic_transceiver {new HamlibTransceiver {params.ptt_type, params.ptt_port}}; if (PTT_method_CAT != params.ptt_type)
if (target_thread)
{ {
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 // 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}); result.reset (new DXLabSuiteCommanderTransceiver {std::move (basic_transceiver), params.network_port, PTT_method_CAT == params.ptt_type, params.poll_interval});
if (target_thread) if (target_thread)
{ {
result.get ()->moveToThread (target_thread); result->moveToThread (target_thread);
} }
} }
break; break;
case HRDId: case HRDId:
{ {
// we start with a dummy HamlibTransceiver object instance that can support direct PTT std::unique_ptr<TransceiverBase> basic_transceiver;
std::unique_ptr<TransceiverBase> basic_transceiver {new HamlibTransceiver {params.ptt_type, params.ptt_port}}; if (PTT_method_CAT != params.ptt_type)
if (target_thread)
{ {
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 // 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) if (target_thread)
{ {
result.get ()->moveToThread (target_thread); result->moveToThread (target_thread);
} }
} }
break; break;
@ -119,36 +127,44 @@ std::unique_ptr<Transceiver> TransceiverFactory::create (ParameterPack const& pa
#if defined (WIN32) #if defined (WIN32)
case OmniRigOneId: case OmniRigOneId:
{ {
// we start with a dummy HamlibTransceiver object instance that can support direct PTT std::unique_ptr<TransceiverBase> basic_transceiver;
std::unique_ptr<TransceiverBase> basic_transceiver {new HamlibTransceiver {params.ptt_type, params.ptt_port}}; if (PTT_method_CAT != params.ptt_type && "CAT" != params.ptt_port)
if (target_thread)
{ {
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 // 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}); result.reset (new OmniRigTransceiver {std::move (basic_transceiver), OmniRigTransceiver::One, params.ptt_type, params.ptt_port});
if (target_thread) if (target_thread)
{ {
result.get ()->moveToThread (target_thread); result->moveToThread (target_thread);
} }
} }
break; break;
case OmniRigTwoId: case OmniRigTwoId:
{ {
// we start with a dummy HamlibTransceiver object instance that can support direct PTT std::unique_ptr<TransceiverBase> basic_transceiver;
std::unique_ptr<TransceiverBase> basic_transceiver {new HamlibTransceiver {params.ptt_type, params.ptt_port}}; if (PTT_method_CAT != params.ptt_type && "CAT" != params.ptt_port)
if (target_thread)
{ {
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 // 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}); result.reset (new OmniRigTransceiver {std::move (basic_transceiver), OmniRigTransceiver::Two, params.ptt_type, params.ptt_port});
if (target_thread) if (target_thread)
{ {
result.get ()->moveToThread (target_thread); result->moveToThread (target_thread);
} }
} }
break; break;
@ -158,7 +174,7 @@ std::unique_ptr<Transceiver> TransceiverFactory::create (ParameterPack const& pa
result.reset (new HamlibTransceiver {supported_transceivers ()[params.rig_name].model_number_, params}); result.reset (new HamlibTransceiver {supported_transceivers ()[params.rig_name].model_number_, params});
if (target_thread) if (target_thread)
{ {
result.get ()->moveToThread (target_thread); result->moveToThread (target_thread);
} }
break; break;
} }
@ -169,7 +185,7 @@ std::unique_ptr<Transceiver> TransceiverFactory::create (ParameterPack const& pa
result.reset (new EmulateSplitTransceiver {std::move (result)}); result.reset (new EmulateSplitTransceiver {std::move (result)});
if (target_thread) if (target_thread)
{ {
result.get ()->moveToThread (target_thread); result->moveToThread (target_thread);
} }
} }

View File

@ -104,17 +104,18 @@ public:
StopBits stop_bits; StopBits stop_bits;
Handshake handshake; Handshake handshake;
bool force_dtr; bool force_dtr;
bool dtr_high; // to power interface bool dtr_high; // to power interface
bool force_rts; bool force_rts;
bool rts_high; // to power interface bool rts_high; // to power interface
PTTMethod ptt_type; // "CAT" | "DTR" | "RTS" | "VOX" PTTMethod ptt_type; // "CAT" | "DTR" | "RTS" | "VOX"
TXAudioSource audio_source; // some rigs allow audio routing TXAudioSource audio_source; // some rigs allow audio routing
// to Mic/Data connector // to Mic/Data connector
SplitMode split_mode; // how to support split TX mode bool set_rig_mode; // handle rig mode when required
QString ptt_port; // serial port device name or special SplitMode split_mode; // how to support split TX mode
// value "CAT" QString ptt_port; // serial port device name or special
int poll_interval; // in seconds for interfaces that // value "CAT"
// require polling for state changes int poll_interval; // in seconds for interfaces that
// require polling for state changes
bool operator == (ParameterPack const& rhs) const bool operator == (ParameterPack const& rhs) const
{ {
@ -132,9 +133,11 @@ public:
&& rhs.rts_high == rts_high && rhs.rts_high == rts_high
&& rhs.ptt_type == ptt_type && rhs.ptt_type == ptt_type
&& rhs.audio_source == audio_source && rhs.audio_source == audio_source
&& rhs.set_rig_mode == set_rig_mode
&& rhs.split_mode == split_mode && rhs.split_mode == split_mode
&& rhs.ptt_port == ptt_port && rhs.ptt_port == ptt_port
&& rhs.poll_interval == poll_interval; && rhs.poll_interval == poll_interval
;
} }
}; };

190
astro.cpp
View File

@ -20,30 +20,37 @@
#include "ui_astro.h" #include "ui_astro.h"
#include "moc_astro.cpp" #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) 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} , settings_ {settings}
, configuration_ {configuration} , configuration_ {configuration}
, ui_ {new Ui::Astro} , ui_ {new Ui::Astro}
, m_bRxAudioTrack {false}
, m_bTxAudioTrack {false}
, m_bTrackVFO {true}
, m_DopplerMethod {0} , m_DopplerMethod {0}
, m_kHz {0}
, m_Hz {0}
, m_stepHz {1}
{ {
ui_->setupUi (this); ui_->setupUi (this);
setWindowTitle (QApplication::applicationName () + " - " + tr ("Astronomical Data")); setWindowTitle (QApplication::applicationName () + " - " + tr ("Astronomical Data"));
setStyleSheet ("QWidget {background: white;}"); setStyleSheet ("QWidget {background: white;}");
connect (ui_->cbDopplerTracking, &QAbstractButton::toggled, ui_->doppler_widget, &QWidget::setVisible); connect (ui_->cbDopplerTracking, &QAbstractButton::toggled, ui_->doppler_widget, &QWidget::setVisible);
connect (ui_->cbDopplerTracking, &QAbstractButton::toggled, this, &Astro::doppler_tracking_toggled);
read_settings (); read_settings ();
ui_->text_label->clear (); ui_->text_label->clear ();
} }
Astro::~Astro () Astro::~Astro ()
{ {
ui_->cbDopplerTracking->setChecked (false);
Q_EMIT tracking_update ();
if (isVisible ()) write_settings (); if (isVisible ()) write_settings ();
} }
@ -56,40 +63,30 @@ void Astro::closeEvent (QCloseEvent * e)
void Astro::read_settings () void Astro::read_settings ()
{ {
SettingsGroup g (settings_, "Astro"); SettingsGroup g (settings_, "Astro");
ui_->cbDopplerTracking->setChecked (settings_->value ("DopplerTracking",false).toBool ());
ui_->doppler_widget->setVisible (ui_->cbDopplerTracking->isChecked ()); ui_->doppler_widget->setVisible (ui_->cbDopplerTracking->isChecked ());
m_DopplerMethod=settings_->value("DopplerMethod",0).toInt(); m_DopplerMethod=settings_->value("DopplerMethod",0).toInt();
if(m_DopplerMethod==0) ui_->rbNoDoppler->setChecked(true); switch (m_DopplerMethod)
if(m_DopplerMethod==1) ui_->rbFullTrack->setChecked(true); {
if(m_DopplerMethod==2) ui_->rbConstFreqOnMoon->setChecked(true); case 0: ui_->rbNoDoppler->setChecked (true); break;
m_stepHz=settings_->value("StepHz",1).toInt(); case 1: ui_->rbFullTrack->setChecked (true); break;
if(m_stepHz==1) ui_->rb1Hz->setChecked(true); case 2: ui_->rbConstFreqOnMoon->setChecked (true); break;
if(m_stepHz==10) ui_->rb10Hz->setChecked(true); case 3: ui_->rbRxOnly->setChecked (true); break;
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);
move (settings_->value ("window/pos", pos ()).toPoint ()); move (settings_->value ("window/pos", pos ()).toPoint ());
} }
void Astro::write_settings () void Astro::write_settings ()
{ {
SettingsGroup g (settings_, "Astro"); SettingsGroup g (settings_, "Astro");
settings_->setValue ("DopplerTracking", ui_->cbDopplerTracking->isChecked ()); //settings_->setValue ("DopplerTracking", ui_->cbDopplerTracking->isChecked ());
settings_->setValue ("DopplerMethod",m_DopplerMethod); 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 ()); settings_->setValue ("window/pos", pos ());
} }
auto Astro::astroUpdate(QDateTime const& t, QString const& mygrid, QString const& hisgrid, Frequency freq, 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 azsun,elsun,azmoon,elmoon,azmoondx,elmoondx;
double ramoon,decmoon,dgrd,poloffset,xnr,techo,width1,width2; double ramoon,decmoon,dgrd,poloffset,xnr,techo,width1,width2;
int ntsky; int ntsky;
@ -148,94 +145,125 @@ auto Astro::astroUpdate(QDateTime const& t, QString const& mygrid, QString const
} }
ui_->text_label->setText(message); ui_->text_label->setText(message);
FrequencyDelta astro_correction {0}; Correction correction;
if (ui_->cbDopplerTracking->isChecked ()) { if (ui_->cbDopplerTracking->isChecked ()) {
switch (m_DopplerMethod) switch (m_DopplerMethod)
{ {
case 1: case 1: // All Doppler correction done here; DX station stays at nominal dial frequency.
// 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
if(dx_is_self) { correction.rx = dx_is_self ? ndop00 : ndop;
astro_correction = m_stepHz*qRound(double(ndop00)/m_stepHz);
} else {
astro_correction = m_stepHz*qRound(double(ndop)/m_stepHz);
}
break; break;
case 2: case 2:
// Doppler correction to constant frequency on Moon // Doppler correction to constant frequency on Moon
astro_correction = m_stepHz*qRound(double(ndop00/2.0)/m_stepHz); correction.rx = ndop00 / 2;
break; break;
} }
if (bTx) { if (3 != m_DopplerMethod) correction.tx = -correction.rx;
astro_correction = 1000 * m_kHz + m_Hz - astro_correction; if(dx_is_self && m_DopplerMethod == 1) correction.rx = 0;
} else {
if(dx_is_self && m_DopplerMethod==1) { if (no_tx_QSY && 3 != m_DopplerMethod && 0 != m_DopplerMethod)
astro_correction = 1000*m_kHz + m_Hz; {
} else { // calculate a single correction for transmit half way through
astro_correction += 1000*m_kHz + m_Hz; // 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() void Astro::on_rbFullTrack_clicked()
{ {
m_DopplerMethod = 1; 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() void Astro::on_rbConstFreqOnMoon_clicked()
{ {
m_DopplerMethod = 2; m_DopplerMethod = 2;
check_split ();
Q_EMIT tracking_update ();
} }
void Astro::on_rbNoDoppler_clicked() void Astro::on_rbNoDoppler_clicked()
{ {
m_DopplerMethod = 0; m_DopplerMethod = 0;
} Q_EMIT tracking_update ();
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;
} }
bool Astro::doppler_tracking () const 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);
} }

71
astro.h
View File

@ -2,7 +2,8 @@
#ifndef ASTRO_H #ifndef ASTRO_H
#define ASTRO_H #define ASTRO_H
#include <QWidget> #include <QDialog>
#include "Radio.hpp" #include "Radio.hpp"
class QSettings; class QSettings;
@ -12,63 +13,75 @@ namespace Ui {
} }
class Astro final class Astro final
: public QWidget : public QDialog
{ {
Q_OBJECT; Q_OBJECT;
public:
using Frequency = Radio::Frequency; using Frequency = Radio::Frequency;
using FrequencyDelta = Radio::FrequencyDelta; using FrequencyDelta = Radio::FrequencyDelta;
public:
explicit Astro(QSettings * settings, Configuration const *, QWidget * parent = nullptr); explicit Astro(QSettings * settings, Configuration const *, QWidget * parent = nullptr);
~Astro (); ~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 doppler_tracking () const;
bool trackVFO(); Q_SLOT void nominal_frequency (Frequency rx, Frequency tx);
Q_SIGNAL void doppler_tracking_toggled (bool); Q_SIGNAL void tracking_update () const;
protected: protected:
void hideEvent (QHideEvent *) override;
void closeEvent (QCloseEvent *) override; void closeEvent (QCloseEvent *) override;
private slots: private slots:
void on_rbConstFreqOnMoon_clicked(); void on_rbConstFreqOnMoon_clicked();
void on_rbFullTrack_clicked(); void on_rbFullTrack_clicked();
void on_rbRxOnly_clicked();
void on_rbNoDoppler_clicked(); void on_rbNoDoppler_clicked();
void on_rb1Hz_clicked(); void on_cbDopplerTracking_toggled(bool);
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);
private: private:
void read_settings (); void read_settings ();
void write_settings (); void write_settings ();
void check_split ();
QSettings * settings_; QSettings * settings_;
Configuration const * configuration_; Configuration const * configuration_;
Ui::Astro * ui_; Ui::Astro * ui_;
bool m_bRxAudioTrack;
bool m_bTxAudioTrack;
bool m_bTrackVFO;
qint32 m_DopplerMethod; qint32 m_DopplerMethod;
qint32 m_kHz;
qint32 m_Hz;
qint32 m_stepHz;
}; };
extern "C" { inline
void astrosub_(int* nyear, int* month, int* nday, double* uth, double* freqMoon, bool operator == (Astro::Correction const& lhs, Astro::Correction const& rhs)
const char* mygrid, const char* hisgrid, double* azsun, {
double* elsun, double* azmoon, double* elmoon, double* azmoondx, return lhs.rx == rhs.rx && lhs.tx == rhs.tx;
double* elmoondx, int* ntsky, int* ndop, int* ndop00, }
double* ramoon, double* decmoon, double* dgrd, double* poloffset,
double* xnr, double* techo, double* width1, double* width2, inline
bool* bTx, const char* AzElFileName, const char* jpleph, bool operator != (Astro::Correction const& lhs, Astro::Correction const& rhs)
int len1, int len2, int len3, int len4); {
return !(lhs == rhs);
} }
#endif // ASTRO_H #endif // ASTRO_H

489
astro.ui
View File

@ -1,243 +1,246 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"> <ui version="4.0">
<class>Astro</class> <class>Astro</class>
<widget class="QWidget" name="Astro"> <widget class="QWidget" name="Astro">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>440</width> <width>359</width>
<height>406</height> <height>287</height>
</rect> </rect>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <property name="sizePolicy">
<property name="sizeConstraint"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<enum>QLayout::SetFixedSize</enum> <horstretch>0</horstretch>
</property> <verstretch>0</verstretch>
<item row="0" column="1"> </sizepolicy>
<widget class="QWidget" name="doppler_widget" native="true"> </property>
<property name="styleSheet"> <layout class="QGridLayout" name="gridLayout">
<string notr="true">* { <property name="sizeConstraint">
font-weight: normal; <enum>QLayout::SetFixedSize</enum>
}</string> </property>
</property> <item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout"> <widget class="QWidget" name="doppler_widget" native="true">
<item> <property name="styleSheet">
<widget class="QGroupBox" name="groupBox_4"> <string notr="true">* {
<property name="title"> font-weight: normal;
<string>Frequency above nominal band edge</string> }</string>
</property> </property>
<layout class="QHBoxLayout" name="horizontalLayout_3"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="QSpinBox" name="kHzSpinBox"> <widget class="QGroupBox" name="groupBox">
<property name="alignment"> <property name="title">
<set>Qt::AlignCenter</set> <string>Doppler tracking</string>
</property> </property>
<property name="suffix"> <layout class="QVBoxLayout" name="verticalLayout_2">
<string> kHz</string> <item>
</property> <widget class="QRadioButton" name="rbFullTrack">
<property name="maximum"> <property name="toolTip">
<number>999</number> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;One station does all Doppler shift correction, their QSO partner receives and transmits on the sked frequency.&lt;/p&gt;&lt;p&gt;If the rig does not accept CAT QSY commands while transmitting a single correction is applied for the whole transmit period.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="value"> <property name="text">
<number>200</number> <string>Full Doppler to DX Grid</string>
</property> </property>
</widget> <property name="checked">
</item> <bool>true</bool>
<item> </property>
<widget class="QSpinBox" name="HzSpinBox"> </widget>
<property name="alignment"> </item>
<set>Qt::AlignCenter</set> <item>
</property> <widget class="QRadioButton" name="rbRxOnly">
<property name="suffix"> <property name="toolTip">
<string> Hz</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Both stations do full correction to their QSO partner's grid square during receive, no correction is applied on transmit.&lt;/p&gt;&lt;p&gt;This mode facilitates accurate Doppler shift correction when one or both stations have a rig that does not accept CAT QSY commands while transmitting.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="minimum"> <property name="text">
<number>-2000</number> <string>Receive only</string>
</property> </property>
<property name="maximum"> </widget>
<number>2000</number> </item>
</property> <item>
<property name="singleStep"> <widget class="QRadioButton" name="rbConstFreqOnMoon">
<number>100</number> <property name="toolTip">
</property> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Both stations correct for Doppler shift such that they would be heard on the moon at the sked frequency.&lt;/p&gt;&lt;p&gt;If the rig does not accept CAT QSY commands while transmitting a single correction is applied for the whole transmit period.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</widget> </property>
</item> <property name="text">
</layout> <string>Constant frequency on Moon</string>
</widget> </property>
</item> <property name="checked">
<item> <bool>false</bool>
<widget class="QGroupBox" name="groupBox"> </property>
<property name="title"> </widget>
<string>Doppler tracking</string> </item>
</property> <item>
<layout class="QVBoxLayout" name="verticalLayout_2"> <widget class="QRadioButton" name="rbNoDoppler">
<item> <property name="toolTip">
<widget class="QRadioButton" name="rbFullTrack"> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;No Doppler shift correction is applied. This may be used when the QSO partner does full Doppler correction to your grid square.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<property name="text"> </property>
<string>Full Doppler to DX Grid</string> <property name="text">
</property> <string>None</string>
<property name="checked"> </property>
<bool>true</bool> <property name="checked">
</property> <bool>false</bool>
</widget> </property>
</item> </widget>
<item> </item>
<widget class="QRadioButton" name="rbConstFreqOnMoon"> </layout>
<property name="text"> </widget>
<string>Constant frequency on Moon</string> </item>
</property> <item>
<property name="checked"> <widget class="QGroupBox" name="groupBox_3">
<bool>false</bool> <property name="enabled">
</property> <bool>true</bool>
</widget> </property>
</item> <property name="title">
<item> <string>Sked frequency</string>
<widget class="QRadioButton" name="rbNoDoppler"> </property>
<property name="text"> <layout class="QGridLayout" name="gridLayout_2" columnstretch="0,1">
<string>None</string> <item row="1" column="1">
</property> <widget class="QLabel" name="sked_tx_frequency_label">
<property name="checked"> <property name="styleSheet">
<bool>false</bool> <string notr="true">* {
</property> font-family: Courier;
</widget> font-size: 12pt;
</item> font-weight: bold;
</layout> }</string>
</widget> </property>
</item> <property name="text">
<item> <string>0</string>
<widget class="QGroupBox" name="groupBox_2"> </property>
<property name="title"> <property name="alignment">
<string>Transceiver step size</string> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_4"> </widget>
<item> </item>
<widget class="QRadioButton" name="rb1Hz"> <item row="0" column="1">
<property name="text"> <widget class="QLabel" name="sked_frequency_label">
<string>1 Hz</string> <property name="styleSheet">
</property> <string notr="true">* {
<property name="checked"> font-family: Courier;
<bool>true</bool> font-size: 12pt;
</property> font-weight: bold;
</widget> }</string>
</item> </property>
<item> <property name="text">
<widget class="QRadioButton" name="rb10Hz"> <string>0</string>
<property name="text"> </property>
<string>10 Hz</string> <property name="alignment">
</property> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</widget> </property>
</item> </widget>
<item> </item>
<widget class="QRadioButton" name="rb100Hz"> <item row="0" column="0">
<property name="text"> <widget class="QLabel" name="label">
<string>100 Hz</string> <property name="styleSheet">
</property> <string notr="true">* {
</widget> font-family: Courier;
</item> font-size: 12pt;
</layout> font-weight: bold;
</widget> }</string>
</item> </property>
<item> <property name="text">
<widget class="QGroupBox" name="groupBox_3"> <string>Rx:</string>
<property name="enabled"> </property>
<bool>true</bool> </widget>
</property> </item>
<property name="title"> <item row="1" column="0">
<string>Enable</string> <widget class="QLabel" name="label_2">
</property> <property name="styleSheet">
<layout class="QVBoxLayout" name="verticalLayout_5"> <string notr="true">* {
<item> font-family: Courier;
<widget class="QCheckBox" name="cbTrackVFO"> font-size: 12pt;
<property name="enabled"> font-weight: bold;
<bool>true</bool> }</string>
</property> </property>
<property name="text"> <property name="text">
<string>Track VFOs</string> <string>Tx:</string>
</property> </property>
<property name="checked"> </widget>
<bool>true</bool> </item>
</property> <item row="2" column="0" colspan="2">
</widget> <widget class="QLabel" name="label_3">
</item> <property name="text">
<item> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<widget class="QCheckBox" name="cbTxAudioTrack"> </property>
<property name="enabled"> <property name="textFormat">
<bool>false</bool> <enum>Qt::AutoText</enum>
</property> </property>
<property name="text"> <property name="alignment">
<string>Track Tx audio</string> <set>Qt::AlignCenter</set>
</property> </property>
</widget> <property name="wordWrap">
</item> <bool>true</bool>
</layout> </property>
</widget> </widget>
</item> </item>
<item> </layout>
<spacer name="verticalSpacer_2"> </widget>
<property name="orientation"> </item>
<enum>Qt::Vertical</enum> <item>
</property> <spacer name="verticalSpacer_2">
<property name="sizeHint" stdset="0"> <property name="orientation">
<size> <enum>Qt::Vertical</enum>
<width>0</width> </property>
<height>0</height> <property name="sizeHint" stdset="0">
</size> <size>
</property> <width>0</width>
</spacer> <height>0</height>
</item> </size>
</layout> </property>
</widget> </spacer>
</item> </item>
<item row="0" column="0"> </layout>
<layout class="QVBoxLayout" name="verticalLayout_3"> </widget>
<item alignment="Qt::AlignHCenter"> </item>
<widget class="QLabel" name="text_label"> <item row="0" column="0">
<property name="sizePolicy"> <layout class="QVBoxLayout" name="verticalLayout_3">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding"> <item alignment="Qt::AlignHCenter">
<horstretch>0</horstretch> <widget class="QLabel" name="text_label">
<verstretch>0</verstretch> <property name="sizePolicy">
</sizepolicy> <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
</property> <horstretch>0</horstretch>
<property name="styleSheet"> <verstretch>0</verstretch>
<string notr="true">* { </sizepolicy>
font-family: Courier; </property>
font-size: 12pt; <property name="styleSheet">
font-weight: bold; <string notr="true">* {
}</string> font-family: Courier;
</property> font-size: 12pt;
<property name="frameShadow"> font-weight: bold;
<enum>QFrame::Sunken</enum> }</string>
</property> </property>
<property name="text"> <property name="frameShadow">
<string>Astro Data</string> <enum>QFrame::Sunken</enum>
</property> </property>
<property name="alignment"> <property name="text">
<set>Qt::AlignCenter</set> <string>Astro Data</string>
</property> </property>
<property name="margin"> <property name="alignment">
<number>6</number> <set>Qt::AlignCenter</set>
</property> </property>
</widget> <property name="margin">
</item> <number>6</number>
<item> </property>
<layout class="QHBoxLayout" name="horizontalLayout"> </widget>
<item> </item>
<widget class="QCheckBox" name="cbDopplerTracking"> <item>
<property name="styleSheet"> <layout class="QHBoxLayout" name="horizontalLayout">
<string notr="true"/> <item>
</property> <widget class="QCheckBox" name="cbDopplerTracking">
<property name="text"> <property name="styleSheet">
<string>Doppler tracking</string> <string notr="true"/>
</property> </property>
</widget> <property name="text">
</item> <string>Doppler tracking</string>
</layout> </property>
</item> </widget>
</layout> </item>
</item> </layout>
</layout> </item>
</widget> </layout>
<resources/> </item>
<connections/> </layout>
</ui> </widget>
<resources/>
<connections/>
</ui>

File diff suppressed because it is too large Load Diff

View File

@ -30,6 +30,7 @@
#include "logbook/logbook.h" #include "logbook/logbook.h"
#include "decodedtext.h" #include "decodedtext.h"
#include "commons.h" #include "commons.h"
#include "astro.h"
#define NUM_JT4_SYMBOLS 206 //(72+31)*2, embedded sync #define NUM_JT4_SYMBOLS 206 //(72+31)*2, embedded sync
#define NUM_JT65_SYMBOLS 126 //63 data + 63 sync #define NUM_JT65_SYMBOLS 126 //63 data + 63 sync
@ -59,7 +60,6 @@ class FastGraph;
class WideGraph; class WideGraph;
class LogQSO; class LogQSO;
class Transceiver; class Transceiver;
class Astro;
class MessageAveraging; class MessageAveraging;
class MessageClient; class MessageClient;
class QTime; class QTime;
@ -78,6 +78,7 @@ class MainWindow : public QMainWindow
public: public:
using Frequency = Radio::Frequency; using Frequency = Radio::Frequency;
using FrequencyDelta = Radio::FrequencyDelta;
using Mode = Modes::Mode; using Mode = Modes::Mode;
// Multiple instances: call MainWindow() with *thekey // Multiple instances: call MainWindow() with *thekey
@ -103,7 +104,7 @@ public slots:
void p1ReadFromStdout(); void p1ReadFromStdout();
void p1ReadFromStderr(); void p1ReadFromStderr();
void p1Error(QProcess::ProcessError); void p1Error(QProcess::ProcessError);
void setXIT(int n); void setXIT(int n, Frequency base = 0u);
void setFreq4(int rxFreq, int txFreq); void setFreq4(int rxFreq, int txFreq);
void msgAvgDecode2(); void msgAvgDecode2();
void fastPick(int x0, int x1, int y); void fastPick(int x0, int x1, int y);
@ -199,9 +200,9 @@ private slots:
void on_cbTxLock_clicked(bool checked); void on_cbTxLock_clicked(bool checked);
void on_outAttenuation_valueChanged (int); void on_outAttenuation_valueChanged (int);
void rigOpen (); void rigOpen ();
void handle_transceiver_update (Transceiver::TransceiverState); void handle_transceiver_update (Transceiver::TransceiverState const&);
void handle_transceiver_failure (QString reason); void handle_transceiver_failure (QString const& reason);
void on_actionAstronomical_data_triggered(); void on_actionAstronomical_data_toggled (bool);
void on_actionShort_list_of_add_on_prefixes_and_suffixes_triggered(); void on_actionShort_list_of_add_on_prefixes_and_suffixes_triggered();
void getpfx(); void getpfx();
void band_changed (Frequency); void band_changed (Frequency);
@ -238,7 +239,6 @@ private slots:
void on_pbTxNext_clicked(bool b); void on_pbTxNext_clicked(bool b);
void on_actionEcho_Graph_triggered(); void on_actionEcho_Graph_triggered();
void on_actionEcho_triggered(); void on_actionEcho_triggered();
void DopplerTracking_toggled (bool);
void on_actionISCAT_triggered(); void on_actionISCAT_triggered();
void on_actionFast_Graph_triggered(); void on_actionFast_Graph_triggered();
void fast_decode_done(); void fast_decode_done();
@ -274,6 +274,8 @@ private:
Q_SIGNAL void toggleShorthand () const; Q_SIGNAL void toggleShorthand () const;
private: private:
void astroUpdate ();
QDir m_dataDir; QDir m_dataDir;
QString m_revision; QString m_revision;
bool m_multiple; bool m_multiple;
@ -298,9 +300,11 @@ private:
QScopedPointer<HelpTextWindow> m_mouseCmnds; QScopedPointer<HelpTextWindow> m_mouseCmnds;
QScopedPointer<MessageAveraging> m_msgAvgWidget; QScopedPointer<MessageAveraging> m_msgAvgWidget;
Frequency m_dialFreq; Transceiver::TransceiverState m_rigState;
Frequency m_dialFreq0; Frequency m_lastDialFreq;
Frequency m_dialFreqRxWSPR; QString m_lastBand;
Frequency m_callingFrequency;
Frequency m_dialFreqRxWSPR; // best guess at WSPR QRG
Detector * m_detector; Detector * m_detector;
SoundInput * m_soundInput; SoundInput * m_soundInput;
@ -311,9 +315,9 @@ private:
qint64 m_msErase; qint64 m_msErase;
qint64 m_secBandChanged; qint64 m_secBandChanged;
qint64 m_freqMoon; qint64 m_freqMoon;
qint64 m_freqNominal; Frequency m_freqNominal;
qint64 m_dialFreqTx; Frequency m_freqTxNominal;
qint64 m_dialFreqRx; Astro::Correction m_astroCorrection;
double m_s6; double m_s6;
double m_tRemaining; double m_tRemaining;
@ -536,7 +540,6 @@ private:
void msgtype(QString t, QLineEdit* tx); void msgtype(QString t, QLineEdit* tx);
void stub(); void stub();
void statusChanged(); void statusChanged();
void qsy(Frequency f);
bool gridOK(QString g); bool gridOK(QString g);
bool shortList(QString callsign); bool shortList(QString callsign);
void transmit (double snr = 99.); void transmit (double snr = 99.);
@ -552,7 +555,7 @@ private:
void enable_DXCC_entity (bool on); void enable_DXCC_entity (bool on);
void switch_mode (Mode); void switch_mode (Mode);
void WSPR_scheduling (); void WSPR_scheduling ();
void astroCalculations (QDateTime const&, bool adjust); void setRig ();
void WSPR_history(Frequency dialFreq, int ndecodes); void WSPR_history(Frequency dialFreq, int ndecodes);
QString WSPR_hhmm(int n); QString WSPR_hhmm(int n);
void fast_config(bool b); void fast_config(bool b);

File diff suppressed because it is too large Load Diff