2026-01-18 12:53:55 -06:00
|
|
|
/**
|
|
|
|
|
* @file PollingTransceiver.cpp
|
|
|
|
|
* @brief Implementation of the PollingTransceiver class
|
|
|
|
|
* Helper base class that encapsulates the emulation of continuous update and
|
|
|
|
|
* caching of a transceiver state.
|
|
|
|
|
*/
|
2026-01-01 15:12:24 -06:00
|
|
|
|
2026-01-18 12:53:55 -06:00
|
|
|
#include "PollingTransceiver.h"
|
2026-01-01 15:12:24 -06:00
|
|
|
|
|
|
|
|
#include <QObject>
|
|
|
|
|
#include <QString>
|
|
|
|
|
#include <QTimer>
|
|
|
|
|
|
2026-01-18 12:53:55 -06:00
|
|
|
#include <exception>
|
|
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
#include "moc_PollingTransceiver.cpp"
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
unsigned const polls_to_stabilize{3};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PollingTransceiver::PollingTransceiver(int poll_interval, QObject *parent)
|
|
|
|
|
: TransceiverBase{parent}, interval_{poll_interval * 1000},
|
|
|
|
|
poll_timer_{nullptr}, retries_{0} {}
|
|
|
|
|
|
|
|
|
|
void PollingTransceiver::start_timer() {
|
|
|
|
|
if (interval_) {
|
|
|
|
|
if (!poll_timer_) {
|
|
|
|
|
poll_timer_ = new QTimer{this}; // pass ownership to
|
|
|
|
|
// QObject which handles
|
|
|
|
|
// destruction for us
|
|
|
|
|
|
|
|
|
|
connect(poll_timer_, &QTimer::timeout, this,
|
|
|
|
|
&PollingTransceiver::handle_timeout);
|
|
|
|
|
}
|
|
|
|
|
poll_timer_->start(interval_);
|
|
|
|
|
} else {
|
|
|
|
|
stop_timer();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PollingTransceiver::stop_timer() {
|
|
|
|
|
if (poll_timer_) {
|
|
|
|
|
poll_timer_->stop();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PollingTransceiver::do_post_start() {
|
|
|
|
|
start_timer();
|
|
|
|
|
if (!next_state_.online()) {
|
|
|
|
|
// remember that we are expecting to go online
|
|
|
|
|
next_state_.online(true);
|
|
|
|
|
retries_ = polls_to_stabilize;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PollingTransceiver::do_post_stop() {
|
|
|
|
|
// not much point waiting for rig to go offline since we are ceasing
|
|
|
|
|
// polls
|
|
|
|
|
stop_timer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PollingTransceiver::do_post_frequency(Frequency f, MODE m) {
|
|
|
|
|
// take care not to set the expected next mode to unknown since some
|
|
|
|
|
// callers use mode == unknown to signify that they do not know the
|
|
|
|
|
// mode and don't care
|
|
|
|
|
if (next_state_.frequency() != f || (m != UNK && next_state_.mode() != m)) {
|
|
|
|
|
// update expected state with new frequency and set poll count
|
|
|
|
|
next_state_.frequency(f);
|
|
|
|
|
if (m != UNK) {
|
|
|
|
|
next_state_.mode(m);
|
|
|
|
|
}
|
|
|
|
|
retries_ = polls_to_stabilize;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PollingTransceiver::do_post_tx_frequency(Frequency f, MODE) {
|
|
|
|
|
if (next_state_.tx_frequency() != f) {
|
|
|
|
|
// update expected state with new TX frequency and set poll
|
|
|
|
|
// count
|
|
|
|
|
next_state_.tx_frequency(f);
|
|
|
|
|
next_state_.split(f); // setting non-zero TX frequency means split
|
|
|
|
|
retries_ = polls_to_stabilize;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PollingTransceiver::do_post_mode(MODE m) {
|
|
|
|
|
// we don't ever expect mode to goto to unknown
|
|
|
|
|
if (m != UNK && next_state_.mode() != m) {
|
|
|
|
|
// update expected state with new mode and set poll count
|
|
|
|
|
next_state_.mode(m);
|
|
|
|
|
retries_ = polls_to_stabilize;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PollingTransceiver::do_post_ptt(bool p) {
|
|
|
|
|
if (next_state_.ptt() != p) {
|
|
|
|
|
// update expected state with new PTT and set poll count
|
|
|
|
|
next_state_.ptt(p);
|
|
|
|
|
retries_ = polls_to_stabilize;
|
|
|
|
|
// retries_ = 0; // fast feedback on PTT
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool PollingTransceiver::do_pre_update() {
|
|
|
|
|
// if we are holding off a change then withhold the signal
|
|
|
|
|
if (retries_ && state() != next_state_) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PollingTransceiver::do_sync(bool force_signal, bool no_poll) {
|
|
|
|
|
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
|
|
|
|
|
// polls. Unsolicited changes will be signalled immediately unless
|
|
|
|
|
// they intervene in a expected sequence where they will be delayed.
|
|
|
|
|
if (retries_) {
|
|
|
|
|
--retries_;
|
|
|
|
|
if (force_signal || state() == next_state_ || !retries_) {
|
|
|
|
|
// our client wants a signal regardless
|
|
|
|
|
// or the expected state has arrived
|
|
|
|
|
// or there are no more retries
|
|
|
|
|
force_signal = true;
|
|
|
|
|
}
|
|
|
|
|
} 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 state has
|
|
|
|
|
// changed asynchronously
|
|
|
|
|
force_signal = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (force_signal) {
|
|
|
|
|
// reset everything, record and signal the current state
|
|
|
|
|
retries_ = 0;
|
|
|
|
|
next_state_ = state();
|
|
|
|
|
last_signalled_state_ = state();
|
|
|
|
|
update_complete(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PollingTransceiver::handle_timeout() {
|
|
|
|
|
QString message;
|
|
|
|
|
|
|
|
|
|
// we must catch all exceptions here since we are called by Qt and
|
|
|
|
|
// inform our parent of the failure via the offline() message
|
|
|
|
|
try {
|
|
|
|
|
do_sync();
|
|
|
|
|
} catch (std::exception const &e) {
|
|
|
|
|
message = e.what();
|
|
|
|
|
} catch (...) {
|
|
|
|
|
message = tr("Unexpected rig error");
|
|
|
|
|
}
|
|
|
|
|
if (!message.isEmpty()) {
|
|
|
|
|
offline(message);
|
|
|
|
|
}
|
|
|
|
|
}
|