mirror of
				https://github.com/saitohirga/WSJT-X.git
				synced 2025-11-04 05:50:31 -05:00 
			
		
		
		
	Merged from wsjtx-1.4 branch. git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@4573 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
		
			
				
	
	
		
			434 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			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:10>CmdGetFreq<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:12>CmdGetTXFreq<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;
 | 
						|
}
 |