mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-14 20:31:53 -05:00
Add rigctrl plugin.
plugins/misc/rigctrl - Add rigctrl plugin. sdrbase/plugin/pluginapi.h/.cpp - Add misc plugin registration. sdrbase/plugin/pluginmanager.h/.cpp - Add misc plugin registration. sdrbase/plugin/plugininterface.h/.cpp - Add top level UI and global settings serialization callbacks. sdrbase/settings/mainsettings.h/cpp - Allow plugins to save global settings in main settings file. sdrgui/mainwindow.cpp - Load settings after plugins are loaded, to allow plugin settings to be loaded and saved. sdrsrv/maincore.cpp - Support loading/saving of plugin settings.
This commit is contained in:
parent
13a791ca11
commit
49cb082c7e
@ -42,3 +42,4 @@ add_subdirectory(channelrx)
|
|||||||
add_subdirectory(channeltx)
|
add_subdirectory(channeltx)
|
||||||
add_subdirectory(samplesource)
|
add_subdirectory(samplesource)
|
||||||
add_subdirectory(samplesink)
|
add_subdirectory(samplesink)
|
||||||
|
add_subdirectory(misc)
|
||||||
|
3
plugins/misc/CMakeLists.txt
Normal file
3
plugins/misc/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
project(misc)
|
||||||
|
|
||||||
|
add_subdirectory(rigctrl)
|
52
plugins/misc/rigctrl/CMakeLists.txt
Normal file
52
plugins/misc/rigctrl/CMakeLists.txt
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
project(rigctrl)
|
||||||
|
|
||||||
|
set(rigctrl_SOURCES
|
||||||
|
rigctrl.cpp
|
||||||
|
rigctrlsettings.cpp
|
||||||
|
rigctrlplugin.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(rigctrl_HEADERS
|
||||||
|
rigctrl.h
|
||||||
|
rigctrlsettings.h
|
||||||
|
rigctrlplugin.h
|
||||||
|
)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
|
||||||
|
)
|
||||||
|
|
||||||
|
if(NOT SERVER_MODE)
|
||||||
|
set(rigctrl_SOURCES
|
||||||
|
${rigctrl_SOURCES}
|
||||||
|
rigctrlgui.cpp
|
||||||
|
rigctrlgui.ui
|
||||||
|
)
|
||||||
|
set(rigctrl_HEADERS
|
||||||
|
${rigctrl_HEADERS}
|
||||||
|
rigctrlgui.h
|
||||||
|
)
|
||||||
|
set(TARGET_NAME rigctrl)
|
||||||
|
set(TARGET_LIB "Qt5::Widgets")
|
||||||
|
set(TARGET_LIB_GUI "sdrgui")
|
||||||
|
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
|
||||||
|
else()
|
||||||
|
set(TARGET_NAME rigctrlsrv)
|
||||||
|
set(TARGET_LIB "")
|
||||||
|
set(TARGET_LIB_GUI "")
|
||||||
|
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library(${TARGET_NAME} SHARED
|
||||||
|
${rigctrl_SOURCES}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(${TARGET_NAME}
|
||||||
|
Qt5::Core
|
||||||
|
${TARGET_LIB}
|
||||||
|
sdrbase
|
||||||
|
${TARGET_LIB_GUI}
|
||||||
|
swagger
|
||||||
|
)
|
||||||
|
|
||||||
|
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
|
59
plugins/misc/rigctrl/readme.md
Normal file
59
plugins/misc/rigctrl/readme.md
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<h1>Rigctrl plugin</h1>
|
||||||
|
|
||||||
|
<h2>Introduction</h2>
|
||||||
|
|
||||||
|
The rigctrl plugin allows SDRangel to be controlled via ![Hamlib](http://hamlib.sourceforge.net/manuals/hamlib.html)'s rigctrld protocol. This allows other software that implements the rigctrld protocol, such at the satelite tracking software GPredict, to control SDRangel, to adjust for doppler or to automatically switch between different satellite frequencies and modes.
|
||||||
|
|
||||||
|
<h2>Interface</h2>
|
||||||
|
|
||||||
|
<h3>Enable rigctrl server</h3>
|
||||||
|
|
||||||
|
Checking this option will enable the rigctrl server in SDRangel. The default is disabled.
|
||||||
|
|
||||||
|
<h3>API Address</h3>
|
||||||
|
|
||||||
|
The rigctrl plugin using the SDRangel REST API to control SDRangel. Please specify the API address of the SDRangel instance to control. The default is http://127.0.0.1:8091.
|
||||||
|
|
||||||
|
<h3>Port</h3>
|
||||||
|
|
||||||
|
The rigctrl plugin opens a TCP port to receive commands from a rigctrl client on. Please specify a free TCP port number. The default rigctrld port is 4532.
|
||||||
|
|
||||||
|
<h3>Max Frequency Offset</h3>
|
||||||
|
|
||||||
|
The maximum frequency offset controls whether the center frequency or frequency offset is adjusted when a new frequency is received by a rigctrl command.
|
||||||
|
If the difference between the new frequency and the current center frequency is less than this value, the input offset (in the demodulator) will be adjusted.
|
||||||
|
If the difference is greater than this value, the center frequency will be set to the received frequency.
|
||||||
|
To only ever set the center frequency, set this value to 0. The default value is 10000.
|
||||||
|
|
||||||
|
<h3>Device Index</h3>
|
||||||
|
|
||||||
|
The device index specifies the SDRangel device set that will be controlled by received rigctrl commands. Defaults to 0.
|
||||||
|
|
||||||
|
<h3>Channel Index</h3>
|
||||||
|
|
||||||
|
The channel index specifies the SDRangel channel that will be controlled by received rigctrl commands. Defaults to 0.
|
||||||
|
|
||||||
|
<h2>Supported rigctrl Commands</h2>
|
||||||
|
|
||||||
|
The following rigctrl commands are supported:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>F / set_freq
|
||||||
|
<li>f / get_freq
|
||||||
|
<li>M / set_mode
|
||||||
|
<li>get_powerstat
|
||||||
|
<li>set_powerstat
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Example rigctrl Session</h2>
|
||||||
|
|
||||||
|
Run SDRangel and from the Preferences menu select rigctrl. Check "Enable rigctrl server" and press OK.
|
||||||
|
|
||||||
|
In a terminal window, run:
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
telnet localhost 4532
|
||||||
|
set_mode AM, 1000
|
||||||
|
set_freq 100000000
|
||||||
|
set_powerstat 1
|
||||||
|
</pre>
|
507
plugins/misc/rigctrl/rigctrl.cpp
Normal file
507
plugins/misc/rigctrl/rigctrl.cpp
Normal file
@ -0,0 +1,507 @@
|
|||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||||
|
// //
|
||||||
|
// This program is free software; you can redistribute it and/or modify //
|
||||||
|
// it under the terms of the GNU General Public License as published by //
|
||||||
|
// the Free Software Foundation as version 3 of the License, or //
|
||||||
|
// (at your option) any later version. //
|
||||||
|
// //
|
||||||
|
// This program is distributed in the hope that it will be useful, //
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||||
|
// GNU General Public License V3 for more details. //
|
||||||
|
// //
|
||||||
|
// You should have received a copy of the GNU General Public License //
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "rigctrl.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QtNetwork>
|
||||||
|
|
||||||
|
// Length of buffers
|
||||||
|
#define CMD_LENGTH 1024
|
||||||
|
#define URL_LENGTH 1024
|
||||||
|
#define RESPONSE_LENGTH 1024
|
||||||
|
#define DATA_LENGTH 1024
|
||||||
|
|
||||||
|
// Hamlib rigctrl error codes
|
||||||
|
enum rig_errcode_e {
|
||||||
|
RIG_OK = 0, /*!< No error, operation completed successfully */
|
||||||
|
RIG_EINVAL = -1, /*!< invalid parameter */
|
||||||
|
RIG_ECONF = -2, /*!< invalid configuration (serial,..) */
|
||||||
|
RIG_ENOMEM = -3, /*!< memory shortage */
|
||||||
|
RIG_ENIMPL = -4, /*!< function not implemented, but will be */
|
||||||
|
RIG_ETIMEOUT = -5, /*!< communication timed out */
|
||||||
|
RIG_EIO = -6, /*!< IO error, including open failed */
|
||||||
|
RIG_EINTERNAL = -7, /*!< Internal Hamlib error, huh! */
|
||||||
|
RIG_EPROTO = -8, /*!< Protocol error */
|
||||||
|
RIG_ERJCTED = -9, /*!< Command rejected by the rig */
|
||||||
|
RIG_ETRUNC = -10, /*!< Command performed, but arg truncated */
|
||||||
|
RIG_ENAVAIL = -11, /*!< function not available */
|
||||||
|
RIG_ENTARGET = -12, /*!< VFO not targetable */
|
||||||
|
RIG_BUSERROR = -13, /*!< Error talking on the bus */
|
||||||
|
RIG_BUSBUSY = -14, /*!< Collision on the bus */
|
||||||
|
RIG_EARG = -15, /*!< NULL RIG handle or any invalid pointer parameter in get arg */
|
||||||
|
RIG_EVFO = -16, /*!< Invalid VFO */
|
||||||
|
RIG_EDOM = -17 /*!< Argument out of domain of func */
|
||||||
|
};
|
||||||
|
|
||||||
|
// Map rigctrl mode names to SDRangel modem names
|
||||||
|
const static struct mode_demod {
|
||||||
|
const char *mode;
|
||||||
|
const char *modem;
|
||||||
|
} mode_map[] = {
|
||||||
|
{"FM", "NFMDemod"},
|
||||||
|
{"WFM", "WFMDemod"},
|
||||||
|
{"AM", "AMDemod"},
|
||||||
|
{"LSB", "SSBDemod"},
|
||||||
|
{"USB", "SSBDemod"},
|
||||||
|
{nullptr, nullptr}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get double value from within nested JSON object
|
||||||
|
static double getSubObjectDouble(QJsonObject &json, const QString &key)
|
||||||
|
{
|
||||||
|
double value = -1.0;
|
||||||
|
|
||||||
|
for (QJsonObject::const_iterator it = json.begin(); it != json.end(); it++) {
|
||||||
|
QJsonValue jsonValue = it.value();
|
||||||
|
if (jsonValue.isObject()) {
|
||||||
|
QJsonObject settings = jsonValue.toObject();
|
||||||
|
if (settings.contains(key)) {
|
||||||
|
value = settings[key].toDouble();
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set double value withing nested JSON object
|
||||||
|
static bool setSubObjectDouble(QJsonObject &json, const QString &key, double value)
|
||||||
|
{
|
||||||
|
for (QJsonObject::iterator it = json.begin(); it != json.end(); it++) {
|
||||||
|
QJsonValue jsonValue = it.value();
|
||||||
|
if (jsonValue.isObject()) {
|
||||||
|
QJsonObject settings = jsonValue.toObject();
|
||||||
|
if (settings.contains(key)) {
|
||||||
|
settings[key] = value;
|
||||||
|
it.value() = settings;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RigCtrl::RigCtrl() :
|
||||||
|
m_settings(),
|
||||||
|
m_state(idle),
|
||||||
|
m_tcpServer(nullptr),
|
||||||
|
m_clientConnection(nullptr)
|
||||||
|
{
|
||||||
|
m_netman = new QNetworkAccessManager(this);
|
||||||
|
connect(m_netman, &QNetworkAccessManager::finished, this, &RigCtrl::processAPIResponse);
|
||||||
|
|
||||||
|
setSettings(&m_settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
RigCtrl::~RigCtrl()
|
||||||
|
{
|
||||||
|
if (m_clientConnection != nullptr) {
|
||||||
|
m_clientConnection->close();
|
||||||
|
delete m_clientConnection;
|
||||||
|
}
|
||||||
|
if (m_tcpServer != nullptr) {
|
||||||
|
m_tcpServer->close();
|
||||||
|
delete m_tcpServer;
|
||||||
|
}
|
||||||
|
delete m_netman;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RigCtrl::getSettings(RigCtrlSettings *settings)
|
||||||
|
{
|
||||||
|
*settings = m_settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is there a potential for this to be called while in getCommand or processAPIResponse?
|
||||||
|
void RigCtrl::setSettings(RigCtrlSettings *settings)
|
||||||
|
{
|
||||||
|
bool disabled = (!settings->m_enabled) && m_settings.m_enabled;
|
||||||
|
bool enabled = settings->m_enabled && !m_settings.m_enabled;
|
||||||
|
bool portChanged = settings->m_rigCtrlPort != m_settings.m_rigCtrlPort;
|
||||||
|
|
||||||
|
if (disabled || portChanged) {
|
||||||
|
if (m_clientConnection != nullptr) {
|
||||||
|
m_clientConnection->close();
|
||||||
|
delete m_clientConnection;
|
||||||
|
m_clientConnection = nullptr;
|
||||||
|
}
|
||||||
|
if (m_tcpServer != nullptr) {
|
||||||
|
m_tcpServer->close();
|
||||||
|
delete m_tcpServer;
|
||||||
|
m_tcpServer = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enabled || portChanged) {
|
||||||
|
qDebug() << "RigCtrl enabled on port " << settings->m_rigCtrlPort;
|
||||||
|
m_tcpServer = new QTcpServer(this);
|
||||||
|
if(!m_tcpServer->listen(QHostAddress::Any, settings->m_rigCtrlPort)) {
|
||||||
|
qDebug() << "RigCtrl failed to listen on port " << settings->m_rigCtrlPort << ". Check it is not already in use.";
|
||||||
|
} else {
|
||||||
|
connect(m_tcpServer, &QTcpServer::newConnection, this, &RigCtrl::acceptConnection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_settings = *settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray RigCtrl::serialize() const
|
||||||
|
{
|
||||||
|
return m_settings.serialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RigCtrl::deserialize(const QByteArray& data)
|
||||||
|
{
|
||||||
|
bool success = true;
|
||||||
|
RigCtrlSettings settings;
|
||||||
|
|
||||||
|
if (!settings.deserialize(data)) {
|
||||||
|
success = false;
|
||||||
|
} else {
|
||||||
|
setSettings(&settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept connection from rigctrl client
|
||||||
|
void RigCtrl::acceptConnection()
|
||||||
|
{
|
||||||
|
m_clientConnection = m_tcpServer->nextPendingConnection();
|
||||||
|
if (!m_clientConnection) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
connect(m_clientConnection, &QIODevice::readyRead, this, &RigCtrl::getCommand);
|
||||||
|
connect(m_clientConnection, &QAbstractSocket::disconnected, m_clientConnection, &QObject::deleteLater);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get rigctrl command and start processing it
|
||||||
|
void RigCtrl::getCommand()
|
||||||
|
{
|
||||||
|
char cmd[CMD_LENGTH];
|
||||||
|
char url[URL_LENGTH];
|
||||||
|
char response[RESPONSE_LENGTH];
|
||||||
|
qint64 len;
|
||||||
|
QNetworkRequest request;
|
||||||
|
char *p;
|
||||||
|
int i, l;
|
||||||
|
|
||||||
|
// Get rigctrld command from client
|
||||||
|
len = m_clientConnection->readLine(cmd, sizeof(cmd));
|
||||||
|
if (len != -1) {
|
||||||
|
//qDebug() << "RigCtrl::getCommand - " << cmd;
|
||||||
|
|
||||||
|
if (!strncmp(cmd, "F ", 2) || !strncmp(cmd, "set_freq ", 9)) {
|
||||||
|
// Set frequency
|
||||||
|
m_targetFrequency = atof(cmd[0] == 'F' ? &cmd[2] : &cmd[9]);
|
||||||
|
// Get current centre frequency
|
||||||
|
sprintf(url, "%s/deviceset/%d/device/settings", qUtf8Printable(m_settings.m_APIAddress), m_settings.m_deviceIndex);
|
||||||
|
request.setUrl(QUrl(url));
|
||||||
|
m_netman->get(request);
|
||||||
|
m_state = set_freq;
|
||||||
|
} else if (!strncmp(cmd, "f", 1) || !strncmp(cmd, "get_freq", 8)) {
|
||||||
|
// Get frequency - need to add centerFrequency and inputFrequencyOffset
|
||||||
|
sprintf(url, "%s/deviceset/%d/device/settings", qUtf8Printable(m_settings.m_APIAddress), m_settings.m_deviceIndex);
|
||||||
|
request.setUrl(QUrl(url));
|
||||||
|
m_netman->get(request);
|
||||||
|
m_state = get_freq_center;
|
||||||
|
} else if (!strncmp(cmd, "M ?", 3) || !(strncmp(cmd, "set_mode ?", 10))) {
|
||||||
|
// Return list of modes supported
|
||||||
|
p = response;
|
||||||
|
for (i = 0; mode_map[i].mode != nullptr; i++) {
|
||||||
|
p += sprintf(p, "%s ", mode_map[i].mode);
|
||||||
|
}
|
||||||
|
p += sprintf(p, "\n");
|
||||||
|
m_clientConnection->write(response, strlen(response));
|
||||||
|
} else if (!strncmp(cmd, "M ", 2) || !(strncmp(cmd, "set_mode ", 9))) {
|
||||||
|
// Set mode
|
||||||
|
// Map rigctrl mode name to SDRangel modem name
|
||||||
|
m_targetModem = nullptr;
|
||||||
|
m_targetBW = -1;
|
||||||
|
p = cmd[0] == 'M' ? &cmd[2] : &cmd[9];
|
||||||
|
for (i = 0; mode_map[i].mode != nullptr; i++) {
|
||||||
|
l = strlen(mode_map[i].mode);
|
||||||
|
if (!strncmp(p, mode_map[i].mode, l)) {
|
||||||
|
m_targetModem = mode_map[i].modem;
|
||||||
|
p += l;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Save bandwidth, if given
|
||||||
|
while(isspace(*p)) {
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
if (*p == ',') {
|
||||||
|
p++;
|
||||||
|
m_targetBW = atoi(p);
|
||||||
|
}
|
||||||
|
if (mode_map[i].modem != nullptr) {
|
||||||
|
// Delete current modem
|
||||||
|
sprintf(url, "%s/deviceset/%d/channel/%d", qUtf8Printable(m_settings.m_APIAddress), m_settings.m_deviceIndex, m_settings.m_channelIndex);
|
||||||
|
request.setUrl(QUrl(url));
|
||||||
|
m_netman->sendCustomRequest(request, "DELETE");
|
||||||
|
m_state = set_mode_mod;
|
||||||
|
} else {
|
||||||
|
sprintf(response, "RPRT %d\n", RIG_EINVAL);
|
||||||
|
m_clientConnection->write(response, strlen(response));
|
||||||
|
}
|
||||||
|
} else if (!strncmp(cmd, "set_powerstat 0", 15)) {
|
||||||
|
// Power off radio
|
||||||
|
sprintf(url, "%s/deviceset/%d/device/run", qUtf8Printable(m_settings.m_APIAddress), m_settings.m_deviceIndex);
|
||||||
|
request.setUrl(QUrl(url));
|
||||||
|
m_netman->sendCustomRequest(request, "DELETE");
|
||||||
|
m_state = set_power_off;
|
||||||
|
} else if (!strncmp(cmd, "set_powerstat 1", 15)) {
|
||||||
|
// Power on radio
|
||||||
|
sprintf(url, "%s/deviceset/%d/device/run", qUtf8Printable(m_settings.m_APIAddress), m_settings.m_deviceIndex);
|
||||||
|
request.setUrl(QUrl(url));
|
||||||
|
m_netman->post(request, "");
|
||||||
|
m_state = set_power_on;
|
||||||
|
} else if (!strncmp(cmd, "get_powerstat", 13)) {
|
||||||
|
// Return if powered on or off
|
||||||
|
sprintf(url, "%s/deviceset/%d/device/run", qUtf8Printable(m_settings.m_APIAddress), m_settings.m_deviceIndex);
|
||||||
|
request.setUrl(QUrl(url));
|
||||||
|
m_netman->get(request);
|
||||||
|
m_state = get_power;
|
||||||
|
} else {
|
||||||
|
// Unimplemented command
|
||||||
|
sprintf(response, "RPRT %d\n", RIG_ENIMPL);
|
||||||
|
m_clientConnection->write(response, strlen(response));
|
||||||
|
m_state = idle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process reply from SDRangel API server
|
||||||
|
void RigCtrl::processAPIResponse(QNetworkReply *reply)
|
||||||
|
{
|
||||||
|
double freq;
|
||||||
|
char response[RESPONSE_LENGTH];
|
||||||
|
char url[URL_LENGTH];
|
||||||
|
char data[DATA_LENGTH];
|
||||||
|
QNetworkRequest request;
|
||||||
|
|
||||||
|
if (reply->error() == QNetworkReply::NoError) {
|
||||||
|
QString answer = reply->readAll();
|
||||||
|
//qDebug("RigCtrl::processAPIResponse - '%s'", qUtf8Printable(answer));
|
||||||
|
|
||||||
|
QJsonDocument jsonResponse = QJsonDocument::fromJson(answer.toUtf8());
|
||||||
|
QJsonObject jsonObj = jsonResponse.object();
|
||||||
|
|
||||||
|
switch (m_state) {
|
||||||
|
|
||||||
|
case get_freq_center:
|
||||||
|
// Reply with current center frequency
|
||||||
|
freq = getSubObjectDouble(jsonObj, "centerFrequency");
|
||||||
|
if (freq >= 0.0) {
|
||||||
|
m_targetFrequency = freq;
|
||||||
|
sprintf(url, "%s/deviceset/%d/channel/%d/settings", qUtf8Printable(m_settings.m_APIAddress), m_settings.m_deviceIndex, m_settings.m_channelIndex);
|
||||||
|
request.setUrl(QUrl(url));
|
||||||
|
m_netman->get(request);
|
||||||
|
} else {
|
||||||
|
sprintf(response, "RPRT %d\n", RIG_ENIMPL); // File source doesn't have centre frequency
|
||||||
|
}
|
||||||
|
m_state = get_freq_offset;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case get_freq_offset:
|
||||||
|
freq = m_targetFrequency + getSubObjectDouble(jsonObj, "inputFrequencyOffset");
|
||||||
|
sprintf(response, "%u\n", (unsigned)freq);
|
||||||
|
m_clientConnection->write(response, strlen(response));
|
||||||
|
m_state = idle;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case set_freq:
|
||||||
|
// Check if target requency is within max offset from current center frequency
|
||||||
|
freq = getSubObjectDouble(jsonObj, "centerFrequency");
|
||||||
|
if (fabs(freq - m_targetFrequency) > m_settings.m_maxFrequencyOffset) {
|
||||||
|
// Update centerFrequency
|
||||||
|
setSubObjectDouble(jsonObj, "centerFrequency", m_targetFrequency);
|
||||||
|
sprintf(url, "%s/deviceset/%d/device/settings", qUtf8Printable(m_settings.m_APIAddress), m_settings.m_deviceIndex);
|
||||||
|
request.setUrl(QUrl(url));
|
||||||
|
m_netman->sendCustomRequest(request, "PATCH", QJsonDocument(jsonObj).toJson());
|
||||||
|
m_state = set_freq_center;
|
||||||
|
} else {
|
||||||
|
// In range, so update inputFrequencyOffset
|
||||||
|
m_targetOffset = m_targetFrequency - freq;
|
||||||
|
// Get settings containg inputFrequencyOffset, so we can patch them
|
||||||
|
sprintf(url, "%s/deviceset/%d/channel/%d/settings", qUtf8Printable(m_settings.m_APIAddress), m_settings.m_deviceIndex, m_settings.m_channelIndex);
|
||||||
|
request.setUrl(QUrl(url));
|
||||||
|
m_netman->get(request);
|
||||||
|
m_state = set_freq_set_offset;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case set_freq_no_offset:
|
||||||
|
// Update centerFrequency, without trying to set offset
|
||||||
|
setSubObjectDouble(jsonObj, "centerFrequency", m_targetFrequency);
|
||||||
|
sprintf(url, "%s/deviceset/%d/device/settings", qUtf8Printable(m_settings.m_APIAddress), m_settings.m_deviceIndex);
|
||||||
|
request.setUrl(QUrl(url));
|
||||||
|
m_netman->sendCustomRequest(request, "PATCH", QJsonDocument(jsonObj).toJson());
|
||||||
|
m_state = set_freq_center_no_offset;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case set_freq_center:
|
||||||
|
// Check whether frequency was set as expected
|
||||||
|
freq = getSubObjectDouble(jsonObj, "centerFrequency");
|
||||||
|
if (freq == m_targetFrequency) {
|
||||||
|
// Set inputFrequencyOffset to 0
|
||||||
|
m_targetOffset = 0;
|
||||||
|
sprintf(url, "%s/deviceset/%d/channel/%d/settings", qUtf8Printable(m_settings.m_APIAddress), m_settings.m_deviceIndex, m_settings.m_channelIndex);
|
||||||
|
request.setUrl(QUrl(url));
|
||||||
|
m_netman->get(request);
|
||||||
|
m_state = set_freq_set_offset;
|
||||||
|
} else {
|
||||||
|
sprintf(response, "RPRT %d\n", RIG_EINVAL);
|
||||||
|
m_clientConnection->write(response, strlen(response));
|
||||||
|
m_state = idle;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case set_freq_center_no_offset:
|
||||||
|
// Check whether frequency was set as expected
|
||||||
|
freq = getSubObjectDouble(jsonObj, "centerFrequency");
|
||||||
|
if (freq == m_targetFrequency) {
|
||||||
|
sprintf(response, "RPRT 0\n");
|
||||||
|
} else {
|
||||||
|
sprintf(response, "RPRT %d\n", RIG_EINVAL);
|
||||||
|
}
|
||||||
|
m_clientConnection->write(response, strlen(response));
|
||||||
|
m_state = idle;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case set_freq_set_offset:
|
||||||
|
// Patch inputFrequencyOffset
|
||||||
|
if (setSubObjectDouble(jsonObj, "inputFrequencyOffset", m_targetOffset)) {
|
||||||
|
sprintf(url, "%s/deviceset/%d/channel/%d/settings", qUtf8Printable(m_settings.m_APIAddress), m_settings.m_deviceIndex, m_settings.m_channelIndex);
|
||||||
|
request.setUrl(QUrl(url));
|
||||||
|
m_netman->sendCustomRequest(request, "PATCH", QJsonDocument(jsonObj).toJson());
|
||||||
|
m_state = set_freq_offset;
|
||||||
|
} else {
|
||||||
|
// No inputFrequencyOffset
|
||||||
|
sprintf(response, "RPRT %d\n", RIG_EINVAL);
|
||||||
|
m_clientConnection->write(response, strlen(response));
|
||||||
|
m_state = idle;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case set_freq_offset:
|
||||||
|
freq = getSubObjectDouble(jsonObj, "inputFrequencyOffset");
|
||||||
|
if (freq == m_targetOffset) {
|
||||||
|
sprintf(response, "RPRT 0\n");
|
||||||
|
} else {
|
||||||
|
sprintf(response, "RPRT %d\n", RIG_EINVAL);
|
||||||
|
}
|
||||||
|
m_clientConnection->write(response, strlen(response));
|
||||||
|
m_state = idle;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case set_mode_mod:
|
||||||
|
// Create new modem
|
||||||
|
sprintf(url, "%s/deviceset/%d/channel", qUtf8Printable(m_settings.m_APIAddress), m_settings.m_deviceIndex);
|
||||||
|
sprintf(data, "{ \"channelType\": \"%s\", \"direction\": 0, \"originatorDeviceSetIndex\": %d}\n", m_targetModem, m_settings.m_deviceIndex);
|
||||||
|
request.setUrl(QUrl(url));
|
||||||
|
request.setHeader(QNetworkRequest::ContentTypeHeader,"application/json");
|
||||||
|
m_netman->post(request, data);
|
||||||
|
if (m_targetBW >= 0) {
|
||||||
|
m_state = set_mode_settings;
|
||||||
|
} else {
|
||||||
|
m_state = set_mode_reply;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case set_mode_settings:
|
||||||
|
// Set modem bandwidth
|
||||||
|
sprintf(url, "%s/deviceset/%d/channel/%d/settings", qUtf8Printable(m_settings.m_APIAddress), m_settings.m_deviceIndex, m_settings.m_channelIndex);
|
||||||
|
sprintf(data, "{ \"channelType\": \"%s\", \"%sSettings\": {\"rfBandwidth\":%d}}\n", m_targetModem, m_targetModem, m_targetBW);
|
||||||
|
request.setUrl(QUrl(url));
|
||||||
|
request.setHeader(QNetworkRequest::ContentTypeHeader,"application/json");
|
||||||
|
m_netman->sendCustomRequest(request, "PATCH", data);
|
||||||
|
m_state = set_mode_reply;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case set_mode_reply:
|
||||||
|
sprintf(response, "RPRT 0\n");
|
||||||
|
m_clientConnection->write(response, strlen(response));
|
||||||
|
m_state = idle;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case get_power:
|
||||||
|
if (!jsonObj["state"].toString().compare("running")) {
|
||||||
|
sprintf(response, "1\n");
|
||||||
|
} else {
|
||||||
|
sprintf(response, "0\n");
|
||||||
|
}
|
||||||
|
m_clientConnection->write(response, strlen(response));
|
||||||
|
m_state = idle;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case set_power_on:
|
||||||
|
case set_power_off:
|
||||||
|
// Reply contains previous state, not current state, so assume it worked
|
||||||
|
sprintf(response, "RPRT 0\n");
|
||||||
|
m_clientConnection->write(response, strlen(response));
|
||||||
|
m_state = idle;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
//qDebug("RigCtrl::processAPIResponse - got error: '%s'", qUtf8Printable(reply->errorString()));
|
||||||
|
//qDebug() << reply->readAll();
|
||||||
|
|
||||||
|
switch (m_state) {
|
||||||
|
case get_freq_offset:
|
||||||
|
// Probably no modem enabled on the specified channel
|
||||||
|
sprintf(response, "%u\n", (unsigned)m_targetFrequency);
|
||||||
|
m_clientConnection->write(response, strlen(response));
|
||||||
|
m_state = idle;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case set_freq_set_offset:
|
||||||
|
// Probably no demodulator enabled on the specified channel
|
||||||
|
// Just set as center frequency
|
||||||
|
sprintf(url, "%s/deviceset/%d/device/settings", qUtf8Printable(m_settings.m_APIAddress), m_settings.m_deviceIndex);
|
||||||
|
request.setUrl(QUrl(url));
|
||||||
|
m_netman->get(request);
|
||||||
|
m_state = set_freq_no_offset;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case set_mode_mod:
|
||||||
|
// Probably no modem on channel to delete, so continue to try to create one
|
||||||
|
sprintf(url, "%s/deviceset/%d/channel", qUtf8Printable(m_settings.m_APIAddress), m_settings.m_deviceIndex);
|
||||||
|
sprintf(data, "{ \"channelType\": \"%s\", \"direction\": 0, \"originatorDeviceSetIndex\": %d}\n", m_targetModem, m_settings.m_deviceIndex);
|
||||||
|
request.setUrl(QUrl(url));
|
||||||
|
request.setHeader(QNetworkRequest::ContentTypeHeader,"application/json");
|
||||||
|
m_netman->post(request, data);
|
||||||
|
if (m_targetBW >= 0) {
|
||||||
|
m_state = set_mode_settings;
|
||||||
|
} else {
|
||||||
|
m_state = set_mode_reply;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
sprintf(response, "RPRT %d\n", RIG_EIO);
|
||||||
|
m_clientConnection->write(response, strlen(response));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reply->deleteLater();
|
||||||
|
}
|
67
plugins/misc/rigctrl/rigctrl.h
Normal file
67
plugins/misc/rigctrl/rigctrl.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||||
|
// //
|
||||||
|
// This program is free software; you can redistribute it and/or modify //
|
||||||
|
// it under the terms of the GNU General Public License as published by //
|
||||||
|
// the Free Software Foundation as version 3 of the License, or //
|
||||||
|
// (at your option) any later version. //
|
||||||
|
// //
|
||||||
|
// This program is distributed in the hope that it will be useful, //
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||||
|
// GNU General Public License V3 for more details. //
|
||||||
|
// //
|
||||||
|
// You should have received a copy of the GNU General Public License //
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef INCLUDE_RIGCTRL_H
|
||||||
|
#define INCLUDE_RIGCTRL_H
|
||||||
|
|
||||||
|
#include <QThread>
|
||||||
|
#include <QtNetwork>
|
||||||
|
|
||||||
|
#include "rigctrlsettings.h"
|
||||||
|
|
||||||
|
class RigCtrl : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
RigCtrl();
|
||||||
|
~RigCtrl();
|
||||||
|
void getSettings(RigCtrlSettings *settings);
|
||||||
|
void setSettings(RigCtrlSettings *settings);
|
||||||
|
QByteArray serialize() const;
|
||||||
|
bool deserialize(const QByteArray& data);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void acceptConnection();
|
||||||
|
void getCommand();
|
||||||
|
void processAPIResponse(QNetworkReply *reply);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QTcpServer *m_tcpServer;
|
||||||
|
QTcpSocket *m_clientConnection;
|
||||||
|
QNetworkAccessManager *m_netman;
|
||||||
|
|
||||||
|
enum RigCtrlState
|
||||||
|
{
|
||||||
|
idle,
|
||||||
|
set_freq, set_freq_no_offset, set_freq_center, set_freq_center_no_offset, set_freq_set_offset, set_freq_offset,
|
||||||
|
get_freq_center, get_freq_offset,
|
||||||
|
set_mode_mod, set_mode_settings, set_mode_reply,
|
||||||
|
get_power,
|
||||||
|
set_power_on, set_power_off
|
||||||
|
};
|
||||||
|
RigCtrlState m_state;
|
||||||
|
|
||||||
|
double m_targetFrequency;
|
||||||
|
double m_targetOffset;
|
||||||
|
const char *m_targetModem;
|
||||||
|
int m_targetBW;
|
||||||
|
|
||||||
|
RigCtrlSettings m_settings;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INCLUDE_RIGCTRL_H
|
52
plugins/misc/rigctrl/rigctrlgui.cpp
Normal file
52
plugins/misc/rigctrl/rigctrlgui.cpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||||
|
// //
|
||||||
|
// This program is free software; you can redistribute it and/or modify //
|
||||||
|
// it under the terms of the GNU General Public License as published by //
|
||||||
|
// the Free Software Foundation as version 3 of the License, or //
|
||||||
|
// (at your option) any later version. //
|
||||||
|
// //
|
||||||
|
// This program is distributed in the hope that it will be useful, //
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||||
|
// GNU General Public License V3 for more details. //
|
||||||
|
// //
|
||||||
|
// You should have received a copy of the GNU General Public License //
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "rigctrlgui.h"
|
||||||
|
#include "ui_rigctrlgui.h"
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
RigCtrlGUI::RigCtrlGUI(RigCtrl *rigCtrl, QWidget* parent) :
|
||||||
|
m_rigCtrl(rigCtrl),
|
||||||
|
QDialog(parent),
|
||||||
|
ui(new Ui::RigCtrlGUI)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
m_rigCtrl->getSettings(&m_settings);
|
||||||
|
ui->enable->setChecked(m_settings.m_enabled);
|
||||||
|
ui->api->setText(QString(m_settings.m_APIAddress));
|
||||||
|
ui->rigCtrlPort->setValue(m_settings.m_rigCtrlPort);
|
||||||
|
ui->maxFrequencyOffset->setValue(m_settings.m_maxFrequencyOffset);
|
||||||
|
ui->deviceIndex->setValue(m_settings.m_deviceIndex);
|
||||||
|
ui->channelIndex->setValue(m_settings.m_channelIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
RigCtrlGUI::~RigCtrlGUI()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RigCtrlGUI::accept()
|
||||||
|
{
|
||||||
|
m_settings.m_enabled = ui->enable->isChecked();
|
||||||
|
m_settings.m_APIAddress = ui->api->text();
|
||||||
|
m_settings.m_rigCtrlPort = ui->rigCtrlPort->value();
|
||||||
|
m_settings.m_maxFrequencyOffset = ui->maxFrequencyOffset->value();
|
||||||
|
m_settings.m_deviceIndex = ui->deviceIndex->value();
|
||||||
|
m_settings.m_channelIndex = ui->channelIndex->value();
|
||||||
|
m_rigCtrl->setSettings(&m_settings);
|
||||||
|
QDialog::accept();
|
||||||
|
}
|
45
plugins/misc/rigctrl/rigctrlgui.h
Normal file
45
plugins/misc/rigctrl/rigctrlgui.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||||
|
// //
|
||||||
|
// This program is free software; you can redistribute it and/or modify //
|
||||||
|
// it under the terms of the GNU General Public License as published by //
|
||||||
|
// the Free Software Foundation as version 3 of the License, or //
|
||||||
|
// (at your option) any later version. //
|
||||||
|
// //
|
||||||
|
// This program is distributed in the hope that it will be useful, //
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||||
|
// GNU General Public License V3 for more details. //
|
||||||
|
// //
|
||||||
|
// You should have received a copy of the GNU General Public License //
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef INCLUDE_RIGCTRLGUI_H
|
||||||
|
#define INCLUDE_RIGCTRLGUI_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include "rigctrlsettings.h"
|
||||||
|
#include "rigctrl.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class RigCtrlGUI;
|
||||||
|
}
|
||||||
|
|
||||||
|
class RigCtrlGUI : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit RigCtrlGUI(RigCtrl *rigCtrl, QWidget *parent = 0);
|
||||||
|
~RigCtrlGUI();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void accept();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::RigCtrlGUI* ui;
|
||||||
|
RigCtrlSettings m_settings;
|
||||||
|
RigCtrl *m_rigCtrl;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INCLUDE_RIGCTRLGUI_H
|
178
plugins/misc/rigctrl/rigctrlgui.ui
Normal file
178
plugins/misc/rigctrl/rigctrlgui.ui
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>RigCtrlGUI</class>
|
||||||
|
<widget class="QDialog" name="RigCtrlGUI">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>351</width>
|
||||||
|
<height>235</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Liberation Sans</family>
|
||||||
|
<pointsize>9</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>rigctrl Preferences</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<item row="0" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="enable">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Select to enable rigctrl server.</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Enable rigctrl server</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="rigCtrlPortLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>rigctrl Port</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="deviceIndexLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Device Index</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QSpinBox" name="deviceIndex">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Index of the device that should be controlled by rigctrl commands.
|
||||||
|
Default is 0.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
|
<widget class="QSpinBox" name="channelIndex">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Index of the channel that is to be controlled by rigctrl commands.
|
||||||
|
Default is 0.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QLabel" name="channelIndexLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Channel Index</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="apiLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>API Address</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="api">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>URL of SDRangel API server to control.
|
||||||
|
Default is http://127.0.0.1:8091/sdrangel</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>http://127.0.0.1:8091/sdrangel</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="maxFrequencyOffsetLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Max Frequency Offset</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QSpinBox" name="maxFrequencyOffset">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Controls whether the center frequency or frequency offset is adjusted when a new frequency is received via a rigctrl command.
|
||||||
|
If the difference between the new frequency and the current center frequency is less than this value, the offset will be adjusted. If it is greater than this value, the center frequency will be set to the new frequency.
|
||||||
|
To only ever set the center frequency, set this value to 0.
|
||||||
|
Default is 10000.</string>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>9999999</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QSpinBox" name="rigCtrlPort">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>TCP port to listen for rigctrl commands on.
|
||||||
|
Default is 4532.</string>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1024</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>65536</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<tabstops>
|
||||||
|
<tabstop>buttonBox</tabstop>
|
||||||
|
</tabstops>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>RigCtrlGUI</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>257</x>
|
||||||
|
<y>194</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>203</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>RigCtrlGUI</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>314</x>
|
||||||
|
<y>194</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>203</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
109
plugins/misc/rigctrl/rigctrlplugin.cpp
Normal file
109
plugins/misc/rigctrl/rigctrlplugin.cpp
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||||
|
// //
|
||||||
|
// This program is free software; you can redistribute it and/or modify //
|
||||||
|
// it under the terms of the GNU General Public License as published by //
|
||||||
|
// the Free Software Foundation as version 3 of the License, or //
|
||||||
|
// (at your option) any later version. //
|
||||||
|
// //
|
||||||
|
// This program is distributed in the hope that it will be useful, //
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||||
|
// GNU General Public License V3 for more details. //
|
||||||
|
// //
|
||||||
|
// You should have received a copy of the GNU General Public License //
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <QtPlugin>
|
||||||
|
#include <QtDebug>
|
||||||
|
#include <QtWidgets/QAction>
|
||||||
|
#include <QtWidgets/QMainWindow>
|
||||||
|
#include <QtWidgets/QMenu>
|
||||||
|
#include <QtWidgets/QMenuBar>
|
||||||
|
|
||||||
|
#include "plugin/pluginapi.h"
|
||||||
|
#include "util/simpleserializer.h"
|
||||||
|
|
||||||
|
#include "rigctrl.h"
|
||||||
|
#ifdef SERVER_MODE
|
||||||
|
#else
|
||||||
|
#include "rigctrlgui.h"
|
||||||
|
#endif
|
||||||
|
#include "rigctrlplugin.h"
|
||||||
|
|
||||||
|
const PluginDescriptor RigCtrlPlugin::m_pluginDescriptor = {
|
||||||
|
QString("rigctrl"),
|
||||||
|
QString("rigctrl Server"),
|
||||||
|
QString("4.15.5"),
|
||||||
|
QString("(c) Jon Beniston, M7RCE"),
|
||||||
|
QString("https://github.com/f4exb/sdrangel"),
|
||||||
|
true,
|
||||||
|
QString("https://github.com/srcejon/sdrangel/tree/rigctrl")
|
||||||
|
};
|
||||||
|
|
||||||
|
RigCtrlPlugin::RigCtrlPlugin(RigCtrl *rigCtrl, QObject* parent) :
|
||||||
|
m_rigCtrl(rigCtrl),
|
||||||
|
QObject(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const PluginDescriptor& RigCtrlPlugin::getPluginDescriptor() const
|
||||||
|
{
|
||||||
|
return m_pluginDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RigCtrlPlugin::initPlugin(PluginAPI* pluginAPI)
|
||||||
|
{
|
||||||
|
m_rigCtrl = new RigCtrl();
|
||||||
|
pluginAPI->registerMiscPlugin(RIGCTRL_DEVICE_TYPE_ID, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef SERVER_MODE
|
||||||
|
bool RigCtrlPlugin::createTopLevelGUI(
|
||||||
|
QMainWindow* mainWindow
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
bool RigCtrlPlugin::createTopLevelGUI(
|
||||||
|
QMainWindow* mainWindow
|
||||||
|
)
|
||||||
|
{
|
||||||
|
m_mainWindow = mainWindow;
|
||||||
|
QMenuBar *menuBar = mainWindow->menuBar();
|
||||||
|
|
||||||
|
QMenu *prefMenu = menuBar->findChild<QMenu *>("menuPreferences", Qt::FindDirectChildrenOnly);
|
||||||
|
if (prefMenu == nullptr) {
|
||||||
|
qDebug() << "RigCtrlPlugin::createTopLevelGUI - Failed to find Preferences menu";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QAction *prefAction;
|
||||||
|
prefAction = new QAction(tr("rigctrl"), this);
|
||||||
|
prefAction->setStatusTip(tr("Display preferences for rigctrl"));
|
||||||
|
prefMenu->addAction(prefAction);
|
||||||
|
connect(prefAction, &QAction::triggered, this, &RigCtrlPlugin::showRigCtrlUI);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RigCtrlPlugin::showRigCtrlUI()
|
||||||
|
{
|
||||||
|
RigCtrlGUI dlg(m_rigCtrl, m_mainWindow);
|
||||||
|
dlg.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QByteArray RigCtrlPlugin::serializeGlobalSettings() const
|
||||||
|
{
|
||||||
|
return m_rigCtrl->serialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RigCtrlPlugin::deserializeGlobalSettings(const QByteArray& data)
|
||||||
|
{
|
||||||
|
return m_rigCtrl->deserialize(data);
|
||||||
|
}
|
53
plugins/misc/rigctrl/rigctrlplugin.h
Normal file
53
plugins/misc/rigctrl/rigctrlplugin.h
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||||
|
// //
|
||||||
|
// This program is free software; you can redistribute it and/or modify //
|
||||||
|
// it under the terms of the GNU General Public License as published by //
|
||||||
|
// the Free Software Foundation as version 3 of the License, or //
|
||||||
|
// (at your option) any later version. //
|
||||||
|
// //
|
||||||
|
// This program is distributed in the hope that it will be useful, //
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||||
|
// GNU General Public License V3 for more details. //
|
||||||
|
// //
|
||||||
|
// You should have received a copy of the GNU General Public License //
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef INCLUDE_RIGCTRLPLUGIN_H
|
||||||
|
#define INCLUDE_RIGCTRLPLUGIN_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include "plugin/plugininterface.h"
|
||||||
|
#include "rigctrl.h"
|
||||||
|
|
||||||
|
class PluginAPI;
|
||||||
|
|
||||||
|
#define RIGCTRL_DEVICE_TYPE_ID "sdrangel.misc.rigctrl"
|
||||||
|
|
||||||
|
class RigCtrlPlugin : public QObject, public PluginInterface {
|
||||||
|
Q_OBJECT
|
||||||
|
Q_INTERFACES(PluginInterface)
|
||||||
|
Q_PLUGIN_METADATA(IID RIGCTRL_DEVICE_TYPE_ID)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit RigCtrlPlugin(RigCtrl *rigCtrl = NULL, QObject* parent = NULL);
|
||||||
|
|
||||||
|
const PluginDescriptor& getPluginDescriptor() const;
|
||||||
|
void initPlugin(PluginAPI* pluginAPI);
|
||||||
|
|
||||||
|
virtual bool createTopLevelGUI(QMainWindow* mainWindow);
|
||||||
|
virtual QByteArray serializeGlobalSettings() const;
|
||||||
|
virtual bool deserializeGlobalSettings(const QByteArray& data);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void showRigCtrlUI();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const PluginDescriptor m_pluginDescriptor;
|
||||||
|
QMainWindow* m_mainWindow;
|
||||||
|
RigCtrl *m_rigCtrl;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INCLUDE_RIGCTRLPLUGIN_H
|
98
plugins/misc/rigctrl/rigctrlsettings.cpp
Normal file
98
plugins/misc/rigctrl/rigctrlsettings.cpp
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||||
|
// //
|
||||||
|
// This program is free software; you can redistribute it and/or modify //
|
||||||
|
// it under the terms of the GNU General Public License as published by //
|
||||||
|
// the Free Software Foundation as version 3 of the License, or //
|
||||||
|
// (at your option) any later version. //
|
||||||
|
// //
|
||||||
|
// This program is distributed in the hope that it will be useful, //
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||||
|
// GNU General Public License V3 for more details. //
|
||||||
|
// //
|
||||||
|
// You should have received a copy of the GNU General Public License //
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "rigctrlsettings.h"
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include <QtDebug>
|
||||||
|
#include "util/simpleserializer.h"
|
||||||
|
|
||||||
|
#define ENABLED_DEFAULT false
|
||||||
|
#define API_ADDRESS_DEFAULT "http://127.0.0.1:8091/sdrangel"
|
||||||
|
#define RIG_CTRL_PORT_DEFAULT 4532
|
||||||
|
#define MAX_FREQUENCY_OFFSET_DEFAULT 10000
|
||||||
|
#define DEVICE_INDEX_DEFAULT 0
|
||||||
|
#define CHANNEL_INDEX_DEFAULT 0
|
||||||
|
|
||||||
|
void RigCtrlSettings::resetToDefaults()
|
||||||
|
{
|
||||||
|
m_enabled = ENABLED_DEFAULT;
|
||||||
|
m_APIAddress = API_ADDRESS_DEFAULT;
|
||||||
|
m_rigCtrlPort = RIG_CTRL_PORT_DEFAULT;
|
||||||
|
m_maxFrequencyOffset = MAX_FREQUENCY_OFFSET_DEFAULT;
|
||||||
|
m_deviceIndex = DEVICE_INDEX_DEFAULT;
|
||||||
|
m_channelIndex = CHANNEL_INDEX_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray RigCtrlSettings::serialize() const
|
||||||
|
{
|
||||||
|
SimpleSerializer s(1);
|
||||||
|
|
||||||
|
s.writeBool(1, m_enabled);
|
||||||
|
s.writeString(2, m_APIAddress);
|
||||||
|
s.writeS32(3, m_rigCtrlPort);
|
||||||
|
s.writeS32(4, m_maxFrequencyOffset);
|
||||||
|
s.writeS32(5, m_deviceIndex);
|
||||||
|
s.writeS32(6, m_channelIndex);
|
||||||
|
|
||||||
|
return s.final();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RigCtrlSettings::deserialize(const QByteArray& data)
|
||||||
|
{
|
||||||
|
SimpleDeserializer d(data);
|
||||||
|
|
||||||
|
if (!d.isValid())
|
||||||
|
{
|
||||||
|
resetToDefaults();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d.getVersion() == 1)
|
||||||
|
{
|
||||||
|
d.readBool(1, &m_enabled, ENABLED_DEFAULT);
|
||||||
|
d.readString(2, &m_APIAddress, API_ADDRESS_DEFAULT);
|
||||||
|
d.readS32(3, &m_rigCtrlPort, RIG_CTRL_PORT_DEFAULT);
|
||||||
|
d.readS32(4, &m_maxFrequencyOffset, MAX_FREQUENCY_OFFSET_DEFAULT);
|
||||||
|
d.readS32(5, &m_deviceIndex, DEVICE_INDEX_DEFAULT);
|
||||||
|
d.readS32(6, &m_channelIndex, CHANNEL_INDEX_DEFAULT);
|
||||||
|
|
||||||
|
if (!((m_rigCtrlPort > 1023) && (m_rigCtrlPort < 65536))) {
|
||||||
|
qDebug() << "RigCtrlSettings::deserialize invalid port number ignored";
|
||||||
|
m_rigCtrlPort = RIG_CTRL_PORT_DEFAULT;
|
||||||
|
}
|
||||||
|
if (m_maxFrequencyOffset < 0) {
|
||||||
|
qDebug() << "RigCtrlSettings::deserialize invalid max frequency offset ignored";
|
||||||
|
m_maxFrequencyOffset = MAX_FREQUENCY_OFFSET_DEFAULT;
|
||||||
|
}
|
||||||
|
if (m_deviceIndex < 0) {
|
||||||
|
qDebug() << "RigCtrlSettings::deserialize invalid device index ignored";
|
||||||
|
m_deviceIndex = DEVICE_INDEX_DEFAULT;
|
||||||
|
}
|
||||||
|
if (m_channelIndex < 0) {
|
||||||
|
qDebug() << "RigCtrlSettings::deserialize invalid channel index ignored";
|
||||||
|
m_deviceIndex = CHANNEL_INDEX_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
resetToDefaults();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
42
plugins/misc/rigctrl/rigctrlsettings.h
Normal file
42
plugins/misc/rigctrl/rigctrlsettings.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||||
|
// //
|
||||||
|
// This program is free software; you can redistribute it and/or modify //
|
||||||
|
// it under the terms of the GNU General Public License as published by //
|
||||||
|
// the Free Software Foundation as version 3 of the License, or //
|
||||||
|
// (at your option) any later version. //
|
||||||
|
// //
|
||||||
|
// This program is distributed in the hope that it will be useful, //
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||||
|
// GNU General Public License V3 for more details. //
|
||||||
|
// //
|
||||||
|
// You should have received a copy of the GNU General Public License //
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef INCLUDE_RIGCTRLSETTINGS_H
|
||||||
|
#define INCLUDE_RIGCTRLSETTINGS_H
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
struct RigCtrlSettings {
|
||||||
|
|
||||||
|
bool m_enabled;
|
||||||
|
QString m_APIAddress;
|
||||||
|
int m_rigCtrlPort;
|
||||||
|
int m_maxFrequencyOffset;
|
||||||
|
int m_deviceIndex;
|
||||||
|
int m_channelIndex;
|
||||||
|
|
||||||
|
RigCtrlSettings()
|
||||||
|
{
|
||||||
|
resetToDefaults();
|
||||||
|
};
|
||||||
|
void resetToDefaults();
|
||||||
|
QByteArray serialize() const;
|
||||||
|
bool deserialize(const QByteArray& data);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* INCLUDE_RIGCTRLSETTINGS_H */
|
@ -46,6 +46,11 @@ void PluginAPI::registerSampleMIMO(const QString& mimoName, PluginInterface* plu
|
|||||||
m_pluginManager->registerSampleMIMO(mimoName, plugin);
|
m_pluginManager->registerSampleMIMO(mimoName, plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PluginAPI::registerMiscPlugin(const QString& id, PluginInterface* plugin)
|
||||||
|
{
|
||||||
|
m_pluginManager->registerMiscPlugin(id, plugin);
|
||||||
|
}
|
||||||
|
|
||||||
PluginAPI::PluginAPI(PluginManager* pluginManager) :
|
PluginAPI::PluginAPI(PluginManager* pluginManager) :
|
||||||
m_pluginManager(pluginManager)
|
m_pluginManager(pluginManager)
|
||||||
{
|
{
|
||||||
|
@ -45,6 +45,18 @@ public:
|
|||||||
|
|
||||||
typedef QList<ChannelRegistration> ChannelRegistrations;
|
typedef QList<ChannelRegistration> ChannelRegistrations;
|
||||||
|
|
||||||
|
struct MiscPluginRegistration
|
||||||
|
{
|
||||||
|
QString m_id;
|
||||||
|
PluginInterface* m_plugin;
|
||||||
|
MiscPluginRegistration(const QString& id, PluginInterface* plugin) :
|
||||||
|
m_id(id),
|
||||||
|
m_plugin(plugin)
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef QList<MiscPluginRegistration> MiscPluginRegistrations;
|
||||||
|
|
||||||
// Rx Channel stuff
|
// Rx Channel stuff
|
||||||
void registerRxChannel(const QString& channelIdURI, const QString& channelId, PluginInterface* plugin);
|
void registerRxChannel(const QString& channelIdURI, const QString& channelId, PluginInterface* plugin);
|
||||||
ChannelRegistrations *getRxChannelRegistrations();
|
ChannelRegistrations *getRxChannelRegistrations();
|
||||||
@ -66,6 +78,9 @@ public:
|
|||||||
// Sample MIMO stuff
|
// Sample MIMO stuff
|
||||||
void registerSampleMIMO(const QString& sinkName, PluginInterface* plugin);
|
void registerSampleMIMO(const QString& sinkName, PluginInterface* plugin);
|
||||||
|
|
||||||
|
// Access to top-level GUI and main settings. id should be unique among plugins.
|
||||||
|
void registerMiscPlugin(const QString& id, PluginInterface* plugin);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
PluginManager* m_pluginManager;
|
PluginManager* m_pluginManager;
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <QtPlugin>
|
#include <QtPlugin>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QtWidgets/QMainWindow>
|
||||||
|
|
||||||
#include "export.h"
|
#include "export.h"
|
||||||
|
|
||||||
@ -311,6 +312,24 @@ public:
|
|||||||
virtual void deleteSampleMIMOPluginInstanceGUI(PluginInstanceGUI *ui);
|
virtual void deleteSampleMIMOPluginInstanceGUI(PluginInstanceGUI *ui);
|
||||||
virtual void deleteSampleMIMOPluginInstanceMIMO(DeviceSampleMIMO *mimo);
|
virtual void deleteSampleMIMOPluginInstanceMIMO(DeviceSampleMIMO *mimo);
|
||||||
|
|
||||||
|
// Callback to allow plugin to add elements to top-level GUI (such as menu items)
|
||||||
|
virtual bool createTopLevelGUI(QMainWindow* mainWindow)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialise global plugin settings (i.e. settings that are not per-instance)
|
||||||
|
virtual QByteArray serializeGlobalSettings() const
|
||||||
|
{
|
||||||
|
QByteArray empty;
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool deserializeGlobalSettings(const QByteArray& data)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// all devices
|
// all devices
|
||||||
|
|
||||||
virtual DeviceWebAPIAdapter* createDeviceWebAPIAdapter() const {
|
virtual DeviceWebAPIAdapter* createDeviceWebAPIAdapter() const {
|
||||||
|
@ -189,6 +189,15 @@ void PluginManager::registerSampleMIMO(const QString& mimoName, PluginInterface*
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PluginManager::registerMiscPlugin(const QString& id, PluginInterface* plugin)
|
||||||
|
{
|
||||||
|
qDebug() << "PluginManager::registerMiscPlugin "
|
||||||
|
<< plugin->getPluginDescriptor().displayedName.toStdString().c_str()
|
||||||
|
<< " with id " << id.toStdString().c_str();
|
||||||
|
|
||||||
|
m_miscPluginRegistrations.append(PluginAPI::MiscPluginRegistration(id, plugin));
|
||||||
|
}
|
||||||
|
|
||||||
void PluginManager::loadPluginsDir(const QDir& dir)
|
void PluginManager::loadPluginsDir(const QDir& dir)
|
||||||
{
|
{
|
||||||
QDir pluginsDir(dir);
|
QDir pluginsDir(dir);
|
||||||
|
@ -70,6 +70,7 @@ public:
|
|||||||
void registerSampleSource(const QString& sourceName, PluginInterface* plugin);
|
void registerSampleSource(const QString& sourceName, PluginInterface* plugin);
|
||||||
void registerSampleSink(const QString& sinkName, PluginInterface* plugin);
|
void registerSampleSink(const QString& sinkName, PluginInterface* plugin);
|
||||||
void registerSampleMIMO(const QString& mimoName, PluginInterface* plugin);
|
void registerSampleMIMO(const QString& mimoName, PluginInterface* plugin);
|
||||||
|
void registerMiscPlugin(const QString& id, PluginInterface* plugin);
|
||||||
|
|
||||||
PluginAPI::SamplingDeviceRegistrations& getSourceDeviceRegistrations() { return m_sampleSourceRegistrations; }
|
PluginAPI::SamplingDeviceRegistrations& getSourceDeviceRegistrations() { return m_sampleSourceRegistrations; }
|
||||||
PluginAPI::SamplingDeviceRegistrations& getSinkDeviceRegistrations() { return m_sampleSinkRegistrations; }
|
PluginAPI::SamplingDeviceRegistrations& getSinkDeviceRegistrations() { return m_sampleSinkRegistrations; }
|
||||||
@ -77,6 +78,7 @@ public:
|
|||||||
PluginAPI::ChannelRegistrations *getRxChannelRegistrations() { return &m_rxChannelRegistrations; }
|
PluginAPI::ChannelRegistrations *getRxChannelRegistrations() { return &m_rxChannelRegistrations; }
|
||||||
PluginAPI::ChannelRegistrations *getTxChannelRegistrations() { return &m_txChannelRegistrations; }
|
PluginAPI::ChannelRegistrations *getTxChannelRegistrations() { return &m_txChannelRegistrations; }
|
||||||
PluginAPI::ChannelRegistrations *getMIMOChannelRegistrations() { return &m_mimoChannelRegistrations; }
|
PluginAPI::ChannelRegistrations *getMIMOChannelRegistrations() { return &m_mimoChannelRegistrations; }
|
||||||
|
PluginAPI::MiscPluginRegistrations *getMiscPluginRegistrations() { return &m_miscPluginRegistrations; }
|
||||||
|
|
||||||
void createRxChannelInstance(int channelPluginIndex, DeviceUISet *deviceUISet, DeviceAPI *deviceAPI);
|
void createRxChannelInstance(int channelPluginIndex, DeviceUISet *deviceUISet, DeviceAPI *deviceAPI);
|
||||||
void listRxChannels(QList<QString>& list);
|
void listRxChannels(QList<QString>& list);
|
||||||
@ -130,6 +132,8 @@ private:
|
|||||||
PluginAPI::SamplingDeviceRegistrations m_sampleSinkRegistrations; //!< Output sink plugins (one per device kind) register here
|
PluginAPI::SamplingDeviceRegistrations m_sampleSinkRegistrations; //!< Output sink plugins (one per device kind) register here
|
||||||
PluginAPI::SamplingDeviceRegistrations m_sampleMIMORegistrations; //!< MIMO sink plugins (one per device kind) register here
|
PluginAPI::SamplingDeviceRegistrations m_sampleMIMORegistrations; //!< MIMO sink plugins (one per device kind) register here
|
||||||
|
|
||||||
|
PluginAPI::MiscPluginRegistrations m_miscPluginRegistrations; //<! Misc plugins accessing top-level UI and main settings register here
|
||||||
|
|
||||||
// "Local" sample source device IDs
|
// "Local" sample source device IDs
|
||||||
static const QString m_localInputHardwareID; //!< Local input hardware ID
|
static const QString m_localInputHardwareID; //!< Local input hardware ID
|
||||||
static const QString m_localInputDeviceTypeID; //!< Local input plugin ID
|
static const QString m_localInputDeviceTypeID; //!< Local input plugin ID
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
@ -41,7 +42,7 @@ int MainSettings::getFileFormat() const
|
|||||||
return (int) s.format();
|
return (int) s.format();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainSettings::load()
|
void MainSettings::load(PluginManager *pluginManager)
|
||||||
{
|
{
|
||||||
QSettings s;
|
QSettings s;
|
||||||
|
|
||||||
@ -92,13 +93,33 @@ void MainSettings::load()
|
|||||||
|
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
}
|
}
|
||||||
|
else if (groups[i].startsWith("plugin-"))
|
||||||
|
{
|
||||||
|
int j;
|
||||||
|
|
||||||
|
// Find plugin with matching ID
|
||||||
|
PluginAPI::MiscPluginRegistrations *miscPluginRegistrations = pluginManager->getMiscPluginRegistrations();
|
||||||
|
for (j = 0; j < miscPluginRegistrations->count(); j++)
|
||||||
|
{
|
||||||
|
if (groups[i].indexOf((*miscPluginRegistrations)[j].m_id) == 7)
|
||||||
|
{
|
||||||
|
s.beginGroup(groups[i]);
|
||||||
|
(*miscPluginRegistrations)[j].m_plugin->deserializeGlobalSettings(qUncompress(QByteArray::fromBase64(s.value("data").toByteArray())));
|
||||||
|
s.endGroup();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (j == miscPluginRegistrations->count()) {
|
||||||
|
qDebug() << "Failed to find plugin to deserialize for " << groups[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_hardwareDeviceUserArgs.deserialize(qUncompress(QByteArray::fromBase64(s.value("hwDeviceUserArgs").toByteArray())));
|
m_hardwareDeviceUserArgs.deserialize(qUncompress(QByteArray::fromBase64(s.value("hwDeviceUserArgs").toByteArray())));
|
||||||
m_limeRFEUSBCalib.deserialize(qUncompress(QByteArray::fromBase64(s.value("limeRFEUSBCalib").toByteArray())));
|
m_limeRFEUSBCalib.deserialize(qUncompress(QByteArray::fromBase64(s.value("limeRFEUSBCalib").toByteArray())));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainSettings::save() const
|
void MainSettings::save(PluginManager *pluginManager) const
|
||||||
{
|
{
|
||||||
QSettings s;
|
QSettings s;
|
||||||
|
|
||||||
@ -141,6 +162,16 @@ void MainSettings::save() const
|
|||||||
|
|
||||||
s.setValue("hwDeviceUserArgs", qCompress(m_hardwareDeviceUserArgs.serialize()).toBase64());
|
s.setValue("hwDeviceUserArgs", qCompress(m_hardwareDeviceUserArgs.serialize()).toBase64());
|
||||||
s.setValue("limeRFEUSBCalib", qCompress(m_limeRFEUSBCalib.serialize()).toBase64());
|
s.setValue("limeRFEUSBCalib", qCompress(m_limeRFEUSBCalib.serialize()).toBase64());
|
||||||
|
|
||||||
|
// Plugin global settings
|
||||||
|
PluginAPI::MiscPluginRegistrations *miscPluginRegistrations = pluginManager->getMiscPluginRegistrations();
|
||||||
|
for (int i = 0; i < miscPluginRegistrations->count(); i++)
|
||||||
|
{
|
||||||
|
QString group = QString("plugin-%1").arg((*miscPluginRegistrations)[i].m_id);
|
||||||
|
s.beginGroup(group);
|
||||||
|
s.setValue("data", qCompress((*miscPluginRegistrations)[i].m_plugin->serializeGlobalSettings()).toBase64());
|
||||||
|
s.endGroup();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainSettings::initialize()
|
void MainSettings::initialize()
|
||||||
|
@ -7,18 +7,20 @@
|
|||||||
#include "preferences.h"
|
#include "preferences.h"
|
||||||
#include "preset.h"
|
#include "preset.h"
|
||||||
#include "export.h"
|
#include "export.h"
|
||||||
|
#include "plugin/pluginmanager.h"
|
||||||
|
|
||||||
class Command;
|
class Command;
|
||||||
class AudioDeviceManager;
|
class AudioDeviceManager;
|
||||||
class AMBEEngine;
|
class AMBEEngine;
|
||||||
|
|
||||||
|
|
||||||
class SDRBASE_API MainSettings {
|
class SDRBASE_API MainSettings {
|
||||||
public:
|
public:
|
||||||
MainSettings();
|
MainSettings();
|
||||||
~MainSettings();
|
~MainSettings();
|
||||||
|
|
||||||
void load();
|
void load(PluginManager *pluginManager);
|
||||||
void save() const;
|
void save(PluginManager *pluginManager) const;
|
||||||
|
|
||||||
void resetToDefaults();
|
void resetToDefaults();
|
||||||
void initialize();
|
void initialize();
|
||||||
|
@ -195,8 +195,6 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
|
|||||||
splash->showStatusMessage("load settings...", Qt::white);
|
splash->showStatusMessage("load settings...", Qt::white);
|
||||||
qDebug() << "MainWindow::MainWindow: load settings...";
|
qDebug() << "MainWindow::MainWindow: load settings...";
|
||||||
|
|
||||||
loadSettings();
|
|
||||||
|
|
||||||
splash->showStatusMessage("load plugins...", Qt::white);
|
splash->showStatusMessage("load plugins...", Qt::white);
|
||||||
qDebug() << "MainWindow::MainWindow: load plugins...";
|
qDebug() << "MainWindow::MainWindow: load plugins...";
|
||||||
|
|
||||||
@ -204,6 +202,9 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
|
|||||||
m_pluginManager->loadPlugins(QString("plugins"));
|
m_pluginManager->loadPlugins(QString("plugins"));
|
||||||
m_pluginManager->loadPluginsNonDiscoverable(m_settings.getDeviceUserArgs());
|
m_pluginManager->loadPluginsNonDiscoverable(m_settings.getDeviceUserArgs());
|
||||||
|
|
||||||
|
// Load settings after plugins have loaded, as they can include misc plugin settings
|
||||||
|
loadSettings();
|
||||||
|
|
||||||
splash->showStatusMessage("load file input...", Qt::white);
|
splash->showStatusMessage("load file input...", Qt::white);
|
||||||
qDebug() << "MainWindow::MainWindow: select SampleSource from settings or default (file input)...";
|
qDebug() << "MainWindow::MainWindow: select SampleSource from settings or default (file input)...";
|
||||||
|
|
||||||
@ -261,6 +262,13 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
|
|||||||
|
|
||||||
delete splash;
|
delete splash;
|
||||||
|
|
||||||
|
// Allow plugins to create top-level GUI elements
|
||||||
|
PluginAPI::MiscPluginRegistrations *miscPluginRegistrations = m_pluginManager->getMiscPluginRegistrations();
|
||||||
|
for (int i = 0; i < miscPluginRegistrations->count(); i++)
|
||||||
|
{
|
||||||
|
(*miscPluginRegistrations)[i].m_plugin->createTopLevelGUI(this);
|
||||||
|
}
|
||||||
|
|
||||||
qDebug() << "MainWindow::MainWindow: end";
|
qDebug() << "MainWindow::MainWindow: end";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -635,7 +643,7 @@ void MainWindow::loadSettings()
|
|||||||
{
|
{
|
||||||
qDebug() << "MainWindow::loadSettings";
|
qDebug() << "MainWindow::loadSettings";
|
||||||
|
|
||||||
m_settings.load();
|
m_settings.load(m_pluginManager);
|
||||||
m_settings.sortPresets();
|
m_settings.sortPresets();
|
||||||
int middleIndex = m_settings.getPresetCount() / 2;
|
int middleIndex = m_settings.getPresetCount() / 2;
|
||||||
QTreeWidgetItem *treeItem;
|
QTreeWidgetItem *treeItem;
|
||||||
@ -752,7 +760,7 @@ void MainWindow::closeEvent(QCloseEvent *closeEvent)
|
|||||||
qDebug("MainWindow::closeEvent");
|
qDebug("MainWindow::closeEvent");
|
||||||
|
|
||||||
savePresetSettings(m_settings.getWorkingPreset(), 0);
|
savePresetSettings(m_settings.getWorkingPreset(), 0);
|
||||||
m_settings.save();
|
m_settings.save(m_pluginManager);
|
||||||
|
|
||||||
while (m_deviceUIs.size() > 0)
|
while (m_deviceUIs.size() > 0)
|
||||||
{
|
{
|
||||||
@ -895,7 +903,7 @@ bool MainWindow::handleMessage(const Message& cmd)
|
|||||||
savePresetSettings(notif.getPreset(), notif.getDeviceSetIndex());
|
savePresetSettings(notif.getPreset(), notif.getDeviceSetIndex());
|
||||||
if (notif.isNewPreset()) { ui->presetTree->setCurrentItem(addPresetToTree(notif.getPreset())); }
|
if (notif.isNewPreset()) { ui->presetTree->setCurrentItem(addPresetToTree(notif.getPreset())); }
|
||||||
m_settings.sortPresets();
|
m_settings.sortPresets();
|
||||||
m_settings.save();
|
m_settings.save(m_pluginManager);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (MsgDeletePreset::match(cmd))
|
else if (MsgDeletePreset::match(cmd))
|
||||||
@ -1232,7 +1240,7 @@ void MainWindow::on_commandOutput_clicked()
|
|||||||
void MainWindow::on_commandsSave_clicked()
|
void MainWindow::on_commandsSave_clicked()
|
||||||
{
|
{
|
||||||
saveCommandSettings();
|
saveCommandSettings();
|
||||||
m_settings.save();
|
m_settings.save(m_pluginManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::commandKeysConnect(QObject *object, const char *slot)
|
void MainWindow::commandKeysConnect(QObject *object, const char *slot)
|
||||||
@ -1489,7 +1497,7 @@ void MainWindow::on_presetImport_clicked()
|
|||||||
void MainWindow::on_settingsSave_clicked()
|
void MainWindow::on_settingsSave_clicked()
|
||||||
{
|
{
|
||||||
savePresetSettings(m_settings.getWorkingPreset(), ui->tabInputsView->currentIndex());
|
savePresetSettings(m_settings.getWorkingPreset(), ui->tabInputsView->currentIndex());
|
||||||
m_settings.save();
|
m_settings.save(m_pluginManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_presetLoad_clicked()
|
void MainWindow::on_presetLoad_clicked()
|
||||||
|
@ -97,7 +97,7 @@ MainCore::~MainCore()
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_apiServer->stop();
|
m_apiServer->stop();
|
||||||
m_settings.save();
|
m_settings.save(m_pluginManager);
|
||||||
delete m_apiServer;
|
delete m_apiServer;
|
||||||
delete m_requestMapper;
|
delete m_requestMapper;
|
||||||
delete m_apiAdapter;
|
delete m_apiAdapter;
|
||||||
@ -131,7 +131,7 @@ bool MainCore::handleMessage(const Message& cmd)
|
|||||||
MsgSavePreset& notif = (MsgSavePreset&) cmd;
|
MsgSavePreset& notif = (MsgSavePreset&) cmd;
|
||||||
savePresetSettings(notif.getPreset(), notif.getDeviceSetIndex());
|
savePresetSettings(notif.getPreset(), notif.getDeviceSetIndex());
|
||||||
m_settings.sortPresets();
|
m_settings.sortPresets();
|
||||||
m_settings.save();
|
m_settings.save(m_pluginManager);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (MsgDeletePreset::match(cmd))
|
else if (MsgDeletePreset::match(cmd))
|
||||||
@ -215,7 +215,7 @@ void MainCore::loadSettings()
|
|||||||
{
|
{
|
||||||
qDebug() << "MainCore::loadSettings";
|
qDebug() << "MainCore::loadSettings";
|
||||||
|
|
||||||
m_settings.load();
|
m_settings.load(m_pluginManager);
|
||||||
m_settings.sortPresets();
|
m_settings.sortPresets();
|
||||||
setLoggingOptions();
|
setLoggingOptions();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user