#include "TCITransceiver.hpp"

#include <QRegularExpression>
#include <QLocale>
#include <QThread>
#include <qmath.h>
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
#include <QRandomGenerator>
#endif
#include "commons.h"
#include "vvv.h"

#include "NetworkServerLookup.hpp"

#include "moc_TCITransceiver.cpp"

namespace
{
  char const * const TCI_transceiver_1_name {"TCI Client RX1"};
  char const * const TCI_transceiver_2_name {"TCI Client RX2"};

  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 "digl";
      case Transceiver::DIG_U: return "digu";
      case Transceiver::FM: return "wfm";
      case Transceiver::DIG_FM:
        return "nfm";
      default: break;
      }
    return "";
  }
static const QString SmTZ(";");
static const QString SmDP(":");
static const QString SmCM(",");
static const QString SmTrue("true");
static const QString SmFalse("false");

// Command maps
static const QString CmdDevice("device");
static const QString CmdReceiveOnly("receive_only");
static const QString CmdTrxCount("trx_count");
static const QString CmdChannelCount("channels_count");
static const QString CmdVfoLimits("vfo_limits");
static const QString CmdIfLimits("if_limits");
static const QString CmdModeList("modulations_list");
static const QString CmdMode("modulation");
static const QString CmdReady("ready");
static const QString CmdStop("stop");
static const QString CmdStart("start");
static const QString CmdPreamp("preamp");
static const QString CmdDds("dds");
static const QString CmdIf("if");
static const QString CmdTrx("trx");
static const QString CmdRxEnable("rx_enable");
static const QString CmdTxEnable("tx_enable");
static const QString CmdRxChannelEnable("rx_channel_enable");
static const QString CmdRitEnable("rit_enable");
static const QString CmdRitOffset("rit_offset");
static const QString CmdXitEnable("xit_enable");
static const QString CmdXitOffset("xit_offset");
static const QString CmdSplitEnable("split_enable");
static const QString CmdIqSR("iq_samplerate");
static const QString CmdIqStart("iq_start");
static const QString CmdIqStop("iq_stop");
static const QString CmdCWSpeed("cw_macros_speed");
static const QString CmdCWDelay("cw_macros_delay");
static const QString CmdSpot("spot");
static const QString CmdSpotDelete("spot_delete");
static const QString CmdFilterBand("rx_filter_band");
static const QString CmdVFO("vfo");
static const QString CmdVersion("protocol"); //protocol:esdr,1.2;
static const QString CmdTune("tune");
static const QString CmdRxMute("rx_mute");
static const QString CmdSmeter("rx_smeter");
static const QString CmdPower("tx_power");
static const QString CmdSWR("tx_swr");
static const QString CmdECoderRX("ecoder_switch_rx");
static const QString CmdECoderVFO("ecoder_switch_channel");
static const QString CmdAudioSR("audio_samplerate");
static const QString CmdAudioStart("audio_start");
static const QString CmdAudioStop("audio_stop");
static const QString CmdAppFocus("app_focus");
static const QString CmdVolume("volume");
static const QString CmdSqlEnable("sql_enable");
static const QString CmdSqlLevel("sql_level");
static const QString CmdDrive("drive");
static const QString CmdTuneDrive("tune_drive");
static const QString CmdMute("mute");
static const QString CmdRxSensorsEnable("rx_sensors_enable");
static const QString CmdTxSensorsEnable("tx_sensors_enable");
static const QString CmdRxSensors("rx_sensors");
static const QString CmdTxSensors("tx_sensors");
static const QString CmdAgcMode("agc_mode");
static const QString CmdAgcGain("agc_gain");
static const QString CmdLock("lock");

}

extern "C" {
  void   fil4_(qint32*, qint32*, qint32*, qint32*, float*);
}
extern dec_data dec_data;

extern float gran();		// Noise generator (for tests only)

//#define RAMP_INCREMENT 64  // MUST be an integral factor of 2^16
#define RAMP_INCREMENT 65536  // MUST be an integral factor of 2^16

#if defined (WSJT_SOFT_KEYING)
# define SOFT_KEYING WSJT_SOFT_KEYING
#else
# define SOFT_KEYING 1
#endif

double constexpr TCITransceiver::m_twoPi;

void TCITransceiver::register_transceivers (TransceiverFactory::Transceivers * registry, unsigned id1, unsigned id2)
{
  (*registry)[TCI_transceiver_1_name] = TransceiverFactory::Capabilities {id1, TransceiverFactory::Capabilities::tci, true};
  (*registry)[TCI_transceiver_2_name] = TransceiverFactory::Capabilities {id2, TransceiverFactory::Capabilities::tci, true};
}

static constexpr quint32 AudioHeaderSize = 16u*sizeof(quint32);

TCITransceiver::TCITransceiver (std::unique_ptr<TransceiverBase> wrapped,QString const& rignr,
                                                                QString const& address, bool use_for_ptt,
                                                                int poll_interval, QObject * parent)
  : PollingTransceiver {poll_interval, parent}
  , wrapped_ {std::move (wrapped)}
  , rx_ {rignr}
  , server_ {address}
  , use_for_ptt_ {use_for_ptt}
  , errortable {tr("ConnectionRefused"),
  tr("RemoteHostClosed"),
  tr("HostNotFound"),
  tr("SocketAccess"),
  tr("SocketResource"),
  tr("SocketTimeout"),
  tr("DatagramTooLarge"),
  tr("Network"),
  tr("AddressInUse"),
  tr("SocketAddressNotAvailable"),
  tr("UnsupportedSocketOperation"),
  tr("UnfinishedSocketOperation"),
  tr("ProxyAuthenticationRequired"),
  tr("SslHandshakeFailed"),
  tr("ProxyConnectionRefused"),
  tr("ProxyConnectionClosed"),
  tr("ProxyConnectionTimeout"),
  tr("ProxyNotFound"),
  tr("ProxyProtocol"),
  tr("Operation"),
  tr("SslInternal"),
  tr("SslInvalidUserData"),
  tr("Temporary"),
  tr("UnknownSocket") }
  , error_ {""}
  , do_snr_ {(poll_interval & do__snr) == do__snr}
  , do_pwr_ {(poll_interval & do__pwr) == do__pwr}
  , rig_power_ {(poll_interval & rig__power) == rig__power}
  , rig_power_off_ {(poll_interval & rig__power_off) == rig__power_off}
  , tci_audio_ {(poll_interval & tci__audio) == tci__audio}
  , commander_ {nullptr}
  , tci_timer1_ {nullptr}
  , tci_loop1_ {nullptr}
  , tci_timer2_ {nullptr}
  , tci_loop2_ {nullptr}
  , tci_timer3_ {nullptr}
  , tci_loop3_ {nullptr}
  , wavptr_ {nullptr} 
  , m_jtdxtime {nullptr}
  , m_downSampleFactor {4}
  , m_buffer ((m_downSampleFactor > 1) ?
              new int [max_buffer_size * m_downSampleFactor] : nullptr)
  , m_quickClose {false}
  , m_phi {0.0}
  , m_toneSpacing {0.0}
  , m_fSpread {0.0}
  , m_state {Idle}
  , m_tuning {false}
  , m_cwLevel {false}
  , m_j0 {-1}
  , m_toneFrequency0 {1500.0}
//  , debug_file_ {QDir(QStandardPaths::writableLocation (QStandardPaths::DataLocation)).absoluteFilePath ("jtdx_debug.txt").toStdString()}
//  , wav_file_ {QDir(QStandardPaths::writableLocation (QStandardPaths::DataLocation)).absoluteFilePath ("tx.wav").toStdString()}
  , debug_file_ {QDir(selectedsettingdirectory).absoluteFilePath ("jtdx_debug.txt").toStdString()}
  , wav_file_ {QDir(selectedsettingdirectory).absoluteFilePath ("tx.wav").toStdString()}
{
    m_samplesPerFFT = 6912 / 2;
    tci_Ready = false;
    trxA = 0;
    trxB = 0;
    cntIQ = 0;
    bIQ = false;
    inConnected = false;
    audioSampleRate = 48000u;
    mapCmd_[CmdDevice]       = Cmd_Device;
    mapCmd_[CmdReceiveOnly]  = Cmd_ReceiveOnly;
    mapCmd_[CmdTrxCount]     = Cmd_TrxCount;
    mapCmd_[CmdChannelCount] = Cmd_ChannelCount;
    mapCmd_[CmdVfoLimits]    = Cmd_VfoLimits;
    mapCmd_[CmdIfLimits]     = Cmd_IfLimits;
    mapCmd_[CmdModeList]     = Cmd_ModeList;
    mapCmd_[CmdMode]         = Cmd_Mode;
    mapCmd_[CmdReady]        = Cmd_Ready;
    mapCmd_[CmdStop]         = Cmd_Stop;
    mapCmd_[CmdStart]        = Cmd_Start;
    mapCmd_[CmdPreamp]       = Cmd_Preamp;
    mapCmd_[CmdDds]          = Cmd_Dds;
    mapCmd_[CmdIf]           = Cmd_If;
    mapCmd_[CmdTrx]          = Cmd_Trx;
    mapCmd_[CmdRxEnable]     = Cmd_RxEnable;
    mapCmd_[CmdTxEnable]     = Cmd_TxEnable;
    mapCmd_[CmdRxChannelEnable] = Cmd_RxChannelEnable;
    mapCmd_[CmdRitEnable]    = Cmd_RitEnable;
    mapCmd_[CmdRitOffset]    = Cmd_RitOffset;
    mapCmd_[CmdXitEnable]    = Cmd_XitEnable;
    mapCmd_[CmdXitOffset]    = Cmd_XitOffset;
    mapCmd_[CmdSplitEnable]  = Cmd_SplitEnable;
    mapCmd_[CmdIqSR]         = Cmd_IqSR;
    mapCmd_[CmdIqStart]      = Cmd_IqStart;
    mapCmd_[CmdIqStop]       = Cmd_IqStop;
    mapCmd_[CmdCWSpeed]      = Cmd_CWSpeed;
    mapCmd_[CmdCWDelay]      = Cmd_CWDelay;
    mapCmd_[CmdFilterBand]   = Cmd_FilterBand;
    mapCmd_[CmdVFO]          = Cmd_VFO;
    mapCmd_[CmdVersion]      = Cmd_Version;
    mapCmd_[CmdTune]         = Cmd_Tune;
    mapCmd_[CmdRxMute]       = Cmd_RxMute;
    mapCmd_[CmdSmeter]       = Cmd_Smeter;
    mapCmd_[CmdPower]        = Cmd_Power;
    mapCmd_[CmdSWR]          = Cmd_SWR;
    mapCmd_[CmdECoderRX]     = Cmd_ECoderRX;
    mapCmd_[CmdECoderVFO]    = Cmd_ECoderVFO;
    mapCmd_[CmdAudioSR]      = Cmd_AudioSR;
    mapCmd_[CmdAudioStart]   = Cmd_AudioStart;
    mapCmd_[CmdAudioStop]    = Cmd_AudioStop;
    mapCmd_[CmdAppFocus]     = Cmd_AppFocus;
    mapCmd_[CmdVolume]       = Cmd_Volume;
    mapCmd_[CmdSqlEnable]    = Cmd_SqlEnable;
    mapCmd_[CmdSqlLevel]     = Cmd_SqlLevel;
    mapCmd_[CmdDrive]        = Cmd_Drive;
    mapCmd_[CmdTuneDrive]    = Cmd_TuneDrive;
    mapCmd_[CmdMute]         = Cmd_Mute;
    mapCmd_[CmdRxSensorsEnable] = Cmd_RxSensorsEnable;
    mapCmd_[CmdTxSensorsEnable] = Cmd_TxSensorsEnable;
    mapCmd_[CmdRxSensors]    = Cmd_RxSensors;
    mapCmd_[CmdTxSensors]    = Cmd_TxSensors;
    mapCmd_[CmdAgcMode]      = Cmd_AgcMode;
    mapCmd_[CmdAgcGain]      = Cmd_AgcGain;
    mapCmd_[CmdLock]         = Cmd_Lock;
}

void TCITransceiver::onConnected()
{
    inConnected = true;
//    printf("%s(%0.1f) TCI connected\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset());
#if JTDX_DEBUG_TO_FILE
    FILE * pFile = fopen (debug_file_.c_str(),"a");
    fprintf (pFile,"%s(%0.1f) TCI connected\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset());
    fclose (pFile);
#endif
}

void TCITransceiver::onDisconnected()
{
    inConnected = false;
//    printf("%s(%0.1f) TCI disconnected\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset());
#if JTDX_DEBUG_TO_FILE
    FILE * pFile = fopen (debug_file_.c_str(),"a");
    fprintf (pFile,"%s(%0.1f) TCI disconnected\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset());
    fclose (pFile);
#endif
}


void TCITransceiver::onError(QAbstractSocket::SocketError err)
{
//qDebug() << "WebInThread::onError";
//    printf("%s(%0.1f) TCI error:%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),err);
#if JTDX_DEBUG_TO_FILE
    FILE * pFile = fopen (debug_file_.c_str(),"a");
    fprintf (pFile,"%s(%0.1f) TCI error:%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),err);
    fclose (pFile);
#endif
    error_ = tr ("TCI websocket error: %1").arg (errortable.at (err));
}

int TCITransceiver::do_start (JTDXDateTime * jtdxtime)
{
  if (tci_audio_) QThread::currentThread()->setPriority(QThread::HighPriority);
//  printf("do_start tci_Ready:%d\n",tci_Ready);
#if JTDX_DEBUG_TO_FILE
  FILE * pFile = fopen (debug_file_.c_str(),"a");
  fprintf (pFile,"do_start tci_Ready:%d\n",tci_Ready);
  fclose (pFile);
#endif
  TRACE_CAT ("TCITransceiver", "starting");
  m_jtdxtime = jtdxtime;
  if (wrapped_) wrapped_->start (0,m_jtdxtime);
  url_.setUrl("ws://" + server_); //server_
  if (url_.host() == "") url_.setHost("localhost");
  if (url_.port() == -1) url_.setPort(40001);

  if (!commander_)
    {
      commander_ = new QWebSocket {}; // QObject takes ownership
//      printf ("commander created");
#if JTDX_DEBUG_TO_FILE
      FILE * pFile = fopen (debug_file_.c_str(),"a");
      fprintf (pFile,"commander created");
      fclose (pFile);
#endif
      connect(commander_,SIGNAL(connected()),this,SLOT(onConnected()));
      connect(commander_,SIGNAL(disconnected()),this,SLOT(onDisconnected()));
      connect(commander_,SIGNAL(binaryMessageReceived(QByteArray)),this,SLOT(onBinaryReceived(QByteArray)));
      connect(commander_,SIGNAL(textMessageReceived(QString)),this,SLOT(onMessageReceived(QString)));
      connect(commander_,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(onError(QAbstractSocket::SocketError)));
    }
  if (!tci_loop1_) {
    tci_loop1_ = new QEventLoop  {this};
  }
  if (!tci_timer1_) {
    tci_timer1_ = new QTimer {this};
    tci_timer1_ -> setSingleShot(true);
    connect( tci_timer1_, &QTimer::timeout, tci_loop1_, &QEventLoop::quit);
    connect( this, &TCITransceiver::tci_done1, tci_loop1_, &QEventLoop::quit);
  }
  if (!tci_loop2_) {
    tci_loop2_ = new QEventLoop  {this};
  }
  if (!tci_timer2_) {
    tci_timer2_ = new QTimer {this};
    tci_timer2_ -> setSingleShot(true);
    connect( tci_timer2_, &QTimer::timeout, tci_loop2_, &QEventLoop::quit);
    connect( this, &TCITransceiver::tci_done2, tci_loop2_, &QEventLoop::quit);
  }
  if (!tci_loop3_) {
    tci_loop3_ = new QEventLoop  {this};
  }
  if (!tci_timer3_) {
    tci_timer3_ = new QTimer {this};
    tci_timer3_ -> setSingleShot(true);
    connect( tci_timer3_, &QTimer::timeout, tci_loop3_, &QEventLoop::quit);
    connect( this, &TCITransceiver::tci_done3, tci_loop3_, &QEventLoop::quit);
  }
  tx_fifo = 0; tx_top_ = true;
  tci_Ready = false;
  ESDR3 = false;
  HPSDR = false;
  band_change = false;
  trxA = 0;
  trxB = 0;
  busy_rx_frequency_ = false;
  busy_mode_ = false;
  busy_other_frequency_ = false;
  busy_split_ = false;
  busy_drive_ = false;
  busy_PTT_ = false;
  busy_rx2_ = false;
  rx2_ = false;
  requested_rx2_ = false;
  started_rx2_ = false;
  split_ = false;
  requested_split_ = false;
  started_split_ = false;
  PTT_ = false;
  requested_PTT_ = false;
  mode_ = "";
  requested_mode_ = "";
  started_mode_ = "";
  requested_rx_frequency_ = "";
  rx_frequency_ = "";
  requested_other_frequency_ = "";
  other_frequency_ = "";
  requested_drive_ = "";
  drive_ = "";
  level_ = -54;
  power_ = 0;
  swr_ = 0;
  m_bufferPos = 0;
  m_downSampleFactor =4;
  m_ns = 999;
  audio_ = false;
  requested_stream_audio_ = false;
  stream_audio_ = false;
  _power_ = false;
//  printf ("%s(%0.1f) TCI open %s rig_power:%d rig_power_off:%d tci_audio:%d do_snr:%d do_pwr:%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),url_.toString().toStdString().c_str(),rig_power_,rig_power_off_,tci_audio_,do_snr_,do_pwr_);
#if JTDX_DEBUG_TO_FILE
  pFile = fopen (debug_file_.c_str(),"a");
  fprintf (pFile,"%s(%0.1f) TCI open %s rig_power:%d rig_power_off:%d tci_audio:%d do_snr:%d do_pwr:%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),url_.toString().toStdString().c_str(),rig_power_,rig_power_off_,tci_audio_,do_snr_,do_pwr_);
  fclose (pFile);
#endif
  commander_->open (url_);
  mysleep1 (1500);
//  if (ESDR3) mysleep1 (500); else mysleep1 (100);
  mysleep1 (210);
  if (HPSDR) {
      busy_split_ = true;
      const QString cmd = CmdSplitEnable + SmDP + rx_ + SmTZ;
      sendTextMessage(cmd);
      mysleep2(500);
      busy_split_ = false;
  }
  if (error_.isEmpty()) {
    tci_Ready = true;
    if (!_power_) {
      if (rig_power_) {
        rig_power(true);
        mysleep1(1000);
        if(!_power_) throw error {tr ("TCI SDR could not be switched on")};
      } else {
        tci_Ready = false;
        throw error {tr ("TCI SDR is not switched on")};
      }
    }
    if (rx_ == "1" && !rx2_) {
      rx2_enable (true);
      if (!rx2_) {
        tci_Ready = false;
        throw error {tr ("TCI RX2 could not be enabled")};
      }
    }
    if (tci_audio_) {
        stream_audio (true);
        mysleep1(500);
        if (!stream_audio_) {
          tci_Ready = false;
          throw error {tr ("TCI Audio could not be switched on")};
        }
    }
    if (ESDR3) {
      const QString cmd = CmdRxSensorsEnable + SmDP + (do_snr_ ? "true" : "false") + SmCM + "500" +  SmTZ;
      sendTextMessage(cmd);
    } else if (do_snr_) {
      const QString cmd = CmdSmeter + SmDP + rx_ + SmCM + "0" +  SmTZ;
      sendTextMessage(cmd);
    }
    if (!requested_rx_frequency_.isEmpty()) do_frequency(string_to_frequency (requested_rx_frequency_),get_mode(true),false);
    if (!requested_other_frequency_.isEmpty()) do_tx_frequency(string_to_frequency (requested_other_frequency_),get_mode(true),false);
    else if (requested_split_ != split_) {/*printf("splt from start %d\n",requested_split_);*/ rig_split();} // split_ = requested_split_; mysleep2(100);}
    if (!requested_drive_.isEmpty() && requested_drive_ != drive_) {
        busy_drive_ = true;
        if (ESDR3) {
          const QString cmd = CmdDrive + SmDP + rx_ + SmCM + requested_drive_ + SmTZ;
          sendTextMessage(cmd);
        } else {
          const QString cmd = CmdDrive + SmDP + requested_drive_ + SmTZ;
          sendTextMessage(cmd);
        }
    }
    do_poll ();
    if (ESDR3) {
      const QString cmd = CmdTxSensorsEnable + SmDP + (do_pwr_ ? "true" : "false") + SmCM + "500" +  SmTZ;
      sendTextMessage(cmd);
    }
    if (stream_audio_) do_audio(true);

    TRACE_CAT ("TCITransceiver", "started");
//    printf("%s(%0.1f) TCI Transceiver started\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset());
#if JTDX_DEBUG_TO_FILE
    pFile = fopen (debug_file_.c_str(),"a");
    fprintf (pFile,"%s(%0.1f) TCI Transceiver started\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset());
    fclose (pFile);
#endif
    return 0;
  } else {
//    printf("%s(%0.1f) TCI Transceiver not started %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),error_.toStdString().c_str());
#if JTDX_DEBUG_TO_FILE
    pFile = fopen (debug_file_.c_str(),"a");
    fprintf (pFile,"%s(%0.1f) TCI Transceiver not started %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),error_.toStdString().c_str());
    fclose (pFile);
#endif
    if (error_.isEmpty())
      throw error {tr ("TCI could not be opened")};
    else
      throw error {error_};
  }
}

void TCITransceiver::do_stop ()
{
//  printf ("TCI close\n");
  if (!commander_) return;
#if JTDX_DEBUG_TO_FILE
  FILE * pFile = fopen (debug_file_.c_str(),"a");
  fprintf (pFile,"TCI close\n");
#endif
  if (stream_audio_ && tci_Ready && inConnected && _power_) {
    stream_audio (false);
    mysleep1(500);
//    printf ("TCI audio closed\n");
#if JTDX_DEBUG_TO_FILE
    fprintf (pFile,"TCI audio closed\n");
#endif
  }
  if (tci_Ready && inConnected && _power_) {
    requested_other_frequency_ = "";
    if (started_split_ != split_) {requested_split_ = started_split_; rig_split();}
    if (started_mode_ != mode_) sendTextMessage(mode_to_command(started_mode_));
    if (!started_rx2_ && rx2_) {
      rx2_enable (false);
    }
  }
  if (_power_ && rig_power_off_ && tci_Ready && inConnected && _power_) {
    rig_power(false);
    mysleep1(500);
//    printf ("TCI power down\n");
#if JTDX_DEBUG_TO_FILE
    fprintf (pFile,"TCI power down\n");
#endif
  }
  tci_Ready = false;
  if (commander_)
    {
      if (inConnected) commander_->close(QWebSocketProtocol::CloseCodeNormal,"end");
      delete commander_, commander_ = nullptr;
//      printf ("commander ");
#if JTDX_DEBUG_TO_FILE
      fprintf (pFile,"commander ");
#endif
    }
  if (tci_timer1_)
    {
      if (tci_timer1_->isActive()) tci_timer1_->stop();
      delete tci_timer1_, tci_timer1_ = nullptr;
//      printf ("timer1 ");
#if JTDX_DEBUG_TO_FILE
      fprintf (pFile,"timer1 ");
#endif
    }
  if (tci_loop1_)
    {
      tci_loop1_->quit();
      delete tci_loop1_, tci_loop1_ = nullptr;
//      printf ("loop1 ");
#if JTDX_DEBUG_TO_FILE
      fprintf (pFile,"loop1 ");
#endif
    }
  if (tci_timer2_)
    {
      if (tci_timer2_->isActive()) tci_timer2_->stop();
      delete tci_timer2_, tci_timer2_ = nullptr;
//      printf ("timer2 ");
#if JTDX_DEBUG_TO_FILE
      fprintf (pFile,"timer2 ");
#endif
    }
  if (tci_loop2_)
    {
      tci_loop2_->quit();
      delete tci_loop2_, tci_loop2_ = nullptr;
//      printf ("loop2 ");
#if JTDX_DEBUG_TO_FILE
      fprintf (pFile,"loop2 ");
#endif
    }
  if (tci_timer3_)
    {
      if (tci_timer3_->isActive()) tci_timer3_->stop();
      delete tci_timer3_, tci_timer3_ = nullptr;
//      printf ("timer3 ");
#if JTDX_DEBUG_TO_FILE
      fprintf (pFile,"timer3 ");
#endif
    }
  if (tci_loop3_)
    {
      tci_loop3_->quit();
      delete tci_loop3_, tci_loop3_ = nullptr;
//      printf ("loop3 ");
#if JTDX_DEBUG_TO_FILE
      fprintf (pFile,"loop3 ");
#endif
    }

  if (wrapped_) wrapped_->stop ();
  TRACE_CAT ("TCITransceiver", "stopped");
//  printf ("deleted\nTCI closed\n");
#if JTDX_DEBUG_TO_FILE
  fprintf (pFile,"deleted\nTCI closed\n");
  fclose (pFile);
#endif
}

void TCITransceiver::onMessageReceived(const QString &str)
{
//qDebug() << "From WEB" << str;
    QStringList cmd_list = str.split(";", SkipEmptyParts);
#if JTDX_DEBUG_TO_FILE
    FILE * pFile;
#endif
    for (QString cmds : cmd_list){
        QStringList cmd = cmds.split(":", SkipEmptyParts);
        QStringList args = cmd.last().split(",", SkipEmptyParts);
        Tci_Cmd idCmd = mapCmd_[cmd.first()];
//        if (idCmd != Cmd_Power && idCmd != Cmd_SWR && idCmd != Cmd_Smeter && idCmd != Cmd_AppFocus && idCmd != Cmd_RxSensors && idCmd != Cmd_TxSensors) { printf ("%s(%0.1f) TCI message received:|%s| ",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),str.toStdString().c_str()); printf("idCmd : %d args : %s\n",idCmd,args.join("|").toStdString().c_str());}
#if JTDX_DEBUG_TO_FILE
        if (idCmd != Cmd_Power && idCmd != Cmd_SWR && idCmd != Cmd_Smeter && idCmd != Cmd_AppFocus && idCmd != Cmd_RxSensors && idCmd != Cmd_TxSensors) {
          pFile = fopen (debug_file_.c_str(),"a");
          fprintf (pFile,"%s(%0.1f) TCI message received:|%s| ",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),str.toStdString().c_str());
          fprintf (pFile,"idCmd : %d args : %s\n",idCmd,args.join("|").toStdString().c_str());
          fclose (pFile);
        }
#endif
//qDebug() << cmds << idCmd;
        if (idCmd <=0) continue;

        switch (idCmd) {
        case Cmd_Smeter:
          if(args.at(0)==rx_ && args.at(1) == "0") level_ = args.at(2).toInt() + 73;
          break;	
        case Cmd_RxSensors:
          if(args.at(0)==rx_) level_ = args.at(1).split(".")[0].toInt() + 73;
//            printf("Smeter=%d\n",level_);
          break;	
        case Cmd_TxSensors:
          if(args.at(0)==rx_) {
            power_ = 10 * args.at(3).split(".")[0].toInt() + args.at(3).split(".")[1].toInt();
            swr_ = 10 * args.at(4).split(".")[0].toInt() + args.at(4).split(".")[1].toInt();
//            printf("Power=%d SWR=%d\n",power_,swr_);
          }
          break;	
        case Cmd_SWR:
//          printf("%s(%0.1f) Cmd_SWR : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
          swr_ = 10 * args.at(0).split(".")[0].toInt() + args.at(0).split(".")[1].toInt();
          break;	
        case Cmd_Power:
          power_ = 10 * args.at(0).split(".")[0].toInt() + args.at(0).split(".")[1].toInt();
//          printf("%s(%0.1f) Cmd_Power : %s %d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str(),power_);
          break;	
        case Cmd_VFO:
//            printf("%s(%0.1f) Cmd_VFO : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
//            printf("band_change:%d busy_other_frequency_:%d timer1_remaining:%d timer2_remaining:%d",band_change,busy_other_frequency_,tci_timer1_->remainingTime(),tci_timer2_->remainingTime());
#if JTDX_DEBUG_TO_FILE
            pFile = fopen (debug_file_.c_str(),"a");
            fprintf (pFile,"%s(%0.1f) Cmd_VFO : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
            fprintf (pFile,"band_change:%d busy_other_frequency_:%d timer1_remaining:%d timer2_remaining:%d",band_change,busy_other_frequency_,tci_timer1_->remainingTime(),tci_timer2_->remainingTime());
#endif
            if(args.at(0)==rx_ && args.at(1) == "0") {
              if (args.at(2).left(1) != "-") rx_frequency_ = args.at(2);
              if (!tci_Ready && requested_rx_frequency_.isEmpty()) {requested_rx_frequency_ = rx_frequency_; }
              if (busy_rx_frequency_ && !band_change) {
//                printf (" cmdvfo0 done1");
#if JTDX_DEBUG_TO_FILE
                fprintf (pFile," cmdvfo0 done1");
#endif
                tci_done1();
              } else if (!tci_timer2_->isActive() && split_) {
//                printf (" cmdvfo0 timer2 start 210");
#if JTDX_DEBUG_TO_FILE
                fprintf (pFile," cmdvfo0 timer2 start 210");
#endif
                tci_timer2_->start(210);
              }
            }
            else if (args.at(0)==rx_ && args.at(1) == "1") {
              if (args.at(2).left(1) != "-") other_frequency_ = args.at(2);
//              if (!tci_Ready && requested_other_frequency_.isEmpty()) requested_other_frequency_ = other_frequency_;
              if (band_change && tci_timer1_->isActive()) {
//                printf (" cmdvfo1 done1");
#if JTDX_DEBUG_TO_FILE
                fprintf (pFile," cmdvfo1 done1");
#endif
                band_change = false;
                tci_timer2_->start(210);
                tci_done1();
              } else if (busy_other_frequency_) {
//                printf (" cmdvfo1 done2");
#if JTDX_DEBUG_TO_FILE
                fprintf (pFile," cmdvfo1 done2");
#endif
                tci_done2();
              } else if (tci_timer2_->isActive()) {
//                printf (" cmdvfo1 timer2 reset 210");
#if JTDX_DEBUG_TO_FILE
                fprintf (pFile," cmdvfo1 timer2 reset 210");
#endif
                tci_timer2_->start(210);
              } else if (other_frequency_ != requested_other_frequency_ && tci_Ready && split_ && !tci_timer2_->isActive()) {
//                printf (" cmdvfo1 timer2 start 210");
#if JTDX_DEBUG_TO_FILE
                fprintf (pFile," cmdvfo1 timer2 start 210");
#endif
                tci_timer2_->start(210);
              }
            }
//            printf("->%d\n",tci_timer2_->remainingTime());
#if JTDX_DEBUG_TO_FILE
            fprintf(pFile,"->%d\n",tci_timer2_->remainingTime());
            fclose (pFile);
#endif
            break;
        case Cmd_Mode:
//            printf("%s(%0.1f) Cmd_Mode : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
#if JTDX_DEBUG_TO_FILE
            pFile = fopen (debug_file_.c_str(),"a");
            fprintf (pFile,"%s(%0.1f) Cmd_Mode : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
            fclose (pFile);
#endif
            if(args.at(0)==rx_) {
              if (ESDR3 || HPSDR) {
                if (args.at(1) == "0" ) mode_ = args.at(2).toLower(); else mode_ = args.at(1).toLower();
              }  else mode_ = args.at(1);
              if (started_mode_.isEmpty()) started_mode_ = mode_;
              if (busy_mode_) tci_done1();
              else if (!requested_mode_.isEmpty() && requested_mode_ != mode_ && !band_change) {
                sendTextMessage(mode_to_command(requested_mode_));
              }
            }
            break;
        case Cmd_SplitEnable:
//            printf("%s(%0.1f) Cmd_SplitEnable : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
#if JTDX_DEBUG_TO_FILE
            pFile = fopen (debug_file_.c_str(),"a");
            fprintf (pFile,"%s(%0.1f) Cmd_SplitEnable : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
            fclose (pFile);
#endif
            if(args.at(0)==rx_) {
              if (args.at(1) == "false") split_ = false;
              else if (args.at(1) == "true") split_ = true;
              if (!tci_Ready) {started_split_ = split_;} 
              else if (busy_split_) tci_done2();
              else if (requested_split_ != split_ && !tci_timer2_->isActive()) {
                tci_timer2_->start(210);
                rig_split();
              }
            }
            break;
        case Cmd_Drive:
//            printf("%s(%0.1f) Cmd_Drive : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
#if JTDX_DEBUG_TO_FILE
            pFile = fopen (debug_file_.c_str(),"a");
            fprintf (pFile,"%s(%0.1f) Cmd_Drive : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
            fclose (pFile);
#endif
            if((!ESDR3 && !HPSDR) || args.at(0)==rx_) {
              if (ESDR3 || HPSDR) drive_ = args.at(1); else drive_ = args.at(0);
              if (requested_drive_.isEmpty()) requested_drive_ = drive_;
              busy_drive_ = false;
            }
            break;
        case Cmd_Trx:
//            printf("%s(%0.1f) Cmd_Trx : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
#if JTDX_DEBUG_TO_FILE
            pFile = fopen (debug_file_.c_str(),"a");
            fprintf (pFile,"%s(%0.1f) Cmd_Trx : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
            fclose (pFile);
#endif
            if(args.at(0)==rx_) {
              if (args.at(1) == "false") PTT_ = false;
              else if (args.at(1) == "true") PTT_ = true;
              if (tci_Ready && requested_PTT_ == PTT_) tci_done3();
              else if (tci_Ready && !PTT_) {
                requested_PTT_ = PTT_;
                update_PTT(PTT_);
                power_ = 0; if (do_pwr_) update_power (0);
                swr_ = 0; if (do_pwr_) update_swr (0);
              }  
            }
            break;
        case Cmd_AudioStart:
//          printf("%s(%0.1f) Cmd_AudioStart : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
#if JTDX_DEBUG_TO_FILE
          pFile = fopen (debug_file_.c_str(),"a");
          fprintf (pFile,"%s(%0.1f) Cmd_AudioStart : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
          fclose (pFile);
#endif
            if(args.at(0)==rx_) {
              stream_audio_ = true;
              if (tci_Ready) { //printf ("cmdaudiostart done1\n");
              tci_done1();}
            }
          break;	
        case Cmd_RxEnable:
//          printf("%s(%0.1f) Cmd_RxEnable : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
#if JTDX_DEBUG_TO_FILE
          pFile = fopen (debug_file_.c_str(),"a");
          fprintf (pFile,"%s(%0.1f) Cmd_RxEnable : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
          fclose (pFile);
#endif
            if(args.at(0)=="1") {
              if (args.at(1) == "false") rx2_ = false;
              else if (args.at(1) == "true") rx2_ = true;
              if(!tci_Ready) {requested_rx2_ = rx2_; started_rx2_ = rx2_;}
              else if (tci_Ready && busy_rx2_ && requested_rx2_ == rx2_) tci_done1();
            }
          break;	
        case Cmd_AudioStop:
//          printf("%s(%0.1f) CmdAudioStop : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
#if JTDX_DEBUG_TO_FILE
          pFile = fopen (debug_file_.c_str(),"a");
          fprintf (pFile,"%s(%0.1f) CmdAudioStop : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
          fclose (pFile);
#endif
            if(args.at(0)==rx_) {
              stream_audio_ = false;
              if (tci_Ready) { //printf ("cmdaudiostop done1\n");
              tci_done1();}
            }
          break;	
        case Cmd_Start:
//          printf("%s(%0.1f) CmdStart : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
#if JTDX_DEBUG_TO_FILE
          pFile = fopen (debug_file_.c_str(),"a");
          fprintf (pFile,"%s(%0.1f) CmdStart : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
          fclose (pFile);
#endif
          _power_ = true;
//          printf ("cmdstart done1\n");
          if (tci_Ready) tci_done1();
          break;	
        case Cmd_Stop:
//          printf("%s(%0.1f) CmdStop : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
#if JTDX_DEBUG_TO_FILE
          pFile = fopen (debug_file_.c_str(),"a");
          fprintf (pFile,"%s(%0.1f) CmdStop : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
          fclose (pFile);
#endif
          if (tci_Ready && PTT_) {
                PTT_ = false;
                requested_PTT_ = PTT_;
                update_PTT(PTT_);
                power_ = 0; if (do_pwr_) update_power (0);
                swr_ = 0; if (do_pwr_) update_swr (0);
                m_state = Idle;
                Q_EMIT tci_mod_active(m_state != Idle);
              }  
          _power_ = false;
          if (tci_timer1_->isActive()) { /*printf ("cmdstop done1\n");*/ tci_done1();}
          else {
            tci_Ready = false;
          }
          break;	
        case Cmd_Version:
//          printf("%s(%0.1f) CmdVersion : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
#if JTDX_DEBUG_TO_FILE
          pFile = fopen (debug_file_.c_str(),"a");
          fprintf (pFile,"%s(%0.1f) CmdVersion : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
          fclose (pFile);
#endif
          if(args.at(0)=="ExpertSDR3") ESDR3 = true;
          else if (args.at(0)=="Thetis") HPSDR = true;
          break;	
        case Cmd_Device:
//          printf("%s(%0.1f) CmdDevice : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
#if JTDX_DEBUG_TO_FILE
          pFile = fopen (debug_file_.c_str(),"a");
          fprintf (pFile,"%s(%0.1f) CmdDevice : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
          fclose (pFile);
#endif
          if((args.at(0)=="SunSDR2DX" || args.at(0)=="SunSDR2PRO") && !ESDR3) tx_top_ = false;
//          printf ("tx_top_:%d\n",tx_top_);
          break;	
        case Cmd_Ready:
//          printf("%s(%0.1f) CmdReady : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
#if JTDX_DEBUG_TO_FILE
          pFile = fopen (debug_file_.c_str(),"a");
          fprintf (pFile,"%s(%0.1f) CmdReady : %s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),args.join("|").toStdString().c_str());
          fclose (pFile);
#endif
          tci_done1();
          break;	
        
        default:
            break;
        }
    }

}

void TCITransceiver::sendTextMessage(const QString &message)
{
    if (inConnected) commander_->sendTextMessage(message);
}


void TCITransceiver::onBinaryReceived(const QByteArray &data)
{
/*    if (++cntIQ % 50 == 0){
        bIQ = !bIQ;
        nIqBytes+=data.size();
        printf("receiveIQ\n");
        emit receiveIQ(bIQ,nIqBytes);
        nIqBytes = 0;
    } else {
        nIqBytes+=data.size();
    } */
    Data_Stream *pStream = (Data_Stream*)(data.data());
    if (pStream->type != last_type) {
//        printf ("%s(%0.1f) binary received type=%d last_type=%d %d samplerate %d stream_size %d frame_size %d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),pStream->type,last_type,data.size(),pStream->sampleRate,pStream->length,data.size());
#if JTDX_DEBUG_TO_FILE
        FILE * pFile = fopen (debug_file_.c_str(),"a");
        fprintf (pFile,"%s(%0.1f) binary received type=%d last_type=%d %d samplerate %d stream_size %d frame_size %d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),pStream->type,last_type,data.size(),pStream->sampleRate,pStream->length,data.size());
        fclose (pFile);
#endif
        last_type = pStream->type;
#if JTDX_DEBUG_TO_FILE
        if (last_type == TxChrono) { // audio transmit starts
          struct {
            char ariff[4];         //ChunkID:    "RIFF"
            int nchunk;            //ChunkSize: 36+SubChunk2Size
            char awave[4];         //Format: "WAVE"
            char afmt[4];          //Subchunk1ID: "fmt "
            int lenfmt;            //Subchunk1Size: 16
            short int nfmt2;       //AudioFormat: 1
            short int nchan2;      //NumChannels: 1
            int nsamrate;          //SampleRate: 12000
            int nbytesec;          //ByteRate: SampleRate*NumChannels*BitsPerSample/8
            short int nbytesam2;   //BlockAlign: NumChannels*BitsPerSample/8
            short int nbitsam2;    //BitsPerSample: 16
            char adata[4];         //Subchunk2ID: "data"
            int ndata;             //Subchunk2Size: numSamples*NumChannels*BitsPerSample/8
          } hdr;
          int npts=static_cast<int>(m_period) * 48000;
          wavptr_ = fopen(wav_file_.c_str(),"wb");
          if (wavptr_  != NULL) {
            // Write a WAV header
            hdr.ariff[0]='R';
            hdr.ariff[1]='I';
            hdr.ariff[2]='F';
            hdr.ariff[3]='F';
            hdr.nchunk=36 + 4*npts;
            hdr.awave[0]='W';
            hdr.awave[1]='A';
            hdr.awave[2]='V';
            hdr.awave[3]='E';
            hdr.afmt[0]='f';
            hdr.afmt[1]='m';
            hdr.afmt[2]='t';
            hdr.afmt[3]=' ';
            hdr.lenfmt=16;
            hdr.nfmt2=1;
            hdr.nchan2=1;
            hdr.nsamrate=48000;
            hdr.nbytesec=4*48000;
            hdr.nbytesam2=4;
            hdr.nbitsam2=32;
            hdr.adata[0]='d';
            hdr.adata[1]='a';
            hdr.adata[2]='t';
            hdr.adata[3]='a';
            hdr.ndata=4*npts;
            fwrite(&hdr,sizeof(hdr),1,wavptr_);
          }

        }
        else if (last_type == RxAudioStream) { // audio switched back to resceive
          if (wavptr_ != NULL) {
            fclose(wavptr_);
            wavptr_ = nullptr;
          }
        }
#endif
    }
    if (pStream->type == Iq_Stream){
        bool tx = false;
        if (pStream->receiver == 0){
            tx = trxA == 0;
            trxA = 1;

        }
        if (pStream->receiver == 1) {
            tx = trxB == 0;
            trxB = 1;
        }
//        printf("sendIqData\n");
#if JTDX_DEBUG_TO_FILE
        FILE * pFile = fopen (debug_file_.c_str(),"a");
        fprintf (pFile,"sendIqData\n");
        fclose (pFile);
#endif
        emit sendIqData(pStream->receiver,pStream->length,pStream->data,tx);
qDebug() << "IQ" << data.size() << pStream->length;
    } else if (pStream->type == RxAudioStream && audio_  && pStream->receiver == rx_.toUInt()){
//        printf("%s(%0.1f) writeAudioData\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset());
        writeAudioData(pStream->data,pStream->length);
qDebug() << "Audio" << data.size() << pStream->length;
    } else if (pStream->type == TxChrono &&  pStream->receiver == rx_.toUInt()){
        mtx_.lock(); tx_fifo += 1; tx_fifo &= 7;
        int ssize = AudioHeaderSize+pStream->length*sizeof(float)*2;
//        printf("%s(%0.1f) TxChrono ",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset());
        quint32 tehtud;
        if (m_tx1[tx_fifo].size() != ssize) {m_tx1[tx_fifo].resize(ssize);/* m_tx1[tx_fifo].fill('\0x00');*/}
        Data_Stream * pOStream1 = (Data_Stream*)(m_tx1[tx_fifo].data());
        pOStream1->receiver = pStream->receiver;
        pOStream1->sampleRate = pStream->sampleRate; 
        pOStream1->format = pStream->format;
        pOStream1->codec = 0;
        pOStream1->crc = 0;
        pOStream1->length = pStream->length;
        pOStream1->type = TxAudioStream;
//        for (size_t i = 0; i < pStream->length; i++) pOStream1->data[i] = 0;
//        printf("%s(%0.1f) txAudioChrono %d %d %d",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),ssize,pStream->length,pStream->sampleRate);
        tehtud = readAudioData(pOStream1->data,pOStream1->length);
//        printf(" %s(%0.1f) tehtud%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),tehtud);
        if (tehtud && tehtud != pOStream1->length) {
          quint16 valmis = tehtud;
//          printf("%s(%0.1f) Audio build 1 mismatch requested %d done %d",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),pOStream1->length,tehtud);
#if JTDX_DEBUG_TO_FILE
          FILE * pFile = fopen (debug_file_.c_str(),"a");
          fprintf (pFile,"%s(%0.1f) Audio build 1 mismatch requested %d done %d",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),pOStream1->length,tehtud);
#endif
          tehtud = readAudioData(pOStream1->data + valmis,pOStream1->length - valmis);
//          printf("got %d\n",tehtud);
#if JTDX_DEBUG_TO_FILE
          fprintf (pFile,"got %d\n",tehtud);
          fclose (pFile);
#endif
//          if (tehtud && tehtud != pOStream1->length - valmis) {
//            valmis += tehtud;
//            for (size_t i = 0; i < pOStream1->length - valmis; i++) pOStream1->data[i+valmis] = 0;
//          }
//          else if (tehtud == 0)  for (size_t i = 0; i < pOStream1->length - valmis; i++) pOStream1->data[i+valmis] = 0;
//        }
//        else if (tehtud == 0) {
//          for (size_t i = 0; i < pStream->length; i++) pOStream1->data[i] = 0;
        }
#if JTDX_DEBUG_TO_FILE
        if (wavptr_ != NULL) {
          static constexpr float K = 0x7FFFFFFF;
          for (size_t i = 0; i < pOStream1->length; i+=2) {
            qint32 value = static_cast<int32_t>(K*pOStream1->data[i]);
            fwrite(&value,4,1,wavptr_);
          }
        }
#endif
        tx_fifo2 = tx_fifo; mtx_.unlock();
        if (!inConnected || commander_->sendBinaryMessage(m_tx1[tx_fifo2]) != m_tx1[tx_fifo2].size()) {
//          printf("%s(%0.1f) Sent 1 loaded failed\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset());
#if JTDX_DEBUG_TO_FILE
          FILE * pFile = fopen (debug_file_.c_str(),"a");
          fprintf (pFile,"%s(%0.1f) Sent 1 loaded failed\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset());
          fclose (pFile);
#endif
        }
    }// else printf ("Unused binary message received type=%d receiver=%d\n",pStream->type,pStream->receiver);
}

void TCITransceiver::txAudioData(quint32 len, float * data)
{
    QByteArray tx;
//    printf("%s(%0.1f) txAudioData %d %ld\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),len,(AudioHeaderSize+len*sizeof(float)*2));
    tx.resize(AudioHeaderSize+len*sizeof(float)*2);
//    printf("%s(%0.1f) txAudioData %d %d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),len,tx.size());
//    Data_Stream * pStream = reinterpret_cast<Data_Stream*>(tx.data());
    Data_Stream * pStream = (Data_Stream*)(tx.data());
    pStream->receiver = 0;
    pStream->sampleRate = audioSampleRate;
    pStream->format = 3;
    pStream->codec = 0;
    pStream->crc = 0;
    pStream->length = len;
    pStream->type = TxAudioStream;
    memcpy(pStream->data,data,len*sizeof(float)*2);
    commander_->sendBinaryMessage(tx);
}

quint32 TCITransceiver::writeAudioData (float * data, qint32 maxSize)
{
  static unsigned mstr0=999999;
  qint64 ms0 = m_jtdxtime->currentMSecsSinceEpoch2() % 86400000; //m_jtdxtime -> currentMSecsSinceEpoch2() % 86400000;
  unsigned mstr = ms0 % int(1000.0*m_period); // ms into the nominal Tx start time
  if(mstr < mstr0/2) {              //When mstr has wrapped around to 0, restart the buffer
    dec_data.params.kin = 0;
    m_bufferPos = 0;
//    printf("%s(%0.1f) reset buffer mstr:%d mstr0:%d maxSize:%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),mstr,mstr0,maxSize);
#if JTDX_DEBUG_TO_FILE
    FILE * pFile = fopen (debug_file_.c_str(),"a");
    fprintf (pFile,"%s(%0.1f) reset buffer mstr:%d mstr0:%d maxSize:%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),mstr,mstr0,maxSize);
    fclose (pFile);
#endif
  }
  mstr0=mstr;

  // no torn frames
  Q_ASSERT (!(maxSize % static_cast<qint32> (bytesPerFrame)));
  // these are in terms of input frames (not down sampled)
  size_t framesAcceptable ((sizeof (dec_data.d2) /
                            sizeof (dec_data.d2[0]) - dec_data.params.kin) * m_downSampleFactor);
  size_t framesAccepted (qMin (static_cast<size_t> (maxSize /
                                                    bytesPerFrame), framesAcceptable));

  if (framesAccepted < static_cast<size_t> (maxSize / bytesPerFrame)) {
    qDebug () << "dropped " << maxSize / bytesPerFrame - framesAccepted
                << " frames of data on the floor!"
                << dec_data.params.kin << mstr;
//    printf("%s(%0.1f) dropped %ld frames of data %d %d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),maxSize / bytesPerFrame - framesAccepted,dec_data.params.kin,mstr);
#if JTDX_DEBUG_TO_FILE
    FILE * pFile = fopen (debug_file_.c_str(),"a");
    fprintf (pFile,"%s(%0.1f) dropped %ld frames of data %d %d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),maxSize / bytesPerFrame - framesAccepted,dec_data.params.kin,mstr);
    fclose (pFile);
#endif
    }

    for (unsigned remaining = framesAccepted; remaining; ) {
      size_t numFramesProcessed (qMin (m_samplesPerFFT *
                                       m_downSampleFactor - m_bufferPos, remaining));

      if(m_downSampleFactor > 1) {
//  printf ("%s(%0.1f) writeAudioData maxs %d bytesPerFrame %ld Accepted %ld remaining %d Processed %ld Bufferpos %d kin %d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),
//  m_jtdxtime->GetOffset(),maxSize,bytesPerFrame,framesAccepted,remaining,numFramesProcessed,m_bufferPos,dec_data.params.kin);
        store (&data[(framesAccepted - remaining) * bytesPerFrame],
               numFramesProcessed, &m_buffer[m_bufferPos]);
        m_bufferPos += numFramesProcessed;

        if(m_bufferPos==m_samplesPerFFT*m_downSampleFactor) {
          qint32 framesToProcess (m_samplesPerFFT * m_downSampleFactor);
          qint32 framesAfterDownSample (m_samplesPerFFT);
          if(m_downSampleFactor > 1 && dec_data.params.kin>=0 &&
             dec_data.params.kin < (NTMAX*12000 - framesAfterDownSample)) {
            fil4_(&m_buffer[0], &framesToProcess, &dec_data.d2[dec_data.params.kin],
                  &framesAfterDownSample, &dec_data.dd2[dec_data.params.kin]);
            dec_data.params.kin += framesAfterDownSample;
          } else {
            // qDebug() << "framesToProcess     = " << framesToProcess;
            // qDebug() << "dec_data.params.kin = " << dec_data.params.kin;
            // qDebug() << "secondInPeriod      = " << secondInPeriod();
            // qDebug() << "framesAfterDownSample" << framesAfterDownSample;
          }
//    printf("%s(%0.1f) frameswritten %d downSampleFactor %d samplesPerFFT %d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),dec_data.params.kin,m_downSampleFactor,m_samplesPerFFT);
          Q_EMIT tciframeswritten (dec_data.params.kin);
//    printf("%s(%0.1f) frameswritten done\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset());
          m_bufferPos = 0;
        }

      } else {
//         printf ("%s(%0.1f) writeAudioData2 maxs %d bytesPerFrame %ld Accepted %ld remaining %d Processed %ld Bufferpos %d kin %d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),maxSize,bytesPerFrame,framesAccepted,remaining,numFramesProcessed,m_bufferPos,dec_data.params.kin);
         store (&data[(framesAccepted - remaining) * bytesPerFrame],
               numFramesProcessed, &dec_data.d2[dec_data.params.kin]);
        m_bufferPos += numFramesProcessed;
        dec_data.params.kin += numFramesProcessed;
        if (m_bufferPos == static_cast<unsigned> (m_samplesPerFFT)) {
//          printf("%s(%0.1f) frameswritten2 %d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),dec_data.params.kin);
          Q_EMIT tciframeswritten (dec_data.params.kin);
//          printf("%s(%0.1f) frameswritten2 done\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset());
          m_bufferPos = 0;
        }
      }
      remaining -= numFramesProcessed;
    }



  return maxSize;    // we drop any data past the end of the buffer on
  // the floor until the next period starts
}

  void TCITransceiver::rx2_enable (bool on)
{
//  printf ("%s(%0.1f) TCI rx2_enable:%d->%d busy:%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),rx2_,on,busy_rx2_);
#if JTDX_DEBUG_TO_FILE
  FILE * pFile = fopen (debug_file_.c_str(),"a");
  fprintf (pFile,"%s(%0.1f) TCI rx2_enable:%d->%d busy:%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),rx2_,on,busy_rx2_);
  fclose (pFile);
#endif
  if (busy_rx2_) return;
  requested_rx2_ = on;
  busy_rx2_ = true;
  const QString cmd = CmdRxEnable + SmDP + "1" + SmCM + (requested_rx2_ ? "true" : "false") + SmTZ;
  sendTextMessage(cmd);
  mysleep1(1000);
  busy_rx2_ = false;
}

  void TCITransceiver::rig_split ()
{
//  printf ("%s(%0.1f) TCI rig_split:%d->%d busy:%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),split_,requested_split_,busy_split_);
#if JTDX_DEBUG_TO_FILE
  FILE * pFile = fopen (debug_file_.c_str(),"a");
  fprintf (pFile,"%s(%0.1f) TCI rig_split:%d->%d busy:%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),split_,requested_split_,busy_split_);
  fclose (pFile);
#endif
  if (busy_split_) return;
  if (tci_timer2_->isActive()) mysleep2(0);
  busy_split_ = true;
  const QString cmd = CmdSplitEnable + SmDP + rx_ + SmCM + (requested_split_ ? "true" : "false") + SmTZ;
  sendTextMessage(cmd);
  mysleep2(500);
  busy_split_ = false;
  if (requested_split_ == split_) update_split (split_);
  else {
//    printf ("%s(%0.1f) TCI failed set split:%d->%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),split_,requested_split_);
#if JTDX_DEBUG_TO_FILE
    FILE * pFile = fopen (debug_file_.c_str(),"a");
    fprintf (pFile,"%s(%0.1f) TCI failed set split:%d->%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),split_,requested_split_);
    fclose (pFile);
#endif
    error_ = tr ("TCI failed set split");
  }
}

  void TCITransceiver::rig_power (bool on)
{
  TRACE_CAT ("TCITransceiver", on << state ());
//  printf ("%s(%0.1f) TCI rig_power:%d _power_:%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),on,_power_);
#if JTDX_DEBUG_TO_FILE
  FILE * pFile = fopen (debug_file_.c_str(),"a");
  fprintf (pFile,"%s(%0.1f) TCI rig_power:%d _power_:%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),on,_power_);
  fclose (pFile);
#endif
  if (on != _power_) {
    if (on) {
      const QString cmd = CmdStart + SmTZ;
      sendTextMessage(cmd);
    } else {
      const QString cmd = CmdStop + SmTZ;
      sendTextMessage(cmd);
    }
  } 

}

  void TCITransceiver::stream_audio (bool on)
{
  TRACE_CAT ("TCITransceiver", on << state ());
//  printf ("%s(%0.1f) TCI stream_audio:%d stream_audio_:%d requested_stream_audio_:%d rx_=%s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),on,stream_audio_,requested_stream_audio_,rx_.toStdString().c_str());
#if JTDX_DEBUG_TO_FILE
  FILE * pFile = fopen (debug_file_.c_str(),"a");
  fprintf (pFile,"%s(%0.1f) TCI stream_audio:%d stream_audio_:%d requested_stream_audio_:%d rx_=%s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),on,stream_audio_,requested_stream_audio_,rx_.toStdString().c_str());
  fclose (pFile);
#endif
  if (on != stream_audio_ && tci_Ready) {
    requested_stream_audio_ = on;
    if (on) {
      const QString cmd = CmdAudioStart + SmDP + rx_ + SmTZ;
      sendTextMessage(cmd);
    } else {
      const QString cmd = CmdAudioStop + SmDP + rx_ + SmTZ;
      sendTextMessage(cmd);
    }
  } 

}

  void TCITransceiver::do_audio (bool on)
{
  TRACE_CAT ("TCITransceiver", on << state ());
//  printf ("%s(%0.1f) TCI do_audio:%d audio_:%d state:%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),on,audio_,state().audio());
#if JTDX_DEBUG_TO_FILE
  FILE * pFile = fopen (debug_file_.c_str(),"a");
  fprintf (pFile,"%s(%0.1f) TCI do_audio:%d audio_:%d state:%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),on,audio_,state().audio());
  fclose (pFile);
#endif
  if (on) {
    dec_data.params.kin = 0;
    m_bufferPos = 0;
  }
  audio_ = on;
}

  void TCITransceiver::do_period (double period)
{
  TRACE_CAT ("TCITransceiver", period << state ());
//  printf ("%s(%0.1f) TCI do_period:%0.1f m_period_:%0.1f state:%0.1f\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),period,m_period,state().period());
#if JTDX_DEBUG_TO_FILE
  FILE * pFile = fopen (debug_file_.c_str(),"a");
  fprintf (pFile,"%s(%0.1f) TCI do_period:%0.1f m_period_:%0.1f state:%0.1f\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),period,m_period,state().period());
  fclose (pFile);
#endif
  m_period = period;
}

  void TCITransceiver::do_txvolume (qreal volume)
{
  TRACE_CAT ("TCITransceiver", volume << state ());
  QString drive = QString::number(round(100 - volume * 2.2222222));
//  printf ("%s(%0.1f) TCI tci_Ready:%d do_txvolume:%0.1f state:%0.1f drive:%s drive_:%s drive_busy:%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),tci_Ready,volume,state().volume(),drive.toStdString().c_str(),drive_.toStdString().c_str(),busy_drive_);
#if JTDX_DEBUG_TO_FILE
  FILE * pFile = fopen (debug_file_.c_str(),"a");
  fprintf (pFile,"%s(%0.1f) TCI tci_Ready:%d do_txvolume:%0.1f state:%0.1f drive:%s drive_:%s drive_busy:%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),tci_Ready,volume,state().volume(),drive.toStdString().c_str(),drive_.toStdString().c_str(),busy_drive_);
  fclose (pFile);
#endif
  requested_drive_ = drive;
  if (busy_drive_ || !tci_Ready || drive_ == drive) {
  if (busy_drive_) busy_drive_ = false;
  } else {
    busy_drive_ = true;
    if (ESDR3) {
      const QString cmd = CmdDrive + SmDP + rx_ + SmCM + drive + SmTZ;
      sendTextMessage(cmd);
    } else {
      const QString cmd = CmdDrive + SmDP + drive + SmTZ;
      sendTextMessage(cmd);
    }
  }
}

  void TCITransceiver::do_blocksize (qint32 blocksize)
{
  TRACE_CAT ("TCITransceiver", blocksize << state ());
//  printf ("%s(%0.1f) TCI do_blocksize:%d m_samplesPerFFT:%d state:%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),blocksize,m_samplesPerFFT,state().blocksize());
#if JTDX_DEBUG_TO_FILE
  FILE * pFile = fopen (debug_file_.c_str(),"a");
  fprintf (pFile,"%s(%0.1f) TCI do_blocksize:%d m_samplesPerFFT:%d state:%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),blocksize,m_samplesPerFFT,state().blocksize());
  fclose (pFile);
#endif
  m_samplesPerFFT = blocksize;
}

  void TCITransceiver::do_ptt (bool on)
{
  TRACE_CAT ("TCITransceiver", on << state ());
//  printf ("%s(%0.1f) TCI do_ptt:%d PTT_:%d requested_PTT_:%d busy_PTT_:%d state:%d use_for_ptt:%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),on,PTT_,requested_PTT_,busy_PTT_,state().ptt(),use_for_ptt_);
#if JTDX_DEBUG_TO_FILE
  FILE * pFile = fopen (debug_file_.c_str(),"a");
  fprintf (pFile,"%s(%0.1f) TCI do_ptt:%d PTT_:%d requested_PTT_:%d busy_PTT_:%d state:%d use_for_ptt:%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),on,PTT_,requested_PTT_,busy_PTT_,state().ptt(),use_for_ptt_);
  fclose (pFile);
#endif
  if (use_for_ptt_)
    {
      if (on != PTT_) {
        if (!inConnected && !error_.isEmpty()) throw error {error_};
//        if (!inConnected){ PTT_ = on; update_PTT(on); return; }
        else if (busy_PTT_ || !tci_Ready || !_power_) return;
        else busy_PTT_ = true;
        requested_PTT_ = on;
        if (ESDR3) {
          const QString cmd = CmdTrx + SmDP + rx_ + SmCM + (on ? "true" : "false") + SmCM + "tci"+ SmTZ;
          sendTextMessage(cmd);
        } else {
          const QString cmd = CmdTrx + SmDP + rx_ + SmCM + (on ? "true" : "false") + SmTZ;
          sendTextMessage(cmd);
        }
        mysleep3(1000);
        busy_PTT_ = false;
        if (requested_PTT_ == PTT_) {
          update_PTT(PTT_);
          if (PTT_ && do_snr_) update_level (-54);
          else { power_ = 0; if (do_pwr_) update_power (0); swr_ = 0; if (do_pwr_) update_swr (0); }
        } else {
//          printf ("%s(%0.1f) TCI failed set ptt %d->%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),PTT_,requested_PTT_);
#if JTDX_DEBUG_TO_FILE
          FILE * pFile = fopen (debug_file_.c_str(),"a");
          fprintf (pFile,"%s(%0.1f) TCI failed set ptt %d->%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),PTT_,requested_PTT_);
          fclose (pFile);
#endif
          error_ = tr ("TCI failed to set ptt");
//          tci_Ready = false;
//          throw error {tr ("TCI failed to set ptt")};
        }
      } else update_PTT(on); 
    }
  else
    {
          TRACE_CAT ("TCITransceiver", "TCI should use PTT via CAT");
          throw error {tr ("TCI should use PTT via CAT")};
    }
}

void TCITransceiver::do_frequency (Frequency f, MODE m, bool no_ignore)
{
  TRACE_CAT ("TCITransceiver", f << state ());
  auto f_string = frequency_to_string (f);
//  printf ("%s(%0.1f) TCI do_frequency:%s current_frequency:%s mode:%s current_mode:%s no_ignore:%d busy:%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),f_string.toStdString().c_str(),rx_frequency_.toStdString().c_str(),map_mode(m).toStdString().c_str(),requested_mode_.toStdString().c_str(),no_ignore,busy_rx_frequency_);
#if JTDX_DEBUG_TO_FILE
  FILE * pFile = fopen (debug_file_.c_str(),"a");
  fprintf (pFile,"%s(%0.1f) TCI do_frequency:%s current_frequency:%s mode:%s current_mode:%s no_ignore:%d busy:%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),f_string.toStdString().c_str(),rx_frequency_.toStdString().c_str(),map_mode(m).toStdString().c_str(),requested_mode_.toStdString().c_str(),no_ignore,busy_rx_frequency_);
  fclose (pFile);
#endif
  if  (tci_Ready && busy_rx_frequency_ && no_ignore) {
//    printf ("%s(%0.1f) TCI do_frequency critical no_ignore set vfo will be missed\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset());
#if JTDX_DEBUG_TO_FILE
    FILE * pFile = fopen (debug_file_.c_str(),"a");
    fprintf (pFile,"%s(%0.1f) TCI do_frequency critical no_ignore set vfo will be missed\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset());
    fclose (pFile);
#endif
  }
  if (busy_rx_frequency_) return;
  else {
    requested_rx_frequency_ = f_string;
    requested_mode_ = map_mode (m);
  }
  if (tci_Ready && _power_) {
    if (rx_frequency_ != requested_rx_frequency_) {
      busy_rx_frequency_ = true;
      band_change = abs(rx_frequency_.toInt()-requested_rx_frequency_.toInt()) > 1000000;
      const QString cmd = CmdVFO + SmDP + rx_ + SmCM + "0" + SmCM + requested_rx_frequency_ + SmTZ;
      sendTextMessage(cmd);
      mysleep1(2000);
//      if (band_change) mysleep1(500);
      if (requested_rx_frequency_ == rx_frequency_) update_rx_frequency (f);
      else {
//        printf ("%s(%0.1f) TCI failed set rxfreq:%s->%s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),rx_frequency_.toStdString().c_str(),requested_rx_frequency_.toStdString().c_str());
#if JTDX_DEBUG_TO_FILE
        FILE * pFile = fopen (debug_file_.c_str(),"a");
        fprintf (pFile,"%s(%0.1f) TCI failed set rxfreq:%s->%s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),rx_frequency_.toStdString().c_str(),requested_rx_frequency_.toStdString().c_str());
        fclose (pFile);
#endif
        error_ = tr ("TCI failed set rxfreq");
//        tci_Ready = false;
//        throw error {tr ("TCI failed set rxfreq")};
      }
      busy_rx_frequency_ = false;
    } else update_rx_frequency (string_to_frequency (rx_frequency_));
      
    if (!requested_mode_.isEmpty() && requested_mode_ != mode_ && !busy_mode_) {
      busy_mode_ = true;
      sendTextMessage(mode_to_command(requested_mode_));
      mysleep1(1000);
      if (requested_mode_.isEmpty() || requested_mode_ == mode_) update_mode (m);
      else {
//        printf ("%s(%0.1f) TCI failed set mode %s->%s",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),mode_.toStdString().c_str(),requested_mode_.toStdString().c_str());
#if JTDX_DEBUG_TO_FILE
        FILE * pFile = fopen (debug_file_.c_str(),"a");
        fprintf (pFile,"%s(%0.1f) TCI failed set mode %s->%s",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),mode_.toStdString().c_str(),requested_mode_.toStdString().c_str());
        fclose (pFile);
#endif
        error_ = tr ("TCI failed set mode");
//        tci_Ready = false;
//        throw error {tr ("TCI failed set mode")};
      }
      busy_mode_ = false;
    } 
//    if (band_change) {
//      tci_timer2_->start(210);
//      band_change = false;
//    }
  } else { 
    update_rx_frequency (f);
    update_mode (m);
  }
}

void TCITransceiver::do_tx_frequency (Frequency tx, MODE mode, bool no_ignore)
{
  TRACE_CAT ("TCITransceiver", tx << state ());
  auto f_string = frequency_to_string (tx);
//  printf ("%s(%0.1f) TCI do_tx_frequency:%s current_frequency:%s mode:%s no_ignore:%d busy:%d timer:%d remaining:%d band_change:%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),f_string.toStdString().c_str(),other_frequency_.toStdString().c_str(),map_mode(mode).toStdString().c_str(),no_ignore,busy_other_frequency_,tci_timer2_->isActive(),tci_timer2_->remainingTime(),band_change);
#if JTDX_DEBUG_TO_FILE
  FILE * pFile = fopen (debug_file_.c_str(),"a");
  fprintf (pFile,"%s(%0.1f) TCI do_tx_frequency:%s current_frequency:%s mode:%s no_ignore:%d busy:%d timer:%d remaining:%d band_change:%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),f_string.toStdString().c_str(),other_frequency_.toStdString().c_str(),map_mode(mode).toStdString().c_str(),no_ignore,busy_other_frequency_,tci_timer2_->isActive(),tci_timer2_->remainingTime(),band_change);
  fclose (pFile);
#endif
  if  (tci_Ready && busy_other_frequency_ && no_ignore) {
//    printf ("%s(%0.1f) TCI do_txfrequency critical no_ignore set tx vfo will be missed\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset());
#if JTDX_DEBUG_TO_FILE
    FILE * pFile = fopen (debug_file_.c_str(),"a");
    fprintf (pFile,"%s(%0.1f) TCI do_txfrequency critical no_ignore set tx vfo will be missed\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset());
    fclose (pFile);
#endif
  }
  if (busy_other_frequency_) return;
  requested_other_frequency_ = f_string;
  requested_mode_ = map_mode (mode);
  if (tx)
    {
      requested_split_ = true;
      if (tci_Ready && _power_) {
        if (band_change && !tci_timer2_->isActive()) {if (!HPSDR) mysleep2(210); else mysleep2(2000);}
        else if (tci_timer2_->isActive()) mysleep2(0);
        if (requested_split_ != split_) rig_split();
        else update_split (split_);
        if (other_frequency_ != requested_other_frequency_) {
          busy_other_frequency_ = true;
//          printf ("%s(%0.1f) TCI VFO1 command sent\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset());
#if JTDX_DEBUG_TO_FILE
          FILE * pFile = fopen (debug_file_.c_str(),"a");
          fprintf (pFile,"%s(%0.1f) TCI VFO1 command sent\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset());
          fclose (pFile);
#endif
          const QString cmd = CmdVFO + SmDP + rx_ + SmCM + "1" + SmCM + requested_other_frequency_ + SmTZ;
          sendTextMessage(cmd);
          mysleep2(1000);
          if (requested_other_frequency_ == other_frequency_) update_other_frequency (tx);
          else {
//            printf ("%s(%0.1f) TCI failed set txfreq:%s->%s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),other_frequency_.toStdString().c_str(),requested_other_frequency_.toStdString().c_str());
#if JTDX_DEBUG_TO_FILE
            FILE * pFile = fopen (debug_file_.c_str(),"a");
            fprintf (pFile,"%s(%0.1f) TCI failed set txfreq:%s->%s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),other_frequency_.toStdString().c_str(),requested_other_frequency_.toStdString().c_str());
            fclose (pFile);
#endif
            error_ = tr ("TCI failed set txfreq");
//            tci_Ready = false;
//            throw error {tr ("TCI failed set txfreq")};
          }
          busy_other_frequency_ = false;
        } else update_other_frequency (string_to_frequency (other_frequency_));
          
      } else {
        update_split (requested_split_);
        update_other_frequency (tx);
      }
    }
  else {
    requested_split_ = false;
    requested_other_frequency_ = "";
    if (tci_Ready && _power_) {
      if (band_change) {mysleep2(2000); if (!HPSDR) mysleep2(200);}
      if (tci_timer2_->isActive()) mysleep2(0);
      if (requested_split_ != split_) rig_split();
      else update_split (split_);
      update_other_frequency (tx);
    } else {
      update_split (requested_split_);
      update_other_frequency (tx);
    }
  }
}

void TCITransceiver::do_mode (MODE m)
{
  TRACE_CAT ("TCITransceiver", m << state ());
  auto m_string = map_mode (m);
//  printf ("%s(%0.1f) TCI do_mode:%s->%s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),mode_.toStdString().c_str(),m_string.toStdString().c_str());
#if JTDX_DEBUG_TO_FILE
  FILE * pFile = fopen (debug_file_.c_str(),"a");
  fprintf (pFile,"%s(%0.1f) TCI do_mode:%s->%s\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),mode_.toStdString().c_str(),m_string.toStdString().c_str());
  fclose (pFile);
#endif
  if (requested_mode_ != m_string) requested_mode_ = m_string;
  if (!requested_mode_.isEmpty() && mode_ != requested_mode_ && !busy_mode_) {
    busy_mode_ = true;
    sendTextMessage(mode_to_command(requested_mode_));
    mysleep1(1000);
    if (requested_mode_ == mode_) update_mode (m);
    else {
//      printf ("%s(%0.1f) TCI failed set mode %s->%s",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),mode_.toStdString().c_str(),requested_mode_.toStdString().c_str());
#if JTDX_DEBUG_TO_FILE
      FILE * pFile = fopen (debug_file_.c_str(),"a");
      fprintf (pFile,"%s(%0.1f) TCI failed set mode %s->%s",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),mode_.toStdString().c_str(),requested_mode_.toStdString().c_str());
      fclose (pFile);
#endif
      error_ = tr ("TCI failed set mode");
//      tci_Ready = false;
//      throw error {tr ("TCI failed set mode")};
    }
    busy_mode_ = false;
  }
}

void TCITransceiver::do_poll ()
{
  if (!inConnected) {
//    printf("%s(%0.1f) TCI do_poll |%s| split:%d ptt:%d rx_busy:%d tx_busy:%d level:%d power:%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),error_.toStdString().c_str(),state ().split (),state (). ptt (),busy_rx_frequency_,busy_other_frequency_,level_,power_);
#if JTDX_DEBUG_TO_FILE
    FILE * pFile = fopen (debug_file_.c_str(),"a");
    fprintf (pFile,"%s(%0.1f) TCI do_poll |%s| split:%d ptt:%d rx_busy:%d tx_busy:%d level:%d power:%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),error_.toStdString().c_str(),state ().split (),state (). ptt (),busy_rx_frequency_,busy_other_frequency_,level_,power_);
    fclose (pFile);
#endif
  }
  if (/*!inConnected && */!error_.isEmpty()) {tci_Ready = false; throw error {error_};}
  else if (!tci_Ready) throw error {tr ("TCI could not be opened")};
  update_rx_frequency (string_to_frequency (rx_frequency_));
  update_split(split_);
  if (state ().split ()) {
    if (other_frequency_ == requested_other_frequency_) update_other_frequency (string_to_frequency (other_frequency_));
    else if (!busy_rx_frequency_) do_tx_frequency(string_to_frequency (requested_other_frequency_),get_mode(true),false);
  }
  update_mode (get_mode());
  if (do_pwr_ && PTT_) {update_power (power_ * 100); update_swr (swr_*10);}
  if (do_snr_ && !PTT_) {
      update_level (level_);
      if(!ESDR3) {
        const QString cmd = CmdSmeter + SmDP + rx_ + SmCM + "0" +  SmTZ;
        sendTextMessage(cmd);
      }
  }
}

auto TCITransceiver::get_mode (bool requested) -> MODE
{
  MODE m {UNK};
  if (requested) {
    if ("am" == requested_mode_)
      {
        m = AM;
      }
    else if ("cw" == requested_mode_)
      {
        m = CW;
      }
    else if ("wfm" == requested_mode_)
      {
        m = FM;
      }
    else if ("nfm" == requested_mode_)
      {
        m = DIG_FM;
      }
    else if ("lsb" == requested_mode_)
      {
        m = LSB;
      }
    else if ("usb" == requested_mode_)
      {
        m = USB;
      }
    else if ("digl" == requested_mode_)
      {
        m = DIG_L;
      }
    else if ("digu" == requested_mode_)
      {
        m = DIG_U;
      }
  } else {
    if ("am" == mode_)
      {
        m = AM;
      }
    else if ("cw" == mode_)
      {
        m = CW;
      }
    else if ("wfm" == mode_)
      {
        m = FM;
      }
    else if ("nfm" == mode_)
      {
        m = DIG_FM;
      }
    else if ("lsb" == mode_)
      {
        m = LSB;
      }
    else if ("usb" == mode_)
      {
        m = USB;
      }
    else if ("digl" == mode_)
      {
        m = DIG_L;
      }
    else if ("digu" == mode_)
      {
        m = DIG_U;
      }
  }
  return m;
}

QString TCITransceiver::mode_to_command (QString m_string) const
{
//    if (ESDR3) {
//      const QString cmd = CmdMode + SmDP + rx_ + SmCM + m_string.toUpper() + SmTZ;
//      return cmd;
//    } else {
      const QString cmd = CmdMode + SmDP + rx_ + SmCM + m_string + SmTZ;
      return cmd;
//    }
}

QString TCITransceiver::frequency_to_string (Frequency f) const
{
  // number is localized and in kHz, avoid floating point translation
  // errors by adding a small number (0.1Hz)
//  auto f_string = QString {"%L2"}.arg (f);
  auto f_string = QString {}.setNum(f);
//  f_string = f_string.simplified().remove(' ');
//  f_string = f_string.replace(",","");
//  f_string = f_string.replace(".","");
//  f_string = f_string.replace("'","");
//  f_string = f_string.replace("’","");
//  printf ("frequency_to_string3 |%s|\n",f_string.toStdString().c_str());
  return f_string;
  
}

auto TCITransceiver::string_to_frequency (QString s) const -> Frequency
{
  // temporary hack because Commander is returning invalid UTF-8 bytes
  s.replace (QChar {QChar::ReplacementCharacter}, locale_.groupSeparator ());

  bool ok;

  auto f = QLocale::c ().toDouble (s, &ok); // temporary fix

  if (!ok)
    {
      throw error {tr ("TCI sent an unrecognized frequency") + " |" + s + "|"};
    }
  return f;
}

void TCITransceiver::mysleep1 (int ms)
{
//  tci_timer1->setSingleShot(true);
//  printf("%s(%0.1f) TCI sleep1 start %d %d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),ms,tci_timer1_->isActive());
  if (ms) tci_timer1_->start(ms);
  tci_loop1_->exec();
#if JTDX_DEBUG_TO_FILE
  FILE * pFile = fopen (debug_file_.c_str(),"a");
  fprintf (pFile,"%s(%0.1f) TCI sleep1 %d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),tci_timer1_->isActive());
  fclose (pFile);
#endif
//  printf("%s(%0.1f) TCI sleep1 end %d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),tci_timer1_->isActive());
  if (tci_timer1_->isActive() && tci_Ready) tci_timer1_->stop();
}
void TCITransceiver::mysleep2 (int ms)
{
//  tci_timer2->setSingleShot(true);
//  printf("%s(%0.1f) TCI sleep2 start %d %d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),ms,tci_timer2_->isActive());
  if (ms) tci_timer2_->start(ms);
  tci_loop2_->exec();
#if JTDX_DEBUG_TO_FILE
  FILE * pFile = fopen (debug_file_.c_str(),"a");
  fprintf (pFile,"%s(%0.1f) TCI sleep2 %d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),tci_timer2_->isActive());
  fclose (pFile);
#endif
//  printf("%s(%0.1f) TCI sleep2 end %d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),tci_timer2_->isActive());
  if (tci_timer2_->isActive()) tci_timer2_->stop();
}
void TCITransceiver::mysleep3 (int ms)
{
//  tci_timer3->setSingleShot(true);
//  printf("%s(%0.1f) TCI sleep3 start %d %d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),ms,tci_timer3_->isActive());
  if (ms) tci_timer3_->start(ms);
  tci_loop3_->exec();
#if JTDX_DEBUG_TO_FILE
  FILE * pFile = fopen (debug_file_.c_str(),"a");
  fprintf (pFile,"%s(%0.1f) TCI sleep3 %d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),tci_timer3_->isActive());
  fclose (pFile);
#endif
//  printf("%s(%0.1f) TCI sleep3 end %d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),tci_timer3_->isActive());
  if (tci_timer3_->isActive()) tci_timer3_->stop();
}
// Modulator part

void TCITransceiver::do_modulator_start (unsigned symbolsLength, double framesPerSymbol,
                       double frequency, double toneSpacing, bool synchronize, double dBSNR, double TRperiod)
{
//  QThread::currentThread()->setPriority(QThread::HighPriority);
//  Q_ASSERT (stream);

// Time according to this computer which becomes our base time
  qint64 ms0 = m_jtdxtime->currentMSecsSinceEpoch2() % 86400000;
//  qDebug() << "ModStart" << QDateTime::currentDateTimeUtc().toString("hh:mm:ss.sss");
  unsigned mstr = ms0 % int(1000.0*m_period); // ms into the nominal Tx start time
  if (m_state != Idle) {
//    stop ();
    throw error {tr ("TCI modulator not Idle")};
  }
  m_quickClose = false;
  m_symbolsLength = symbolsLength;
  m_isym0 = std::numeric_limits<unsigned>::max (); // big number
  m_frequency0 = 0.;
  m_phi = 0.;
  m_addNoise = dBSNR < 0.;
  m_nsps = framesPerSymbol;
  m_trfrequency = frequency;
  m_amp = std::numeric_limits<qint32>::max ();
  m_toneSpacing = toneSpacing;
  m_TRperiod=TRperiod;
  unsigned delay_ms=1000;
  if(m_nsps==1920) delay_ms=500;   //FT8
  else if(m_nsps==576) {
    delay_ms=500;   //FT4
  }
  // noise generator parameters
  if (m_addNoise) {
    m_snr = qPow (10.0, 0.05 * (dBSNR - 6.0));
    m_fac = 3000.0;
    if (m_snr > 1.0) m_fac = 3000.0 / m_snr;
  }

  // round up to an exact portion of a second that allows for startup delays
  //m_ic = (mstr / delay_ms) * audioSampleRate * delay_ms / 1000;
  auto mstr2 = mstr - delay_ms;
  if (mstr <= delay_ms) {
    m_ic = 0;
  } else {
    m_ic = mstr2 * (audioSampleRate / 1000);
  }
  m_silentFrames = 0;
  // calculate number of silent frames to send
  if (m_ic == 0 && synchronize && !m_tuning)	{
    m_silentFrames = audioSampleRate / (1000 / delay_ms) - (mstr * (audioSampleRate / 1000));
  }
  m_state = (synchronize && m_silentFrames) ?
                        Synchronizing : Active;
//  printf ("%s(%0.1f) TCI modulator startdelay_ms=%d ASR=%d mstr=%d mstr2=%d m_ic=%d s_Frames=%lld synchronize=%d m_tuning=%d State=%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),delay_ms,audioSampleRate,mstr,mstr2,m_ic,m_silentFrames,synchronize,m_tuning,m_state);
#if JTDX_DEBUG_TO_FILE
  FILE * pFile = fopen (debug_file_.c_str(),"a");
  fprintf (pFile,"%s(%0.1f) TCI modulator startdelay_ms=%d ASR=%d mstr=%d mstr2=%d m_ic=%d s_Frames=%lld synchronize=%d m_tuning=%d State=%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),delay_ms,audioSampleRate,mstr,mstr2,m_ic,m_silentFrames,synchronize,m_tuning,m_state);
  fclose (pFile);
#endif
  Q_EMIT tci_mod_active(m_state != Idle);
}

void TCITransceiver::do_tune (bool newState)
{
//  printf("%s(%0.1f) TCI modulator tune %d ->%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),m_tuning,newState);
#if JTDX_DEBUG_TO_FILE
  FILE * pFile = fopen (debug_file_.c_str(),"a");
  fprintf (pFile,"%s(%0.1f) TCI modulator tune %d ->%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),m_tuning,newState);
  fclose (pFile);
#endif
  m_tuning = newState;
  if (!m_tuning) do_modulator_stop (true);
}

void TCITransceiver::do_modulator_stop (bool quick)
{
  m_quickClose = quick;
  if(m_state != Idle) {
  m_state = Idle;
  Q_EMIT tci_mod_active(m_state != Idle);
  }
  tx_audio_ = false;
}

quint32 TCITransceiver::readAudioData (float * data, qint32 maxSize)
{
  double toneFrequency=1500.0;
  if(m_nsps==6) {
    toneFrequency=1000.0;
    m_trfrequency=1000.0;
    m_frequency0=1000.0;
  }
  if(maxSize==0) return 0;

  qint64 numFrames (maxSize/bytesPerFrame);
  float * samples (reinterpret_cast<float *> (data));
  float * end (samples + numFrames * bytesPerFrame);
//  printf("%s(%0.1f) readAudioData %f %f %lld %lld %d %p %p\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),m_nsps,m_TRperiod,numFrames,m_silentFrames,maxSize,samples,end);
  qint64 framesGenerated (0);

  switch (m_state)
    {
    case Synchronizing:
      {
        if (m_silentFrames)	{  // send silence up to first second
          framesGenerated = qMin (m_silentFrames, numFrames);
          for ( ; samples != end; samples = load (0, samples)) { // silence
          }
          m_silentFrames -= framesGenerated;
          return framesGenerated * bytesPerFrame;
        }
        m_state = Active;
        Q_EMIT tci_mod_active(m_state != Idle);
        m_cwLevel = false;
        m_ramp = 0;		// prepare for CW wave shaping
      }
      // fall through

    case Active:
      {
        unsigned int isym=0;
        qint32 sample=0;
        if(!m_tuning) isym=m_ic/(4.0*m_nsps);          // Actual fsample=48000
		bool slowCwId=((isym >= m_symbolsLength) && (icw[0] > 0));
        m_nspd=2560;                 // 22.5 WPM

        if(m_TRperiod > 16.0 && slowCwId) {     // Transmit CW ID?
          m_dphi = m_twoPi*m_trfrequency/audioSampleRate;
          unsigned ic0 = m_symbolsLength * 4 * m_nsps;
          unsigned j(0);

          while (samples != end) {
            j = (m_ic - ic0)/m_nspd + 1; // symbol of this sample
            bool level {bool (icw[j])};
            m_phi += m_dphi;
            if (m_phi > m_twoPi) m_phi -= m_twoPi;
            sample=0;
            float amp=2147483647.0;
            float x=0.0;
            if(m_ramp!=0) {
              x=qSin(float(m_phi));
              if(SOFT_KEYING) {
                amp=qAbs(qint32(m_ramp));
                if(amp>2147483647.0) amp=2147483647.0;
              }
              sample=round(amp*x);
            }
            if (int (j) <= icw[0] && j < NUM_CW_SYMBOLS) { // stopu condition
              samples = load (postProcessSample (sample), samples);
              ++framesGenerated;
              ++m_ic;
            } else {
              m_state = Idle;
//              printf("%s(%0.1f) TCI modulator Idle1 %lld frames generated\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),framesGenerated);
#if JTDX_DEBUG_TO_FILE
              FILE * pFile = fopen (debug_file_.c_str(),"a");
              fprintf (pFile,"%s(%0.1f) TCI modulator Idle1 %lld frames generated\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),framesGenerated);
              fclose (pFile);
#endif
              Q_EMIT tci_mod_active(m_state != Idle);
              return framesGenerated * bytesPerFrame;
            }

            // adjust ramp
            if ((m_ramp != 0 && m_ramp != std::numeric_limits<qint32>::min ()) || level != m_cwLevel) {
              // either ramp has terminated at max/min or direction has changed
              m_ramp += RAMP_INCREMENT; // ramp
            }
            m_cwLevel = level;
          }
          return framesGenerated * bytesPerFrame;
        } //End of code for CW ID

        double const baud (12000.0 / m_nsps);
        // fade out parameters (no fade out for tuning)
        unsigned int i0,i1;
        if(m_tuning) {
          i1 = i0 = 9999 * m_nsps;
        } else {
          i0=(m_symbolsLength - 0.017) * 4.0 * m_nsps;
          i1= m_symbolsLength * 4.0 * m_nsps;
        }

        sample=0;
        for (unsigned i = 0; i < numFrames && m_ic <= i1; ++i) {
//          printf("algus %d %lld %d %d",i,numFrames,m_ic,i1);
          if(m_TRperiod > 16.0 || m_tuning) {
            isym=0;
            if(!m_tuning) isym=m_ic / (4.0 * m_nsps);       //Actual fsample=48000
            if (isym != m_isym0 || m_trfrequency != m_frequency0) {
              if(itone[0]>=100) {
                m_toneFrequency0=itone[0];
              } else {
                if(m_toneSpacing==0.0) {
                  m_toneFrequency0=m_trfrequency + itone[isym]*baud;
                } else {
                  m_toneFrequency0=m_trfrequency + itone[isym]*m_toneSpacing;
                }
              }
//            qDebug() << "B" << m_ic << numFrames << isym << itone[isym] << toneFrequency0 << m_nsps;
              m_dphi = m_twoPi * m_toneFrequency0 / audioSampleRate;
              m_isym0 = isym;
              m_frequency0 = m_trfrequency;         //???
            }

            int j=m_ic/480;
            if(m_fSpread>0.0 and j!=m_j0) {
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
            float x1=QRandomGenerator::global ()->generateDouble ();
            float x2=QRandomGenerator::global ()->generateDouble ();
#else
            float x1=(float)qrand()/RAND_MAX;
            float x2=(float)qrand()/RAND_MAX;
#endif
              toneFrequency = m_toneFrequency0 + 0.5*m_fSpread*(x1+x2-1.0);
              m_dphi = m_twoPi * toneFrequency / audioSampleRate;
              m_j0=j;
            }

            m_phi += m_dphi;
            if (m_phi > m_twoPi) m_phi -= m_twoPi;
            //ramp for first tone
            if (m_ic==0) m_amp = m_amp * 0.008144735;
            if (m_ic > 0 and  m_ic < 191) m_amp = m_amp / 0.975;
            //ramp for last tone
            if (m_ic > i0) m_amp = 0.99 * m_amp;
            if (m_ic > i1) m_amp = 0.0;
            sample=qRound(m_amp*qSin(m_phi));
          }
          //transmit from a precomputed FT8 wave[] array:
          if(!m_tuning and (m_toneSpacing < 0.0)) { m_amp=2147483647.0; sample=qRound(m_amp*foxcom_.wave[m_ic]); }
          samples = load (postProcessSample (sample), samples);
          ++framesGenerated; ++m_ic;
        }

//          printf("sample saved %lld %d\n",framesGenerated,m_ic);
        if (m_amp == 0.0) { // TODO G4WJS: compare double with zero might not be wise
          if (icw[0] == 0) {
            // no CW ID to send
            m_state = Idle;
//            printf("%s(%0.1f) TCI modulator Idle2 %lld frames generated\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),framesGenerated);
#if JTDX_DEBUG_TO_FILE
            FILE * pFile = fopen (debug_file_.c_str(),"a");
            fprintf (pFile,"%s(%0.1f) TCI modulator Idle2 %lld frames generated\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),framesGenerated);
            fclose (pFile);
#endif
            Q_EMIT tci_mod_active(m_state != Idle);
            return framesGenerated * bytesPerFrame;
          }
          m_phi = 0.0;
        }

        m_frequency0 = m_trfrequency;
        // done for this chunk - continue on next call
        if (samples != end && framesGenerated) {
//          printf("%s(%0.1f) TCI modulator Idle3 %lld frames generated\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),framesGenerated);
#if JTDX_DEBUG_TO_FILE
          FILE * pFile = fopen (debug_file_.c_str(),"a");
          fprintf (pFile,"%s(%0.1f) TCI modulator Idle3 %lld frames generated\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),framesGenerated);
          fclose (pFile);
#endif
        }
        while (samples != end) { // pad block with silence
          samples = load (0, samples);
          ++framesGenerated;
        }
        return framesGenerated * bytesPerFrame;
      }
      // fall through

    case Idle:
      while (samples != end) samples = load (0, samples);
    }

  Q_ASSERT (Idle == m_state);
  return 0;
}

qint32 TCITransceiver::postProcessSample (qint32 sample) const
{
  if (m_addNoise) {  // Test frame, we'll add noise
    qint32 s = m_fac * (gran () + sample * m_snr / 32768.0);
    if (s > std::numeric_limits<qint32>::max ()) {
      s = std::numeric_limits<qint32>::max ();
    }
    if (s < std::numeric_limits<qint32>::min ()) {
      s = std::numeric_limits<qint32>::min ();
    }
    sample = s;
  }
  return sample;
}
