WSJT-X/DXLabSuiteCommanderTransceiver.cpp
Bill Somerville 7ff8b8329d Only poll split frequency if in split mode
Because PowerSDR for the FlexRadio rigs doesn't implement split or VFO
B,  querying the  TX VFO  in transceiver  polls causes  an error  with
them. This query is unecessary. Removing  it will cause a delay of one
poll period  after split is set  for the Tx frequency  to appear. This
should be  harmless, if not  then there  is a possibility  of querying
split before  the Tx frequency. This  seems obvious but I  believe the
order matters with some rigs so it needs more investigation.

Merged from wsjtx-1.4 branch.



git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@4435 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
2014-10-02 01:21:56 +00:00

434 lines
12 KiB
C++

#include "DXLabSuiteCommanderTransceiver.hpp"
#include <QTcpSocket>
#include <QRegularExpression>
#include "NetworkServerLookup.hpp"
#include "moc_DXLabSuiteCommanderTransceiver.cpp"
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";
}
}
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)
: PollingTransceiver {poll_interval}
, wrapped_ {std::move (wrapped)}
, use_for_ptt_ {use_for_ptt}
, server_ {address}
, commander_ {nullptr}
{
}
DXLabSuiteCommanderTransceiver::~DXLabSuiteCommanderTransceiver ()
{
}
void DXLabSuiteCommanderTransceiver::do_start ()
{
#if WSJT_TRACE_CAT
qDebug () << "DXLabSuiteCommanderTransceiver::start";
#endif
wrapped_->start ();
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 ())
{
#if WSJT_TRACE_CAT
qDebug () << "DXLabSuiteCommanderTransceiver::start failed to connect" << commander_->errorString ();
#endif
throw error {tr ("Failed to connect to DX Lab Suite Commander\n") + commander_->errorString ()};
}
poll ();
}
void DXLabSuiteCommanderTransceiver::do_stop ()
{
if (commander_)
{
commander_->close ();
delete commander_, commander_ = nullptr;
}
wrapped_->stop ();
#if WSJT_TRACE_CAT
qDebug () << "DXLabSuiteCommanderTransceiver::stop";
#endif
}
void DXLabSuiteCommanderTransceiver::do_ptt (bool on)
{
#if WSJT_TRACE_CAT
qDebug () << "DXLabSuiteCommanderTransceiver::do_ptt:" << on << state ();
#endif
if (use_for_ptt_)
{
simple_command (on ? "<command:5>CmdTX<parameters:0>" : "<command:5>CmdRX<parameters:0>");
}
else
{
wrapped_->ptt (on);
}
update_PTT (on);
do_frequency (state ().frequency ()); // gets Commander synchronized
}
void DXLabSuiteCommanderTransceiver::do_frequency (Frequency f)
{
#if WSJT_TRACE_CAT
qDebug () << "DXLabSuiteCommanderTransceiver::do_frequency:" << f << state ();
#endif
// number is localised
// avoid floating point translation errors by adding a small number (0.1Hz)
simple_command ("<command:10>CmdSetFreq<parameters:23><xcvrfreq:10>" + QString ("%L1").arg (f / 1e3 + 1e-4, 10, 'f', 3).toLocal8Bit ());
update_rx_frequency (f);
}
void DXLabSuiteCommanderTransceiver::do_tx_frequency (Frequency tx, bool /* rationalise_mode */)
{
#if WSJT_TRACE_CAT
qDebug () << "DXLabSuiteCommanderTransceiver::do_tx_frequency:" << tx << state ();
#endif
if (tx)
{
simple_command ("<command:8>CmdSplit<parameters:7><1:2>on");
update_split (true);
// number is localised
// avoid floating point translation errors by adding a small number (0.1Hz)
// set TX frequency after going split because going split
// rationalises TX VFO mode and that can change the frequency on
// Yaesu rigs if CW is involved
simple_command ("<command:12>CmdSetTxFreq<parameters:23><xcvrfreq:10>" + QString ("%L1").arg (tx / 1e3 + 1e-4, 10, 'f', 3).toLocal8Bit ());
}
else
{
simple_command ("<command:8>CmdSplit<parameters:8><1:3>off");
}
update_other_frequency (tx);
do_frequency (state ().frequency ()); // gets Commander synchronized
}
void DXLabSuiteCommanderTransceiver::do_mode (MODE mode, bool /* rationalise */)
{
#if WSJT_TRACE_CAT
qDebug () << "DXLabSuiteCommanderTransceiver::do_mode:" << mode << state ();
#endif
auto mapped = map_mode (mode);
simple_command ((QString ("<command:10>CmdSetMode<parameters:%1><1:%2>").arg (5 + mapped.size ()).arg (mapped.size ()) + mapped).toLocal8Bit ());
if (state ().split ())
{
// this toggle ensures that the TX VFO mode is the same as the RX VFO
simple_command ("<command:8>CmdSplit<parameters:8><1:3>off");
simple_command ("<command:8>CmdSplit<parameters:7><1:2>on");
}
do_frequency (state ().frequency ()); // gets Commander synchronized
// setting TX frequency rationalises the mode on Icoms so get current and set
poll ();
}
void DXLabSuiteCommanderTransceiver::poll ()
{
#if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS
bool quiet {false};
#else
bool quiet {true};
#endif
auto reply = command_with_reply ("<command:11>CmdSendFreq<parameters:0>", quiet);
if (0 == reply.indexOf ("<CmdFreq:"))
{
// remove thousands separator and DP - relies of n.nnn kHz
// format so we can do uint conversion
reply = reply.mid (reply.indexOf ('>') + 1).replace (",", "").replace (".", "");
if (!state ().ptt ()) // Commander is not reliable on frequency
// polls while transmitting
{
update_rx_frequency (reply.toUInt ());
}
}
else
{
#if WSJT_TRACE_CAT
qDebug () << "DXLabSuiteCommanderTransceiver::poll: get frequency unexpected response";
#endif
throw error {tr ("DX Lab Suite Commander didn't respond correctly polling frequency")};
}
if (state ().split ())
{
reply = command_with_reply ("<command:13>CmdSendTXFreq<parameters:0>", quiet);
if (0 == reply.indexOf ("<CmdTXFreq:"))
{
// remove thousands separator and DP - relies of n.nnn kHz format so we ca do uint conversion
auto text = reply.mid (reply.indexOf ('>') + 1).replace (",", "").replace (".", "");
if ("000" != text)
{
update_other_frequency (text.toUInt ());
}
}
else
{
#if WSJT_TRACE_CAT
qDebug () << "DXLabSuiteCommanderTransceiver::poll: get tx frequency unexpected response";
#endif
throw error {tr ("DX Lab Suite Commander didn't respond correctly polling TX frequency")};
}
}
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
{
#if WSJT_TRACE_CAT
qDebug () << "DXLabSuiteCommanderTransceiver::poll: unexpected split state" << split;
#endif
throw error {tr ("DX Lab Suite Commander sent an unrecognised split state: ") + split};
}
}
else
{
#if WSJT_TRACE_CAT
qDebug () << "DXLabSuiteCommanderTransceiver::poll: get split mode unexpected response";
#endif
throw error {tr ("DX Lab Suite Commander didn't respond correctly polling split status")};
}
reply = command_with_reply ("<command:11>CmdSendMode<parameters:0>", quiet);
if (0 == reply.indexOf ("<CmdMode:"))
{
auto mode = reply.mid (reply.indexOf ('>') + 1);
MODE m {UNK};
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)
{
m = DIG_L;
}
else if ("PKT-R" == mode || "DATA-U" == mode || "Data-U" == mode)
{
m = DIG_U;
}
else
{
#if WSJT_TRACE_CAT
qDebug () << "DXLabSuiteCommanderTransceiver::poll: unexpected mode name" << mode;
#endif
throw error {tr ("DX Lab Suite Commander sent an unrecognised mode: ") + mode};
}
update_mode (m);
}
else
{
#if WSJT_TRACE_CAT
qDebug () << "DXLabSuiteCommanderTransceiver::poll: unexpected response";
#endif
throw error {tr ("DX Lab Suite Commander didn't respond correctly polling mode")};
}
}
void DXLabSuiteCommanderTransceiver::simple_command (QByteArray const& cmd, bool no_debug)
{
Q_ASSERT (commander_);
if (!no_debug)
{
#if WSJT_TRACE_CAT
qDebug () << "DXLabSuiteCommanderTransceiver:simple_command(" << cmd << ')';
#endif
}
if (!write_to_port (cmd))
{
#if WSJT_TRACE_CAT
qDebug () << "DXLabSuiteCommanderTransceiver::simple_command failed:" << commander_->errorString ();
#endif
throw error {tr ("DX Lab Suite Commander send command failed\n") + commander_->errorString ()};
}
}
QByteArray DXLabSuiteCommanderTransceiver::command_with_reply (QByteArray const& cmd, bool no_debug)
{
Q_ASSERT (commander_);
if (!no_debug)
{
#if WSJT_TRACE_CAT
qDebug () << "DXLabSuiteCommanderTransceiver:command_with_reply(" << cmd << ')';
#endif
}
if (!write_to_port (cmd))
{
#if WSJT_TRACE_CAT
qDebug () << "DXLabSuiteCommanderTransceiver::command_with_reply failed to send command:" << commander_->errorString ();
#endif
throw error {
tr ("DX Lab Suite Commander failed to send command \"%1\": %2\n")
.arg (cmd.constData ())
.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)
{
#if WSJT_TRACE_CAT
qDebug () << "DXLabSuiteCommanderTransceiver::command_with_reply \"" << cmd << "\" failed to read reply:" << commander_->errorString ();
#endif
throw error {
tr ("DX Lab Suite Commander send command \"%1\" read reply failed: %2\n")
.arg (cmd.constData ())
.arg (commander_->errorString ())
};
}
}
if (!replied)
{
#if WSJT_TRACE_CAT
qDebug () << "DXLabSuiteCommanderTransceiver::command_with_reply \"" << cmd << "\" retries exhausted";
#endif
throw error {
tr ("DX Lab Suite Commander retries exhausted sending command \"%1\"")
.arg (cmd.constData ())
};
}
auto result = commander_->readAll ();
if (!no_debug)
{
#if WSJT_TRACE_CAT
qDebug () << "DXLabSuiteCommanderTransceiver:command_with_reply(" << cmd << ") ->" << result;
#endif
}
return result;
}
bool DXLabSuiteCommanderTransceiver::write_to_port (QByteArray const& data)
{
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;
}