#include "rigctld.h" #include "logcategories.h" rigCtlD::rigCtlD(QObject* parent) : QTcpServer(parent) { } rigCtlD::~rigCtlD() { qInfo(logRigCtlD()) << "closing rigctld"; } void rigCtlD::receiveFrequency(freqt freq) { emit setFrequency(freq); } void rigCtlD::receiveStateInfo(rigStateStruct* state) { qInfo("Setting rig state"); rigState = state; } int rigCtlD::startServer(qint16 port) { if (!this->listen(QHostAddress::Any, port)) { qInfo(logRigCtlD()) << "could not start on port " << port; return -1; } else { qInfo(logRigCtlD()) << "started on port " << port; } return 0; } void rigCtlD::incomingConnection(qintptr socket) { rigCtlClient* client = new rigCtlClient(socket, rigCaps, rigState, this); connect(this, SIGNAL(onStopped()), client, SLOT(closeSocket())); } void rigCtlD::stopServer() { qInfo(logRigCtlD()) << "stopping server"; emit onStopped(); } void rigCtlD::receiveRigCaps(rigCapabilities caps) { qInfo(logRigCtlD()) << "Got rigcaps for:" << caps.modelName; this->rigCaps = caps; } rigCtlClient::rigCtlClient(int socketId, rigCapabilities caps, rigStateStruct* state, rigCtlD* parent) : QObject(parent) { commandBuffer.clear(); sessionId = socketId; rigCaps = caps; rigState = state; socket = new QTcpSocket(this); this->parent = parent; if (!socket->setSocketDescriptor(sessionId)) { qInfo(logRigCtlD()) << " error binding socket: " << sessionId; return; } connect(socket, SIGNAL(readyRead()), this, SLOT(socketReadyRead()), Qt::DirectConnection); connect(socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()), Qt::DirectConnection); connect(parent, SIGNAL(sendData(QString)), this, SLOT(sendData(QString)), Qt::DirectConnection); qInfo(logRigCtlD()) << " session connected: " << sessionId; } void rigCtlClient::socketReadyRead() { QByteArray data = socket->readAll(); commandBuffer.append(data); QString sep = "\n"; static int num = 0; bool longReply = false; char responseCode = 0; QStringList response; bool setCommand = false; if (commandBuffer.endsWith('\n')) { qDebug(logRigCtlD()) << sessionId << "command received" << commandBuffer; commandBuffer.chop(1); // Remove \n character if (commandBuffer.endsWith('\r')) { commandBuffer.chop(1); // Remove \n character } // We have a full line so process command. if (rigState == Q_NULLPTR) { qInfo(logRigCtlD()) << "no rigState!"; return; } if (commandBuffer[num] == ";" || commandBuffer[num] == "|" || commandBuffer[num] == ",") { sep = commandBuffer[num].toLatin1(); num++; } else if (commandBuffer[num] == "+") { longReply = true; sep = "\n"; num++; } else if (commandBuffer[num] == "#") { return; } else if (commandBuffer[num].toLower() == "q") { closeSocket(); return; } if (commandBuffer[num] == "\\") { num++; } QStringList command = commandBuffer.mid(num).split(" "); if (command[0] == 0xf0 || command[0] == "chk_vfo") { QString resp; if (longReply) { resp.append(QString("ChkVFO: ")); } resp.append(QString("%1").arg(rigState->currentVfo)); response.append(resp); } else if (command[0] == "dump_state") { // Currently send "fake" state information until I can work out what is required! response.append("1"); response.append("1"); response.append("0"); for (bandType band : rigCaps.bands) { response.append(generateFreqRange(band)); } response.append("0 0 0 0 0 0 0"); if (rigCaps.hasTransmit) { for (bandType band : rigCaps.bands) { response.append(generateFreqRange(band)); } } response.append("0 0 0 0 0 0 0"); response.append("0x1ff 1"); response.append("0x1ff 0"); response.append("0 0"); response.append("0x1e 2400"); response.append("0x2 500"); response.append("0x1 8000"); response.append("0x1 2400"); response.append("0x20 15000"); response.append("0x20 8000"); response.append("0x40 230000"); response.append("0 0"); response.append("9900"); response.append("9900"); response.append("10000"); response.append("0"); response.append("10"); response.append("10 20 30"); response.append("0x3effffff"); response.append("0x3effffff"); response.append("0x7fffffff"); response.append("0x7fffffff"); response.append("0x7fffffff"); response.append("0x7fffffff"); response.append("done"); } else if (command[0] == "f" || command[0] == "get_freq") { QString resp; if (longReply) { resp.append(QString("Frequency: ")); } if (rigState->currentVfo == 0) { resp.append(QString("%1").arg(rigState->vfoAFreq.Hz)); } else { resp.append(QString("%1").arg(rigState->vfoBFreq.Hz)); } response.append(resp); } else if (command[0] == "F" || command[0] == "set_freq") { setCommand = true; freqt freq; bool ok=false; double newFreq=0.0f; QString vfo = "VFOA"; if (command.length() == 2) { newFreq = command[1].toDouble(&ok); } else if (command.length() == 3) // Includes VFO { newFreq = command[2].toDouble(&ok); vfo = command[1]; } if (ok) { freq.Hz = static_cast(newFreq); qDebug(logRigCtlD()) << QString("Set frequency: %1 (%2)").arg(freq.Hz).arg(command[1]); emit parent->setFrequency(freq); } } else if (command[0] == "1" || command[0] == "dump_caps") { response.append(QString("Caps dump for model: %1").arg(rigCaps.modelID)); response.append(QString("Model Name:\t%1").arg(rigCaps.modelName)); response.append(QString("Mfg Name:\tIcom")); response.append(QString("Backend version:\t0.1")); response.append(QString("Backend copyright:\t2021")); if (rigCaps.hasTransmit) { response.append(QString("Rig type:\tTransceiver")); } else { response.append(QString("Rig type:\tReceiver")); } if (rigCaps.hasPTTCommand) { response.append(QString("PTT type:\tRig capable")); } response.append(QString("DCD type:\tRig capable")); response.append(QString("Port type:\tNetwork link")); } else if (command[0] == "t" || command[0] == "get_ptt") { if (rigCaps.hasPTTCommand) { QString resp; if (longReply) { resp.append(QString("PTT: ")); } resp.append(QString("%1").arg(rigState->ptt)); response.append(resp); } else { responseCode = -1; } } else if (command[0] == "T" || command[0] == "set_ptt") { setCommand = true; if (rigCaps.hasPTTCommand) { if (command.length() > 1 && command[1] == "0") { emit parent->setPTT(false); } else { emit parent->setPTT(true); } } else { responseCode = -1; } } else if (command[0] == "v" || command[0] == "get_vfo") { QString resp; if (longReply) { resp.append("VFO: "); } if (rigState->currentVfo == 0) { resp.append("VFOA"); } else { resp.append("VFOB"); } response.append(resp); } else if (command[0] == "V" || command[0] == "set_vfo") { setCommand = true; if (command.length() > 1 && command[1] == "?") { response.append("set_vfo: ?"); response.append("VFOA"); response.append("VFOB"); response.append("Sub"); response.append("Main"); response.append("MEM"); } else if (command.length() > 1 && (command[1] == "VFOB" || command[1] == "Sub")) { emit parent->setVFO(1); } else { emit parent->setVFO(0); } } else if (command[0] == "s" || command[0] == "get_split_vfo") { if (longReply) { response.append(QString("Split: %1").arg(rigState->splitEnabled)); } else { response.append(QString("%1").arg(rigState->splitEnabled)); } QString resp; if (longReply) { resp.append("TX VFO: "); } if (rigState->currentVfo == 0) { resp.append(QString("%1").arg("VFOB")); } else { resp.append(QString("%1").arg("VFOA")); } response.append(resp); } else if (command[0] == "S" || command[0] == "set_split_vfo") { setCommand = true; if (command.length() > 1 && command[1] == "1") { emit parent->setSplit(1); } else { emit parent->setSplit(0); } } else if (command[0] == "\xf3" || command[0] == "get_vfo_info") { if (longReply) { //response.append(QString("set_vfo: %1").arg(command[1])); if (command[1] == "VFOB") { response.append(QString("Freq: %1").arg(rigState->vfoBFreq.Hz)); } else { response.append(QString("Freq: %1").arg(rigState->vfoAFreq.Hz)); } response.append(QString("Mode: %1").arg(getMode(rigState->mode, rigState->datamode))); response.append(QString("Width: %1").arg(getFilter(rigState->mode, rigState->filter))); response.append(QString("Split: %1").arg(rigState->splitEnabled)); response.append(QString("SatMode: %1").arg(0)); // Need to get satmode } else { if (command[1] == "VFOB") { response.append(QString("%1").arg(rigState->vfoBFreq.Hz)); } else { response.append(QString("%1").arg(rigState->vfoAFreq.Hz)); } response.append(QString("%1").arg(getMode(rigState->mode, rigState->datamode))); response.append(QString("%1").arg(getFilter(rigState->mode, rigState->filter))); } } else if (command[0] == "i" || command[0] == "get_split_freq") { if (rigState->currentVfo == 0) { response.append(QString("%1").arg(rigState->vfoBFreq.Hz)); } else { response.append(QString("%1").arg(rigState->vfoAFreq.Hz)); } } else if (command[0] == "I" || command[0] == "set_split_freq") { setCommand = true; } else if (command[0] == "m" || command[0] == "get_mode") { if (longReply) { response.append(QString("Mode: %1").arg(getMode(rigState->mode, rigState->datamode))); response.append(QString("Passband: %1").arg(getFilter(rigState->mode, rigState->filter))); } else { response.append(QString("%1").arg(getMode(rigState->mode, rigState->datamode))); response.append(QString("%1").arg(getFilter(rigState->mode, rigState->filter))); } } else if (command[0] == "M" || command[0] == "set_mode") { // Set mode setCommand = true; int width = -1; QString vfo = "VFOA"; QString mode = "USB"; if (command.length() == 3) { width = command[2].toInt(); mode = command[1]; } else if (command.length() == 4) { width = command[3].toInt(); mode = command[2]; vfo = command[1]; } qDebug(logRigCtlD()) << "setting mode: VFO:" << vfo << getMode(mode) << mode << "width" << width; if (width != -1 && width <= 1800) width = 2; else width = 1; emit parent->setMode(getMode(mode), width); if (mode.mid(0, 3) == "PKT") { emit parent->setDataMode(true, width); } else { emit parent->setDataMode(false, width); } } else if (command[0] == "s" || command[0] == "get_split_vfo") { response.append(QString("0")); response.append(QString("VFOA")); } else if (command[0] == "j" || command[0] == "get_rit") { QString resp; if (longReply) { resp.append("RIT: "); } resp.append(QString("%1").arg(0)); response.append(resp); } else if (command[0] == "J" || command[0] == "set_rit") { setCommand = true; } else if (command[0] == "z" || command[0] == "get_zit") { QString resp; if (longReply) { resp.append("ZIT: "); } resp.append(QString("%1").arg(0)); response.append(resp); } else if (command[0] == "Z" || command[0] == "set_zit") { setCommand = true; } else if (command[0] == "l" || command[0] == "get_level") { QString resp; float value = 0; if (longReply && command.length() > 1) { resp.append(QString("%1: ").arg(command[1])); } if (command[1] == "STRENGTH") { value = (float)rigState->sMeter; if (value > 240) value = value - 176; else if (value > 120) value = value - 120; else if (value > 90) value = value - 102; else if (value > 60) value = value - 84; else if (value > 30) value = value - 66; else if (value > 10) value = value - 58; else if (value > 0) value = value - 54; } else if (command[1] == "AF") { value = (float)rigState->afGain / 255; } else if (command[1] == "RF") { value = (float)rigState->rfGain / 255; } else if (command[1] == "SQL") { value = (float)rigState->squelch / 255; } else if (command[1] == "COMP") { value = (float)rigState->compLevel / 255; } else if (command[1] == "MICGAIN") { value = (float)rigState->micGain / 255; emit parent->setMicGain(value); } else if (command[1] == "MON") { value = (float)rigState->monitorLevel / 255; } else if (command[1] == "VOXGAIN") { value = (float)rigState->voxGain / 255; } else if (command[1] == "ANTIVOX") { value = (float)rigState->antiVoxGain / 255; } else if (command[1] == "RFPOWER") { value = (float)rigState->txPower / 255; } resp.append(QString("%1").arg(value)); response.append(resp); } else if (command[0] == "L" || command[0] == "set_level" && command.length() > 2) { int value; setCommand = true; if (command[1] == "AF") { value = command[2].toFloat() * 255; emit parent->setAfGain(value); } else if (command[1] == "RF") { value = command[2].toFloat() * 255; emit parent->setRfGain(value); } else if (command[1] == "SQL") { value = command[2].toFloat() * 255; emit parent->setSql(value); } else if (command[1] == "COMP") { value = command[2].toFloat() * 255; emit parent->setCompLevel(value); } else if (command[1] == "MICGAIN") { value = command[2].toFloat() * 255; emit parent->setMicGain(value); } else if (command[1] == "MON") { value = command[2].toFloat() * 255; emit parent->setMonitorLevel(value); } else if (command[1] == "VOXGAIN") { value = command[2].toFloat() * 255; emit parent->setVoxGain(value); } else if (command[1] == "ANTIVOX") { value = command[2].toFloat() * 255; emit parent->setAntiVoxGain(value); } else if (command[1] == "RFPOWER") { value = command[2].toFloat() * 255; emit parent->setTxPower(value); } qInfo(logRigCtlD()) << "Setting:" << command[1] << command[2] << value; } else if (command[0] == "u" || command[0] == "get_func") { QString resp; if (longReply && command.length() > 1) { resp.append(QString("%1: ").arg(command[1])); } resp.append(QString("%1").arg(0)); response.append(resp); } else if (command[0] == "R" || command[0] == "set_func") { setCommand = true; } else if (command[0] == 0x88 || command[0] == "get_powerstat") { /* QString resp; if (longReply && command.length() > 1) { resp.append(QString("Power Status: ")); } resp.append(QString("%1").arg(0)); // Always reply with ON response.append(resp); */ } else if (command.length() > 1 && command[0] == 0x87 || command[0] == "set_powerstat") { setCommand = true; if (command[1] == "0") { emit parent->sendPowerOff(); } else { emit parent->sendPowerOn(); } } else { qInfo(logRigCtlD()) << "Unimplemented command" << commandBuffer; } if (longReply) { if (command.length() == 2) sendData(QString("%1: %2%3").arg(command[0]).arg(command[1]).arg(sep)); if (command.length() == 3) sendData(QString("%1: %2 %3%4").arg(command[0]).arg(command[1]).arg(command[2]).arg(sep)); if (command.length() == 4) sendData(QString("%1: %2 %3 %4%5").arg(command[0]).arg(command[1]).arg(command[2]).arg(command[3]).arg(sep)); } if (setCommand || responseCode != 0 || longReply) { if (responseCode == 0) { response.append(QString("RPRT 0")); } else { response.append(QString("RPRT %1").arg(responseCode)); } } for (QString str : response) { if (str != "") sendData(QString("%1%2").arg(str).arg(sep)); } if (sep != "\n") { sendData(QString("\n")); } commandBuffer.clear(); sep = " "; num = 0; } } void rigCtlClient::socketDisconnected() { qInfo(logRigCtlD()) << sessionId << "disconnected"; socket->deleteLater(); this->deleteLater(); } void rigCtlClient::closeSocket() { socket->close(); } void rigCtlClient::sendData(QString data) { qDebug(logRigCtlD()) << "Sending:" << data; if (socket != Q_NULLPTR && socket->isValid() && socket->isOpen()) { socket->write(data.toLatin1()); } else { qInfo(logRigCtlD()) << "socket not open!"; } } QString rigCtlClient::getFilter(unsigned char mode, unsigned char filter) { if (mode == 3 || mode == 7 || mode == 12 || mode == 17) { switch (filter) { case 1: return QString("1200"); case 2: return QString("500"); case 3: return QString("250"); } } else if (mode == 4 || mode == 8) { switch (filter) { case 1: return QString("2400"); case 2: return QString("500"); case 3: return QString("250"); } } else if (mode == 2) { switch (filter) { case 1: return QString("9000"); case 2: return QString("6000"); case 3: return QString("3000"); } } else if (mode == 5) { switch (filter) { case 1: return QString("15000"); case 2: return QString("10000"); case 3: return QString("7000"); } } else { // SSB or unknown mode switch (filter) { case 1: return QString("3000"); case 2: return QString("2400"); case 3: return QString("1800"); } } return QString(""); } QString rigCtlClient::getMode(unsigned char mode, bool datamode) { QString ret; if (datamode) { ret="PKT"; } switch (mode) { case 0: ret.append("LSB"); break; case 1: ret.append("USB"); break; case 2: ret.append("AM"); break; case 3: ret.append("CW"); break; case 4: ret.append("RTTY"); break; case 5: ret.append("FM"); break; case 6: ret.append("WFM"); break; case 7: ret.append("CWR"); break; case 8: ret.append("RTTYR"); break; case 12: ret.append("USB"); break; case 17: ret.append("LSB"); break; case 22: ret.append("FM"); break; } return ret; } unsigned char rigCtlClient::getMode(QString modeString) { if (modeString == QString("LSB")) { return 0; } else if (modeString == QString("USB")) { return 1; } else if (modeString == QString("AM")) { return 2; } else if (modeString == QString("CW")) { return 3; } else if (modeString == QString("RTTY")) { return 4; } else if (modeString == QString("FM")) { return 5; } else if (modeString == QString("WFM")) { return 6; } else if (modeString == QString("CWR")) { return 7; } else if (modeString == QString("RTTYR")) { return 8; } else if (modeString == QString("PKTUSB")) { return 1; } else if (modeString == QString("PKTLSB")) { return 0; } else if (modeString == QString("PKTFM")) { return 22; } else { return 0; } return 0; } QString rigCtlClient::generateFreqRange(bandType band) { unsigned int lowFreq = 0; unsigned int highFreq = 0; switch (band) { case band2200m: lowFreq = 135000; highFreq = 138000; break; case band630m: lowFreq = 493000; highFreq = 595000; break; case band160m: lowFreq = 1800000; highFreq = 2000000; break; case band80m: lowFreq = 3500000; highFreq = 4000000; break; case band60m: lowFreq = 5250000; highFreq = 5450000; break; case band40m: lowFreq = 7000000; highFreq = 7300000; break; case band30m: lowFreq = 10100000; highFreq = 10150000; break; case band20m: lowFreq = 14000000; highFreq = 14350000; break; case band17m: lowFreq = 18068000; highFreq = 18168000; break; case band15m: lowFreq = 21000000; highFreq = 21450000; break; case band12m: lowFreq = 24890000; highFreq = 24990000; break; case band10m: lowFreq = 28000000; highFreq = 29700000; break; case band6m: lowFreq = 50000000; highFreq = 54000000; break; case band4m: lowFreq = 70000000; highFreq = 70500000; break; case band2m: lowFreq = 144000000; highFreq = 148000000; break; case band70cm: lowFreq = 420000000; highFreq = 450000000; break; case band23cm: lowFreq = 1240000000; highFreq = 1400000000; break; case bandAir: lowFreq = 108000000; highFreq = 137000000; break; case bandWFM: lowFreq = 88000000; highFreq = 108000000; break; case bandGen: lowFreq = 10000; highFreq = 30000000; break; } quint64 modes=0; for (mode_info mode : rigCaps.modes) { for (int i = 0; mode_str[i].str[0] != '\0'; i++) { QString curMode = mode.name; if (!strcmp(curMode.toLocal8Bit(), mode_str[i].str)) { modes |= mode_str[i].mode; } if (rigCaps.hasDataModes) { curMode = "PKT" + mode.name; if (!strcmp(curMode.toLocal8Bit(), mode_str[i].str)) { modes |= mode_str[i].mode; } } } } QString ret = ""; if (lowFreq > 0 && highFreq > 0) { ret = QString("%1 %2 0x%3 %4 %5 0x%6 0x%7").arg(lowFreq).arg(highFreq).arg(modes,0,16).arg(-1).arg(-1).arg(0x16000003,0,16).arg(0xf,0,16); } return ret; }