mirror of
https://github.com/JS8Call-improved/JS8Call-improved
synced 2026-08-13 17:47:36 -04:00
504 lines
18 KiB
C++
504 lines
18 KiB
C++
|
|
#include "DXLabSuiteCommanderTransceiver.h"
|
||
|
|
|
||
|
|
#include <QDateTime>
|
||
|
|
#include <QLocale>
|
||
|
|
#include <QLoggingCategory>
|
||
|
|
#include <QRegularExpression>
|
||
|
|
#include <QTcpSocket>
|
||
|
|
#include <QThread>
|
||
|
|
|
||
|
|
#include "JS8_Main/DriftingDateTime.h"
|
||
|
|
|
||
|
|
#include "JS8_Network/NetworkServerLookup.h"
|
||
|
|
|
||
|
|
#include "moc_DXLabSuiteCommanderTransceiver.cpp"
|
||
|
|
|
||
|
|
Q_DECLARE_LOGGING_CATEGORY(dxlabsuitecommandertransceiver_js8)
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
char const *const commander_transceiver_name{"DX Lab Suite Commander"};
|
||
|
|
|
||
|
|
QString map_mode(Transceiver::MODE mode) {
|
||
|
|
switch (mode) {
|
||
|
|
case Transceiver::AM:
|
||
|
|
return "AM";
|
||
|
|
case Transceiver::CW:
|
||
|
|
return "CW";
|
||
|
|
case Transceiver::CW_R:
|
||
|
|
return "CW-R";
|
||
|
|
case Transceiver::USB:
|
||
|
|
return "USB";
|
||
|
|
case Transceiver::LSB:
|
||
|
|
return "LSB";
|
||
|
|
case Transceiver::FSK:
|
||
|
|
return "RTTY";
|
||
|
|
case Transceiver::FSK_R:
|
||
|
|
return "RTTY-R";
|
||
|
|
case Transceiver::DIG_L:
|
||
|
|
return "DATA-L";
|
||
|
|
case Transceiver::DIG_U:
|
||
|
|
return "DATA-U";
|
||
|
|
case Transceiver::FM:
|
||
|
|
case Transceiver::DIG_FM:
|
||
|
|
return "FM";
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
return "USB";
|
||
|
|
}
|
||
|
|
} // namespace
|
||
|
|
|
||
|
|
void DXLabSuiteCommanderTransceiver::register_transceivers(
|
||
|
|
TransceiverFactory::Transceivers *registry, int id) {
|
||
|
|
(*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, QObject *parent)
|
||
|
|
: PollingTransceiver{poll_interval, parent}, wrapped_{std::move(wrapped)},
|
||
|
|
use_for_ptt_{use_for_ptt}, server_{address}, commander_{nullptr} {}
|
||
|
|
|
||
|
|
int DXLabSuiteCommanderTransceiver::do_start() {
|
||
|
|
TRACE_CAT("DXLabSuiteCommanderTransceiver", "starting");
|
||
|
|
if (wrapped_)
|
||
|
|
wrapped_->start(0);
|
||
|
|
|
||
|
|
auto server_details =
|
||
|
|
network_server_lookup(server_, 52002u, QHostAddress::LocalHost,
|
||
|
|
QAbstractSocket::IPv4Protocol);
|
||
|
|
|
||
|
|
if (!commander_) {
|
||
|
|
commander_ = new QTcpSocket{this}; // QObject takes ownership
|
||
|
|
}
|
||
|
|
|
||
|
|
commander_->connectToHost(std::get<0>(server_details),
|
||
|
|
std::get<1>(server_details));
|
||
|
|
if (!commander_->waitForConnected()) {
|
||
|
|
TRACE_CAT("DXLabSuiteCommanderTransceiver",
|
||
|
|
"failed to connect" << commander_->errorString());
|
||
|
|
throw error{tr("Failed to connect to DX Lab Suite Commander\n") +
|
||
|
|
commander_->errorString()};
|
||
|
|
}
|
||
|
|
|
||
|
|
// sleeps here are to ensure Commander has actually queried the rig
|
||
|
|
// rather than returning cached data which maybe stale or simply
|
||
|
|
// read backs of not yet committed values, the 2s delays are
|
||
|
|
// arbitrary but hopefully enough as the actual time required is rig
|
||
|
|
// and Commander setting dependent
|
||
|
|
int resolution{0};
|
||
|
|
QThread::msleep(2000);
|
||
|
|
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()));
|
||
|
|
QThread::msleep(2000);
|
||
|
|
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 -15:
|
||
|
|
resolution = -2;
|
||
|
|
break; // 20Hz truncated
|
||
|
|
case -55:
|
||
|
|
resolution = -2;
|
||
|
|
break; // 100Hz truncated
|
||
|
|
case 45:
|
||
|
|
resolution = 2;
|
||
|
|
break; // 100Hz rounded
|
||
|
|
}
|
||
|
|
if (1 == resolution) // may be 20Hz rounded
|
||
|
|
{
|
||
|
|
test_frequency = f - f % 100 + 51;
|
||
|
|
f_string = frequency_to_string(test_frequency);
|
||
|
|
params = ("<xcvrfreq:%1>" + f_string).arg(f_string.size());
|
||
|
|
simple_command(
|
||
|
|
("<command:10>CmdSetFreq<parameters:%1>" + params)
|
||
|
|
.arg(params.size()));
|
||
|
|
QThread::msleep(2000);
|
||
|
|
reply = command_with_reply(
|
||
|
|
"<command:10>CmdGetFreq<parameters:0>");
|
||
|
|
new_frequency =
|
||
|
|
string_to_frequency(reply.mid(reply.indexOf('>') + 1));
|
||
|
|
if (9 == static_cast<Radio::FrequencyDelta>(
|
||
|
|
new_frequency - test_frequency)) {
|
||
|
|
resolution = 2; // 20Hz 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" << reply);
|
||
|
|
throw error{tr("DX Lab Suite Commander didn't respond correctly "
|
||
|
|
"reading frequency: ") +
|
||
|
|
reply};
|
||
|
|
}
|
||
|
|
|
||
|
|
poll();
|
||
|
|
return resolution;
|
||
|
|
}
|
||
|
|
|
||
|
|
void DXLabSuiteCommanderTransceiver::do_stop() {
|
||
|
|
if (commander_) {
|
||
|
|
commander_->close();
|
||
|
|
delete commander_, commander_ = nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (wrapped_)
|
||
|
|
wrapped_->stop();
|
||
|
|
TRACE_CAT("DXLabSuiteCommanderTransceiver", "stopped");
|
||
|
|
}
|
||
|
|
|
||
|
|
void DXLabSuiteCommanderTransceiver::do_ptt(bool on) {
|
||
|
|
TRACE_CAT("DXLabSuiteCommanderTransceiver", on << state());
|
||
|
|
if (use_for_ptt_) {
|
||
|
|
simple_command(on ? "<command:5>CmdTX<parameters:0>"
|
||
|
|
: "<command:5>CmdRX<parameters:0>");
|
||
|
|
|
||
|
|
bool tx{!on};
|
||
|
|
auto start = DriftingDateTime::currentMSecsSinceEpoch();
|
||
|
|
// we must now wait for Tx on the rig, we will wait a short while
|
||
|
|
// before bailing out
|
||
|
|
while (tx != on &&
|
||
|
|
DriftingDateTime::currentMSecsSinceEpoch() - start < 1000) {
|
||
|
|
auto reply =
|
||
|
|
command_with_reply("<command:9>CmdSendTx<parameters:0>");
|
||
|
|
if (0 == reply.indexOf("<CmdTX:")) {
|
||
|
|
auto state = reply.mid(reply.indexOf('>') + 1);
|
||
|
|
if ("ON" == state) {
|
||
|
|
tx = true;
|
||
|
|
} else if ("OFF" == state) {
|
||
|
|
tx = false;
|
||
|
|
} else {
|
||
|
|
TRACE_CAT("DXLabSuiteCommanderTransceiver",
|
||
|
|
"unexpected TX state" << state);
|
||
|
|
throw error{tr("DX Lab Suite Commander sent an "
|
||
|
|
"unrecognised TX state: ") +
|
||
|
|
state};
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
TRACE_CAT("DXLabSuiteCommanderTransceiver",
|
||
|
|
"get TX unexpected response" << reply);
|
||
|
|
throw error{tr("DX Lab Suite Commander didn't respond "
|
||
|
|
"correctly polling TX status: ") +
|
||
|
|
reply};
|
||
|
|
}
|
||
|
|
if (tx != on)
|
||
|
|
QThread::msleep(10); // don't thrash Commander
|
||
|
|
}
|
||
|
|
update_PTT(tx);
|
||
|
|
if (tx != on) {
|
||
|
|
TRACE_CAT("DXLabSuiteCommanderTransceiver",
|
||
|
|
"rig failed to respond to PTT: " << on);
|
||
|
|
throw error{
|
||
|
|
tr("DX Lab Suite Commander rig did not respond to PTT: ") +
|
||
|
|
(on ? "ON" : "OFF")};
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
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,
|
||
|
|
bool /*no_ignore*/) {
|
||
|
|
TRACE_CAT("DXLabSuiteCommanderTransceiver", f << state());
|
||
|
|
auto f_string = frequency_to_string(f);
|
||
|
|
if (UNK != m && m != get_mode()) {
|
||
|
|
auto m_string = map_mode(m);
|
||
|
|
auto params = ("<xcvrfreq:%1>" + f_string + "<xcvrmode:%2>" + m_string +
|
||
|
|
"<preservesplitanddual:1>Y")
|
||
|
|
.arg(f_string.size())
|
||
|
|
.arg(m_string.size());
|
||
|
|
simple_command(("<command:14>CmdSetFreqMode<parameters:%1>" + params)
|
||
|
|
.arg(params.size()));
|
||
|
|
update_mode(m);
|
||
|
|
} else {
|
||
|
|
auto params = ("<xcvrfreq:%1>" + f_string).arg(f_string.size());
|
||
|
|
simple_command(("<command:10>CmdSetFreq<parameters:%1>" + params)
|
||
|
|
.arg(params.size()));
|
||
|
|
}
|
||
|
|
update_rx_frequency(f);
|
||
|
|
}
|
||
|
|
|
||
|
|
void DXLabSuiteCommanderTransceiver::do_tx_frequency(Frequency tx, MODE mode,
|
||
|
|
bool /*no_ignore*/) {
|
||
|
|
TRACE_CAT("DXLabSuiteCommanderTransceiver", tx << state());
|
||
|
|
if (tx) {
|
||
|
|
auto f_string = frequency_to_string(tx);
|
||
|
|
auto params = ("<xcvrfreq:%1>" + f_string + "<SuppressDual:1>Y")
|
||
|
|
.arg(f_string.size());
|
||
|
|
if (UNK == mode) {
|
||
|
|
params += "<SuppressModeChange:1>Y";
|
||
|
|
}
|
||
|
|
simple_command(("<command:11>CmdQSXSplit<parameters:%1>" + params)
|
||
|
|
.arg(params.size()));
|
||
|
|
} else {
|
||
|
|
simple_command("<command:8>CmdSplit<parameters:8><1:3>off");
|
||
|
|
}
|
||
|
|
update_split(tx);
|
||
|
|
update_other_frequency(tx);
|
||
|
|
}
|
||
|
|
|
||
|
|
void DXLabSuiteCommanderTransceiver::do_mode(MODE m) {
|
||
|
|
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()));
|
||
|
|
update_mode(m);
|
||
|
|
}
|
||
|
|
|
||
|
|
void DXLabSuiteCommanderTransceiver::poll() {
|
||
|
|
bool quiet{!transceiverbase_js8().isDebugEnabled()};
|
||
|
|
|
||
|
|
auto reply =
|
||
|
|
command_with_reply("<command:10>CmdGetFreq<parameters:0>", quiet);
|
||
|
|
if (0 == reply.indexOf("<CmdFreq:")) {
|
||
|
|
auto f = string_to_frequency(reply.mid(reply.indexOf('>') + 1));
|
||
|
|
if (f) {
|
||
|
|
if (!state().ptt()) // Commander is not reliable on frequency
|
||
|
|
// polls while transmitting
|
||
|
|
{
|
||
|
|
update_rx_frequency(f);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
TRACE_CAT_POLL("DXLabSuiteCommanderTransceiver",
|
||
|
|
"get frequency unexpected response" << reply);
|
||
|
|
throw error{tr("DX Lab Suite Commander didn't respond correctly "
|
||
|
|
"polling frequency: ") +
|
||
|
|
reply};
|
||
|
|
}
|
||
|
|
|
||
|
|
if (state().split()) {
|
||
|
|
reply =
|
||
|
|
command_with_reply("<command:12>CmdGetTXFreq<parameters:0>", quiet);
|
||
|
|
if (0 == reply.indexOf("<CmdTXFreq:")) {
|
||
|
|
auto f = string_to_frequency(reply.mid(reply.indexOf('>') + 1));
|
||
|
|
if (f) {
|
||
|
|
if (!state().ptt()) // Commander is not reliable on frequency
|
||
|
|
// polls while transmitting
|
||
|
|
{
|
||
|
|
update_other_frequency(f);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
TRACE_CAT_POLL("DXLabSuiteCommanderTransceiver",
|
||
|
|
"get tx frequency unexpected response" << reply);
|
||
|
|
throw error{tr("DX Lab Suite Commander didn't respond correctly "
|
||
|
|
"polling TX frequency: ") +
|
||
|
|
reply};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
reply = command_with_reply("<command:12>CmdSendSplit<parameters:0>", quiet);
|
||
|
|
if (0 == reply.indexOf("<CmdSplit:")) {
|
||
|
|
auto split = reply.mid(reply.indexOf('>') + 1);
|
||
|
|
if ("ON" == split) {
|
||
|
|
update_split(true);
|
||
|
|
} else if ("OFF" == split) {
|
||
|
|
update_split(false);
|
||
|
|
} else {
|
||
|
|
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("DXLabSuiteCommanderTransceiver",
|
||
|
|
"get split mode unexpected response" << reply);
|
||
|
|
throw error{tr("DX Lab Suite Commander didn't respond correctly "
|
||
|
|
"polling split status: ") +
|
||
|
|
reply};
|
||
|
|
}
|
||
|
|
|
||
|
|
get_mode(quiet);
|
||
|
|
}
|
||
|
|
|
||
|
|
auto DXLabSuiteCommanderTransceiver::get_mode(bool no_debug) -> MODE {
|
||
|
|
MODE m{UNK};
|
||
|
|
auto reply =
|
||
|
|
command_with_reply("<command:11>CmdSendMode<parameters:0>", no_debug);
|
||
|
|
if (0 == reply.indexOf("<CmdMode:")) {
|
||
|
|
auto mode = reply.mid(reply.indexOf('>') + 1);
|
||
|
|
if ("AM" == mode) {
|
||
|
|
m = AM;
|
||
|
|
} else if ("CW" == mode) {
|
||
|
|
m = CW;
|
||
|
|
} else if ("CW-R" == mode) {
|
||
|
|
m = CW_R;
|
||
|
|
} else if ("FM" == mode || "WBFM" == mode) {
|
||
|
|
m = FM;
|
||
|
|
} else if ("LSB" == mode) {
|
||
|
|
m = LSB;
|
||
|
|
} else if ("USB" == mode) {
|
||
|
|
m = USB;
|
||
|
|
} else if ("RTTY" == mode) {
|
||
|
|
m = FSK;
|
||
|
|
} else if ("RTTY-R" == mode) {
|
||
|
|
m = FSK_R;
|
||
|
|
} else if ("PKT" == mode || "DATA-L" == mode || "Data-L" == mode ||
|
||
|
|
"DIGL" == mode) {
|
||
|
|
m = DIG_L;
|
||
|
|
} else if ("PKT-R" == mode || "DATA-U" == mode || "Data-U" == mode ||
|
||
|
|
"DIGU" == mode) {
|
||
|
|
m = DIG_U;
|
||
|
|
} else {
|
||
|
|
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("DXLabSuiteCommanderTransceiver",
|
||
|
|
"unexpected response" << reply);
|
||
|
|
throw error{tr("DX Lab Suite Commander didn't respond correctly "
|
||
|
|
"polling mode: ") +
|
||
|
|
reply};
|
||
|
|
}
|
||
|
|
return m;
|
||
|
|
}
|
||
|
|
|
||
|
|
void DXLabSuiteCommanderTransceiver::simple_command(QString const &cmd,
|
||
|
|
bool no_debug) {
|
||
|
|
Q_ASSERT(commander_);
|
||
|
|
|
||
|
|
if (!no_debug) {
|
||
|
|
TRACE_CAT("DXLabSuiteCommanderTransceiver", cmd);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!write_to_port(cmd)) {
|
||
|
|
TRACE_CAT("DXLabSuiteCommanderTransceiver",
|
||
|
|
"failed:" << commander_->errorString());
|
||
|
|
throw error{tr("DX Lab Suite Commander send command failed\n") +
|
||
|
|
commander_->errorString()};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
QString DXLabSuiteCommanderTransceiver::command_with_reply(QString const &cmd,
|
||
|
|
bool no_debug) {
|
||
|
|
Q_ASSERT(commander_);
|
||
|
|
|
||
|
|
if (!write_to_port(cmd)) {
|
||
|
|
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)
|
||
|
|
.arg(commander_->errorString())};
|
||
|
|
}
|
||
|
|
|
||
|
|
// waitForReadReady appears to be unreliable on Windows timing out
|
||
|
|
// when data is waiting so retry a few times
|
||
|
|
unsigned retries{5};
|
||
|
|
bool replied{false};
|
||
|
|
while (!replied && --retries) {
|
||
|
|
replied = commander_->waitForReadyRead();
|
||
|
|
if (!replied && commander_->error() != commander_->SocketTimeoutError) {
|
||
|
|
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)
|
||
|
|
.arg(commander_->errorString())};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!replied) {
|
||
|
|
TRACE_CAT("DXLabSuiteCommanderTransceiver", cmd << "retries exhausted");
|
||
|
|
throw error{tr("DX Lab Suite Commander retries exhausted sending "
|
||
|
|
"command \"%1\"")
|
||
|
|
.arg(cmd)};
|
||
|
|
}
|
||
|
|
|
||
|
|
auto result = commander_->readAll();
|
||
|
|
// qCDebug (dxlabsuitecommandertransceiver_js8) << "result: " << result;
|
||
|
|
// for (int i = 0; i < result.size (); ++i)
|
||
|
|
// {
|
||
|
|
// qCDebug (dxlabsuitecommandertransceiver_js8) << i << ":" << hex <<
|
||
|
|
// int (result[i]);
|
||
|
|
// }
|
||
|
|
|
||
|
|
if (!no_debug) {
|
||
|
|
TRACE_CAT("DXLabSuiteCommanderTransceiver", cmd << "->" << result);
|
||
|
|
}
|
||
|
|
|
||
|
|
return result; // converting raw UTF-8 bytes to QString
|
||
|
|
}
|
||
|
|
|
||
|
|
bool DXLabSuiteCommanderTransceiver::write_to_port(QString const &s) {
|
||
|
|
auto data = s.toLocal8Bit();
|
||
|
|
auto to_send = data.constData();
|
||
|
|
auto length = data.size();
|
||
|
|
|
||
|
|
qint64 total_bytes_sent{0};
|
||
|
|
while (total_bytes_sent < length) {
|
||
|
|
auto bytes_sent = commander_->write(to_send + total_bytes_sent,
|
||
|
|
length - total_bytes_sent);
|
||
|
|
if (bytes_sent < 0 || !commander_->waitForBytesWritten()) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
total_bytes_sent += bytes_sent;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
QString DXLabSuiteCommanderTransceiver::frequency_to_string(Frequency f) const {
|
||
|
|
// number is localized and in kHz, avoid floating point translation
|
||
|
|
// errors by adding a small number (0.1Hz)
|
||
|
|
return QString{"%L1"}.arg(f / 1e3 + 1e-4, 10, 'f', 3);
|
||
|
|
}
|
||
|
|
|
||
|
|
auto DXLabSuiteCommanderTransceiver::string_to_frequency(QString s) const
|
||
|
|
-> Frequency {
|
||
|
|
// temporary hack because Commander is returning invalid UTF-8 bytes
|
||
|
|
s.replace(QChar{QChar::ReplacementCharacter}, locale_.groupSeparator());
|
||
|
|
|
||
|
|
// remove DP - relies on n.nnn kHz format so we can do ulonglong
|
||
|
|
// conversion to Hz
|
||
|
|
bool ok;
|
||
|
|
|
||
|
|
// auto f = locale_.toDouble (s, &ok); // use when CmdSendFreq and
|
||
|
|
// CmdSendTxFreq reinstated
|
||
|
|
|
||
|
|
auto f = QLocale::c().toDouble(s, &ok); // temporary fix
|
||
|
|
|
||
|
|
if (!ok) {
|
||
|
|
throw error{
|
||
|
|
tr("DX Lab Suite Commander sent an unrecognized frequency")};
|
||
|
|
}
|
||
|
|
return (f + 1e-4) * 1e3;
|
||
|
|
}
|
||
|
|
|
||
|
|
Q_LOGGING_CATEGORY(dxlabsuitecommandertransceiver_js8,
|
||
|
|
"dxlabsuitecommandertransceiver.js8", QtWarningMsg)
|