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 initialize_models ();
bool open_rig ();
bool open_rig (bool force = false);
//bool set_mode ();
void close_rig ();
TransceiverFactory::ParameterPack gather_rig_data ();
@ -416,8 +416,8 @@ private:
Q_SLOT void insert_frequency ();
Q_SLOT void delete_stations ();
Q_SLOT void insert_station ();
Q_SLOT void handle_transceiver_update (TransceiverState);
Q_SLOT void handle_transceiver_failure (QString reason);
Q_SLOT void handle_transceiver_update (TransceiverState const&, unsigned sequence_number);
Q_SLOT void handle_transceiver_failure (QString const& reason);
Q_SLOT void on_pbCQmsg_clicked();
Q_SLOT void on_pbMyCall_clicked();
Q_SLOT void on_pbTxMsg_clicked();
@ -425,17 +425,14 @@ private:
Q_SLOT void on_pbNewCall_clicked();
// typenames used as arguments must match registered type names :(
Q_SIGNAL void start_transceiver () const;
Q_SIGNAL void stop_transceiver (bool reset_split) const;
Q_SIGNAL void frequency (Frequency rx, Transceiver::MODE) const;
Q_SIGNAL void tx_frequency (Frequency tx, bool rationalize_mode) const;
Q_SIGNAL void mode (Transceiver::MODE, bool rationalize) const;
Q_SIGNAL void ptt (bool) const;
Q_SIGNAL void sync (bool force_signal) const;
Q_SIGNAL void start_transceiver (unsigned seqeunce_number) const;
Q_SIGNAL void set_transceiver (Transceiver::TransceiverState const&,
unsigned sequence_number) const;
Q_SIGNAL void stop_transceiver () const;
Configuration * const self_; // back pointer to public interface
QThread transceiver_thread_;
QThread * transceiver_thread_;
TransceiverFactory transceiver_factory_;
QList<QMetaObject::Connection> rig_connections_;
@ -473,6 +470,7 @@ private:
StationList stations_;
StationList next_stations_;
FrequencyDelta current_offset_;
FrequencyDelta current_tx_offset_;
QAction * frequency_delete_action_;
QAction * frequency_insert_action_;
@ -489,24 +487,10 @@ private:
bool have_rig_;
bool rig_changed_;
TransceiverState cached_rig_state_;
int rig_resolution_; // see Transceiver::resolution signal
double frequency_calibration_intercept_;
double frequency_calibration_slope_ppm_;
// the following members are required to get the rig into split the
// first time monitor or tune or Tx occur
bool setup_split_;
Frequency required_tx_frequency_; // this is needed because DX Lab
// Suite Commander in particular
// insists on reporting out of
// date state after successful
// commands to change the rig
// state :( Zero is valid and it
// means that we don't know the Tx
// frequency rather than implying
// no split.
bool enforce_mode_and_split_;
bool reset_split_;
unsigned transceiver_command_number_;
// configuration fields that we publish
QString my_callsign_;
@ -588,6 +572,7 @@ QDir Configuration::data_dir () const {return m_->data_dir_;}
QDir Configuration::temp_dir () const {return m_->temp_dir_;}
int Configuration::exec () {return m_->exec ();}
bool Configuration::is_active () const {return m_->isVisible ();}
QAudioDeviceInfo const& Configuration::audio_input_device () const {return m_->audio_input_device_;}
AudioDevice::Channel Configuration::audio_input_channel () const {return m_->audio_input_channel_;}
@ -668,13 +653,18 @@ bool Configuration::transceiver_online (bool open_if_closed)
return m_->have_rig (open_if_closed);
}
int Configuration::transceiver_resolution () const
{
return m_->rig_resolution_;
}
void Configuration::transceiver_offline ()
{
#if WSJT_TRACE_CAT
qDebug () << "Configuration::transceiver_offline:" << m_->cached_rig_state_;
#endif
return m_->close_rig ();
m_->close_rig ();
}
void Configuration::transceiver_frequency (Frequency f)
@ -691,8 +681,6 @@ void Configuration::transceiver_tx_frequency (Frequency f)
qDebug () << "Configuration::transceiver_tx_frequency:" << f << m_->cached_rig_state_;
#endif
m_->setup_split_ = true;
m_->required_tx_frequency_ = f;
m_->transceiver_tx_frequency (f);
}
@ -720,10 +708,11 @@ void Configuration::sync_transceiver (bool force_signal, bool enforce_mode_and_s
qDebug () << "Configuration::sync_transceiver: force signal:" << force_signal << "enforce_mode_and_split:" << enforce_mode_and_split << m_->cached_rig_state_;
#endif
m_->enforce_mode_and_split_ = enforce_mode_and_split;
m_->setup_split_ = enforce_mode_and_split;
m_->required_tx_frequency_ = 0;
m_->sync_transceiver (force_signal);
if (!enforce_mode_and_split)
{
m_->transceiver_tx_frequency (0);
}
}
Configuration::impl::impl (Configuration * self, QSettings * settings, QWidget * parent)
@ -738,15 +727,14 @@ Configuration::impl::impl (Configuration * self, QSettings * settings, QWidget *
, stations_ {&bands_}
, next_stations_ {&bands_}
, current_offset_ {0}
, current_tx_offset_ {0}
, frequency_dialog_ {new FrequencyDialog {&modes_, this}}
, station_dialog_ {new StationDialog {&next_stations_, &bands_, this}}
, rig_active_ {false}
, have_rig_ {false}
, rig_changed_ {false}
// , ptt_state_ {false}
, setup_split_ {false}
, required_tx_frequency_ {0}
, enforce_mode_and_split_ {false}
, rig_resolution_ {0}
, transceiver_command_number_ {0}
, degrade_ {0.} // initialize to zero each run, not
// saved in settings
, default_audio_input_device_selected_ {false}
@ -1018,18 +1006,15 @@ Configuration::impl::impl (Configuration * self, QSettings * settings, QWidget *
enumerate_rigs ();
initialize_models ();
transceiver_thread_.start ();
transceiver_thread_ = new QThread {this};
transceiver_thread_->start ();
}
Configuration::impl::~impl ()
{
transceiver_thread_->quit ();
transceiver_thread_->wait ();
write_settings ();
close_rig ();
transceiver_thread_.quit ();
transceiver_thread_.wait ();
temp_dir_.removeRecursively (); // clean up temp files
}
@ -1092,7 +1077,6 @@ void Configuration::impl::initialize_models ()
ui_->rig_combo_box->setCurrentText (rig_params_.rig_name);
ui_->TX_mode_button_group->button (data_mode_)->setChecked (true);
ui_->split_mode_button_group->button (rig_params_.split_mode)->setChecked (true);
ui_->reset_split_check_box->setChecked (reset_split_);
ui_->CAT_serial_baud_combo_box->setCurrentText (QString::number (rig_params_.baud));
ui_->CAT_data_bits_button_group->button (rig_params_.data_bits)->setChecked (true);
ui_->CAT_stop_bits_button_group->button (rig_params_.stop_bits)->setChecked (true);
@ -1318,8 +1302,8 @@ void Configuration::impl::read_settings ()
EMEonly_ = settings_->value("EMEonly",false).toBool ();
offsetRxFreq_ = settings_->value("OffsetRx",false).toBool();
rig_params_.poll_interval = settings_->value ("Polling", 0).toInt ();
rig_params_.set_rig_mode = data_mode_ != data_mode_none;
rig_params_.split_mode = settings_->value ("SplitMode", QVariant::fromValue (TransceiverFactory::split_mode_none)).value<TransceiverFactory::SplitMode> ();
reset_split_ = settings_->value ("ResetSplitOnExit", true).toBool ();
udp_server_name_ = settings_->value ("UDPServer", "127.0.0.1").toString ();
udp_server_port_ = settings_->value ("UDPServerPort", 2237).toUInt ();
accept_udp_requests_ = settings_->value ("AcceptUDPRequests", false).toBool ();
@ -1407,7 +1391,6 @@ void Configuration::impl::write_settings ()
settings_->setValue ("TXAudioSource", QVariant::fromValue (rig_params_.audio_source));
settings_->setValue ("Polling", rig_params_.poll_interval);
settings_->setValue ("SplitMode", QVariant::fromValue (rig_params_.split_mode));
settings_->setValue ("ResetSplitOnExit", reset_split_);
settings_->setValue ("VHFUHF", enable_VHF_features_);
settings_->setValue ("Decode52", decode_at_52s_);
settings_->setValue ("SingleDecode", single_decode_);
@ -1644,6 +1627,7 @@ TransceiverFactory::ParameterPack Configuration::impl::gather_rig_data ()
result.ptt_type = static_cast<TransceiverFactory::PTTMethod> (ui_->PTT_method_button_group->checkedId ());
result.ptt_port = ui_->PTT_port_combo_box->currentText ();
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 ());
return result;
}
@ -1815,7 +1799,6 @@ void Configuration::impl::accept ()
NDxG_ = ui_->cbNDxG->isChecked ();
NN_ = ui_->cbNN->isChecked ();
EMEonly_ = ui_->cbEMEonly->isChecked ();
reset_split_ = ui_->reset_split_check_box->isChecked ();
offsetRxFreq_ = ui_->offset_Rx_freq_check_box->isChecked();
frequency_calibration_intercept_ = ui_->calibration_intercept_spin_box->value ();
@ -2003,8 +1986,6 @@ void Configuration::impl::on_CAT_poll_interval_spin_box_valueChanged (int /* val
void Configuration::impl::on_split_mode_button_group_buttonClicked (int /* id */)
{
setup_split_ = true;
required_tx_frequency_ = 0;
set_rig_invariants ();
}
@ -2016,9 +1997,9 @@ void Configuration::impl::on_test_CAT_push_button_clicked ()
}
ui_->test_CAT_push_button->setStyleSheet ({});
if (open_rig ())
if (open_rig (true))
{
Q_EMIT sync (true);
//Q_EMIT sync (true);
}
set_rig_invariants ();
@ -2218,52 +2199,58 @@ bool Configuration::impl::have_rig (bool open_if_closed)
return rig_active_;
}
bool Configuration::impl::open_rig ()
bool Configuration::impl::open_rig (bool force)
{
auto result = false;
auto const rig_data = gather_rig_data ();
if (!rig_active_ || rig_data != saved_rig_params_)
if (force || !rig_active_ || rig_data != saved_rig_params_)
{
try
{
close_rig ();
// create a new Transceiver object
auto rig = transceiver_factory_.create (rig_data, &transceiver_thread_);
auto rig = transceiver_factory_.create (rig_data, transceiver_thread_);
cached_rig_state_ = Transceiver::TransceiverState {};
// hook up Configuration transceiver control signals to Transceiver slots
//
// these connections cross the thread boundary
rig_connections_ << connect (this, &Configuration::impl::frequency, rig.get (), &Transceiver::frequency);
rig_connections_ << connect (this, &Configuration::impl::tx_frequency, rig.get (), &Transceiver::tx_frequency);
rig_connections_ << connect (this, &Configuration::impl::mode, rig.get (), &Transceiver::mode);
rig_connections_ << connect (this, &Configuration::impl::ptt, rig.get (), &Transceiver::ptt);
rig_connections_ << connect (this, &Configuration::impl::sync, rig.get (), &Transceiver::sync);
rig_connections_ << connect (this, &Configuration::impl::set_transceiver,
rig.get (), &Transceiver::set);
// hook up Transceiver signals to Configuration signals
//
// these connections cross the thread boundary
connect (rig.get (), &Transceiver::update, this, &Configuration::impl::handle_transceiver_update);
connect (rig.get (), &Transceiver::failure, this, &Configuration::impl::handle_transceiver_failure);
rig_connections_ << connect (rig.get (), &Transceiver::resolution, this, [=] (int resolution) {
rig_resolution_ = resolution;
});
rig_connections_ << connect (rig.get (), &Transceiver::update, this, &Configuration::impl::handle_transceiver_update);
rig_connections_ << connect (rig.get (), &Transceiver::failure, this, &Configuration::impl::handle_transceiver_failure);
// setup thread safe startup and close down semantics
rig_connections_ << connect (this, &Configuration::impl::start_transceiver, rig.get (), &Transceiver::start);
connect (this, &Configuration::impl::stop_transceiver, rig.get (), &Transceiver::stop);
rig_connections_ << connect (this, &Configuration::impl::stop_transceiver, rig.get (), &Transceiver::stop);
auto p = rig.release (); // take ownership
// schedule eventual destruction
// schedule destruction on thread quit
connect (transceiver_thread_, &QThread::finished, p, &QObject::deleteLater);
// schedule eventual destruction for non-closing situations
//
// must be queued connection to avoid premature self-immolation
// since finished signal is going to be emitted from the object
// that will get destroyed in its own stop slot i.e. a same
// thread signal to slot connection which by default will be
// reduced to a method function call.
// must be queued connection to avoid premature
// self-immolation since finished signal is going to be
// emitted from the object that will get destroyed in its
// own stop slot i.e. a same thread signal to slot
// connection which by default will be reduced to a method
// function call.
connect (p, &Transceiver::finished, p, &Transceiver::deleteLater, Qt::QueuedConnection);
ui_->test_CAT_push_button->setStyleSheet ({});
rig_active_ = true;
Q_EMIT start_transceiver (); // start rig on its thread
Q_EMIT start_transceiver (++transceiver_command_number_); // start rig on its thread
result = true;
}
catch (std::exception const& e)
@ -2284,98 +2271,85 @@ bool Configuration::impl::open_rig ()
void Configuration::impl::transceiver_frequency (Frequency f)
{
Transceiver::MODE mode {Transceiver::UNK};
if (ui_->mode_group_box->isEnabled ())
switch (data_mode_)
{
switch (static_cast<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_none: break;
}
case data_mode_USB: mode = Transceiver::USB; break;
case data_mode_data: mode = Transceiver::DIG_U; break;
case data_mode_none: break;
}
if (cached_rig_state_.frequency () != f
|| (mode != Transceiver::UNK && mode != cached_rig_state_.mode ()))
{
cached_rig_state_.frequency (f);
cached_rig_state_.mode (mode);
cached_rig_state_.online (true); // we want the rig online
cached_rig_state_.mode (mode);
// apply any offset & calibration
// we store the offset here for use in feedback from the rig, we
// cannot absolutely determine if the offset should apply but by
// simply picking an offset when the Rx frequency is set and
// sticking to it we get sane behaviour
current_offset_ = stations_.offset (f);
Q_EMIT frequency (apply_calibration (f + current_offset_), mode);
}
// apply any offset & calibration
// we store the offset here for use in feedback from the rig, we
// cannot absolutely determine if the offset should apply but by
// simply picking an offset when the Rx frequency is set and
// sticking to it we get sane behaviour
current_offset_ = stations_.offset (f);
cached_rig_state_.frequency (apply_calibration (f + current_offset_));
Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_);
}
void Configuration::impl::transceiver_tx_frequency (Frequency f)
{
if (/* set_mode () || */ cached_rig_state_.tx_frequency () != f || !cached_rig_state_.compare_split (!!f))
cached_rig_state_.online (true); // we want the rig online
cached_rig_state_.split (f);
cached_rig_state_.tx_frequency (f);
// lookup offset for tx and apply calibration
if (f)
{
cached_rig_state_.split (f);
cached_rig_state_.tx_frequency (f);
// lookup offset and apply calibration if we are in split mode
if (cached_rig_state_.split ())
{
// apply and offset and calibration
// we store the offset here for use in feedback from the
// rig, we cannot absolutely determine if the offset should
// apply but by simply picking an offset when the Rx
// frequency is set and sticking to it we get sane behaviour
current_offset_ = stations_.offset (f);
f = apply_calibration (f + current_offset_);
}
// Rationalise TX VFO mode if we ask for split and are
// responsible for mode.
Q_EMIT tx_frequency (f, cached_rig_state_.split ()
&& ui_->mode_group_box->isEnabled ()
&& data_mode_none != data_mode_);
// apply and offset and calibration
// we store the offset here for use in feedback from the
// rig, we cannot absolutely determine if the offset should
// apply but by simply picking an offset when the Rx
// frequency is set and sticking to it we get sane behaviour
current_tx_offset_ = stations_.offset (f);
cached_rig_state_.tx_frequency (apply_calibration (f + current_tx_offset_));
}
Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_);
}
void Configuration::impl::transceiver_mode (MODE m)
{
if (cached_rig_state_.mode () != m)
{
cached_rig_state_.mode (m);
// Rationalise mode if we are responsible for it and in split mode.
Q_EMIT mode (m, cached_rig_state_.split ()
&& ui_->mode_group_box->isEnabled ()
&& data_mode_none != data_mode_);
}
cached_rig_state_.online (true); // we want the rig online
cached_rig_state_.mode (m);
Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_);
}
void Configuration::impl::transceiver_ptt (bool on)
{
cached_rig_state_.online (true); // we want the rig online
cached_rig_state_.ptt (on);
// pass this on regardless of cache
Q_EMIT ptt (on);
Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_);
}
void Configuration::impl::sync_transceiver (bool force_signal)
void Configuration::impl::sync_transceiver (bool /*force_signal*/)
{
// pass this on as cache must be ignored
Q_EMIT sync (force_signal);
// Q_EMIT sync (force_signal);
}
void Configuration::impl::handle_transceiver_update (TransceiverState state)
void Configuration::impl::handle_transceiver_update (TransceiverState const& state,
unsigned sequence_number)
{
#if WSJT_TRACE_CAT
qDebug () << "Configuration::handle_transceiver_update: Transceiver State:" << state;
qDebug () << "Configuration::handle_transceiver_update: Transceiver State #:" << sequence_number << state;
#endif
// only follow rig on some information, ignore other stuff
cached_rig_state_.online (state.online ());
cached_rig_state_.frequency (state.frequency ());
cached_rig_state_.mode (state.mode ());
cached_rig_state_.split (state.split ());
if (state.online ())
{
ui_->test_PTT_push_button->setChecked (state.ptt ());
TransceiverFactory::SplitMode split_mode_selected;
if (isVisible ())
{
ui_->test_CAT_push_button->setStyleSheet ("QPushButton {background-color: green;}");
@ -2386,80 +2360,37 @@ void Configuration::impl::handle_transceiver_update (TransceiverState state)
ui_->test_PTT_push_button->setEnabled ((TransceiverFactory::PTT_method_CAT == ptt_method && CAT_PTT_enabled)
|| TransceiverFactory::PTT_method_DTR == ptt_method
|| TransceiverFactory::PTT_method_RTS == ptt_method);
// Follow the setup choice.
split_mode_selected = static_cast<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
{
close_rig ();
}
// take off calibration & offset
state.frequency (remove_calibration (state.frequency ()) - current_offset_);
if (state.tx_frequency ())
// pass on to clients if current command is processed
if (sequence_number == transceiver_command_number_)
{
TransceiverState reported_state {state};
// take off calibration & offset
state.tx_frequency (remove_calibration (state.tx_frequency ()) - current_offset_);
reported_state.frequency (remove_calibration (reported_state.frequency ()) - current_offset_);
if (reported_state.tx_frequency ())
{
// take off calibration & offset
reported_state.tx_frequency (remove_calibration (reported_state.tx_frequency ()) - current_tx_offset_);
}
Q_EMIT self_->transceiver_update (reported_state);
}
else
{
#if WSJT_TRACE_CAT
qDebug () << "Configuration::handle_transceiver_update: skipping because of command #:" << transceiver_command_number_;
#endif
}
cached_rig_state_ = state;
// pass on to clients
Q_EMIT self_->transceiver_update (cached_rig_state_);
}
void Configuration::impl::handle_transceiver_failure (QString reason)
void Configuration::impl::handle_transceiver_failure (QString const& reason)
{
#if WSJT_TRACE_CAT
qDebug () << "Configuration::handle_transceiver_failure: reason:" << reason;
@ -2487,7 +2418,7 @@ void Configuration::impl::close_rig ()
if (rig_active_)
{
ui_->test_CAT_push_button->setStyleSheet ("QPushButton {background-color: red;}");
Q_EMIT stop_transceiver (reset_split_);
Q_EMIT stop_transceiver ();
Q_FOREACH (auto const& connection, rig_connections_)
{
disconnect (connection);

View File

@ -72,6 +72,7 @@ public:
~Configuration ();
int exec ();
bool is_active () const;
QDir temp_dir () const;
QDir doc_dir () const;
@ -155,6 +156,15 @@ public:
// open_if_closed parameter is passed as true.
bool transceiver_online (bool open_if_closed = false);
// Frequency resolution of the rig
//
// 0 - 1Hz
// 1 - 10Hz rounded
// -1 - 10Hz truncated
// 2 - 100Hz rounded
// -2 - 100Hz truncated
int transceiver_resolution () const;
// Close down connection to rig.
void transceiver_offline ();
@ -206,7 +216,7 @@ public:
//
// signals a change in one of the TransceiverState members
Q_SIGNAL void transceiver_update (Transceiver::TransceiverState) const;
Q_SIGNAL void transceiver_update (Transceiver::TransceiverState const&) const;
// Signals a failure of a control rig CAT or PTT connection.
//
@ -214,7 +224,7 @@ public:
// connections are closed automatically. The connections can be
// re-established with a call to transceiver_online(true) assuming
// the fault condition has been rectified or is transient.
Q_SIGNAL void transceiver_failure (QString reason) const;
Q_SIGNAL void transceiver_failure (QString const& reason) const;
private:
class impl;

View File

@ -1045,6 +1045,9 @@ this setting allows you to select which audio input will be used
</item>
<item>
<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">
<string> s</string>
</property>
@ -1127,13 +1130,10 @@ radio interface behave as expected.</string>
<string>Split Operation</string>
</property>
<layout class="QGridLayout" name="gridLayout_12">
<item row="0" column="0">
<widget class="QRadioButton" name="split_none_radio_button">
<item row="0" column="2">
<widget class="QRadioButton" name="split_emulate_radio_button">
<property name="text">
<string>None</string>
</property>
<property name="checked">
<bool>true</bool>
<string>Fake It</string>
</property>
<attribute name="buttonGroup">
<string notr="true">split_mode_button_group</string>
@ -1150,27 +1150,17 @@ radio interface behave as expected.</string>
</attribute>
</widget>
</item>
<item row="0" column="2">
<widget class="QRadioButton" name="split_emulate_radio_button">
<item row="0" column="0">
<widget class="QRadioButton" name="split_none_radio_button">
<property name="text">
<string>Fake It</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>
<string>None</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">split_mode_button_group</string>
</attribute>
</widget>
</item>
</layout>
@ -1388,12 +1378,6 @@ both here.</string>
</item>
<item row="3" column="0">
<widget class="QGroupBox" name="azel_path_group_box">
<property name="minimumSize">
<size>
<width>0</width>
<height>56</height>
</size>
</property>
<property name="title">
<string>AzEl Directory</string>
</property>
@ -1403,6 +1387,9 @@ both here.</string>
<property name="text">
<string>Location:</string>
</property>
<property name="buddy">
<cstring>azel_path_select_push_button</cstring>
</property>
</widget>
</item>
<item>
@ -2476,8 +2463,8 @@ soundcard changes</string>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>254</x>
<y>554</y>
<x>272</x>
<y>606</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
@ -2492,8 +2479,8 @@ soundcard changes</string>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>322</x>
<y>554</y>
<x>340</x>
<y>606</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
@ -2508,7 +2495,7 @@ soundcard changes</string>
<slot>setFocus()</slot>
<hints>
<hint type="sourcelabel">
<x>566</x>
<x>404</x>
<y>62</y>
</hint>
<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};
}
DXLabSuiteCommanderTransceiver::DXLabSuiteCommanderTransceiver (std::unique_ptr<TransceiverBase> wrapped, QString const& address, bool use_for_ptt, int poll_interval)
: PollingTransceiver {poll_interval}
DXLabSuiteCommanderTransceiver::DXLabSuiteCommanderTransceiver (std::unique_ptr<TransceiverBase> wrapped,
QString const& address, bool use_for_ptt,
int poll_interval, QObject * parent)
: PollingTransceiver {poll_interval, parent}
, wrapped_ {std::move (wrapped)}
, use_for_ptt_ {use_for_ptt}
, server_ {address}
@ -48,14 +50,10 @@ DXLabSuiteCommanderTransceiver::DXLabSuiteCommanderTransceiver (std::unique_ptr<
{
}
DXLabSuiteCommanderTransceiver::~DXLabSuiteCommanderTransceiver ()
int DXLabSuiteCommanderTransceiver::do_start ()
{
}
void DXLabSuiteCommanderTransceiver::do_start ()
{
TRACE_CAT ("starting");
wrapped_->start ();
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "starting");
if (wrapped_) wrapped_->start (0);
auto server_details = network_server_lookup (server_, 52002u, QHostAddress::LocalHost, QAbstractSocket::IPv4Protocol);
@ -67,11 +65,46 @@ void DXLabSuiteCommanderTransceiver::do_start ()
commander_->connectToHost (std::get<0> (server_details), std::get<1> (server_details));
if (!commander_->waitForConnected ())
{
TRACE_CAT ("failed to connect" << commander_->errorString ());
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "failed to connect" << commander_->errorString ());
throw error {tr ("Failed to connect to DX Lab Suite Commander\n") + commander_->errorString ()};
}
int resolution {0};
auto reply = command_with_reply ("<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 ();
return resolution;
}
void DXLabSuiteCommanderTransceiver::do_stop ()
@ -82,27 +115,30 @@ void DXLabSuiteCommanderTransceiver::do_stop ()
delete commander_, commander_ = nullptr;
}
wrapped_->stop ();
TRACE_CAT ("stopped");
if (wrapped_) wrapped_->stop ();
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "stopped");
}
void DXLabSuiteCommanderTransceiver::do_ptt (bool on)
{
TRACE_CAT (on << state ());
TRACE_CAT ("DXLabSuiteCommanderTransceiver", on << state ());
if (use_for_ptt_)
{
simple_command (on ? "<command:5>CmdTX<parameters:0>" : "<command:5>CmdRX<parameters:0>");
}
else
{
wrapped_->ptt (on);
Q_ASSERT (wrapped_);
TransceiverState new_state {wrapped_->state ()};
new_state.ptt (on);
wrapped_->set (new_state, 0);
}
update_PTT (on);
}
void DXLabSuiteCommanderTransceiver::do_frequency (Frequency f, MODE m)
void DXLabSuiteCommanderTransceiver::do_frequency (Frequency f, MODE m, bool /*no_ignore*/)
{
TRACE_CAT (f << state ());
TRACE_CAT ("DXLabSuiteCommanderTransceiver", f << state ());
auto f_string = frequency_to_string (f);
if (UNK != m)
{
@ -119,9 +155,9 @@ void DXLabSuiteCommanderTransceiver::do_frequency (Frequency f, MODE m)
update_rx_frequency (f);
}
void DXLabSuiteCommanderTransceiver::do_tx_frequency (Frequency tx, bool /* rationalise_mode */)
void DXLabSuiteCommanderTransceiver::do_tx_frequency (Frequency tx, bool /*no_ignore*/)
{
TRACE_CAT (tx << state ());
TRACE_CAT ("DXLabSuiteCommanderTransceiver", tx << state ());
if (tx)
{
auto f_string = frequency_to_string (tx);
@ -136,9 +172,9 @@ void DXLabSuiteCommanderTransceiver::do_tx_frequency (Frequency tx, bool /* rati
update_other_frequency (tx);
}
void DXLabSuiteCommanderTransceiver::do_mode (MODE m, bool /* rationalise */)
void DXLabSuiteCommanderTransceiver::do_mode (MODE m)
{
TRACE_CAT (m << state ());
TRACE_CAT ("DXLabSuiteCommanderTransceiver", m << state ());
auto m_string = map_mode (m);
auto params = ("<1:%1>" + m_string).arg (m_string.size ());
simple_command (("<command:10>CmdSetMode<parameters:%1>" + params).arg (params.size ()));
@ -168,7 +204,7 @@ void DXLabSuiteCommanderTransceiver::poll ()
}
else
{
TRACE_CAT_POLL ("get frequency unexpected response");
TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "get frequency unexpected response");
throw error {tr ("DX Lab Suite Commander didn't respond correctly polling frequency")};
}
@ -189,7 +225,7 @@ void DXLabSuiteCommanderTransceiver::poll ()
}
else
{
TRACE_CAT_POLL ("get tx frequency unexpected response");
TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "get tx frequency unexpected response");
throw error {tr ("DX Lab Suite Commander didn't respond correctly polling TX frequency")};
}
}
@ -208,13 +244,13 @@ void DXLabSuiteCommanderTransceiver::poll ()
}
else
{
TRACE_CAT_POLL ("unexpected split state" << split);
TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "unexpected split state" << split);
throw error {tr ("DX Lab Suite Commander sent an unrecognised split state: ") + split};
}
}
else
{
TRACE_CAT_POLL ("get split mode unexpected response");
TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "get split mode unexpected response");
throw error {tr ("DX Lab Suite Commander didn't respond correctly polling split status")};
}
@ -265,14 +301,14 @@ void DXLabSuiteCommanderTransceiver::poll ()
}
else
{
TRACE_CAT_POLL ("unexpected mode name" << mode);
TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "unexpected mode name" << mode);
throw error {tr ("DX Lab Suite Commander sent an unrecognised mode: \"") + mode + '"'};
}
update_mode (m);
}
else
{
TRACE_CAT_POLL ("unexpected response");
TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "unexpected response");
throw error {tr ("DX Lab Suite Commander didn't respond correctly polling mode")};
}
}
@ -283,12 +319,12 @@ void DXLabSuiteCommanderTransceiver::simple_command (QString const& cmd, bool no
if (!no_debug)
{
TRACE_CAT (cmd);
TRACE_CAT ("DXLabSuiteCommanderTransceiver", cmd);
}
if (!write_to_port (cmd))
{
TRACE_CAT ("failed:" << commander_->errorString ());
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "failed:" << commander_->errorString ());
throw error {tr ("DX Lab Suite Commander send command failed\n") + commander_->errorString ()};
}
}
@ -299,7 +335,7 @@ QString DXLabSuiteCommanderTransceiver::command_with_reply (QString const& cmd,
if (!write_to_port (cmd))
{
TRACE_CAT ("failed to send command:" << commander_->errorString ());
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "failed to send command:" << commander_->errorString ());
throw error {
tr ("DX Lab Suite Commander failed to send command \"%1\": %2\n")
.arg (cmd)
@ -316,7 +352,7 @@ QString DXLabSuiteCommanderTransceiver::command_with_reply (QString const& cmd,
replied = commander_->waitForReadyRead ();
if (!replied && commander_->error () != commander_->SocketTimeoutError)
{
TRACE_CAT (cmd << "failed to read reply:" << commander_->errorString ());
TRACE_CAT ("DXLabSuiteCommanderTransceiver", cmd << "failed to read reply:" << commander_->errorString ());
throw error {
tr ("DX Lab Suite Commander send command \"%1\" read reply failed: %2\n")
.arg (cmd)
@ -327,7 +363,7 @@ QString DXLabSuiteCommanderTransceiver::command_with_reply (QString const& cmd,
if (!replied)
{
TRACE_CAT (cmd << "retries exhausted");
TRACE_CAT ("DXLabSuiteCommanderTransceiver", cmd << "retries exhausted");
throw error {
tr ("DX Lab Suite Commander retries exhausted sending command \"%1\"")
.arg (cmd)
@ -343,7 +379,7 @@ QString DXLabSuiteCommanderTransceiver::command_with_reply (QString const& cmd,
if (!no_debug)
{
TRACE_CAT (cmd << "->" << result);
TRACE_CAT ("DXLabSuiteCommanderTransceiver", cmd << "->" << result);
}
return result; // converting raw UTF-8 bytes to QString

View File

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

View File

@ -1,116 +1,65 @@
#include "EmulateSplitTransceiver.hpp"
EmulateSplitTransceiver::EmulateSplitTransceiver (std::unique_ptr<Transceiver> wrapped)
: wrapped_ {std::move (wrapped)}
, frequency_ {0, 0}
, pre_tx_frequency_ {0}
, tx_ {false}
EmulateSplitTransceiver::EmulateSplitTransceiver (std::unique_ptr<Transceiver> wrapped, QObject * parent)
: Transceiver {parent}
, wrapped_ {std::move (wrapped)}
, rx_frequency_ {0}
, tx_frequency_ {0}
, split_ {false}
{
// Connect update signal of wrapped Transceiver object instance to ours.
connect (wrapped_.get (), &Transceiver::update, this, &EmulateSplitTransceiver::handle_update);
// Connect failure signal of wrapped Transceiver object to our
// parent failure signal.
// Connect other signals of wrapped Transceiver object to our
// parent matching signals.
connect (wrapped_.get (), &Transceiver::resolution, this, &Transceiver::resolution);
connect (wrapped_.get (), &Transceiver::finished, this, &Transceiver::finished);
connect (wrapped_.get (), &Transceiver::failure, this, &Transceiver::failure);
}
void EmulateSplitTransceiver::start () noexcept
{
wrapped_->start ();
wrapped_->tx_frequency (0, false);
}
void EmulateSplitTransceiver::frequency (Frequency rx, MODE m) noexcept
void EmulateSplitTransceiver::set (TransceiverState const& s, unsigned sequence_number) noexcept
{
#if WSJT_TRACE_CAT
qDebug () << "EmulateSplitTransceiver::frequency:" << rx << "mode:" << m;
qDebug () << "EmulateSplitTransceiver::set: state:" << s << "#:" << sequence_number;
#endif
// save for use in updates
rx_frequency_ = s.frequency ();
tx_frequency_ = s.tx_frequency ();
split_ = s.split ();
// Save frequency parameters.
frequency_[0] = rx;
// Set active frequency.
wrapped_->frequency (rx, m);
TransceiverState emulated_state {s};
if (s.ptt () && split_) emulated_state.frequency (s.tx_frequency ());
emulated_state.split (false);
emulated_state.tx_frequency (0);
wrapped_->set (emulated_state, sequence_number);
}
void EmulateSplitTransceiver::tx_frequency (Frequency tx, bool /* rationalise_mode */) noexcept
{
#if WSJT_TRACE_CAT
qDebug () << "EmulateSplitTransceiver::tx_frequency:" << tx;
#endif
// Save frequency parameter.
frequency_[1] = tx;
// Set active frequency.
wrapped_->frequency (frequency_[(tx_ && frequency_[1]) ? 1 : 0]);
}
void EmulateSplitTransceiver::ptt (bool on) noexcept
{
#if WSJT_TRACE_CAT
qDebug () << "EmulateSplitTransceiver::ptt:" << on;
#endif
// Save TX state for future frequency change requests.
if (on)
{
// save the Rx frequency
pre_tx_frequency_ = frequency_[0];
// Switch to other frequency if we have one i.e. client wants
// split operation).
wrapped_->frequency (frequency_[frequency_[1] ? 1 : 0]);
// Change TX state.
wrapped_->ptt (true);
}
else
{
// Change TX state.
wrapped_->ptt (false);
// Switch to RX frequency.
wrapped_->frequency (pre_tx_frequency_);
pre_tx_frequency_ = 0;
}
tx_ = on;
}
void EmulateSplitTransceiver::handle_update (TransceiverState state)
void EmulateSplitTransceiver::handle_update (TransceiverState const& state,
unsigned sequence_number)
{
#if WSJT_TRACE_CAT
qDebug () << "EmulateSplitTransceiver::handle_update: from wrapped:" << state;
#endif
// Change to reflect emulated state, we don't want to report the
// shifted frequency when transmitting.
if (pre_tx_frequency_)
{
state.frequency (pre_tx_frequency_);
}
else
{
// Follow the rig if in RX mode.
frequency_[0] = state.frequency ();
}
// Always report the other frequency as the Tx frequency we will use.
state.tx_frequency (frequency_[1]);
if (state.split ())
{
Q_EMIT failure (tr ("Emulated split mode requires rig to be in simplex mode"));
}
else
{
state.split (true); // override rig state
TransceiverState new_state {state};
// Follow the rig if in RX mode.
if (state.ptt ()) new_state.frequency (rx_frequency_);
// These are always what was requested in prior set state operation
new_state.tx_frequency (tx_frequency_);
new_state.split (split_);
#if WSJT_TRACE_CAT
qDebug () << "EmulateSplitTransceiver::handle_update: signalling:" << state;
#endif
// signal emulated state
Q_EMIT update (state);
Q_EMIT update (new_state, sequence_number);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -13,20 +13,25 @@
LiveFrequencyValidator::LiveFrequencyValidator (QComboBox * combo_box
, Bands const * bands
, FrequencyList const * frequencies
, Frequency const * nominal_frequency
, QWidget * parent)
: QRegExpValidator {
QRegExp { // frequency in MHz or band
bands->data (QModelIndex {}).toString () // out of band string
+ QString {R"(|((\d{0,6}(\)"} // or up to 6 digits
+ QLocale {}.decimalPoint () // (followed by decimal separator
+ R"(\d{0,2})?)([Mm]{1,2}|([Cc][Mm])))|(\d{0,4}(\)" // followed by up to 2 digits and either 'm' or 'cm' or 'mm' (case insensitive))
+ R"(\d{0,2})?)([Mm]{1,2}|([Cc][Mm])))|(\d{0,6}(\)" // followed by up to 2 digits and either 'm' or 'cm' or 'mm' (case insensitive))
+ QLocale {}.decimalPoint () // or a decimal separator
+ R"(\d{0,6})?))" // followed by up to 6 digits
+ R"(\d{0,6})?)|(\d{0,3}(\)" // followed by up to 6
// digits or a decimal number
+ QLocale {}.decimalPoint () // or a decimal separator
+ R"(\d{0,6})?[Kk]))" // followed by a 'k' or 'K'
}
, parent
}
, bands_ {bands}
, frequencies_ {frequencies}
, nominal_frequency_ {nominal_frequency}
, combo_box_ {combo_box}
{
}
@ -65,6 +70,14 @@ void LiveFrequencyValidator::fixup (QString& input) const
input = QString {};
}
}
else if (input.contains (QChar {'k'}, Qt::CaseInsensitive))
{
// kHz in current MHz input
auto f = Radio::frequency (input.remove (QChar {'k'}, Qt::CaseInsensitive), 3);
f += *nominal_frequency_ / 1000000u * 1000000u;
input = bands_->find (f);
Q_EMIT valid (f);
}
else
{
// frequency input

View File

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

View File

@ -3,6 +3,7 @@
#include <QTimer>
#include <QDebug>
#include <objbase.h>
#include <QThread>
#include "qt_helpers.hpP"
@ -48,7 +49,7 @@ auto OmniRigTransceiver::map_mode (OmniRig::RigParamX param) -> MODE
{
return FM;
}
TRACE_CAT ("unrecognized mode");
TRACE_CAT ("OmniRigTransceiver", "unrecognized mode");
throw_qstring (tr ("OmniRig: unrecognized mode"));
return UNK;
}
@ -70,7 +71,7 @@ OmniRig::RigParamX OmniRigTransceiver::map_mode (MODE mode)
case DIG_FM: return OmniRig::PM_FM;
default: break;
}
return OmniRig::PM_SSB_U; // quieten compiler grumble
return OmniRig::PM_SSB_U; // quieten compiler grumble
}
void OmniRigTransceiver::register_transceivers (TransceiverFactory::Transceivers * registry, int id1, int id2)
@ -78,23 +79,26 @@ void OmniRigTransceiver::register_transceivers (TransceiverFactory::Transceivers
(*registry)[OmniRig_transceiver_one_name] = TransceiverFactory::Capabilities {
id1
, TransceiverFactory::Capabilities::none // COM isn't serial or network
, true // does PTT
, false // doesn't select mic/data (use OmniRig config file)
, true // can remote control RTS nd DTR
, true // asynchronous interface
, true // does PTT
, false // doesn't select mic/data (use OmniRig config file)
, true // can remote control RTS nd DTR
, true // asynchronous interface
};
(*registry)[OmniRig_transceiver_two_name] = TransceiverFactory::Capabilities {
id2
, TransceiverFactory::Capabilities::none // COM isn't serial or network
, true // does PTT
, false // doesn't select mic/data (use OmniRig config file)
, true // can remote control RTS nd DTR
, true // asynchronous interface
, true // does PTT
, false // doesn't select mic/data (use OmniRig config file)
, true // can remote control RTS nd DTR
, true // asynchronous interface
};
}
OmniRigTransceiver::OmniRigTransceiver (std::unique_ptr<TransceiverBase> wrapped, RigNumber n, TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port)
: wrapped_ {std::move (wrapped)}
OmniRigTransceiver::OmniRigTransceiver (std::unique_ptr<TransceiverBase> wrapped,
RigNumber n, TransceiverFactory::PTTMethod ptt_type,
QString const& ptt_port, QObject * parent)
: TransceiverBase {parent}
, wrapped_ {std::move (wrapped)}
, use_for_ptt_ {TransceiverFactory::PTT_method_CAT == ptt_type || ("CAT" == ptt_port && (TransceiverFactory::PTT_method_RTS == ptt_type || TransceiverFactory::PTT_method_DTR == ptt_type))}
, ptt_type_ {ptt_type}
, rig_number_ {n}
@ -105,21 +109,17 @@ OmniRigTransceiver::OmniRigTransceiver (std::unique_ptr<TransceiverBase> wrapped
{
}
OmniRigTransceiver::~OmniRigTransceiver ()
int OmniRigTransceiver::do_start ()
{
}
void OmniRigTransceiver::do_start ()
{
TRACE_CAT ("starting");
wrapped_->start ();
TRACE_CAT ("OmniRigTransceiver", "starting");
if (wrapped_) wrapped_->start (0);
CoInitializeEx (nullptr, 0 /*COINIT_APARTMENTTHREADED*/); // required because Qt only does this for GUI thread
omni_rig_.reset (new OmniRig::OmniRigX {this});
if (omni_rig_->isNull ())
{
TRACE_CAT ("failed to start COM server");
TRACE_CAT ("OmniRigTransceiver", "failed to start COM server");
throw_qstring (tr ("Failed to start OmniRig COM server"));
}
@ -135,7 +135,7 @@ void OmniRigTransceiver::do_start ()
, SIGNAL (CustomReply (int, QVariant const&, QVariant const&))
, this, SLOT (handle_custom_reply (int, QVariant const&, QVariant const&)));
TRACE_CAT ("OmniRig s/w version:" << QString::number (omni_rig_->SoftwareVersion ()).toLocal8Bit ()
TRACE_CAT ("OmniRigTransceiver", "OmniRig s/w version:" << QString::number (omni_rig_->SoftwareVersion ()).toLocal8Bit ()
<< "i/f version:" << QString::number (omni_rig_->InterfaceVersion ()).toLocal8Bit ());
// fetch the interface of the RigX CoClass and instantiate a proxy object
@ -155,18 +155,19 @@ void OmniRigTransceiver::do_start ()
Q_ASSERT (port_);
Q_ASSERT (!port_->isNull ());
TRACE_CAT ("OmniRigTransceiver", "OmniRig RTS state:" << port_->Rts ());
// if (!port_->Lock ()) // try to take exclusive use of the OmniRig serial port for PTT
// {
// throw_qstring (tr ("Failed to get exclusive use of %1" from OmniRig").arg (ptt_type));
// }
if (!port_->Lock ()) // try to take exclusive use of the OmniRig serial port for PTT
{
TRACE_CAT ("OmniRigTransceiver", "Failed to get exclusive use of serial port for PTT from OmniRig");
}
// start off so we don't accidentally key the radio
if (TransceiverFactory::PTT_method_DTR == ptt_type_)
{
port_->SetDtr (false);
}
else // RTS
else // RTS
{
port_->SetRts (false);
}
@ -175,66 +176,81 @@ void OmniRigTransceiver::do_start ()
readable_params_ = rig_->ReadableParams ();
writable_params_ = rig_->WriteableParams ();
TRACE_CAT (QString {"OmniRig initial rig type: %1 readable params = 0x%2 writable params = 0x%3 for rig %4"}
TRACE_CAT ("OmniRigTransceiver", QString {"OmniRig initial rig type: %1 readable params = 0x%2 writable params = 0x%3 for rig %4"}
.arg (rig_->RigType ())
.arg (readable_params_, 8, 16, QChar ('0'))
.arg (writable_params_, 8, 16, QChar ('0'))
.arg (rig_number_).toLocal8Bit ());
TRACE_CAT ("OmniRig status:" << rig_->StatusStr ());
init_rig ();
for (unsigned tries {0}; tries < 10; ++tries)
{
QThread::msleep (100); // wait until OmniRig polls the rig
auto f = rig_->GetRxFrequency ();
int resolution {0};
if (f)
{
if (f % 10) return resolution; // 1Hz resolution
auto test_frequency = f - f % 100 + 55;
if (OmniRig::PM_FREQ & writable_params_)
{
rig_->SetFreq (test_frequency);
}
else if (reversed_ && (OmniRig::PM_FREQB & writable_params_))
{
rig_->SetFreqB (test_frequency);
}
else if (!reversed_ && (OmniRig::PM_FREQA & writable_params_))
{
rig_->SetFreqA (test_frequency);
}
else
{
throw_qstring (tr ("OmniRig: don't know how to set rig frequency"));
}
switch (rig_->GetRxFrequency () - test_frequency)
{
case -5: resolution = -1; break; // 10Hz truncated
case 5: resolution = 1; break; // 10Hz rounded
case -55: resolution = -2; break; // 100Hz truncated
case 45: resolution = 2; break; // 100Hz rounded
}
if (OmniRig::PM_FREQ & writable_params_)
{
rig_->SetFreq (f);
}
else if (reversed_ && (OmniRig::PM_FREQB & writable_params_))
{
rig_->SetFreqB (f);
}
else if (!reversed_ && (OmniRig::PM_FREQA & writable_params_))
{
rig_->SetFreqA (f);
}
update_rx_frequency (f);
return resolution;
}
}
throw_qstring (tr ("OmniRig: Initialization timed out"));
return 0; // keep compiler happy
}
void OmniRigTransceiver::do_stop ()
{
QThread::msleep (200); // leave some time for pending
// commands at the server end
if (port_)
{
// port_->Unlock (); // release serial port
port_->Unlock (); // release serial port
port_->clear ();
}
rig_->clear ();
omni_rig_->clear ();
if (rig_) rig_->clear ();
if (omni_rig_) omni_rig_->clear ();
CoUninitialize ();
wrapped_->stop ();
TRACE_CAT ("stopped");
if (wrapped_) wrapped_->stop ();
TRACE_CAT ("OmniRigTransceiver", "stopped");
}
void OmniRigTransceiver::online_check ()
{
if (OmniRig::ST_ONLINE != rig_->Status ())
{
offline ("OmniRig rig went offline for more than 5 seconds");
}
else
{
init_rig ();
}
}
void OmniRigTransceiver::init_rig ()
{
if (OmniRig::ST_ONLINE != rig_->Status ())
{
QTimer::singleShot (5000, this, SLOT (online_check ()));
}
else
{
update_rx_frequency (rig_->GetRxFrequency ());
if (state ().split ())
{
TRACE_CAT ("set split");
rig_->SetSplitMode (state ().frequency (), state ().tx_frequency ());
}
else
{
TRACE_CAT ("set simplex");
rig_->SetSimplexMode (state ().frequency ());
}
}
}
void OmniRigTransceiver::do_sync (bool force_signal)
void OmniRigTransceiver::do_sync (bool force_signal, bool /*no_poll*/)
{
// nothing much we can do here, we just have to let OmniRig do its
// stuff and its first poll should send us and update that will
@ -247,13 +263,13 @@ void OmniRigTransceiver::do_sync (bool force_signal)
void OmniRigTransceiver::handle_COM_exception (int code, QString source, QString desc, QString help)
{
TRACE_CAT (QString::number (code) + " at " + source + ": " + desc + " (" + help + ')');
TRACE_CAT ("OmniRigTransceiver", QString::number (code) + " at " + source + ": " + desc + " (" + help + ')');
throw_qstring (tr ("OmniRig COM/OLE error: %1 at %2: %3 (%4)").arg (QString::number (code)).arg (source). arg (desc). arg (help));
}
void OmniRigTransceiver::handle_visible_change ()
{
TRACE_CAT ("visibility change: visibility =" << omni_rig_->DialogVisible ());
TRACE_CAT ("OmniRigTransceiver", "visibility change: visibility =" << omni_rig_->DialogVisible ());
}
void OmniRigTransceiver::handle_rig_type_change (int rig_number)
@ -262,7 +278,7 @@ void OmniRigTransceiver::handle_rig_type_change (int rig_number)
{
readable_params_ = rig_->ReadableParams ();
writable_params_ = rig_->WriteableParams ();
TRACE_CAT (QString {"OmniRig rig type change to: %1 readable params = 0x%2 writable params = 0x%3 for rig %4"}
TRACE_CAT ("OmniRigTransceiver", QString {"OmniRig rig type change to: %1 readable params = 0x%2 writable params = 0x%3 for rig %4"}
.arg (rig_->RigType ())
.arg (readable_params_, 8, 16, QChar ('0'))
.arg (writable_params_, 8, 16, QChar ('0'))
@ -275,11 +291,15 @@ void OmniRigTransceiver::handle_status_change (int rig_number)
{
if (rig_number_ == rig_number)
{
TRACE_CAT (QString {"OmniRig status change: new status for rig %1 = "}.arg (rig_number).toLocal8Bit () << rig_->StatusStr ().toLocal8Bit ());
TRACE_CAT ("OmniRigTransceiver", QString {"OmniRig status change: new status for rig %1 = "}.arg (rig_number).toLocal8Bit () << rig_->StatusStr ().toLocal8Bit ());
if (OmniRig::ST_ONLINE != rig_->Status ())
{
QTimer::singleShot (5000, this, SLOT (online_check ()));
}
else
{
TRACE_CAT ("OmniRigTransceiver", "OmniRig frequency:" << rig_->GetRxFrequency ());
}
}
}
@ -287,16 +307,16 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params)
{
if (rig_number_ == rig_number)
{
TRACE_CAT (QString {"OmniRig params change: params = 0x%1 for rig %2"}
TRACE_CAT ("OmniRigTransceiver", QString {"OmniRig params change: params = 0x%1 for rig %2"}
.arg (params, 8, 16, QChar ('0'))
.arg (rig_number).toLocal8Bit ()
<< "state before:" << state ());
// starting_ = false;
TransceiverState old_state {state ()};
auto need_frequency = false;
// state_.online = true; // sometimes we don't get an initial
// // OmniRig::ST_ONLINE status change
// // event
// state_.online = true; // sometimes we don't get an initial
// // OmniRig::ST_ONLINE status change
// // event
if (params & OmniRig::PM_VFOAA)
{
update_split (false);
@ -481,7 +501,7 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params)
update_complete ();
send_update_signal_ = false;
}
TRACE_CAT ("OmniRig params change: state after:" << state ());
TRACE_CAT ("OmniRigTransceiver", "OmniRig params change: state after:" << state ());
}
}
@ -492,19 +512,19 @@ void OmniRigTransceiver::handle_custom_reply (int rig_number, QVariant const& co
if (rig_number_ == rig_number)
{
TRACE_CAT ("OmniRig custom command" << command.toString ().toLocal8Bit ()
TRACE_CAT ("OmniRigTransceiver", "custom command" << command.toString ().toLocal8Bit ()
<< "with reply" << reply.toString ().toLocal8Bit ()
<< QString ("for rig %1").arg (rig_number).toLocal8Bit ());
TRACE_CAT ("OmniRig rig number:" << rig_number_ << ':' << state ());
TRACE_CAT ("OmniRigTransceiver", "rig number:" << rig_number_ << ':' << state ());
}
}
void OmniRigTransceiver::do_ptt (bool on)
{
TRACE_CAT (on << state ());
TRACE_CAT ("OmniRigTransceiver", on << state ());
if (use_for_ptt_ && TransceiverFactory::PTT_method_CAT == ptt_type_)
{
TRACE_CAT ("set PTT");
TRACE_CAT ("OmniRigTransceiver", "set PTT");
rig_->SetTx (on ? OmniRig::PM_TX : OmniRig::PM_RX);
}
else
@ -513,19 +533,22 @@ void OmniRigTransceiver::do_ptt (bool on)
{
if (TransceiverFactory::PTT_method_RTS == ptt_type_)
{
TRACE_CAT ("set RTS");
TRACE_CAT ("OmniRigTransceiver", "set RTS");
port_->SetRts (on);
}
else // "DTR"
else // "DTR"
{
TRACE_CAT ("set DTR");
TRACE_CAT ("OmniRigTransceiver", "set DTR");
port_->SetDtr (on);
}
}
else
{
TRACE_CAT ("set PTT using basic transceiver");
wrapped_->ptt (on);
TRACE_CAT ("OmniRigTransceiver", "set PTT using basic transceiver");
Q_ASSERT (wrapped_);
TransceiverState new_state {wrapped_->state ()};
new_state.ptt (on);
wrapped_->set (new_state, 0);
}
if (state ().ptt () != on)
{
@ -536,12 +559,12 @@ void OmniRigTransceiver::do_ptt (bool on)
}
}
void OmniRigTransceiver::do_frequency (Frequency f, MODE m)
void OmniRigTransceiver::do_frequency (Frequency f, MODE m, bool /*no_ignore*/)
{
TRACE_CAT (f << state ());
TRACE_CAT ("OmniRigTransceiver", f << state ());
if (UNK != m)
{
do_mode (m, false);
do_mode (m);
}
if (OmniRig::PM_FREQ & writable_params_)
{
@ -564,20 +587,20 @@ void OmniRigTransceiver::do_frequency (Frequency f, MODE m)
}
}
void OmniRigTransceiver::do_tx_frequency (Frequency tx, bool /* rationalise_mode */)
void OmniRigTransceiver::do_tx_frequency (Frequency tx, bool /*no_ignore*/)
{
TRACE_CAT (tx << state ());
TRACE_CAT ("OmniRigTransceiver", tx << state ());
bool split {tx != 0};
if (split)
{
TRACE_CAT ("set SPLIT mode on");
TRACE_CAT ("OmniRigTransceiver", "set SPLIT mode on");
rig_->SetSplitMode (state ().frequency (), tx);
update_other_frequency (tx);
update_split (true);
}
else
{
TRACE_CAT ("set SPLIT mode off");
TRACE_CAT ("OmniRigTransceiver", "set SPLIT mode off");
rig_->SetSimplexMode (state ().frequency ());
update_split (false);
}
@ -592,8 +615,8 @@ void OmniRigTransceiver::do_tx_frequency (Frequency tx, bool /* rationalise_mode
}
if (!((OmniRig::PM_VFOAB | OmniRig::PM_VFOBA | OmniRig::PM_SPLITON) & readable_params_))
{
TRACE_CAT ("setting SPLIT manually");
update_split (split); // we can't read it so just set and
TRACE_CAT ("OmniRigTransceiver", "setting SPLIT manually");
update_split (split); // we can't read it so just set and
// hope op doesn't change it
notify = true;
}
@ -603,9 +626,9 @@ void OmniRigTransceiver::do_tx_frequency (Frequency tx, bool /* rationalise_mode
}
}
void OmniRigTransceiver::do_mode (MODE mode, bool /* rationalise */)
void OmniRigTransceiver::do_mode (MODE mode)
{
TRACE_CAT (mode << state ());
TRACE_CAT ("OmniRigTransceiver", mode << state ());
// TODO: G4WJS OmniRig doesn't seem to have any capability of tracking/setting VFO B mode
auto mapped = map_mode (mode);
if (mapped & writable_params_)

View File

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

View File

@ -13,8 +13,9 @@ namespace
unsigned const polls_to_stabilize {3};
}
PollingTransceiver::PollingTransceiver (int poll_interval)
: interval_ {poll_interval * 1000}
PollingTransceiver::PollingTransceiver (int poll_interval, QObject * parent)
: TransceiverBase {parent}
, interval_ {poll_interval * 1000}
, poll_timer_ {nullptr}
, retries_ {0}
{
@ -26,12 +27,19 @@ void PollingTransceiver::start_timer ()
{
if (!poll_timer_)
{
poll_timer_ = new QTimer {this}; // pass ownership to QObject which handles destruction for us
poll_timer_ = new QTimer {this}; // pass ownership to
// QObject which handles
// destruction for us
connect (poll_timer_, &QTimer::timeout, this, &PollingTransceiver::handle_timeout);
connect (poll_timer_, &QTimer::timeout, this,
&PollingTransceiver::handle_timeout);
}
poll_timer_->start (interval_);
}
else
{
stop_timer ();
}
}
void PollingTransceiver::stop_timer ()
@ -77,7 +85,7 @@ void PollingTransceiver::do_post_frequency (Frequency f, MODE m)
}
}
void PollingTransceiver::do_post_tx_frequency (Frequency f, bool /* rationalize */)
void PollingTransceiver::do_post_tx_frequency (Frequency f)
{
if (next_state_.tx_frequency () != f)
{
@ -89,7 +97,7 @@ void PollingTransceiver::do_post_tx_frequency (Frequency f, bool /* rationalize
}
}
void PollingTransceiver::do_post_mode (MODE m, bool /*rationalize_mode*/)
void PollingTransceiver::do_post_mode (MODE m)
{
// we don't ever expect mode to goto to unknown
if (m != UNK && next_state_.mode () != m)
@ -106,7 +114,8 @@ void PollingTransceiver::do_post_ptt (bool p)
{
// update expected state with new PTT and set poll count
next_state_.ptt (p);
retries_ = 0; // fast feedback on PTT
retries_ = polls_to_stabilize;
//retries_ = 0; // fast feedback on PTT
}
}
@ -120,10 +129,9 @@ bool PollingTransceiver::do_pre_update ()
return true;
}
void PollingTransceiver::do_sync (bool force_signal)
void PollingTransceiver::do_sync (bool force_signal, bool no_poll)
{
poll (); // tell sub-classes to update our
// state
if (!no_poll) poll (); // tell sub-classes to update our state
// Signal new state if it is directly requested or, what we expected
// or, hasn't become what we expected after polls_to_stabilize
@ -142,9 +150,9 @@ void PollingTransceiver::do_sync (bool force_signal)
}
else if (force_signal || state () != last_signalled_state_)
{
// here is the normal passive polling path
// either our client has requested a state update regardless of change
// or sate has changed asynchronously
// here is the normal passive polling path either our client has
// requested a state update regardless of change or state has
// changed asynchronously
force_signal = true;
}
@ -154,7 +162,7 @@ void PollingTransceiver::do_sync (bool force_signal)
retries_ = 0;
next_state_ = state ();
last_signalled_state_ = state ();
update_complete ();
update_complete (true);
}
}
@ -166,7 +174,7 @@ void PollingTransceiver::handle_timeout ()
// inform our parent of the failure via the offline() message
try
{
do_sync (false);
do_sync ();
}
catch (std::exception const& e)
{

View File

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

View File

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

View File

@ -16,9 +16,9 @@ class QString;
//
// Responsibilities
//
// Provides Qt slots to set the frequency, mode and PTT of some
// transceiver. They are Qt slots so that they may be invoked across
// a thread boundary.
// Provides a Qt slot to set the frequency, mode and PTT of some
// transceiver. This is a Qt slot so that it may be invoked across a
// thread boundary.
//
// Provides a synchronisation Qt slot which should be implemented in
// sub-classes in such a way that normal operation of the rig is not
@ -58,14 +58,10 @@ public:
using Frequency = Radio::Frequency;
protected:
Transceiver ()
{
}
Transceiver (QObject * parent) : QObject {parent} {}
public:
virtual ~Transceiver ()
{
}
virtual ~Transceiver () {}
enum MODE {UNK, CW, CW_R, USB, LSB, FSK, FSK_R, DIG_U, DIG_L, AM, FM, DIG_FM};
Q_ENUM (MODE)
@ -79,33 +75,34 @@ public:
public:
TransceiverState ()
: online_ {false}
, frequency_ {0, 0}
, rx_frequency_ {0}
, tx_frequency_ {0}
, mode_ {UNK}
, split_ {unknown}
, split_ {Split::unknown}
, ptt_ {false}
{
}
bool online () const {return online_;}
Frequency frequency () const {return frequency_[0];}
Frequency tx_frequency () const {return frequency_[1];}
bool split () const {return on == split_;}
bool compare_split (bool with) const {return split_ == (with ? on : off);}
Frequency frequency () const {return rx_frequency_;}
Frequency tx_frequency () const {return tx_frequency_;}
bool split () const {return Split::on == split_;}
MODE mode () const {return mode_;}
bool ptt () const {return ptt_;}
void online (bool state) {online_ = state;}
void frequency (Frequency f) {frequency_[0] = f;}
void tx_frequency (Frequency f) {frequency_[1] = f;}
void split (bool state) {split_ = state ? on : off;}
void frequency (Frequency f) {rx_frequency_ = f;}
void tx_frequency (Frequency f) {tx_frequency_ = f;}
void split (bool state) {split_ = state ? Split::on : Split::off;}
void mode (MODE m) {mode_ = m;}
void ptt (bool state) {ptt_ = state;}
private:
bool online_;
Frequency frequency_[2]; // [0] -> Rx; [1] -> Other
Frequency rx_frequency_;
Frequency tx_frequency_; // 0 means use Rx
MODE mode_;
enum {unknown, off, on} split_;
enum class Split {unknown, off, on} split_;
bool ptt_;
// Don't forget to update the debug print and != operator if you
// add more members here
@ -115,39 +112,42 @@ public:
};
//
// The following slots and signals are expected to all run in the
// same thread which is not necessarily the main GUI thread. It is
// up to the client of the Transceiver class to organise the
// The following slots and signals are expected to all run in the
// same thread which is not necessarily the main GUI thread. It is
// up to the client of the Transceiver class to organise the
// allocation to a thread and the lifetime of the object instances.
//
// Apply state changes to the rig. The sequence_number parameter
// will be included in any status updates generated after this
// transaction is processed. The sequence number may be used to
// ignore any status updates until the results of this transaction
// have been processed thus avoiding any unwanted "ping-pong" due to
// signals crossing in transit.
Q_SLOT virtual void set (Transceiver::TransceiverState const&,
unsigned sequence_number) noexcept = 0;
// Connect and disconnect.
Q_SLOT virtual void start () noexcept = 0;
Q_SLOT virtual void stop (bool reset_split = false) noexcept = 0;
Q_SLOT virtual void start (unsigned sequence_number) noexcept = 0;
Q_SLOT virtual void stop () noexcept = 0;
// Set frequency in Hertz.
Q_SLOT virtual void frequency (Frequency, MODE = UNK) noexcept = 0;
// Setting a non-zero TX frequency means split operation, the value
// zero means simplex operation.
//
// Rationalise_mode means ensure TX uses same mode as RX.
Q_SLOT virtual void tx_frequency (Frequency tx = 0, bool rationalise_mode = true) noexcept = 0;
// Set mode.
// Rationalise means ensure TX uses same mode as RX.
Q_SLOT virtual void mode (MODE, bool rationalise = true) noexcept = 0;
// Set/unset PTT.
Q_SLOT virtual void ptt (bool = true) noexcept = 0;
// Attempt to re-synchronise or query state.
// Force_signal guarantees a update or failure signal.
Q_SLOT virtual void sync (bool force_signal = false) noexcept = 0;
// asynchronous status updates
Q_SIGNAL void update (Transceiver::TransceiverState) const;
Q_SIGNAL void failure (QString reason) const;
//
// 0 - 1Hz
// 1 - 10Hz rounded
// -1 - 10Hz truncated
// 2 - 100Hz rounded
// -2 - 100Hz truncated
Q_SIGNAL void resolution (int);
// rig state changed
Q_SIGNAL void update (Transceiver::TransceiverState const&,
unsigned sequence_number) const;
// something went wrong - not recoverable, start new instance
Q_SIGNAL void failure (QString const& reason) const;
// Ready to be destroyed.
Q_SIGNAL void finished () const;

View File

@ -14,29 +14,15 @@ namespace
auto const unexpected = TransceiverBase::tr ("Unexpected rig error");
}
void TransceiverBase::start () noexcept
void TransceiverBase::start (unsigned sequence_number) noexcept
{
QString message;
try
{
if (state_.online ())
{
try
{
// try and ensure PTT isn't left set
do_ptt (false);
do_post_ptt (false);
}
catch (...)
{
// don't care about exceptions
}
do_stop ();
do_post_stop ();
}
do_start ();
do_post_start ();
state_.online (true);
may_update u {this, true};
shutdown ();
startup ();
last_sequence_number_ = sequence_number;
}
catch (std::exception const& e)
{
@ -52,33 +38,138 @@ void TransceiverBase::start () noexcept
}
}
void TransceiverBase::stop (bool reset_split) noexcept
void TransceiverBase::set (TransceiverState const& s,
unsigned sequence_number) noexcept
{
TRACE_CAT ("TransceiverBase", "#:" << sequence_number << s);
QString message;
try
{
may_update u {this, true};
bool was_online {requested_.online ()};
if (!s.online () && was_online)
{
shutdown ();
}
else if (s.online () && !was_online)
{
shutdown ();
startup ();
}
if (requested_.online ())
{
bool ptt_on {false};
bool ptt_off {false};
if (s.ptt () != requested_.ptt ())
{
ptt_on = s.ptt ();
ptt_off = !s.ptt ();
}
if (ptt_off)
{
do_ptt (false);
do_post_ptt (false);
QThread::msleep (100); // some rigs cannot process CAT
// commands while switching from
// Tx to Rx
}
if ((s.frequency () != requested_.frequency () // and QSY
|| (s.mode () != UNK && s.mode () != requested_.mode ())) // or mode change
|| ptt_off) // or just returned to rx
{
do_frequency (s.frequency (), s.mode (), ptt_off);
do_post_frequency (s.frequency (), s.mode ());
// record what actually changed
requested_.frequency (actual_.frequency ());
requested_.mode (actual_.mode ());
}
if (!s.tx_frequency () || s.tx_frequency () > 10000) // ignore bogus startup values
{
if ((s.tx_frequency () != requested_.tx_frequency () // and QSY
|| (s.mode () != UNK && s.mode () != requested_.mode ())) // or mode change
// || s.split () != requested_.split ())) // or split change
|| ptt_on) // or about to tx
{
do_tx_frequency (s.tx_frequency (), ptt_on);
do_post_tx_frequency (s.tx_frequency ());
// record what actually changed
requested_.tx_frequency (actual_.tx_frequency ());
requested_.split (actual_.split ());
}
}
if (ptt_on)
{
do_ptt (true);
do_post_ptt (true);
QThread::msleep (100); // some rigs cannot process CAT
// commands while switching from
// Rx to Tx
}
// record what actually changed
requested_.ptt (actual_.ptt ());
}
last_sequence_number_ = sequence_number;
}
catch (std::exception const& e)
{
message = e.what ();
}
catch (...)
{
message = unexpected;
}
if (!message.isEmpty ())
{
offline (message);
}
}
void TransceiverBase::startup ()
{
Q_EMIT resolution (do_start ());
do_post_start ();
actual_.online (true);
requested_.online (true);
}
void TransceiverBase::shutdown ()
{
may_update u {this};
if (requested_.online ())
{
try
{
// try and ensure PTT isn't left set
do_ptt (false);
do_post_ptt (false);
if (requested_.split ())
{
// try and reset split mode
do_tx_frequency (0, true);
do_post_tx_frequency (0);
}
}
catch (...)
{
// don't care about exceptions
}
}
do_stop ();
do_post_stop ();
actual_.online (false);
requested_.online (false);
}
void TransceiverBase::stop () noexcept
{
QString message;
try
{
if (state_.online ())
{
try
{
// try and ensure PTT isn't left set
do_ptt (false);
do_post_ptt (false);
if (reset_split)
{
// try and reset split mode
do_tx_frequency (0, false);
do_post_tx_frequency (0, false);
}
}
catch (...)
{
// don't care about exceptions
}
}
do_stop ();
do_post_stop ();
state_.online (false);
shutdown ();
}
catch (std::exception const& e)
{
@ -98,206 +189,82 @@ void TransceiverBase::stop (bool reset_split) noexcept
}
}
void TransceiverBase::frequency (Frequency f, MODE m) noexcept
{
QString message;
try
{
if (state_.online ())
{
do_frequency (f, m);
do_post_frequency (f, m);
}
}
catch (std::exception const& e)
{
message = e.what ();
}
catch (...)
{
message = unexpected;
}
if (!message.isEmpty ())
{
offline (message);
}
}
void TransceiverBase::tx_frequency (Frequency tx, bool rationalise_mode) noexcept
{
QString message;
try
{
if (state_.online ())
{
do_tx_frequency (tx, rationalise_mode);
do_post_tx_frequency (tx, rationalise_mode);
}
}
catch (std::exception const& e)
{
message = e.what ();
}
catch (...)
{
message = unexpected;
}
if (!message.isEmpty ())
{
offline (message);
}
}
void TransceiverBase::mode (MODE m, bool rationalise) noexcept
{
QString message;
try
{
if (state_.online ())
{
do_mode (m, rationalise);
do_post_mode (m, rationalise);
}
}
catch (std::exception const& e)
{
message = e.what ();
}
catch (...)
{
message = unexpected;
}
if (!message.isEmpty ())
{
offline (message);
}
}
void TransceiverBase::ptt (bool on) noexcept
{
QString message;
try
{
if (state_.online ())
{
do_ptt (on);
do_post_ptt (on);
}
}
catch (std::exception const& e)
{
message = e.what ();
}
catch (...)
{
message = unexpected;
}
if (!message.isEmpty ())
{
offline (message);
}
}
void TransceiverBase::sync (bool force_signal) noexcept
{
QString message;
try
{
if (state_.online ())
{
do_sync (force_signal);
}
}
catch (std::exception const& e)
{
message = e.what ();
}
catch (...)
{
message = unexpected;
}
if (!message.isEmpty ())
{
offline (message);
}
}
void TransceiverBase::update_rx_frequency (Frequency rx)
{
state_.frequency (rx);
actual_.frequency (rx);
requested_.frequency (rx); // track rig changes
}
void TransceiverBase::update_other_frequency (Frequency tx)
{
state_.tx_frequency (tx);
actual_.tx_frequency (tx);
}
void TransceiverBase::update_split (bool state)
{
state_.split (state);
actual_.split (state);
}
void TransceiverBase::update_mode (MODE m)
{
state_.mode (m);
actual_.mode (m);
}
void TransceiverBase::update_PTT (bool state)
{
auto prior = state_.ptt ();
state_.ptt (state);
if (state != prior)
{
// always signal PTT changes because some MainWindow logic
// depends on it
update_complete ();
}
actual_.ptt (state);
}
void TransceiverBase::updated ()
{
if (do_pre_update ())
{
Q_EMIT update (state_);
}
}
void TransceiverBase::update_complete ()
bool TransceiverBase::maybe_low_resolution (Radio::Frequency low_res,
Radio::Frequency high_res)
{
// Use a timer to ensure that the calling function completes before
// the Transceiver::update signal is triggered.
QTimer::singleShot (0, this, SLOT (updated ()));
if (resolution_ != Resolution::truncate
&& low_res == (high_res + 5) / 10 * 10) // rounded to 10's
{
resolution_ = Resolution::round;
return true;
}
if (resolution_ != Resolution::round
&& low_res == high_res / 10 * 10) // truncated to 10's
{
resolution_ = Resolution::truncate;
return true;
}
if (resolution_ != Resolution::truncate
&& low_res == (high_res + 50) / 100 * 100) // rounded to 100's
{
resolution_ = Resolution::round;
return true;
}
if (resolution_ != Resolution::round
&& low_res == high_res / 100 * 100) // truncated to 100's
{
resolution_ = Resolution::truncate;
return true;
}
return false;
}
void TransceiverBase::update_complete (bool force_signal)
{
if ((do_pre_update () && actual_ != last_) || force_signal)
{
Q_EMIT update (actual_, last_sequence_number_);
last_ = actual_;
}
}
void TransceiverBase::offline (QString const& reason)
{
QString message;
Q_EMIT failure (reason);
try
{
if (state_.online ())
{
try
{
// try and ensure PTT isn't left set
do_ptt (false);
do_post_ptt (false);
}
catch (...)
{
// don't care about exceptions
}
}
do_stop ();
do_post_stop ();
state_.online (false);
}
catch (std::exception const& e)
{
message = e.what ();
shutdown ();
}
catch (...)
{
message = unexpected;
// don't care
}
Q_EMIT failure (reason + '\n' + message);
}

View File

@ -60,20 +60,29 @@ class TransceiverBase
{
Q_OBJECT;
private:
enum class Resolution {accurate, round, truncate};
protected:
TransceiverBase () = default;
TransceiverBase (QObject * parent)
: Transceiver {parent}
, resolution_ {Resolution::accurate}
, last_sequence_number_ {0}
{}
public:
//
// Implement the Transceiver abstract interface.
//
void start () noexcept override final;
void stop (bool reset_split = false) noexcept override final;
void frequency (Frequency rx, MODE = UNK) noexcept override final;
void tx_frequency (Frequency tx, bool rationalise_mode) noexcept override final;
void mode (MODE, bool rationalise) noexcept override final;
void ptt (bool) noexcept override final;
void sync (bool force_signal) noexcept override final;
void start (unsigned sequence_number) noexcept override final;
void set (TransceiverState const&,
unsigned sequence_number) noexcept override final;
void stop () noexcept override final;
//
// Query operations
//
TransceiverState const& state () const {return actual_;}
protected:
//
@ -82,32 +91,32 @@ protected:
struct error
: public std::runtime_error
{
error (char const * const msg) : std::runtime_error (msg) {}
error (QString const& msg) : std::runtime_error (msg.toStdString ()) {}
explicit error (char const * const msg) : std::runtime_error (msg) {}
explicit error (QString const& msg) : std::runtime_error (msg.toStdString ()) {}
};
// Template methods that sub classes implement to do what they need to do.
//
// These methods may throw exceptions to signal errors.
virtual void do_start () = 0;
virtual int do_start () = 0; // returns resolution, See Transceiver::resolution
virtual void do_post_start () {}
virtual void do_stop () = 0;
virtual void do_post_stop () {}
virtual void do_frequency (Frequency rx, MODE = UNK) = 0;
virtual void do_post_frequency (Frequency, MODE = UNK) {}
virtual void do_frequency (Frequency, MODE, bool no_ignore) = 0;
virtual void do_post_frequency (Frequency, MODE) {}
virtual void do_tx_frequency (Frequency tx = 0u, bool rationalise_mode = false) = 0;
virtual void do_post_tx_frequency (Frequency = 0u, bool /* rationalise_mode */ = false) {}
virtual void do_tx_frequency (Frequency, bool no_ignore) = 0;
virtual void do_post_tx_frequency (Frequency) {}
virtual void do_mode (MODE, bool rationalise = true) = 0;
virtual void do_post_mode (MODE, bool /* rationalise */ = true) {}
virtual void do_mode (MODE) = 0;
virtual void do_post_mode (MODE) {}
virtual void do_ptt (bool = true) = 0;
virtual void do_post_ptt (bool = true) {}
virtual void do_sync (bool force_signal = false) = 0;
virtual void do_sync (bool force_signal = false, bool no_poll = false) = 0;
virtual bool do_pre_update () {return true;}
@ -119,31 +128,48 @@ protected:
void update_PTT (bool = true);
// Calling this eventually triggers the Transceiver::update(State) signal.
void update_complete ();
void update_complete (bool force_signal = false);
// sub class may asynchronously take the rig offline by calling this
void offline (QString const& reason);
// and query state with this one
TransceiverState const& state () const {return state_;}
private:
Q_SLOT void updated ();
void startup ();
void shutdown ();
bool maybe_low_resolution (Frequency low_res, Frequency high_res);
TransceiverState state_;
// use this convenience class to notify in update methods
class may_update
{
public:
explicit may_update (TransceiverBase * self, bool force_signal = false)
: self_ {self}
, force_signal_ {force_signal}
{}
~may_update () {self_->update_complete (force_signal_);}
private:
TransceiverBase * self_;
bool force_signal_;
};
TransceiverState requested_;
TransceiverState actual_;
TransceiverState last_;
Resolution resolution_; // rig accuracy
unsigned last_sequence_number_; // from set state operation
};
// some trace macros
#if WSJT_TRACE_CAT
#define TRACE_CAT(MSG) qDebug () << __PRETTY_FUNCTION__ << MSG
#define TRACE_CAT(FAC, MSG) qDebug () << QString {"%1::%2:"}.arg ((FAC)).arg (__func__) << MSG
#else
#define TRACE_CAT(MSG)
#define TRACE_CAT(FAC, MSG)
#endif
#if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS
#define TRACE_CAT_POLL(MSG) qDebug () << __PRETTY_FUNCTION__ << MSG
#define TRACE_CAT_POLL(FAC, MSG) qDebug () << QString {"%1::%2:"}.arg ((FAC)).arg (__func__) << MSG
#else
#define TRACE_CAT_POLL(MSG)
#define TRACE_CAT_POLL(FAC, MSG)
#endif
#endif

View File

@ -82,36 +82,44 @@ std::unique_ptr<Transceiver> TransceiverFactory::create (ParameterPack const& pa
{
case CommanderId:
{
// we start with a dummy HamlibTransceiver object instance that can support direct PTT
std::unique_ptr<TransceiverBase> basic_transceiver {new HamlibTransceiver {params.ptt_type, params.ptt_port}};
if (target_thread)
std::unique_ptr<TransceiverBase> basic_transceiver;
if (PTT_method_CAT != params.ptt_type)
{
basic_transceiver.get ()->moveToThread (target_thread);
// we start with a dummy HamlibTransceiver object instance that can support direct PTT
basic_transceiver.reset (new HamlibTransceiver {params.ptt_type, params.ptt_port});
if (target_thread)
{
basic_transceiver.get ()->moveToThread (target_thread);
}
}
// wrap the basic Transceiver object instance with a decorator object that talks to DX Lab Suite Commander
result.reset (new DXLabSuiteCommanderTransceiver {std::move (basic_transceiver), params.network_port, PTT_method_CAT == params.ptt_type, params.poll_interval});
if (target_thread)
{
result.get ()->moveToThread (target_thread);
result->moveToThread (target_thread);
}
}
break;
case HRDId:
{
// we start with a dummy HamlibTransceiver object instance that can support direct PTT
std::unique_ptr<TransceiverBase> basic_transceiver {new HamlibTransceiver {params.ptt_type, params.ptt_port}};
if (target_thread)
std::unique_ptr<TransceiverBase> basic_transceiver;
if (PTT_method_CAT != params.ptt_type)
{
basic_transceiver.get ()->moveToThread (target_thread);
// we start with a dummy HamlibTransceiver object instance that can support direct PTT
basic_transceiver.reset (new HamlibTransceiver {params.ptt_type, params.ptt_port});
if (target_thread)
{
basic_transceiver.get ()->moveToThread (target_thread);
}
}
// wrap the basic Transceiver object instance with a decorator object that talks to ham Radio Deluxe
result.reset (new HRDTransceiver {std::move (basic_transceiver), params.network_port, PTT_method_CAT == params.ptt_type, params.poll_interval});
result.reset (new HRDTransceiver {std::move (basic_transceiver), params.network_port, PTT_method_CAT == params.ptt_type, params.poll_interval, params.set_rig_mode});
if (target_thread)
{
result.get ()->moveToThread (target_thread);
result->moveToThread (target_thread);
}
}
break;
@ -119,36 +127,44 @@ std::unique_ptr<Transceiver> TransceiverFactory::create (ParameterPack const& pa
#if defined (WIN32)
case OmniRigOneId:
{
// we start with a dummy HamlibTransceiver object instance that can support direct PTT
std::unique_ptr<TransceiverBase> basic_transceiver {new HamlibTransceiver {params.ptt_type, params.ptt_port}};
if (target_thread)
std::unique_ptr<TransceiverBase> basic_transceiver;
if (PTT_method_CAT != params.ptt_type && "CAT" != params.ptt_port)
{
basic_transceiver.get ()->moveToThread (target_thread);
// we start with a dummy HamlibTransceiver object instance that can support direct PTT
basic_transceiver.reset (new HamlibTransceiver {params.ptt_type, params.ptt_port});
if (target_thread)
{
basic_transceiver.get ()->moveToThread (target_thread);
}
}
// wrap the basic Transceiver object instance with a decorator object that talks to OmniRig rig one
result.reset (new OmniRigTransceiver {std::move (basic_transceiver), OmniRigTransceiver::One, params.ptt_type, params.ptt_port});
if (target_thread)
{
result.get ()->moveToThread (target_thread);
result->moveToThread (target_thread);
}
}
break;
case OmniRigTwoId:
{
// we start with a dummy HamlibTransceiver object instance that can support direct PTT
std::unique_ptr<TransceiverBase> basic_transceiver {new HamlibTransceiver {params.ptt_type, params.ptt_port}};
if (target_thread)
std::unique_ptr<TransceiverBase> basic_transceiver;
if (PTT_method_CAT != params.ptt_type && "CAT" != params.ptt_port)
{
basic_transceiver.get ()->moveToThread (target_thread);
// we start with a dummy HamlibTransceiver object instance that can support direct PTT
basic_transceiver.reset (new HamlibTransceiver {params.ptt_type, params.ptt_port});
if (target_thread)
{
basic_transceiver.get ()->moveToThread (target_thread);
}
}
// wrap the basic Transceiver object instance with a decorator object that talks to OmniRig rig two
result.reset (new OmniRigTransceiver {std::move (basic_transceiver), OmniRigTransceiver::Two, params.ptt_type, params.ptt_port});
if (target_thread)
{
result.get ()->moveToThread (target_thread);
result->moveToThread (target_thread);
}
}
break;
@ -158,7 +174,7 @@ std::unique_ptr<Transceiver> TransceiverFactory::create (ParameterPack const& pa
result.reset (new HamlibTransceiver {supported_transceivers ()[params.rig_name].model_number_, params});
if (target_thread)
{
result.get ()->moveToThread (target_thread);
result->moveToThread (target_thread);
}
break;
}
@ -169,7 +185,7 @@ std::unique_ptr<Transceiver> TransceiverFactory::create (ParameterPack const& pa
result.reset (new EmulateSplitTransceiver {std::move (result)});
if (target_thread)
{
result.get ()->moveToThread (target_thread);
result->moveToThread (target_thread);
}
}

View File

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

190
astro.cpp
View File

@ -20,30 +20,37 @@
#include "ui_astro.h"
#include "moc_astro.cpp"
extern "C" {
void astrosub_(int* nyear, int* month, int* nday, double* uth, double* freqMoon,
const char* mygrid, const char* hisgrid, double* azsun,
double* elsun, double* azmoon, double* elmoon, double* azmoondx,
double* elmoondx, int* ntsky, int* ndop, int* ndop00,
double* ramoon, double* decmoon, double* dgrd, double* poloffset,
double* xnr, double* techo, double* width1, double* width2,
bool* bTx, const char* AzElFileName, const char* jpleph,
int len1, int len2, int len3, int len4);
}
Astro::Astro(QSettings * settings, Configuration const * configuration, QWidget * parent)
: QWidget {parent, Qt::Tool | Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowMinimizeButtonHint | Qt::MSWindowsFixedSizeDialogHint}
: QDialog {parent, Qt::WindowTitleHint}
, settings_ {settings}
, configuration_ {configuration}
, ui_ {new Ui::Astro}
, m_bRxAudioTrack {false}
, m_bTxAudioTrack {false}
, m_bTrackVFO {true}
, m_DopplerMethod {0}
, m_kHz {0}
, m_Hz {0}
, m_stepHz {1}
{
ui_->setupUi (this);
setWindowTitle (QApplication::applicationName () + " - " + tr ("Astronomical Data"));
setStyleSheet ("QWidget {background: white;}");
connect (ui_->cbDopplerTracking, &QAbstractButton::toggled, ui_->doppler_widget, &QWidget::setVisible);
connect (ui_->cbDopplerTracking, &QAbstractButton::toggled, this, &Astro::doppler_tracking_toggled);
read_settings ();
ui_->text_label->clear ();
}
Astro::~Astro ()
{
ui_->cbDopplerTracking->setChecked (false);
Q_EMIT tracking_update ();
if (isVisible ()) write_settings ();
}
@ -56,40 +63,30 @@ void Astro::closeEvent (QCloseEvent * e)
void Astro::read_settings ()
{
SettingsGroup g (settings_, "Astro");
ui_->cbDopplerTracking->setChecked (settings_->value ("DopplerTracking",false).toBool ());
ui_->doppler_widget->setVisible (ui_->cbDopplerTracking->isChecked ());
m_DopplerMethod=settings_->value("DopplerMethod",0).toInt();
if(m_DopplerMethod==0) ui_->rbNoDoppler->setChecked(true);
if(m_DopplerMethod==1) ui_->rbFullTrack->setChecked(true);
if(m_DopplerMethod==2) ui_->rbConstFreqOnMoon->setChecked(true);
m_stepHz=settings_->value("StepHz",1).toInt();
if(m_stepHz==1) ui_->rb1Hz->setChecked(true);
if(m_stepHz==10) ui_->rb10Hz->setChecked(true);
if(m_stepHz==100) ui_->rb100Hz->setChecked(true);
m_kHz=settings_->value("kHzAdd",100).toInt();
ui_->kHzSpinBox->setValue(m_kHz);
m_bRxAudioTrack=settings_->value("RxAudioTrack",false).toBool();
m_bTxAudioTrack=settings_->value("TxAudioTrack",false).toBool();
ui_->cbTxAudioTrack->setChecked(m_bTxAudioTrack);
switch (m_DopplerMethod)
{
case 0: ui_->rbNoDoppler->setChecked (true); break;
case 1: ui_->rbFullTrack->setChecked (true); break;
case 2: ui_->rbConstFreqOnMoon->setChecked (true); break;
case 3: ui_->rbRxOnly->setChecked (true); break;
}
move (settings_->value ("window/pos", pos ()).toPoint ());
}
void Astro::write_settings ()
{
SettingsGroup g (settings_, "Astro");
settings_->setValue ("DopplerTracking", ui_->cbDopplerTracking->isChecked ());
//settings_->setValue ("DopplerTracking", ui_->cbDopplerTracking->isChecked ());
settings_->setValue ("DopplerMethod",m_DopplerMethod);
settings_->setValue ("StepHz",m_stepHz);
settings_->setValue ("kHzAdd",m_kHz);
settings_->setValue ("RxAudioTrack",m_bRxAudioTrack);
settings_->setValue ("TxAudioTrack",m_bTxAudioTrack);
settings_->setValue ("window/pos", pos ());
}
auto Astro::astroUpdate(QDateTime const& t, QString const& mygrid, QString const& hisgrid, Frequency freq,
bool dx_is_self, bool bTx) -> FrequencyDelta
bool dx_is_self, bool bTx, bool no_tx_QSY, int TR_period) -> Correction
{
Frequency freq_moon {freq + 1000 * m_kHz + m_Hz};
Frequency freq_moon {freq};
double azsun,elsun,azmoon,elmoon,azmoondx,elmoondx;
double ramoon,decmoon,dgrd,poloffset,xnr,techo,width1,width2;
int ntsky;
@ -148,94 +145,125 @@ auto Astro::astroUpdate(QDateTime const& t, QString const& mygrid, QString const
}
ui_->text_label->setText(message);
FrequencyDelta astro_correction {0};
Correction correction;
if (ui_->cbDopplerTracking->isChecked ()) {
switch (m_DopplerMethod)
{
case 1:
// All Doppler correction done here; DX station stays at nominal dial frequency.
if(dx_is_self) {
astro_correction = m_stepHz*qRound(double(ndop00)/m_stepHz);
} else {
astro_correction = m_stepHz*qRound(double(ndop)/m_stepHz);
}
case 1: // All Doppler correction done here; DX station stays at nominal dial frequency.
case 3: // Both stations do full correction on Rx and none on Tx
correction.rx = dx_is_self ? ndop00 : ndop;
break;
case 2:
// Doppler correction to constant frequency on Moon
astro_correction = m_stepHz*qRound(double(ndop00/2.0)/m_stepHz);
correction.rx = ndop00 / 2;
break;
}
if (bTx) {
astro_correction = 1000 * m_kHz + m_Hz - astro_correction;
} else {
if(dx_is_self && m_DopplerMethod==1) {
astro_correction = 1000*m_kHz + m_Hz;
} else {
astro_correction += 1000*m_kHz + m_Hz;
if (3 != m_DopplerMethod) correction.tx = -correction.rx;
if(dx_is_self && m_DopplerMethod == 1) correction.rx = 0;
if (no_tx_QSY && 3 != m_DopplerMethod && 0 != m_DopplerMethod)
{
// calculate a single correction for transmit half way through
// the period as a compromise for rigs that can't CAT QSY
// while transmitting
//
// use a base time of (secs-since-epoch + 2) so as to be sure
// we do the next period if we calculate just before it starts
auto sec_since_epoch = t.toMSecsSinceEpoch () / 1000 + 2;
auto target_sec = sec_since_epoch - sec_since_epoch % TR_period + TR_period / 2;
auto target_date_time = QDateTime::fromMSecsSinceEpoch (target_sec * 1000);
QString date {target_date_time.date().toString("yyyy MMM dd").trimmed ()};
QString utc {target_date_time.time().toString().trimmed ()};
int nyear {target_date_time.date().year()};
int month {target_date_time.date().month()};
int nday {target_date_time.date().day()};
int nhr {target_date_time.time().hour()};
int nmin {target_date_time.time().minute()};
double sec {target_date_time.time().second() + 0.001*target_date_time.time().msec()};
double uth {nhr + nmin/60.0 + sec/3600.0};
astrosub_(&nyear, &month, &nday, &uth, &freq8, mygrid.toLatin1().constData(),
hisgrid.toLatin1().constData(), &azsun, &elsun, &azmoon, &elmoon,
&azmoondx, &elmoondx, &ntsky, &ndop, &ndop00, &ramoon, &decmoon,
&dgrd, &poloffset, &xnr, &techo, &width1, &width2, &bTx,
"", jpleph.toLatin1().constData(), 6, 6,
0, jpleph.length());
FrequencyDelta offset {0};
switch (m_DopplerMethod)
{
case 1:
// All Doppler correction done here; DX station stays at nominal dial frequency.
offset = dx_is_self ? ndop00 : ndop;
break;
case 2:
// Doppler correction to constant frequency on Moon
offset = ndop00 / 2;
break;
}
correction.tx = -offset;
}
}
}
return astro_correction;
return correction;
}
void Astro::check_split ()
{
if (doppler_tracking () && !configuration_->split_mode ())
{
QMessageBox::warning (this, "Doppler Tracking",
"Split operating is required for Doppler tracking");
ui_->rbNoDoppler->click ();
}
}
void Astro::on_rbFullTrack_clicked()
{
m_DopplerMethod = 1;
check_split ();
Q_EMIT tracking_update ();
}
void Astro::on_rbRxOnly_clicked()
{
m_DopplerMethod = 3;
check_split ();
Q_EMIT tracking_update ();
}
void Astro::on_rbConstFreqOnMoon_clicked()
{
m_DopplerMethod = 2;
check_split ();
Q_EMIT tracking_update ();
}
void Astro::on_rbNoDoppler_clicked()
{
m_DopplerMethod = 0;
}
void Astro::on_rb1Hz_clicked()
{
m_stepHz=1;
}
void Astro::on_rb10Hz_clicked()
{
m_stepHz=10;
}
void Astro::on_rb100Hz_clicked()
{
m_stepHz=100;
}
void Astro::on_cbTxAudioTrack_toggled(bool b)
{
m_bTxAudioTrack=b;
}
void Astro::on_kHzSpinBox_valueChanged(int n)
{
m_kHz=n;
}
void Astro::on_HzSpinBox_valueChanged(int n)
{
m_Hz=n;
Q_EMIT tracking_update ();
}
bool Astro::doppler_tracking () const
{
return ui_->cbDopplerTracking->isChecked ();
return ui_->cbDopplerTracking->isChecked () && m_DopplerMethod;
}
bool Astro::trackVFO()
void Astro::on_cbDopplerTracking_toggled(bool)
{
return m_bTrackVFO;
check_split ();
Q_EMIT tracking_update ();
}
void Astro::on_cbTrackVFO_toggled(bool b)
void Astro::nominal_frequency (Frequency rx, Frequency tx)
{
m_bTrackVFO=b;
ui_->sked_frequency_label->setText (Radio::pretty_frequency_MHz_string (rx));
ui_->sked_tx_frequency_label->setText (Radio::pretty_frequency_MHz_string (tx));
}
void Astro::hideEvent (QHideEvent * e)
{
Q_EMIT tracking_update ();
QWidget::hideEvent (e);
}

71
astro.h
View File

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

489
astro.ui
View File

@ -1,243 +1,246 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Astro</class>
<widget class="QWidget" name="Astro">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>440</width>
<height>406</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item row="0" column="1">
<widget class="QWidget" name="doppler_widget" native="true">
<property name="styleSheet">
<string notr="true">* {
font-weight: normal;
}</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Frequency above nominal band edge</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QSpinBox" name="kHzSpinBox">
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="suffix">
<string> kHz</string>
</property>
<property name="maximum">
<number>999</number>
</property>
<property name="value">
<number>200</number>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="HzSpinBox">
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="suffix">
<string> Hz</string>
</property>
<property name="minimum">
<number>-2000</number>
</property>
<property name="maximum">
<number>2000</number>
</property>
<property name="singleStep">
<number>100</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Doppler tracking</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QRadioButton" name="rbFullTrack">
<property name="text">
<string>Full Doppler to DX Grid</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rbConstFreqOnMoon">
<property name="text">
<string>Constant frequency on Moon</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rbNoDoppler">
<property name="text">
<string>None</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Transceiver step size</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QRadioButton" name="rb1Hz">
<property name="text">
<string>1 Hz</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rb10Hz">
<property name="text">
<string>10 Hz</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rb100Hz">
<property name="text">
<string>100 Hz</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="enabled">
<bool>true</bool>
</property>
<property name="title">
<string>Enable</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QCheckBox" name="cbTrackVFO">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Track VFOs</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbTxAudioTrack">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Track Tx audio</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item alignment="Qt::AlignHCenter">
<widget class="QLabel" name="text_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">* {
font-family: Courier;
font-size: 12pt;
font-weight: bold;
}</string>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="text">
<string>Astro Data</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="margin">
<number>6</number>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="cbDopplerTracking">
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>Doppler tracking</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Astro</class>
<widget class="QWidget" name="Astro">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>359</width>
<height>287</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item row="0" column="1">
<widget class="QWidget" name="doppler_widget" native="true">
<property name="styleSheet">
<string notr="true">* {
font-weight: normal;
}</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Doppler tracking</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QRadioButton" name="rbFullTrack">
<property name="toolTip">
<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 name="text">
<string>Full Doppler to DX Grid</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rbRxOnly">
<property name="toolTip">
<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 name="text">
<string>Receive only</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rbConstFreqOnMoon">
<property name="toolTip">
<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>
</property>
<property name="text">
<string>Constant frequency on Moon</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rbNoDoppler">
<property name="toolTip">
<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>
<property name="text">
<string>None</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="enabled">
<bool>true</bool>
</property>
<property name="title">
<string>Sked frequency</string>
</property>
<layout class="QGridLayout" name="gridLayout_2" columnstretch="0,1">
<item row="1" column="1">
<widget class="QLabel" name="sked_tx_frequency_label">
<property name="styleSheet">
<string notr="true">* {
font-family: Courier;
font-size: 12pt;
font-weight: bold;
}</string>
</property>
<property name="text">
<string>0</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="sked_frequency_label">
<property name="styleSheet">
<string notr="true">* {
font-family: Courier;
font-size: 12pt;
font-weight: bold;
}</string>
</property>
<property name="text">
<string>0</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="styleSheet">
<string notr="true">* {
font-family: Courier;
font-size: 12pt;
font-weight: bold;
}</string>
</property>
<property name="text">
<string>Rx:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="styleSheet">
<string notr="true">* {
font-family: Courier;
font-size: 12pt;
font-weight: bold;
}</string>
</property>
<property name="text">
<string>Tx:</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QLabel" name="label_3">
<property name="text">
<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>
</property>
<property name="textFormat">
<enum>Qt::AutoText</enum>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item alignment="Qt::AlignHCenter">
<widget class="QLabel" name="text_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">* {
font-family: Courier;
font-size: 12pt;
font-weight: bold;
}</string>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="text">
<string>Astro Data</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="margin">
<number>6</number>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="cbDopplerTracking">
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>Doppler tracking</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</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 "decodedtext.h"
#include "commons.h"
#include "astro.h"
#define NUM_JT4_SYMBOLS 206 //(72+31)*2, embedded sync
#define NUM_JT65_SYMBOLS 126 //63 data + 63 sync
@ -59,7 +60,6 @@ class FastGraph;
class WideGraph;
class LogQSO;
class Transceiver;
class Astro;
class MessageAveraging;
class MessageClient;
class QTime;
@ -78,6 +78,7 @@ class MainWindow : public QMainWindow
public:
using Frequency = Radio::Frequency;
using FrequencyDelta = Radio::FrequencyDelta;
using Mode = Modes::Mode;
// Multiple instances: call MainWindow() with *thekey
@ -103,7 +104,7 @@ public slots:
void p1ReadFromStdout();
void p1ReadFromStderr();
void p1Error(QProcess::ProcessError);
void setXIT(int n);
void setXIT(int n, Frequency base = 0u);
void setFreq4(int rxFreq, int txFreq);
void msgAvgDecode2();
void fastPick(int x0, int x1, int y);
@ -199,9 +200,9 @@ private slots:
void on_cbTxLock_clicked(bool checked);
void on_outAttenuation_valueChanged (int);
void rigOpen ();
void handle_transceiver_update (Transceiver::TransceiverState);
void handle_transceiver_failure (QString reason);
void on_actionAstronomical_data_triggered();
void handle_transceiver_update (Transceiver::TransceiverState const&);
void handle_transceiver_failure (QString const& reason);
void on_actionAstronomical_data_toggled (bool);
void on_actionShort_list_of_add_on_prefixes_and_suffixes_triggered();
void getpfx();
void band_changed (Frequency);
@ -238,7 +239,6 @@ private slots:
void on_pbTxNext_clicked(bool b);
void on_actionEcho_Graph_triggered();
void on_actionEcho_triggered();
void DopplerTracking_toggled (bool);
void on_actionISCAT_triggered();
void on_actionFast_Graph_triggered();
void fast_decode_done();
@ -274,6 +274,8 @@ private:
Q_SIGNAL void toggleShorthand () const;
private:
void astroUpdate ();
QDir m_dataDir;
QString m_revision;
bool m_multiple;
@ -298,9 +300,11 @@ private:
QScopedPointer<HelpTextWindow> m_mouseCmnds;
QScopedPointer<MessageAveraging> m_msgAvgWidget;
Frequency m_dialFreq;
Frequency m_dialFreq0;
Frequency m_dialFreqRxWSPR;
Transceiver::TransceiverState m_rigState;
Frequency m_lastDialFreq;
QString m_lastBand;
Frequency m_callingFrequency;
Frequency m_dialFreqRxWSPR; // best guess at WSPR QRG
Detector * m_detector;
SoundInput * m_soundInput;
@ -311,9 +315,9 @@ private:
qint64 m_msErase;
qint64 m_secBandChanged;
qint64 m_freqMoon;
qint64 m_freqNominal;
qint64 m_dialFreqTx;
qint64 m_dialFreqRx;
Frequency m_freqNominal;
Frequency m_freqTxNominal;
Astro::Correction m_astroCorrection;
double m_s6;
double m_tRemaining;
@ -536,7 +540,6 @@ private:
void msgtype(QString t, QLineEdit* tx);
void stub();
void statusChanged();
void qsy(Frequency f);
bool gridOK(QString g);
bool shortList(QString callsign);
void transmit (double snr = 99.);
@ -552,7 +555,7 @@ private:
void enable_DXCC_entity (bool on);
void switch_mode (Mode);
void WSPR_scheduling ();
void astroCalculations (QDateTime const&, bool adjust);
void setRig ();
void WSPR_history(Frequency dialFreq, int ndecodes);
QString WSPR_hhmm(int n);
void fast_config(bool b);

File diff suppressed because it is too large Load Diff