wfview/rigctld.cpp

1616 wiersze
58 KiB
C++
Czysty Zwykły widok Historia

#include "rigctld.h"
#include "logcategories.h"
2021-08-14 16:20:40 +00:00
static struct
{
quint64 mode;
rigMode_t mk;
2021-08-14 16:20:40 +00:00
const char* str;
uchar data=0;
2021-08-14 16:20:40 +00:00
} mode_str[] =
{
{ RIG_MODE_AM, modeAM, "AM" },
{ RIG_MODE_CW, modeCW, "CW" },
{ RIG_MODE_USB, modeUSB, "USB" },
{ RIG_MODE_LSB, modeLSB, "LSB" },
{ RIG_MODE_RTTY, modeRTTY, "RTTY" },
{ RIG_MODE_FM, modeFM, "FM" },
{ RIG_MODE_WFM, modeWFM, "WFM" },
{ RIG_MODE_CWR, modeCW_R, "CWR" },
{ RIG_MODE_RTTYR, modeRTTY_R, "RTTYR" },
{ RIG_MODE_AMS, modeUnknown, "AMS" },
{ RIG_MODE_PKTLSB, modeLSB, "PKTLSB", 1U},
{ RIG_MODE_PKTUSB, modeUSB, "PKTUSB" , 1U},
{ RIG_MODE_PKTFM, modeFM, "PKTFM", true},
{ RIG_MODE_PKTFMN, modeUnknown, "PKTFMN", 1U},
{ RIG_MODE_ECSSUSB, modeUnknown, "ECSSUSB" },
{ RIG_MODE_ECSSLSB, modeUnknown, "ECSSLSB" },
{ RIG_MODE_FAX, modeUnknown, "FAX" },
{ RIG_MODE_SAM, modeUnknown, "SAM" },
{ RIG_MODE_SAL, modeUnknown, "SAL" },
{ RIG_MODE_SAH, modeUnknown, "SAH" },
{ RIG_MODE_DSB, modeUnknown, "DSB"},
{ RIG_MODE_FMN, modeUnknown, "FMN" },
{ RIG_MODE_PKTAM, modeUnknown, "PKTAM"},
{ RIG_MODE_P25, modeP25, "P25"},
{ RIG_MODE_DSTAR, modeDV, "D-STAR"},
{ RIG_MODE_DPMR, modedPMR, "DPMR"},
{ RIG_MODE_NXDNVN, modeNXDN_VN, "NXDN-VN"},
{ RIG_MODE_NXDN_N, modeNXDN_N, "NXDN-N"},
{ RIG_MODE_DCR, modeUnknown, "DCR"},
{ RIG_MODE_AMN, modeUnknown, "AMN"},
{ RIG_MODE_PSK, modePSK, "PSK"},
{ RIG_MODE_PSKR, modePSK_R, "PSKR"},
{ RIG_MODE_C4FM, modeUnknown, "C4FM"},
{ RIG_MODE_SPEC, modeUnknown, "SPEC"},
{ RIG_MODE_NONE, modeUnknown, "" },
};
static const subCommandStruct levels_str[] =
{
2024-12-31 20:03:38 +00:00
{"PREAMP",funcPreamp,typeUChar},
2024-03-05 21:01:13 +00:00
{"ATT",funcAttenuator,typeUChar},
2024-12-31 20:03:38 +00:00
{"VOXDELAY",funcVOXDelay,typeUChar},
2024-03-05 21:01:13 +00:00
{"AF",funcAfGain,typeFloat},
{"RF",funcRfGain,typeFloat},
{"SQL",funcSquelch,typeFloat},
2025-01-05 15:39:20 +00:00
{"IF",funcFilterWidth,typeFloat},
2024-03-05 21:01:13 +00:00
{"APF",funcAPFLevel,typeFloat},
{"NR",funcNRLevel,typeFloat},
{"PBT_IN",funcPBTInner,typeFloat},
{"PBT_OUT",funcPBTOuter,typeFloat},
{"CWPITCH",funcCwPitch,typeFloat},
{"RFPOWER",funcRFPower,typeFloat},
{"MICGAIN",funcMicGain,typeFloat},
2025-01-03 00:05:50 +00:00
{"KEYSPD",funcKeySpeed,typeUShort},
2024-03-05 21:01:13 +00:00
{"NOTCHF",funcNotchFilter,typeUChar},
{"COMP",funcCompressorLevel,typeFloat},
2025-01-05 15:39:20 +00:00
{"AGC",funcAGCTimeConstant,typeUChar},
2024-03-05 21:01:13 +00:00
{"BKINDL",funcBreakInDelay,typeFloat},
{"BAL",funcNone,typeFloat},
{"METER",funcNone,typeFloat},
{"VOXGAIN",funcVoxGain,typeFloat},
{"ANTIVOX",funcAntiVoxGain,typeFloat},
{"SLOPE_LOW",funcNone,typeFloat},
{"SLOPE_HIGH",funcNone,typeFloat},
{"BKIN_DLYMS",funcBreakInDelay,typeFloat},
{"RAWSTR",funcNone,typeFloat},
2025-02-03 20:20:40 +00:00
{"SWR",funcSWRMeter,typeSWR},
{"ALC",funcALCMeter,typeDouble},
{"STRENGTH",funcSMeter,typeDouble},
{"RFPOWER_METER",funcPowerMeter,typeDouble},
{"COMPMETER",funcCompMeter,typeDouble},
{"VD_METER",funcVdMeter,typeDouble},
{"ID_METER",funcIdMeter,typeDouble},
2024-03-05 21:01:13 +00:00
{"NOTCHF_RAW",funcNone,typeFloat},
{"MONITOR_GAIN",funcMonitorGain,typeFloat},
{"NQ",funcNone,typeFloat},
{"RFPOWER_METER_WATT",funcNone,typeFloat},
{"SPECTRUM_MDOE",funcNone,typeFloat},
{"SPECTRUM_SPAN",funcNone,typeFloat},
{"SPECTRUM_EDGE_LOW",funcNone,typeFloat},
{"SPECTRUM_EDGE_HIGH",funcNone,typeFloat},
{"SPECTRUM_SPEED",funcNone,typeFloat},
{"SPECTRUM_REF",funcNone,typeFloat},
{"SPECTRUM_AVG",funcNone,typeFloat},
{"SPECTRUM_ATT",funcNone,typeFloat},
{"TEMP_METER",funcNone,typeFloat},
{"BAND_SELECT",funcNone,typeFloat},
{"USB_AF",funcNone,typeFloat},
{"",funcNone,'\x0'}
};
static const subCommandStruct functions_str[] =
{
2024-04-05 12:01:26 +00:00
{"FAGC",funcNone,typeBinary},
2024-03-05 21:01:13 +00:00
{"NB",funcNoiseBlanker,typeBinary},
{"COMP",funcCompressor,typeBinary},
{"VOX",funcVox,typeBinary},
{"TONE",funcRepeaterTone,typeBinary},
{"TQSL",funcRepeaterTSQL,typeBinary},
{"SBKIN",funcBreakIn,typeUChar},
{"FBKIN",funcBreakIn,typeUChar},
{"ANF",funcAutoNotch,typeBinary},
{"NR",funcNoiseReduction,typeBinary},
{"AIP",funcNone,typeBinary},
{"APF",funcAPFType,typeBinary},
{"MON",funcMonitor,typeBinary},
{"MN",funcManualNotch,typeBinary},
{"RF",funcNone,typeBinary},
{"ARO",funcNone,typeBinary},
{"LOCK",funcLockFunction,typeBinary},
{"MUTE",funcAFMute,typeBinary},
{"VSC",funcNone,typeBinary},
{"REV",funcNone,typeBinary},
{"SQL",funcSquelch,typeBinary}, // Actually an integer as ICOM doesn't provide on/off for squelch
{"ABM",funcNone,typeBinary},
{"VSC",funcNone,typeBinary},
{"REV",funcNone,typeBinary},
{"BC",funcNone,typeBinary},
{"MBC",funcNone,typeBinary},
{"RIT",funcRitStatus,typeBinary},
{"AFC",funcNone,typeBinary},
{"SATMODE",funcSatelliteMode,typeBinary},
{"SCOPE",funcScopeOnOff,typeBinary},
{"RESUME",funcNone,typeBinary},
{"TBURST",funcNone,typeBinary},
{"TUNER",funcTunerStatus,typeBinary},
{"XIT",funcNone,typeBinary},
{"",funcNone,typeBinary}
2021-08-14 16:20:40 +00:00
};
static const subCommandStruct params_str[] =
{
2024-03-05 21:01:13 +00:00
{"ANN",funcNone,typeUChar},
{"APO",funcNone,typeUChar},
2025-01-05 15:39:20 +00:00
{"BACKLIGHT",funcBackLightLevel,typeUChar},
2024-03-05 21:01:13 +00:00
{"BEEP",funcNone,typeUChar},
{"TIME",funcTime,typeUChar},
{"BAT",funcNone,typeUChar},
{"KEYLIGHT",funcNone,typeUChar},
2024-06-03 18:43:00 +00:00
{"",funcNone,typeNone}
};
#define ARG_IN1 0x01
#define ARG_OUT1 0x02
#define ARG_IN2 0x04
#define ARG_OUT2 0x08
#define ARG_IN3 0x10
#define ARG_OUT3 0x20
#define ARG_IN4 0x40
#define ARG_OUT4 0x80
#define ARG_OUT5 0x100
#define ARG_NONE 0
#define ARG_IN (ARG_IN1|ARG_IN2|ARG_IN3|ARG_IN4)
#define ARG_OUT (ARG_OUT1|ARG_OUT2|ARG_OUT3|ARG_OUT4)
#define ARG_IN_LINE 0x4000
#define ARG_NOVFO 0x8000
#define MAXNAMESIZE 32
2024-03-05 21:01:13 +00:00
static const commandStruct commands_list[] =
{
2024-03-05 21:01:13 +00:00
{ 'F', "set_freq", funcFreqSet, typeFreq, ARG_IN, "Frequency" },
{ 'f', "get_freq", funcFreqGet, typeFreq, ARG_OUT, "Frequency" },
2024-12-29 14:59:52 +00:00
{ 'M', "set_mode", funcModeSet, typeMode, ARG_IN, "Mode", "Passband" },
{ 'm', "get_mode", funcModeGet, typeMode, ARG_OUT, "Mode", "Passband" },
2025-01-03 14:32:38 +00:00
{ 'I', "set_split_freq", funcFreqSet, typeFreq, ARG_IN, "TX Frequency" },
{ 'i', "get_split_freq", funcFreqGet, typeFreq, ARG_OUT, "TX Frequency" },
2024-12-29 14:59:52 +00:00
{ 'X', "set_split_mode", funcSplitStatus, typeMode, ARG_IN, "TX Mode", "TX Passband" },
{ 'x', "get_split_mode", funcSplitStatus, typeMode, ARG_OUT, "TX Mode", "TX Passband" },
2025-01-03 14:32:38 +00:00
{ 'K', "set_split_freq_mode", funcModeSet, typeFreq, ARG_IN, "TX Frequency", "TX Mode", "TX Passband" },
{ 'k', "get_split_freq_mode", funcModeGet, typeFreq, ARG_OUT, "TX Frequency", "TX Mode", "TX Passband" },
2024-12-29 14:59:52 +00:00
{ 'S', "set_split_vfo", funcSplitStatus, typeSplitVFO, ARG_IN, "Split", "TX VFO" },
{ 's', "get_split_vfo", funcSplitStatus, typeSplitVFO, ARG_OUT, "Split", "TX VFO" },
2024-03-05 21:01:13 +00:00
{ 'N', "set_ts", funcTuningStep, typeUChar, ARG_IN, "Tuning Step" },
{ 'n', "get_ts", funcTuningStep, typeUChar, ARG_OUT, "Tuning Step" },
{ 'L', "set_level", funcRigctlLevel, typeLevel, ARG_IN, "Level", "Level Value" },
{ 'l', "get_level", funcRigctlLevel, typeLevel, ARG_IN1 | ARG_OUT2, "Level", "Level Value" },
{ 'U', "set_func", funcRigctlFunction, typeLevel, ARG_IN, "Func", "Func Status" },
{ 'u', "get_func", funcRigctlFunction, typeLevel, ARG_IN1 | ARG_OUT2, "Func", "Func Status" },
{ 'P', "set_parm", funcRigctlParam, typeLevel, ARG_IN1 | ARG_NOVFO, "Parm", "Parm Value" },
{ 'p', "get_parm", funcRigctlParam, typeLevel, ARG_IN1 | ARG_OUT2, "Parm", "Parm Value" },
{ 'G', "vfo_op", funcSelectVFO, typeUChar, ARG_IN, "Mem/VFO Op" },
{ 'g', "scan", funcScanning, typeUChar, ARG_IN, "Scan Fct", "Scan Channel" },
{ 'A', "set_trn", funcCIVTransceive, typeUChar, ARG_IN | ARG_NOVFO, "Transceive" },
{ 'a', "get_trn", funcCIVTransceive, typeUChar, ARG_OUT | ARG_NOVFO, "Transceive" },
{ 'R', "set_rptr_shift", funcSendFreqOffset, typeUChar, ARG_IN, "Rptr Shift" },
{ 'r', "get_rptr_shift", funcReadFreqOffset, typeUChar, ARG_OUT, "Rptr Shift" },
{ 'O', "set_rptr_offs", funcSendFreqOffset, typeUChar, ARG_IN, "Rptr Offset" },
{ 'o', "get_rptr_offs", funcReadFreqOffset, typeUChar, ARG_OUT, "Rptr Offset" },
{ 'C', "set_ctcss_tone", funcToneFreq, typeUChar, ARG_IN, "CTCSS Tone" },
{ 'c', "get_ctcss_tone", funcToneFreq, typeUChar, ARG_OUT, "CTCSS Tone" },
{ 'D', "set_dcs_code", funcDTCSCode, typeUChar, ARG_IN, "DCS Code" },
{ 'd', "get_dcs_code", funcDTCSCode, typeUChar, ARG_OUT, "DCS Code" },
{ 0x90, "set_ctcss_sql", funcTSQLFreq, typeUChar, ARG_IN, "CTCSS Sql" },
{ 0x91, "get_ctcss_sql", funcTSQLFreq, typeUChar, ARG_OUT, "CTCSS Sql" },
{ 0x92, "set_dcs_sql", funcCSQLCode, typeUChar, ARG_IN, "DCS Sql" },
{ 0x93, "get_dcs_sql", funcCSQLCode, typeUChar, ARG_OUT, "DCS Sql" },
{ 'V', "set_vfo", funcSelectVFO, typeVFO, ARG_IN | ARG_NOVFO, "VFO" },
{ 'v', "get_vfo", funcSelectVFO, typeVFO, ARG_NOVFO | ARG_OUT, "VFO" },
2025-01-06 20:16:14 +00:00
{ 'T', "set_ptt", funcTransceiverStatus, typeBinary, ARG_IN, "VFO", "PTT" },
{ 't', "get_ptt", funcTransceiverStatus, typeBinary, ARG_OUT, "VFO", "PTT" },
2024-03-05 21:01:13 +00:00
{ 'E', "set_mem", funcMemoryContents, typeMode, ARG_IN, "Memory#" },
{ 'e', "get_mem", funcMemoryContents, typeMode, ARG_OUT, "Memory#" },
{ 'H', "set_channel", funcMemoryMode, typeUChar, ARG_IN | ARG_NOVFO, "Channel"},
{ 'h', "get_channel", funcMemoryMode, typeUChar, ARG_IN | ARG_NOVFO, "Channel", "Read Only" },
{ 'B', "set_bank", funcMemoryGroup, typeUChar, ARG_IN, "Bank" },
{ '_', "get_info", funcNone, typeUChar, ARG_OUT | ARG_NOVFO, "Info" },
2025-01-18 12:29:34 +00:00
{ 'J', "set_rit", funcRitFreq, typeShort, ARG_IN, "RIT" },
{ 'j', "get_rit", funcRitFreq, typeShort, ARG_OUT, "RIT" },
{ 'Z', "set_xit", funcXitFreq, typeShort, ARG_IN, "XIT" },
{ 'z', "get_xit", funcXitFreq, typeShort, ARG_OUT, "XIT" },
2024-03-05 21:01:13 +00:00
{ 'Y', "set_ant", funcAntenna, typeUChar, ARG_IN, "Antenna", "Option" },
{ 'y', "get_ant", funcAntenna, typeUChar, ARG_IN1 | ARG_OUT2 | ARG_NOVFO, "AntCurr", "Option", "AntTx", "AntRx" },
{ 0x87, "set_powerstat", funcPowerControl, typeBinary, ARG_IN | ARG_NOVFO, "Power Status" },
{ 0x88, "get_powerstat", funcPowerControl, typeBinary, ARG_OUT | ARG_NOVFO, "Power Status" },
{ 0x89, "send_dtmf", funcNone, typeUChar, ARG_IN, "Digits" },
{ 0x8a, "recv_dtmf", funcNone, typeUChar, ARG_OUT, "Digits" },
{ '*', "reset", funcNone, typeUChar, ARG_IN, "Reset" },
{ 'w', "send_cmd", funcNone, typeUChar, ARG_IN1 | ARG_IN_LINE | ARG_OUT2 | ARG_NOVFO, "Cmd", "Reply" },
{ 'W', "send_cmd_rx", funcNone, typeUChar, ARG_IN | ARG_OUT2 | ARG_NOVFO, "Cmd", "Reply"},
2024-12-29 14:59:52 +00:00
{ 'b', "send_morse", funcSendCW, typeString, ARG_IN | ARG_NOVFO | ARG_IN_LINE, "Morse" },
{ 0xbb, "stop_morse", funcSendCW, typeString, },
2024-03-05 21:01:13 +00:00
{ 0xbc, "wait_morse", funcSendCW, typeUChar, },
{ 0x94, "send_voice_mem", funcNone, typeUChar, ARG_IN, "Voice Mem#" },
{ 0x8b, "get_dcd", funcNone, typeUChar, ARG_OUT, "DCD" },
{ 0x8d, "set_twiddle", funcNone, typeUChar, ARG_IN | ARG_NOVFO, "Timeout (secs)" },
{ 0x8e, "get_twiddle", funcNone, typeUChar, ARG_OUT | ARG_NOVFO, "Timeout (secs)" },
{ 0x97, "uplink", funcNone, typeUChar, ARG_IN | ARG_NOVFO, "1=Sub, 2=Main" },
{ 0x95, "set_cache", funcNone, typeUChar, ARG_IN | ARG_NOVFO, "Timeout (msecs)" },
{ 0x96, "get_cache", funcNone, typeUChar, ARG_OUT | ARG_NOVFO, "Timeout (msecs)" },
{ '2', "power2mW", funcNone, typeUChar, ARG_IN1 | ARG_IN2 | ARG_IN3 | ARG_OUT1 | ARG_NOVFO, "Power [0.0..1.0]", "Frequency", "Mode", "Power mW" },
{ '4', "mW2power", funcNone, typeUChar, ARG_IN1 | ARG_IN2 | ARG_IN3 | ARG_OUT1 | ARG_NOVFO, "Pwr mW", "Freq", "Mode", "Power [0.0..1.0]" },
{ '1', "dump_caps", funcNone, typeUChar, ARG_NOVFO },
{ '3', "dump_conf", funcNone, typeUChar, ARG_NOVFO },
{ 0x8f, "dump_state", funcNone, typeUChar, ARG_OUT | ARG_NOVFO },
2025-01-18 12:29:34 +00:00
{ 0xf0, "chk_vfo", funcNone, typeUChar, ARG_NOVFO, "ChkVFO" }, /* rigctld only--check for VFO mode */
2024-03-05 21:01:13 +00:00
{ 0xf2, "set_vfo_opt", funcNone, typeUChar, ARG_NOVFO | ARG_IN, "Status" }, /* turn vfo option on/off */
2024-12-29 14:59:52 +00:00
{ 0xf3, "get_vfo_info", funcSelectVFO, typeVFOInfo, ARG_IN1 | ARG_NOVFO | ARG_OUT5, "VFO", "Freq", "Mode", "Width", "Split", "SatMode" }, /* get several vfo parameters at once */
2024-03-05 21:01:13 +00:00
{ 0xf5, "get_rig_info", funcNone, typeUChar, ARG_NOVFO | ARG_OUT, "RigInfo" }, /* get several vfo parameters at once */
{ 0xf4, "get_vfo_list", funcNone, typeUChar, ARG_OUT | ARG_NOVFO, "VFOs" },
{ 0xf6, "get_modes", funcNone, typeUChar, ARG_OUT | ARG_NOVFO, "Modes" },
{ 0xf9, "get_clock", funcTime, typeUChar, ARG_NOVFO },
{ 0xf8, "set_clock", funcTime, typeUChar, ARG_IN | ARG_NOVFO, "YYYYMMDDHHMMSS.sss+ZZ" },
{ 0xf1, "halt", funcNone, typeUChar, ARG_NOVFO }, /* rigctld only--halt the daemon */
{ 0x8c, "pause", funcNone, typeUChar, ARG_IN, "Seconds" },
{ 0x98, "password", funcNone, typeUChar, ARG_IN | ARG_NOVFO, "Password" },
{ 0xf7, "get_mode_bandwidths", funcNone, typeUChar, ARG_IN | ARG_NOVFO, "Mode" },
{ 0xa0, "set_separator", funcSeparator, typeUChar, ARG_IN | ARG_NOVFO, "Separator" },
{ 0xa1, "get_separator", funcSeparator, typeUChar, ARG_NOVFO, "Separator" },
{ 0xa2, "set_lock_mode", funcLockFunction, typeUChar, ARG_IN | ARG_NOVFO, "Locked" },
{ 0xa3, "get_lock_mode", funcLockFunction, typeUChar, ARG_NOVFO, "Locked" },
{ 0xa4, "send_raw", funcNone, typeUChar, ARG_NOVFO | ARG_IN1 | ARG_IN2 | ARG_OUT3, "Terminator", "Command", "Send raw answer" },
{ 0xa5, "client_version", funcNone, typeUChar, ARG_NOVFO | ARG_IN1, "Version", "Client version" },
{ 0x00, "", funcNone, typeNone},
};
2024-03-05 21:01:13 +00:00
rigCtlD::rigCtlD(QObject* parent) :
QTcpServer(parent)
{
}
rigCtlD::~rigCtlD()
{
qInfo(logRigCtlD()) << "closing rigctld";
}
2021-05-15 09:42:44 +00:00
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;
}
2021-12-01 10:01:05 +00:00
return 0;
}
void rigCtlD::incomingConnection(qintptr socket) {
2024-03-05 21:01:13 +00:00
rigCtlClient* client = new rigCtlClient(socket, this);
connect(this, SIGNAL(onStopped()), client, SLOT(closeSocket()));
}
2021-05-15 09:42:44 +00:00
void rigCtlD::stopServer()
{
qInfo(logRigCtlD()) << "stopping server";
emit onStopped();
}
2024-03-05 21:01:13 +00:00
void rigCtlClient::receiveRigCaps(rigCapabilities* caps)
{
if (caps != Q_NULLPTR) {
qInfo(logRigCtlD()) << "Got rigcaps for:" << caps->modelName;
2024-12-29 14:59:52 +00:00
} else
{
qInfo(logRigCtlD()) << "Rig has gone away, close connection now!";
closeSocket();
}
this->rigCaps = caps;
}
2024-03-05 21:01:13 +00:00
rigCtlClient::rigCtlClient(int socketId, rigCtlD* parent) : QObject(parent)
{
2021-05-15 09:42:44 +00:00
2023-06-08 07:20:50 +00:00
this->setObjectName("RigCtlD");
2024-09-30 11:05:51 +00:00
queue = cachingQueue::getInstance();
2024-03-05 21:01:13 +00:00
connect(queue, SIGNAL(rigCapsUpdated(rigCapabilities*)), this, SLOT(receiveRigCaps(rigCapabilities*)));
rigCaps = queue->getRigCaps();
commandBuffer.clear();
sessionId = socketId;
socket = new QTcpSocket(this);
2021-04-20 11:29:10 +00:00
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;
2021-12-01 10:01:05 +00:00
2025-01-03 14:32:38 +00:00
// Find what VFOs we have:
if (rigCaps->numVFO > 0 && rigCaps->commands.contains(funcVFOASelect))
vfoList |= 1<<0;
if (rigCaps->numVFO > 1 && rigCaps->commands.contains(funcVFOBSelect))
vfoList |= 1<<1;
if (rigCaps->numReceiver > 0 && rigCaps->commands.contains(funcVFOMainSelect))
vfoList |= 1<<26;
if (rigCaps->numReceiver > 1 && rigCaps->commands.contains(funcVFOSubSelect))
vfoList |= 1<<25;
if (rigCaps->commands.contains(funcMemoryMode))
vfoList |= 1<<28;
}
void rigCtlClient::socketReadyRead()
{
2024-12-29 14:59:52 +00:00
if (this->rigCaps == Q_NULLPTR) {
qWarning(logRigCtlD) << "Rig has gone away, closing connection";
closeSocket();
return;
}
QByteArray data = socket->readAll();
commandBuffer.append(data);
QStringList commandList(commandBuffer.split('\n'));
QString sep = "\n";
int ret = -RIG_EINVAL;
2025-01-18 12:29:34 +00:00
bool found = false;
bool setCommand = false;
2024-12-29 14:59:52 +00:00
bool longCommand = false;
2025-01-03 14:32:38 +00:00
bool extended = false;
2025-01-18 12:29:34 +00:00
bool sendStatus = true;
QStringList response;
for (QString &commands : commandList)
{
restart:
if (commands.endsWith('\r'))
2023-02-05 21:28:24 +00:00
{
commands.chop(1); // Remove \n character
2023-02-05 21:28:24 +00:00
}
if (commands.isEmpty())
2023-02-05 21:28:24 +00:00
{
continue;
2023-02-05 21:28:24 +00:00
}
// We have a full line so process command.
2024-12-29 14:59:52 +00:00
qDebug(logRigCtlD) << sessionId << "RX:" << commands;
if (commands[0] == ';' || commands[0] == '|' || commands[0] == ',')
2023-02-05 21:28:24 +00:00
{
sep = commands[0].toLatin1();
commands.remove(0,1);
2025-01-03 19:46:27 +00:00
extended=true;
2023-02-05 21:28:24 +00:00
}
else if (commands[0] == '+')
2023-02-05 21:28:24 +00:00
{
2024-12-29 14:59:52 +00:00
//qDebug(logRigCtlD) << "Extended response requested for this command";
extended = true;
sep = "\n";
commands.remove(0,1);
2023-02-05 21:28:24 +00:00
}
else if (commands[0] == '#')
2023-02-05 21:28:24 +00:00
{
continue;
2023-02-05 21:28:24 +00:00
}
else if (commands[0].toLower() == 'q')
2023-02-05 21:28:24 +00:00
{
closeSocket();
return;
2023-02-05 21:28:24 +00:00
}
if (commands[0] == '\\')
2023-02-05 21:28:24 +00:00
{
commands.remove(0,1);
2024-12-29 14:59:52 +00:00
//qDebug(logRigCtlD) << "This is a long form command";
longCommand = true;
2021-08-05 12:52:06 +00:00
}
2024-12-29 14:59:52 +00:00
//qDebug(logRigCtlD) << "Got command to parse:" << commands;
QStringList command = commands.split(" ");
2024-07-14 19:11:05 +00:00
command.removeAll({}); // Remove any empty strings (double-whitespace issue)
2025-01-18 12:29:34 +00:00
found=false;
sendStatus = true;
for (int i=0; commands_list[i].sstr != 0x00; i++)
2021-11-23 00:39:10 +00:00
{
if ((longCommand && !strncmp(command[0].toLocal8Bit(), commands_list[i].str,MAXNAMESIZE)) ||
(!longCommand && !commands.isNull() && commands[0].toLatin1() == commands_list[i].sstr))
{
command.removeFirst(); // Remove the actual command so it only contains params
2025-01-18 12:29:34 +00:00
found = true;
if (extended && commands_list[i].sstr != 0xf0){
2025-01-03 14:32:38 +00:00
// First we need to repeat the original command back:
2025-01-03 19:46:27 +00:00
QString repeat=QString("%1:").arg(commands_list[i].str);
2025-01-03 14:32:38 +00:00
for( const auto &c: command)
{
repeat+=QString(" %0").arg(c);
}
response.append(repeat);
}
if (((commands_list[i].flags & ARG_IN) == ARG_IN))
{
2024-04-28 19:06:32 +00:00
// For debugging only comment next line M0VSE
2024-12-29 14:59:52 +00:00
//qInfo(logRigCtlD()) << "Received set command" << commands;
setCommand=true;
}
if (commands_list[i].func == funcRigctlFunction)
{
ret = getSubCommand(response, extended, commands_list[i], functions_str, command);
commands.clear();
break;
} else if (commands_list[i].func == funcRigctlLevel)
{
ret = getSubCommand(response, extended, commands_list[i], levels_str, command);
commands.clear();
break;
} else if (commands_list[i].func == funcRigctlParam)
{
ret = getSubCommand(response, extended, commands_list[i], params_str, command);
commands.clear();
break;
} else if (commands_list[i].sstr == 0x8f)
{
ret = dumpState(response, extended);
break;
} else if (commands_list[i].sstr == '1')
{
ret = dumpCaps(response, extended);
setCommand=true;
break;
2025-01-03 14:32:38 +00:00
}
else if (commands_list[i].sstr == '2')
{
// power2mW
ret = power2mW(response, extended, commands_list[i], command);
setCommand=true;
break;
}
else if (commands_list[i].sstr == '3')
{
response.append(QString("Model: WFVIEW(%0)").arg(rigCaps->modelName));
ret = RIG_OK;
break;
2025-01-03 14:32:38 +00:00
}
else if (commands_list[i].sstr == '4')
{
// mW2power
ret = mW2power(response, extended, commands_list[i], command);
setCommand=true;
break;
}
else if (commands_list[i].sstr == 0xf0)
{
chkVfoEecuted = true;
2025-01-18 12:29:34 +00:00
response.append(QString("ChkVFO: %0").arg(uchar(0)));
2025-01-03 00:05:50 +00:00
ret = RIG_OK;
2025-01-18 12:29:34 +00:00
// chk_vfo doesn't output RPRT
sendStatus=false;
2025-01-03 00:05:50 +00:00
break;
}
// Special commands are funcNone so will not get called here
if (commands_list[i].func != funcNone) {
ret = getCommand(response, extended, commands_list[i],command);
}
break;
2021-08-05 10:29:20 +00:00
}
}
if (setCommand)
{
break; // Cannot be a compound command so just output result.
}
2025-01-18 12:29:34 +00:00
if (!longCommand && !commands.isEmpty() ){
commands.remove(0,1);
goto restart; // Need to restart loop without going to next command incase of compound command
2021-08-05 10:29:20 +00:00
}
sep = "\n";
2021-11-23 00:39:10 +00:00
}
// We have finished parsing all commands and have a response to send (hopefully)
commandBuffer.clear();
2025-01-03 19:46:27 +00:00
for (int i = 0;i<response.size();i++)
{
2025-01-03 19:46:27 +00:00
if (!response[i].isEmpty()) {
2025-01-18 12:29:34 +00:00
sendData(QString("%0%1").arg(response[i],sep));
2025-01-03 19:46:27 +00:00
}
}
2025-01-18 12:29:34 +00:00
if (found && sendStatus && (ret < 0 || setCommand || extended))
sendData(QString("RPRT %1\n").arg(QString::number(ret)));
}
void rigCtlClient::socketDisconnected()
{
qInfo(logRigCtlD()) << sessionId << "disconnected";
socket->deleteLater();
this->deleteLater();
}
void rigCtlClient::closeSocket()
{
socket->close();
}
void rigCtlClient::sendData(QString data)
{
2024-12-29 14:59:52 +00:00
qDebug(logRigCtlD()) << sessionId << "TX:" << data;
if (socket != Q_NULLPTR && socket->isValid() && socket->isOpen())
{
socket->write(data.toLatin1());
}
2021-04-20 11:29:10 +00:00
else
{
qInfo(logRigCtlD()) << "socket not open!";
2021-04-20 11:29:10 +00:00
}
}
2022-10-31 12:03:04 +00:00
QString rigCtlClient::getFilter(quint8 mode, quint8 filter) {
2021-04-25 09:58:25 +00:00
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(modeInfo mode)
{
for (int i = 0; mode_str[i].str[0] != '\0'; i++)
{
if (mode_str[i].mk == mode.mk && mode_str[i].data == mode.data)
{
return(QString(mode_str[i].str));
}
2021-04-25 09:58:25 +00:00
}
return("UNKNOWN");
2021-04-25 09:58:25 +00:00
}
bool rigCtlClient::getMode(QString modeString, modeInfo& mode)
{
2024-12-29 15:19:46 +00:00
//qInfo() << "Looking for mode (set)" << modeString;
for (int i = 0; mode_str[i].str[0] != '\0'; i++)
{
if (QString(mode_str[i].str) == modeString)
{
2024-03-05 21:01:13 +00:00
for (auto &m: rigCaps->modes)
{
if (m.mk == mode_str[i].mk)
{
mode = modeInfo(m);
mode.data = mode_str[i].data;
2023-07-20 15:54:07 +00:00
mode.filter = 1;
return true;
}
}
}
2021-04-25 09:58:25 +00:00
}
return false;
}
2021-08-05 00:23:15 +00:00
2021-08-07 00:14:41 +00:00
2022-10-31 12:03:04 +00:00
quint8 rigCtlClient::getAntennas()
2021-08-07 12:34:47 +00:00
{
2022-10-31 12:03:04 +00:00
quint8 ant=0;
2024-03-05 21:01:13 +00:00
for (auto &i: rigCaps->antennas)
2021-08-07 12:34:47 +00:00
{
2023-05-09 11:11:38 +00:00
ant |= 1<<i.num;
2021-08-07 12:34:47 +00:00
}
return ant;
}
2021-08-07 00:14:41 +00:00
2022-10-31 12:03:04 +00:00
quint64 rigCtlClient::getRadioModes(QString md)
2021-08-07 00:14:41 +00:00
{
quint64 modes = 0;
2024-03-05 21:01:13 +00:00
for (auto&& mode : rigCaps->modes)
2021-08-06 08:25:08 +00:00
{
for (int i = 0; mode_str[i].str[0] != '\0'; i++)
{
2022-10-31 12:03:04 +00:00
QString mstr = QString(mode_str[i].str);
2025-02-10 01:28:22 +00:00
if (rigCaps->inputs.size())
{
2022-10-31 12:03:04 +00:00
if (mstr.contains(mode.name))
2021-08-06 11:37:03 +00:00
{
2022-10-31 12:03:04 +00:00
// qDebug(logRigCtlD()) << "Found data mode:" << mode.name << mode_str[i].mode;
if (md.isEmpty() || mstr.contains(md)) {
modes |= mode_str[i].mode;
}
2021-08-06 11:37:03 +00:00
}
}
2022-10-31 12:03:04 +00:00
else if (mode.name==mstr)
{
//qDebug(logRigCtlD()) << "Found mode:" << mode.name << mode_str[i].mode;
if (md.isEmpty() || mstr==md)
{
modes |= mode_str[i].mode;
}
}
2021-08-06 08:25:08 +00:00
}
}
2021-08-07 00:14:41 +00:00
return modes;
2021-08-07 12:34:47 +00:00
}
2022-10-31 12:03:04 +00:00
QString rigCtlClient::getAntName(quint8 ant)
2021-08-07 12:34:47 +00:00
{
QString ret;
switch (ant)
{
case 0: ret = "ANT1"; break;
case 1: ret = "ANT2"; break;
case 2: ret = "ANT3"; break;
case 3: ret = "ANT4"; break;
case 4: ret = "ANT5"; break;
case 30: ret = "ANT_UNKNOWN"; break;
case 31: ret = "ANT_CURR"; break;
default: ret = "ANT_UNK"; break;
}
return ret;
2021-08-08 16:14:48 +00:00
}
2022-10-31 12:03:04 +00:00
quint8 rigCtlClient::antFromName(QString name) {
quint8 ret;
2021-11-23 00:39:10 +00:00
if (name.toUpper() == "ANT1")
2021-11-23 00:39:10 +00:00
ret = 0;
else if (name.toUpper() == "ANT2")
2021-11-23 00:39:10 +00:00
ret = 1;
else if (name.toUpper() == "ANT3")
2021-11-23 00:39:10 +00:00
ret = 2;
else if (name.toUpper() == "ANT4")
2021-11-23 00:39:10 +00:00
ret = 3;
else if (name.toUpper() == "ANT5")
2021-11-23 00:39:10 +00:00
ret = 4;
else if (name.toUpper() == "ANT_UNKNOWN")
2021-11-23 00:39:10 +00:00
ret = 30;
else if (name.toUpper() == "ANT_CURR")
2021-11-23 00:39:10 +00:00
ret = 31;
else
ret = 99;
return ret;
}
2024-12-29 14:59:52 +00:00
vfo_t rigCtlClient::vfoFromName(QString vfo) {
2025-01-18 12:29:34 +00:00
rigStateType state = queue->getState();
2024-12-29 14:59:52 +00:00
vfo_t v = vfoUnknown;
2025-01-18 12:29:34 +00:00
if (vfo.toUpper() == "CURRVFO")
{
v=state.vfo;
}
else if (vfo.toUpper() == "VFOA")
2025-01-03 00:05:50 +00:00
{
if (rigCaps->commands.contains(funcVFOASelect))
v = vfoA;
else if (rigCaps->commands.contains(funcVFOMainSelect))
v = vfoMain;
else
2025-01-18 12:29:34 +00:00
v = state.vfo;
2025-01-03 00:05:50 +00:00
}
2024-12-29 14:59:52 +00:00
else if (vfo.toUpper() == "VFOB")
2025-01-03 00:05:50 +00:00
{
if (rigCaps->commands.contains(funcVFOBSelect))
v = vfoB;
else if (rigCaps->commands.contains(funcVFOSubSelect))
v = vfoSub;
else
2025-01-18 12:29:34 +00:00
v = state.vfo;
2025-01-03 00:05:50 +00:00
}
else if (vfo.toUpper() == "MAIN")
{
if (rigCaps->commands.contains(funcVFOMainSelect))
v = vfoMain;
else if (rigCaps->commands.contains(funcVFOASelect))
v = vfoA;
else
2025-01-18 12:29:34 +00:00
v = state.vfo;
2025-01-03 00:05:50 +00:00
}
2024-12-29 14:59:52 +00:00
else if (vfo.toUpper() == "SUB")
2025-01-03 00:05:50 +00:00
{
if (rigCaps->commands.contains(funcVFOSubSelect))
v = vfoSub;
else if (rigCaps->commands.contains(funcVFOBSelect))
v = vfoB;
else
2025-01-18 12:29:34 +00:00
v = state.vfo;
2025-01-03 00:05:50 +00:00
}
2024-12-29 14:59:52 +00:00
else if (vfo.toUpper() == "MEM")
2025-01-03 00:05:50 +00:00
{
2025-01-18 12:29:34 +00:00
if (rigCaps->commands.contains(funcMemoryMode))
v = vfoMem;
else
v = state.vfo;
2025-01-03 00:05:50 +00:00
}
2025-01-03 16:31:12 +00:00
//qInfo() << "vfoFromName" << vfo << "returns" << v;
2024-12-29 14:59:52 +00:00
return v;
2023-02-05 21:28:24 +00:00
}
2024-12-29 14:59:52 +00:00
QString rigCtlClient::getVfoName(vfo_t vfo)
2023-02-05 21:28:24 +00:00
{
QString ret;
switch (vfo) {
2024-12-29 14:59:52 +00:00
case vfoMain:
ret = "Main";
break;
case vfoSub:
ret = "Sub";
break;
case vfoA:
ret = "VFOA";
break;
case vfoB:
ret = "VFOB";
break;
2025-01-03 00:05:50 +00:00
case vfoCurrent:
ret = "currVFO";
break;
2024-12-29 14:59:52 +00:00
case vfoMem:
ret = "MEM";
break;
default:
2025-01-03 14:32:38 +00:00
ret = "None";
2024-12-29 14:59:52 +00:00
break;
2023-02-05 21:28:24 +00:00
}
2025-01-03 16:31:12 +00:00
//qInfo() << "vfoName" << vfo << "returns" << ret;
2023-02-05 21:28:24 +00:00
return ret;
}
2024-08-13 12:19:50 +00:00
unsigned long rigCtlClient::doCrc(quint8* p, size_t n)
2023-02-05 21:28:24 +00:00
{
unsigned long crc = 0xfffffffful;
size_t i;
if (crcTable[0] == 0) { genCrc(crcTable); }
for (i = 0; i < n; i++)
{
crc = crcTable[*p++ ^ (crc & 0xff)] ^ (crc >> 8);
}
return ((~crc) & 0xffffffff);
}
void rigCtlClient::genCrc(unsigned long crcTable[])
{
unsigned long POLYNOMIAL = 0xEDB88320;
2024-08-13 12:19:50 +00:00
quint8 b = 0;
2023-02-05 21:28:24 +00:00
while (0 != ++b)
{
unsigned long remainder = b;
unsigned long bit;
for (bit = 8; bit > 0; --bit)
{
if (remainder & 1)
{
remainder = (remainder >> 1) ^ POLYNOMIAL;
}
else
{
remainder = (remainder >> 1);
}
}
crcTable[(size_t)b] = remainder;
}
}
int rigCtlClient::getCommand(QStringList& response, bool extended, const commandStruct cmd, QStringList params )
{
// This is a main command
int ret = -RIG_EINVAL;
2024-12-29 14:59:52 +00:00
funcs func = cmd.func;
rigStateType state=queue->getState();
2024-07-14 19:11:05 +00:00
2025-01-18 12:29:34 +00:00
2024-12-29 14:59:52 +00:00
if (rigCaps == Q_NULLPTR)
return ret;
2024-07-14 19:11:05 +00:00
if (((cmd.flags & ARG_IN) == ARG_IN) && params.size())
{
// We are expecting a second argument to the command as it is a set
QVariant val;
ret = RIG_OK;
2024-03-05 21:01:13 +00:00
switch (cmd.type)
{
case typeUChar:
2024-12-29 14:59:52 +00:00
if (cmd.func == funcLockFunction)
val.setValue(modeLock);
else
val.setValue(static_cast<uchar>(params[0].toInt()));
2024-03-05 21:01:13 +00:00
break;
case typeShort:
val.setValue(static_cast<short>(params[0].toInt()));
2024-03-05 21:01:13 +00:00
break;
2025-01-03 00:05:50 +00:00
case typeUShort:
val.setValue(static_cast<ushort>(params[0].toInt()));
break;
2024-03-05 21:01:13 +00:00
case typeFloat:
val.setValue(static_cast<ushort>(float(params[0].toFloat() * 255.0))); // rigctl sends 0.0-1.0, we expect 0-255
2024-03-05 21:01:13 +00:00
break;
case typeFloatDiv:
val.setValue(static_cast<uchar>(float(params[0].toFloat() / 10.0)));
2024-03-05 21:01:13 +00:00
break;
case typeFloatDiv5:
val.setValue(static_cast<ushort>(float(params[0].toFloat() * 5.1)));
2024-03-05 21:01:13 +00:00
break;
2025-01-06 20:16:14 +00:00
case typeBinary:
if (params.length()>1){
queue->add(priorityImmediate, queueItem(funcSelectVFO, QVariant::fromValue<vfo_t>(vfoFromName(params[0])),false,state.receiver));
val.setValue(static_cast<bool>(params[1].toInt()));
} else {
val.setValue(static_cast<bool>(params[0].toInt()));
}
2025-01-06 16:30:25 +00:00
break;
2024-03-05 21:01:13 +00:00
case typeFreq:
2025-01-06 20:16:14 +00:00
{
freqt f;
if (cmd.sstr == 'I') {
state.vfo=splitVfo;
state.receiver = uchar((rigCaps->numVFO == 1 && rigCaps->numReceiver>1));
2025-01-06 20:16:14 +00:00
}
if (params.length() > 1) {
state.vfo = vfoFromName(params[0]);
2025-02-20 16:48:28 +00:00
f.Hz = static_cast<quint64>(params[1].toULongLong());
} else {
2025-02-20 16:48:28 +00:00
f.Hz = static_cast<quint64>(params[0].toULongLong());
}
func = queue->getVfoCommand(state.vfo,state.receiver,true).freqFunc;
f.VFO = activeVFO;
f.MHzDouble = f.Hz/1000000.0;
val.setValue(f);
//qInfo() << "Sending freq command" << funcString[func] << "for freq" << f.Hz;
2024-03-05 21:01:13 +00:00
break;
}
2024-12-29 14:59:52 +00:00
case typeSplitVFO:
2024-12-29 14:59:52 +00:00
val.setValue(static_cast<bool>(params[0].toInt()));
2025-01-03 14:32:38 +00:00
if (params.size() > 1) {// Just in case we have invalid info
2025-01-03 16:31:12 +00:00
//qInfo() << "Split VFO" << params[0] << "VFO" << params[1];
2024-12-29 14:59:52 +00:00
splitVfo = vfoFromName(params[1]);
2025-01-03 14:32:38 +00:00
}
2024-12-29 14:59:52 +00:00
break;
2024-03-05 21:01:13 +00:00
case typeMode:
{
if (cmd.sstr == 'X') {
state.vfo=splitVfo;
state.receiver = uchar((rigCaps->numVFO==1 && rigCaps->numReceiver>1));
}
2025-01-03 14:32:38 +00:00
modeInfo mi;
2025-01-06 20:16:14 +00:00
QString mode="";
QString width="";
if (params.size() == 3)
{
state.vfo=vfoFromName(params[0]);
2025-01-06 20:16:14 +00:00
mode=params[1];
width=params[2];
}
else if (params.size() == 2)
{
mode=params[0];
width=params[1];
}
// We have VFO, Mode and PB
func = queue->getVfoCommand(state.vfo,state.receiver,true).modeFunc;
2025-01-06 20:16:14 +00:00
bool ok;
int pb = width.toInt(&ok);
if (ok && pb>0) {
queue->add(priorityImmediate, queueItem(funcFilterWidth, QVariant::fromValue<ushort>(pb),false,state.receiver));
2025-01-06 20:16:14 +00:00
}
if (getMode(mode,mi))
{
val.setValue(mi);
2023-07-20 15:54:07 +00:00
} else {
qInfo(logRigCtlD()) << "Mode not found:" << params[0];
return -RIG_EINVAL;
}
2024-03-05 21:01:13 +00:00
break;
}
2024-03-05 21:01:13 +00:00
case typeVFO:
// Setting VFO:
2024-12-29 14:59:52 +00:00
if (params[0] == "?") {
// This is a query for a list of values
QString vfo;
if (rigCaps->numVFO > 0 && rigCaps->commands.contains(funcVFOASelect))
vfo.append("VFOA ");
if (rigCaps->numVFO > 1 && rigCaps->commands.contains(funcVFOASelect))
vfo.append("VFOB ");
if (rigCaps->numReceiver > 0 && rigCaps->commands.contains(funcVFOMainSelect))
vfo.append("Main ");
if (rigCaps->numReceiver > 1 && rigCaps->commands.contains(funcVFOSubSelect))
vfo.append("Sub ");
if (rigCaps->commands.contains(funcMemoryMode))
vfo.append("MEM ");
vfo.chop(1);
response.append(vfo);
}
else
2024-07-14 19:11:05 +00:00
{
state.vfo = vfoFromName(params[0]);
val.setValue(state.vfo);
2024-07-14 19:11:05 +00:00
}
2024-03-05 21:01:13 +00:00
break;
2024-04-05 12:01:26 +00:00
case typeString:
{
// Only used for CW?
2024-07-12 09:08:24 +00:00
QString sendCmd;
for (QString &cmd : params) {
sendCmd =sendCmd+cmd+" ";
2024-07-12 09:08:24 +00:00
}
2024-07-12 09:29:50 +00:00
sendCmd.chop(1);
2024-07-12 09:08:24 +00:00
val.setValue(sendCmd);
2024-06-03 18:43:00 +00:00
break;
2024-04-05 12:01:26 +00:00
}
2024-03-05 21:01:13 +00:00
default:
qInfo(logRigCtlD()) << "Unable to parse value of type" << cmd.type << "Command" << cmd.str;
return -RIG_EINVAL;
}
2024-03-05 21:01:13 +00:00
queue->add(priorityImmediate, queueItem(func, val,false,state.receiver));
} else {
// Simple get command
2023-07-20 15:54:07 +00:00
cacheItem item;
2024-07-14 19:11:05 +00:00
2024-12-29 14:59:52 +00:00
// Build QStringList of Prefixes. This is a bit messy, but not sure how else do to it?
2025-01-03 14:32:38 +00:00
QStringList prefixes=buildPrefixes(cmd,extended);
2025-01-18 12:29:34 +00:00
if (!prefixes.length())
{
qWarning(logRigCtlD()) << "No prefixes found for cmd" << cmd.str << "using func" << funcString[func] << "aborting";
}
if (cmd.sstr=='i' || cmd.sstr=='x') {
if (splitVfo == vfoSub && rigCaps->numReceiver > 1) {
state.receiver=1;
}
state.vfo = splitVfo;
2024-12-29 14:59:52 +00:00
}
if (func == funcFreqGet) {
func = queue->getVfoCommand(state.vfo,state.receiver,true).freqFunc;
} else if (func == funcModeGet) {
func = queue->getVfoCommand(state.vfo,state.receiver,true).modeFunc;
2024-12-29 14:59:52 +00:00
}
//qInfo() << "getting Cache Value for func" << funcString[func] << "on rx" << state.receiver;
2024-03-05 21:01:13 +00:00
if (rigCaps->commands.contains(func))
item = queue->getCache(func,state.receiver);
2024-12-29 14:59:52 +00:00
2025-01-18 12:29:34 +00:00
if (prefixes.length() && params.length())
{
response.append(QString("%0%1").arg(prefixes[0], params[0]));
}
ret = RIG_OK;
2024-03-05 21:01:13 +00:00
switch (cmd.type){
2025-01-06 20:16:14 +00:00
case typeBinary:
{
bool b = item.value.toBool();
2025-01-18 12:29:34 +00:00
response.append(QString("%0%1").arg(prefixes[prefixes.length()-1], QString::number(b)));
break;
}
2024-03-05 21:01:13 +00:00
case typeUChar:
2025-01-03 00:05:50 +00:00
case typeUShort:
2024-03-05 21:01:13 +00:00
case typeShort:
{
int i = item.value.toInt();
2024-12-29 14:59:52 +00:00
if (cmd.func == funcLockFunction)
i = modeLock;
2025-01-18 12:29:34 +00:00
response.append(QString("%0%1").arg(prefixes[prefixes.length()-1], QString::number(i)));
2024-03-05 21:01:13 +00:00
break;
}
2024-03-05 21:01:13 +00:00
case typeFloat:
{
float f = item.value.toFloat() / 255.0;
2025-01-18 12:29:34 +00:00
response.append(QString("%0%1").arg(prefixes[prefixes.length()-1],QString::number(f,typeFloat,6)));
2024-03-05 21:01:13 +00:00
break;
}
2024-03-05 21:01:13 +00:00
case typeFloatDiv:
{
float f = item.value.toFloat() * 10;
2025-01-18 12:29:34 +00:00
response.append(QString("%0%1").arg(prefixes[prefixes.length()-1],QString::number(f,typeFloat,6)));
2024-03-05 21:01:13 +00:00
break;
}
2024-03-05 21:01:13 +00:00
case typeFloatDiv5:
{
float f = item.value.toFloat() / 5.1;
2025-01-18 12:29:34 +00:00
response.append(QString("%0%1").arg(prefixes[prefixes.length()-1],QString::number(f,typeFloat,6)));
2024-03-05 21:01:13 +00:00
break;
}
2024-03-05 21:01:13 +00:00
case typeFreq:
{ // Frequency
freqt f = item.value.value<freqt>();
2025-01-18 12:29:34 +00:00
response.append(QString("%0%1").arg(prefixes[prefixes.length()-1],QString::number(f.Hz)));
2024-03-05 21:01:13 +00:00
break;
}
2024-03-05 21:01:13 +00:00
case typeVFO:
{ // VFO
2024-12-29 14:59:52 +00:00
//uchar v = item.value.value<uchar>();
2025-01-18 12:29:34 +00:00
response.append(QString("%0%1").arg(prefixes[prefixes.length()-1],getVfoName(state.vfo)));
2024-12-29 14:59:52 +00:00
break;
}
case typeSplitVFO:
{ // VFO
bool v = item.value.value<uchar>();
if (prefixes.length()>0)
response.append(QString("%0%1").arg(prefixes[0],QString::number(v)));
if (prefixes.length()>1)
response.append(QString("%0%1").arg(prefixes[1],getVfoName(splitVfo)));
break;
}
case typeVFOInfo:
{
funcs freqFunc = queue->getVfoCommand(state.vfo,state.receiver,true).freqFunc;
funcs modeFunc = queue->getVfoCommand(state.vfo,state.receiver,true).modeFunc;
2024-12-29 14:59:52 +00:00
if (rigCaps->numReceiver > 1 && params.size() && params[0] == "Sub") {
state.receiver=1;
}
2024-12-29 14:59:52 +00:00
if (prefixes.length()>1)
response.append(QString("%0%1").arg(prefixes[1],QString::number(queue->getCache(freqFunc,state.receiver).value.value<freqt>().Hz)));
2024-12-29 14:59:52 +00:00
if (prefixes.length()>2)
response.append(QString("%0%1").arg(prefixes[2],getMode(queue->getCache(modeFunc,state.receiver).value.value<modeInfo>())));
2024-12-29 14:59:52 +00:00
if (prefixes.length()>3)
response.append(QString("%0%1").arg(prefixes[3],QString::number(queue->getCache(funcFilterWidth,state.receiver).value.value<ushort>())));
2025-01-06 20:16:14 +00:00
if (prefixes.length()>4) /* Split is only ever on the first rx */
response.append(QString("%0%1").arg(prefixes[4],QString::number(queue->getCache(funcSplitStatus,0).value.value<duplexMode_t>())));
2024-12-29 14:59:52 +00:00
if (prefixes.length()>5)
response.append(QString("%0%1").arg(prefixes[5],QString::number(queue->getCache(funcSatelliteMode,state.receiver).value.value<bool>())));
2024-03-05 21:01:13 +00:00
break;
}
2024-12-29 14:59:52 +00:00
2024-03-05 21:01:13 +00:00
case typeMode:
{ // Mode
modeInfo m = item.value.value<modeInfo>();
cacheItem f = queue->getCache(funcFilterWidth,state.receiver);
2024-12-29 14:59:52 +00:00
if (prefixes.length() > 0)
response.append(QString("%0%1").arg(prefixes[0],getMode(m)));
if (prefixes.length() > 1)
2025-01-03 00:05:50 +00:00
response.append(QString("%0%1").arg(prefixes[1],QString::number(f.value.value<ushort>())));
2024-03-05 21:01:13 +00:00
break;
}
2024-07-12 09:08:24 +00:00
case typeString:
{
2024-07-12 09:29:50 +00:00
// Stop sending CW if a blank command is received.
2024-07-12 09:08:24 +00:00
queue->add(priorityImmediate, queueItem(func, QString(QChar(0xff)),false,0));
2024-08-13 07:56:13 +00:00
break;
2024-07-12 09:08:24 +00:00
}
2024-03-05 21:01:13 +00:00
default:
qInfo(logRigCtlD()) << "Unsupported type (FIXME):" << item.value.typeName();
ret = -RIG_EINVAL;
return ret;
}
}
return ret;
}
int rigCtlClient::getSubCommand(QStringList& response, bool extended, const commandStruct cmd, const subCommandStruct sub[], QStringList params)
{
2025-01-03 19:46:27 +00:00
Q_UNUSED(extended)
int ret = -RIG_EINVAL;
2024-12-29 14:59:52 +00:00
if (rigCaps == Q_NULLPTR)
return ret;
rigStateType state = queue->getState();
2024-12-29 14:59:52 +00:00
QString resp;
// Get the required subCommand and process it
if (params.isEmpty())
{
return ret;
}
if (params[0][0].toLatin1() == '?')
{
for (int i=0; sub[i].str[0] != '\0' ;i++)
{
2024-03-05 21:01:13 +00:00
if (sub[i].func != funcNone && rigCaps->commands.contains(sub[i].func)) {
resp.append(sub[i].str);
resp.append(" ");
}
}
}
else
{
for (int i = 0 ; sub[i].str[0] != '\0'; i++)
{
if (!strncmp(params[0].toLocal8Bit(),sub[i].str,MAXNAMESIZE))
{
ret = RIG_OK;
if (((cmd.flags & ARG_IN) == ARG_IN) && params.size() > 1)
{
// We are expecting a second argument to the command
QVariant val;
2024-03-05 21:01:13 +00:00
switch (sub[i].type)
{
2025-01-03 00:05:50 +00:00
case typeUShort:
val.setValue(static_cast<ushort>(params[1].toInt())); // rigctl sends 0.0-1.0, we expect 0-255
break;
case typeShort:
val.setValue(static_cast<short>(params[1].toInt())); // rigctl sends 0.0-1.0, we expect 0-255
break;
2024-12-29 14:59:52 +00:00
case typeUChar:
{
2023-12-18 10:47:16 +00:00
uchar v = static_cast<uchar>(params[1].toInt());
if (params[0] == "FBKIN")
v = (v << 1) & 0x02; // BREAKIN is not bool!
2024-04-05 12:01:26 +00:00
if (params[0] == "AGC")
v = (v << 1);
val.setValue(v);
2024-03-05 21:01:13 +00:00
break;
}
2024-03-05 21:01:13 +00:00
case typeFloat:
val.setValue(static_cast<ushort>(float(params[1].toFloat() * 255.0))); // rigctl sends 0.0-1.0, we expect 0-255
2024-12-29 14:59:52 +00:00
//qInfo(logRigCtlD()) << "Setting float value" << params[0] << "to" << val << "original" << params[1];
2024-03-05 21:01:13 +00:00
break;
case typeFloatDiv:
val.setValue(static_cast<uchar>(float(params[1].toFloat() / 10.0)));
2024-03-05 21:01:13 +00:00
break;
case typeFloatDiv5:
val.setValue(static_cast<ushort>(float(params[1].toFloat() * 5.1)));
2024-03-05 21:01:13 +00:00
break;
case typeBinary:
val.setValue(static_cast<bool>(params[1].toInt()));
2024-03-05 21:01:13 +00:00
break;
default:
qInfo(logRigCtlD()) << "Unable to parse value of type" << sub[i].type;
return -RIG_EINVAL;
}
2024-03-05 21:01:13 +00:00
if (rigCaps->commands.contains(sub[i].func))
queue->add(priorityImmediate, queueItem(sub[i].func, val,false,state.receiver));
} else if (params.size() == 1){
// Not expecting a second argument as it is a get so dump the cache
2023-07-20 15:54:07 +00:00
cacheItem item;
2024-03-05 21:01:13 +00:00
if (rigCaps->commands.contains(sub[i].func))
item = queue->getCache(sub[i].func,state.receiver);
int val = 0;
switch (sub[i].type)
{
case typeBinary:
resp.append(QString("%1").arg(item.value.toBool()));
break;
case typeUChar:
{
int val = item.value.toInt();
if (params[0] == "FBKIN")
val = (val >> 1) & 0x01;
if (params[0] == "AGC")
val = (val >> 1);
resp.append(QString::number(val));
break;
}
2025-02-18 09:53:14 +00:00
case typeDouble:
resp.append(QString::number(item.value.toDouble(),'f',6));
break;
case typeUShort:
case typeShort:
resp.append(QString::number(item.value.toInt()));
break;
case typeFloat:
resp.append(QString::number(item.value.toFloat() / 255.0,'f',6));
break;
case typeFloatDiv:
resp.append(QString::number(item.value.toFloat() * 10.0,'f',6));
break;
case typeFloatDiv5:
resp.append(QString::number(item.value.toFloat()/5.1,'f',6));
break;
default:
qInfo(logRigCtlD()) << "Unhandled:" << item.value.toUInt() << "OUT" << val;
ret = -RIG_EINVAL;
}
qDebug(logRigCtlD()) << "Sending " << funcString[sub[i].func] << "data:" << resp;
response.append(resp);
}
else
{
qInfo(logRigCtlD()) << "Invalid number of params" << params.size();
ret = -RIG_EINVAL;
}
break;
}
}
}
return ret;
}
int rigCtlClient::dumpState(QStringList &response, bool extended)
{
Q_UNUSED(extended)
quint64 modes = getRadioModes();
// rigctld protocol version
response.append("1");
// Radio model
2024-03-05 21:01:13 +00:00
response.append(QString::number(rigCaps->rigctlModel));
// Print something (used to be ITU region)
response.append("0");
// Supported RX bands (startf,endf,modes,low_power,high_power,vfo,ant)
quint32 lowFreq = 0;
quint32 highFreq = 0;
for (const bandType &band : std::as_const(rigCaps->bands))
{
if (lowFreq == 0 || band.lowFreq < lowFreq)
lowFreq = band.lowFreq;
if (band.highFreq > highFreq)
highFreq = band.highFreq;
}
response.append(QString("%1.000000 %2.000000 0x%3 %4 %5 0x%6 0x%7").arg(lowFreq).arg(highFreq)
2025-01-03 14:32:38 +00:00
.arg(modes, 0, 16).arg(-1).arg(-1).arg(vfoList, 0, 16).arg(getAntennas(), 0, 16));
response.append("0 0 0 0 0 0 0");
2024-03-05 21:01:13 +00:00
if (rigCaps->hasTransmit) {
// Supported TX bands (startf,endf,modes,low_power,high_power,vfo,ant)
for (const bandType &band : std::as_const(rigCaps->bands))
{
response.append(QString("%1.000000 %2.000000 0x%3 %4 %5 0x%6 0x%7").arg(band.lowFreq).arg(band.highFreq)
.arg(modes, 0, 16).arg(2000).arg(100000).arg(0x16000000, 0, 16).arg(getAntennas(), 0, 16));
}
}
response.append("0 0 0 0 0 0 0");
2024-03-05 21:01:13 +00:00
for (auto& step: rigCaps->steps)
{
if (step.num > 0)
response.append(QString("0x%1 %2").arg(modes, 0, 16).arg(step.hz));
}
response.append("0 0");
modes = getRadioModes("SB");
if (modes) {
response.append(QString("0x%1 3000").arg(modes, 0, 16));
response.append(QString("0x%1 2400").arg(modes, 0, 16));
response.append(QString("0x%1 1800").arg(modes, 0, 16));
}
modes = getRadioModes("AM");
if (modes) {
response.append(QString("0x%1 9000").arg(modes, 0, 16));
response.append(QString("0x%1 6000").arg(modes, 0, 16));
response.append(QString("0x%1 3000").arg(modes, 0, 16));
}
modes = getRadioModes("CW");
if (modes) {
response.append(QString("0x%1 1200").arg(modes, 0, 16));
response.append(QString("0x%1 500").arg(modes, 0, 16));
response.append(QString("0x%1 200").arg(modes, 0, 16));
}
modes = getRadioModes("FM");
if (modes) {
response.append(QString("0x%1 15000").arg(modes, 0, 16));
response.append(QString("0x%1 10000").arg(modes, 0, 16));
response.append(QString("0x%1 7000").arg(modes, 0, 16));
}
modes = getRadioModes("RTTY");
if (modes) {
response.append(QString("0x%1 2400").arg(modes, 0, 16));
response.append(QString("0x%1 500").arg(modes, 0, 16));
response.append(QString("0x%1 250").arg(modes, 0, 16));
}
modes = getRadioModes("PSK");
if (modes) {
response.append(QString("0x%1 1200").arg(modes, 0, 16));
response.append(QString("0x%1 500").arg(modes, 0, 16));
response.append(QString("0x%1 250").arg(modes, 0, 16));
}
response.append("0 0");
response.append("9900");
response.append("9900");
response.append("10000");
response.append("0");
QString preamps="";
2024-03-05 21:01:13 +00:00
if(rigCaps->commands.contains(funcPreamp)) {
for (auto &pre: rigCaps->preamps)
{
if (pre.num == 0)
continue;
2024-04-05 12:01:26 +00:00
preamps.append(QString(" %1").arg(pre.num*10));
}
if (preamps.endsWith(" "))
preamps.chop(1);
}
else {
preamps = "0";
}
response.append(preamps);
QString attens = "";
2024-03-05 21:01:13 +00:00
if(rigCaps->commands.contains(funcAttenuator)) {
for (auto att : rigCaps->attenuators)
{
2025-01-17 00:43:57 +00:00
if (att.num == 0)
continue;
2025-01-17 00:43:57 +00:00
attens.append(QString(" %1").arg(att.num));
}
if (attens.endsWith(" "))
attens.chop(1);
}
else {
attens = "0";
}
response.append(attens);
qulonglong hasFuncs=0;
qulonglong hasLevels=0;
qulonglong hasParams=0;
for (int i = 0 ; functions_str[i].str[0] != '\0'; i++)
{
2024-03-05 21:01:13 +00:00
if (functions_str[i].func != funcNone && rigCaps->commands.contains(functions_str[i].func))
{
hasFuncs |= 1ULL << i;
}
}
response.append(QString("0x%0").arg(hasFuncs,16,16,QChar('0')));
response.append(QString("0x%0").arg(hasFuncs,16,16,QChar('0')));
for (int i = 0 ; levels_str[i].str[0] != '\0'; i++)
{
2024-03-05 21:01:13 +00:00
if (levels_str[i].func != funcNone && rigCaps->commands.contains(levels_str[i].func))
{
hasLevels |= 1ULL << i;
}
}
response.append(QString("0x%0").arg(hasLevels,16,16,QChar('0')));
response.append(QString("0x%0").arg(hasLevels,16,16,QChar('0')));
for (int i = 0 ; params_str[i].str[0] != '\0'; i++)
{
2024-03-05 21:01:13 +00:00
if (params_str[i].func != funcNone && rigCaps->commands.contains(params_str[i].func))
{
hasParams |= 1ULL << i;
}
}
response.append(QString("0x%0").arg(hasParams,16,16,QChar('0')));
response.append(QString("0x%0").arg(hasParams,16,16,QChar('0')));
if (chkVfoEecuted) {
response.append(QString("vfo_ops=0x%1").arg(255, 0, 16));
2024-03-05 21:01:13 +00:00
response.append(QString("ptt_type=0x%1").arg(rigCaps->hasTransmit, 0, 16));
response.append(QString("has_set_vfo=0x%1").arg(1, 0, 16));
response.append(QString("has_get_vfo=0x%1").arg(1, 0, 16));
response.append(QString("has_set_freq=0x%1").arg(1, 0, 16));
response.append(QString("has_get_freq=0x%1").arg(1, 0, 16));
response.append(QString("has_set_conf=0x%1").arg(1, 0, 16));
response.append(QString("has_get_conf=0x%1").arg(1, 0, 16));
response.append(QString("has_power2mW=0x%1").arg(1, 0, 16));
response.append(QString("has_mW2power=0x%1").arg(1, 0, 16));
response.append(QString("timeout=0x%1").arg(1000, 0, 16));
response.append("done");
}
return RIG_OK;
}
int rigCtlClient::dumpCaps(QStringList &response, bool extended)
{
Q_UNUSED(extended)
2024-03-05 21:01:13 +00:00
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"));
2024-03-05 21:01:13 +00:00
if (rigCaps->hasTransmit) {
response.append(QString("Rig type:\tTransceiver"));
}
else
{
response.append(QString("Rig type:\tReceiver"));
}
2024-03-05 21:01:13 +00:00
if(rigCaps->commands.contains(funcTransceiverStatus)) {
response.append(QString("PTT type:\tRig capable"));
}
response.append(QString("DCD type:\tRig capable"));
response.append(QString("Port type:\tNetwork link"));
return RIG_OK;
}
2024-12-29 14:59:52 +00:00
2025-01-03 14:32:38 +00:00
QStringList rigCtlClient::buildPrefixes(commandStruct cmd,bool extended)
{
QStringList prefixes;
if (extended) {
if (cmd.arg1 != NULL)
prefixes.append(QString("%0: ").arg(cmd.arg1));
if (cmd.arg2 != NULL)
prefixes.append(QString("%0: ").arg(cmd.arg2));
if (cmd.arg3 != NULL)
prefixes.append(QString("%0: ").arg(cmd.arg3));
if (cmd.arg4 != NULL)
prefixes.append(QString("%0: ").arg(cmd.arg4));
if (cmd.arg5 != NULL)
prefixes.append(QString("%0: ").arg(cmd.arg5));
if (cmd.arg6 != NULL)
prefixes.append(QString("%0: ").arg(cmd.arg6));
} else {
if (cmd.arg1 != NULL)
prefixes.append("");
if (cmd.arg2 != NULL)
prefixes.append("");
if (cmd.arg3 != NULL)
prefixes.append("");
if (cmd.arg4 != NULL)
prefixes.append("");
if (cmd.arg5 != NULL)
prefixes.append("");
if (cmd.arg6 != NULL)
prefixes.append("");
}
return prefixes;
}
int rigCtlClient::power2mW(QStringList& response, bool extended, const commandStruct cmd, QStringList params )
{
int ret=RIG_OK;
QStringList prefixes = buildPrefixes(cmd,extended);
rigStateType state = queue->getState();
2025-01-03 14:32:38 +00:00
if (params.size() >= 2)
{
bool ok=false;
float power = params[0].toFloat(&ok);
quint64 freq = params[1].toULongLong(&ok);
if (ok)
{
if (freq == 0)
{
// Frequency was zero, so get cached freq
freq = queue->getCache(queue->getVfoCommand(state.vfo,state.receiver,false).freqFunc,state.receiver).value.value<freqt>().Hz;
2025-01-03 14:32:38 +00:00
}
for (auto &b: rigCaps->bands)
{
// Highest frequency band is always first!
if (freq >= b.lowFreq && freq <= b.highFreq)
{
// This frequency is contained within this band!
int p = int((b.power * power) * 1000);
if (prefixes.length()>3)
response.append(QString("%0%1").arg(prefixes[3],QString::number(p)));
break;
}
}
}
else
{
ret = RIG_EINVAL;
}
} else {
ret = RIG_EINVAL;
}
return ret;
}
int rigCtlClient::mW2power(QStringList& response, bool extended, const commandStruct cmd, QStringList params )
{
int ret=RIG_OK;
QStringList prefixes = buildPrefixes(cmd,extended);
rigStateType state = queue->getState();
2025-01-03 14:32:38 +00:00
if (params.size() > 2)
{
bool ok=false;
int power = params[0].toInt(&ok);
quint64 freq = params[1].toULongLong(&ok);
if (ok)
{
if (freq == 0)
{
// Frequency was zero, so get cached freq
freq = queue->getCache(queue->getVfoCommand(state.vfo,state.receiver,false).freqFunc,state.receiver).value.value<freqt>().Hz;
2025-01-03 14:32:38 +00:00
}
for (auto &b: rigCaps->bands)
{
// Highest frequency band is always first!
if (freq >= b.lowFreq && freq <= b.highFreq)
{
// This frequency is contained within this band!
float p = float(power / (b.power * 1000.0));
if (prefixes.length()>3)
response.append(QString("%0%1").arg(prefixes[3],QString::number(p,'f',5)));
break;
}
}
}
else
{
ret = RIG_EINVAL;
}
} else {
ret = RIG_EINVAL;
}
return ret;
}