mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-03-22 12:18:32 -04:00
Feature plugins framework initial commit and Misc plugins removal
This commit is contained in:
parent
7297313bd7
commit
e8f3745bf3
@ -42,4 +42,3 @@ add_subdirectory(channelrx)
|
||||
add_subdirectory(channeltx)
|
||||
add_subdirectory(samplesource)
|
||||
add_subdirectory(samplesink)
|
||||
add_subdirectory(misc)
|
||||
|
@ -1,3 +0,0 @@
|
||||
project(misc)
|
||||
|
||||
add_subdirectory(rigctrl)
|
@ -1,52 +0,0 @@
|
||||
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})
|
@ -1,59 +0,0 @@
|
||||
<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>
|
@ -1,507 +0,0 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
const unsigned int RigCtrl::m_CmdLength = 1024;
|
||||
const unsigned int RigCtrl::m_UrlLength = 1024;
|
||||
const unsigned int RigCtrl::m_ResponseLength = 1024;
|
||||
const unsigned int RigCtrl::m_DataLength = 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[m_CmdLength];
|
||||
char url[m_UrlLength];
|
||||
char response[m_ResponseLength];
|
||||
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_APIBaseURI), 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_APIBaseURI), 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_APIBaseURI), 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_APIBaseURI), 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_APIBaseURI), 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_APIBaseURI), 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[m_ResponseLength];
|
||||
char url[m_UrlLength];
|
||||
char data[m_DataLength];
|
||||
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_APIBaseURI), 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_APIBaseURI), 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_APIBaseURI), 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_APIBaseURI), 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_APIBaseURI), 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_APIBaseURI), 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_APIBaseURI), 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_APIBaseURI), 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_APIBaseURI), 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_APIBaseURI), 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();
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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);
|
||||
void setAPIBaseURI(const QString& apiBaseURI) { m_APIBaseURI = apiBaseURI; }
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
|
||||
private slots:
|
||||
void acceptConnection();
|
||||
void getCommand();
|
||||
void processAPIResponse(QNetworkReply *reply);
|
||||
|
||||
private:
|
||||
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;
|
||||
QString m_APIBaseURI; //!< Base URI of own API
|
||||
|
||||
RigCtrlSettings m_settings;
|
||||
|
||||
static const unsigned int m_CmdLength;
|
||||
static const unsigned int m_UrlLength;
|
||||
static const unsigned int m_ResponseLength;
|
||||
static const unsigned int m_DataLength;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_RIGCTRL_H
|
@ -1,160 +0,0 @@
|
||||
<?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>261</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="1" column="0">
|
||||
<widget class="QLabel" name="rigCtrlPortLabel">
|
||||
<property name="text">
|
||||
<string>rigctrl Port</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" 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>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="maxFrequencyOffsetLabel">
|
||||
<property name="text">
|
||||
<string>Max Frequency Offset</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" 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="3" column="0">
|
||||
<widget class="QLabel" name="deviceIndexLabel">
|
||||
<property name="text">
|
||||
<string>Device Index</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" 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="4" column="0">
|
||||
<widget class="QLabel" name="channelIndexLabel">
|
||||
<property name="text">
|
||||
<string>Channel Index</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" 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>
|
||||
</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>
|
@ -1,113 +0,0 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 "plugin/pluginapi.h"
|
||||
#include "util/simpleserializer.h"
|
||||
|
||||
#include "rigctrl.h"
|
||||
#ifdef SERVER_MODE
|
||||
#else
|
||||
#include <QtWidgets/QAction>
|
||||
#include <QtWidgets/QMainWindow>
|
||||
#include <QtWidgets/QMenu>
|
||||
#include <QtWidgets/QMenuBar>
|
||||
#include "mainwindow.h"
|
||||
#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(
|
||||
)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void RigCtrlPlugin::showRigCtrlUI()
|
||||
{
|
||||
}
|
||||
|
||||
#else
|
||||
bool RigCtrlPlugin::createTopLevelGUI()
|
||||
{
|
||||
m_mainWindow = MainWindow::getInstance();
|
||||
m_rigCtrl->setAPIBaseURI(QString("http://%1:%2/sdrangel").arg(m_mainWindow->getAPIHost()).arg(m_mainWindow->getAPIPort()));
|
||||
QMenuBar *menuBar = m_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);
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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"
|
||||
|
||||
const bool RigCtrlSettings::m_EnabledDefault = false;
|
||||
const int RigCtrlSettings::m_RigCtrlPortDefault = 4532;
|
||||
const int RigCtrlSettings::m_MaxFrequencyOffsetDefault = 10000;
|
||||
const int RigCtrlSettings::m_DeviceIndexDefault = 0;
|
||||
const int RigCtrlSettings::m_ChannelIndexDefault = 0;
|
||||
|
||||
|
||||
void RigCtrlSettings::resetToDefaults()
|
||||
{
|
||||
m_enabled = m_EnabledDefault;
|
||||
m_rigCtrlPort = m_RigCtrlPortDefault;
|
||||
m_maxFrequencyOffset = m_MaxFrequencyOffsetDefault;
|
||||
m_deviceIndex = m_DeviceIndexDefault;
|
||||
m_channelIndex = m_ChannelIndexDefault;
|
||||
}
|
||||
|
||||
QByteArray RigCtrlSettings::serialize() const
|
||||
{
|
||||
SimpleSerializer s(1);
|
||||
|
||||
s.writeBool(1, m_enabled);
|
||||
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, m_EnabledDefault);
|
||||
d.readS32(3, &m_rigCtrlPort, m_RigCtrlPortDefault);
|
||||
d.readS32(4, &m_maxFrequencyOffset, m_MaxFrequencyOffsetDefault);
|
||||
d.readS32(5, &m_deviceIndex, m_DeviceIndexDefault);
|
||||
d.readS32(6, &m_channelIndex, m_ChannelIndexDefault);
|
||||
|
||||
if (!((m_rigCtrlPort > 1023) && (m_rigCtrlPort < 65536))) {
|
||||
qDebug() << "RigCtrlSettings::deserialize invalid port number ignored";
|
||||
m_rigCtrlPort = m_RigCtrlPortDefault;
|
||||
}
|
||||
if (m_maxFrequencyOffset < 0) {
|
||||
qDebug() << "RigCtrlSettings::deserialize invalid max frequency offset ignored";
|
||||
m_maxFrequencyOffset = m_MaxFrequencyOffsetDefault;
|
||||
}
|
||||
if (m_deviceIndex < 0) {
|
||||
qDebug() << "RigCtrlSettings::deserialize invalid device index ignored";
|
||||
m_deviceIndex = m_DeviceIndexDefault;
|
||||
}
|
||||
if (m_channelIndex < 0) {
|
||||
qDebug() << "RigCtrlSettings::deserialize invalid channel index ignored";
|
||||
m_deviceIndex = m_ChannelIndexDefault;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
@ -137,6 +137,8 @@ set(sdrbase_SOURCES
|
||||
device/deviceuserargs.cpp
|
||||
device/deviceutils.cpp
|
||||
|
||||
feature/feature.cpp
|
||||
|
||||
limerfe/limerfeusbcalib.cpp
|
||||
|
||||
settings/preferences.cpp
|
||||
@ -289,6 +291,8 @@ set(sdrbase_HEADERS
|
||||
device/deviceuserargs.h
|
||||
device/deviceutils.h
|
||||
|
||||
feature/feature.h
|
||||
|
||||
limerfe/limerfeusbcalib.h
|
||||
|
||||
plugin/plugininstancegui.h
|
||||
|
@ -1,5 +1,7 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// API for features //
|
||||
// //
|
||||
// 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 //
|
||||
@ -15,34 +17,27 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_RIGCTRLSETTINGS_H
|
||||
#define INCLUDE_RIGCTRLSETTINGS_H
|
||||
#include "util/uid.h"
|
||||
#include "util/message.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
#include "feature.h"
|
||||
|
||||
struct RigCtrlSettings {
|
||||
Feature::Feature(const QString& name, WebAPIAdapterInterface *webAPIAdapterInterface) :
|
||||
m_name(name),
|
||||
m_uid(UidCalculator::getNewObjectId()),
|
||||
m_webAPIAdapterInterface(webAPIAdapterInterface)
|
||||
{
|
||||
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
|
||||
}
|
||||
|
||||
bool m_enabled;
|
||||
int m_rigCtrlPort;
|
||||
int m_maxFrequencyOffset;
|
||||
int m_deviceIndex;
|
||||
int m_channelIndex;
|
||||
void Feature::handleInputMessages()
|
||||
{
|
||||
Message* message;
|
||||
|
||||
RigCtrlSettings()
|
||||
{
|
||||
resetToDefaults();
|
||||
};
|
||||
void resetToDefaults();
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
|
||||
private:
|
||||
static const bool m_EnabledDefault;
|
||||
static const int m_RigCtrlPortDefault;
|
||||
static const int m_MaxFrequencyOffsetDefault;
|
||||
static const int m_DeviceIndexDefault;
|
||||
static const int m_ChannelIndexDefault;
|
||||
};
|
||||
|
||||
#endif /* INCLUDE_RIGCTRLSETTINGS_H */
|
||||
while ((message = m_inputMessageQueue.pop()))
|
||||
{
|
||||
if (handleMessage(*message)) {
|
||||
delete message;
|
||||
}
|
||||
}
|
||||
}
|
79
sdrbase/feature/feature.h
Normal file
79
sdrbase/feature/feature.h
Normal file
@ -0,0 +1,79 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// API for features //
|
||||
// //
|
||||
// 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 SDRBASE_FETURE_FEATUREAPI_H_
|
||||
#define SDRBASE_FETURE_FEATUREAPI_H_
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
|
||||
#include "export.h"
|
||||
#include "util/messagequeue.h"
|
||||
|
||||
class WebAPIAdapterInterface;
|
||||
|
||||
class SDRBASE_API Feature : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum FeatureState {
|
||||
StNotStarted, //!< feature is before initialization
|
||||
StIdle, //!< feature is idle
|
||||
StRunning, //!< feature is running
|
||||
StError //!< feature is in error
|
||||
};
|
||||
|
||||
Feature(const QString& name, WebAPIAdapterInterface *webAPIAdapterInterface);
|
||||
virtual ~Feature() {}
|
||||
virtual void destroy() = 0;
|
||||
virtual bool handleMessage(const Message& cmd) = 0; //!< Processing of a message. Returns true if message has actually been processed
|
||||
|
||||
virtual void getIdentifier(QString& id) = 0;
|
||||
virtual void getTitle(QString& title) = 0;
|
||||
virtual void setName(const QString& name) { m_name = name; }
|
||||
virtual const QString& getName() const { return m_name; }
|
||||
|
||||
virtual QByteArray serialize() const = 0;
|
||||
virtual bool deserialize(const QByteArray& data) = 0;
|
||||
|
||||
|
||||
uint64_t getUID() const { return m_uid; }
|
||||
FeatureState getState() const { return m_state; }
|
||||
const QString& getErrorMessage() const { return m_errorMessage; }
|
||||
|
||||
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication
|
||||
void setMessageQueueToGUI(MessageQueue *queue) { m_guiMessageQueue = queue; }
|
||||
MessageQueue *getMessageQueueToGUI() { return m_guiMessageQueue; }
|
||||
|
||||
protected:
|
||||
MessageQueue m_inputMessageQueue;
|
||||
MessageQueue *m_guiMessageQueue; //!< Input message queue to the GUI
|
||||
FeatureState m_state;
|
||||
QString m_errorMessage;
|
||||
WebAPIAdapterInterface *m_webAPIAdapterInterface;
|
||||
|
||||
protected slots:
|
||||
void handleInputMessages();
|
||||
|
||||
private:
|
||||
QString m_name;
|
||||
uint64_t m_uid;
|
||||
};
|
||||
|
||||
#endif // SDRBASE_FETURE_FEATUREAPI_H_
|
@ -46,9 +46,14 @@ void PluginAPI::registerSampleMIMO(const QString& mimoName, PluginInterface* plu
|
||||
m_pluginManager->registerSampleMIMO(mimoName, plugin);
|
||||
}
|
||||
|
||||
void PluginAPI::registerMiscPlugin(const QString& id, PluginInterface* plugin)
|
||||
void PluginAPI::registerFeature(const QString& featureIdURI, const QString& featureId, PluginInterface* plugin)
|
||||
{
|
||||
m_pluginManager->registerMiscPlugin(id, plugin);
|
||||
m_pluginManager->registerFeature(featureIdURI, featureId, plugin);
|
||||
}
|
||||
|
||||
PluginAPI::FeatureRegistrations *PluginAPI::getFeatureRegistrations()
|
||||
{
|
||||
return m_pluginManager->getFeatureRegistrations();
|
||||
}
|
||||
|
||||
PluginAPI::PluginAPI(PluginManager* pluginManager) :
|
||||
|
@ -45,17 +45,19 @@ public:
|
||||
|
||||
typedef QList<ChannelRegistration> ChannelRegistrations;
|
||||
|
||||
struct MiscPluginRegistration
|
||||
struct FeatureRegistration
|
||||
{
|
||||
QString m_id;
|
||||
PluginInterface* m_plugin;
|
||||
MiscPluginRegistration(const QString& id, PluginInterface* plugin) :
|
||||
m_id(id),
|
||||
QString m_featureIdURI; //!< Feature type ID in URI form
|
||||
QString m_featureId; //!< Feature type ID in short form from object name
|
||||
PluginInterface *m_plugin;
|
||||
FeatureRegistration(const QString& featureIdURI, const QString& featureId, PluginInterface* plugin) :
|
||||
m_featureIdURI(featureIdURI),
|
||||
m_featureId(featureId),
|
||||
m_plugin(plugin)
|
||||
{ }
|
||||
};
|
||||
|
||||
typedef QList<MiscPluginRegistration> MiscPluginRegistrations;
|
||||
typedef QList<FeatureRegistration> FeatureRegistrations;
|
||||
|
||||
// Rx Channel stuff
|
||||
void registerRxChannel(const QString& channelIdURI, const QString& channelId, PluginInterface* plugin);
|
||||
@ -78,8 +80,9 @@ public:
|
||||
// Sample MIMO stuff
|
||||
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);
|
||||
// Feature stuff
|
||||
void registerFeature(const QString& featureIdURI, const QString& featureId, PluginInterface* plugin);
|
||||
FeatureRegistrations *getFeatureRegistrations();
|
||||
|
||||
protected:
|
||||
PluginManager* m_pluginManager;
|
||||
|
@ -20,6 +20,8 @@ struct SDRBASE_API PluginDescriptor {
|
||||
class PluginAPI;
|
||||
class DeviceAPI;
|
||||
class DeviceUISet;
|
||||
class FeatureUISet;
|
||||
class WebAPIAdapterInterface;
|
||||
class PluginInstanceGUI;
|
||||
class QWidget;
|
||||
class DeviceSampleSource;
|
||||
@ -31,6 +33,7 @@ class MIMOChannel;
|
||||
class ChannelAPI;
|
||||
class ChannelWebAPIAdapter;
|
||||
class DeviceWebAPIAdapter;
|
||||
class Feature;
|
||||
|
||||
class SDRBASE_API PluginInterface {
|
||||
public:
|
||||
@ -334,6 +337,18 @@ public:
|
||||
virtual DeviceWebAPIAdapter* createDeviceWebAPIAdapter() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Features
|
||||
|
||||
virtual PluginInstanceGUI* createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual Feature* createFeature(WebAPIAdapterInterface *webAPIAdapterInterface) const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
Q_DECLARE_INTERFACE(PluginInterface, "SDRangel.PluginInterface/0.1")
|
||||
|
@ -189,13 +189,13 @@ void PluginManager::registerSampleMIMO(const QString& mimoName, PluginInterface*
|
||||
));
|
||||
}
|
||||
|
||||
void PluginManager::registerMiscPlugin(const QString& id, PluginInterface* plugin)
|
||||
void PluginManager::registerFeature(const QString& featureIdURI, const QString& featureId, PluginInterface* plugin)
|
||||
{
|
||||
qDebug() << "PluginManager::registerMiscPlugin "
|
||||
<< plugin->getPluginDescriptor().displayedName.toStdString().c_str()
|
||||
<< " with id " << id.toStdString().c_str();
|
||||
qDebug() << "PluginManager::registerFeature "
|
||||
<< plugin->getPluginDescriptor().displayedName.toStdString().c_str()
|
||||
<< " with channel name " << featureIdURI;
|
||||
|
||||
m_miscPluginRegistrations.append(PluginAPI::MiscPluginRegistration(id, plugin));
|
||||
m_featureRegistrations.append(PluginAPI::FeatureRegistration(featureIdURI, featureId, plugin));
|
||||
}
|
||||
|
||||
void PluginManager::loadPluginsDir(const QDir& dir)
|
||||
@ -265,6 +265,17 @@ void PluginManager::listMIMOChannels(QList<QString>& list)
|
||||
}
|
||||
}
|
||||
|
||||
void PluginManager::listFeatures(QList<QString>& list)
|
||||
{
|
||||
list.clear();
|
||||
|
||||
for (PluginAPI::FeatureRegistrations::iterator it = m_featureRegistrations.begin(); it != m_featureRegistrations.end(); ++it)
|
||||
{
|
||||
const PluginDescriptor& pluginDesciptor = it->m_plugin->getPluginDescriptor();
|
||||
list.append(pluginDesciptor.displayedName);
|
||||
}
|
||||
}
|
||||
|
||||
void PluginManager::createRxChannelInstance(int channelPluginIndex, DeviceUISet *deviceUISet, DeviceAPI *deviceAPI)
|
||||
{
|
||||
if (channelPluginIndex < m_rxChannelRegistrations.size())
|
||||
@ -295,6 +306,16 @@ void PluginManager::createMIMOChannelInstance(int channelPluginIndex, DeviceUISe
|
||||
}
|
||||
}
|
||||
|
||||
void PluginManager::createFeatureInstance(int featurePluginIndex, FeatureUISet *featureUISet, WebAPIAdapterInterface *webAPIAdapterInterface)
|
||||
{
|
||||
if (featurePluginIndex < m_featureRegistrations.size())
|
||||
{
|
||||
PluginInterface *pluginInterface = m_featureRegistrations[featurePluginIndex].m_plugin;
|
||||
Feature *feature = pluginInterface->createFeature(webAPIAdapterInterface);
|
||||
pluginInterface->createFeatureGUI(featureUISet, feature);
|
||||
}
|
||||
}
|
||||
|
||||
const PluginInterface *PluginManager::getChannelPluginInterface(const QString& channelIdURI) const
|
||||
{
|
||||
for (PluginAPI::ChannelRegistrations::const_iterator it = m_rxChannelRegistrations.begin(); it != m_rxChannelRegistrations.end(); ++it)
|
||||
@ -338,4 +359,4 @@ const PluginInterface *PluginManager::getDevicePluginInterface(const QString& de
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ class Message;
|
||||
class MessageQueue;
|
||||
class DeviceAPI;
|
||||
class DeviceUserArgs;
|
||||
class WebAPIAdapterInterface;
|
||||
|
||||
class SDRBASE_API PluginManager : public QObject {
|
||||
Q_OBJECT
|
||||
@ -70,7 +71,7 @@ public:
|
||||
void registerSampleSource(const QString& sourceName, PluginInterface* plugin);
|
||||
void registerSampleSink(const QString& sinkName, PluginInterface* plugin);
|
||||
void registerSampleMIMO(const QString& mimoName, PluginInterface* plugin);
|
||||
void registerMiscPlugin(const QString& id, PluginInterface* plugin);
|
||||
void registerFeature(const QString& featureIdURI, const QString& featureId, PluginInterface* plugin);
|
||||
|
||||
PluginAPI::SamplingDeviceRegistrations& getSourceDeviceRegistrations() { return m_sampleSourceRegistrations; }
|
||||
PluginAPI::SamplingDeviceRegistrations& getSinkDeviceRegistrations() { return m_sampleSinkRegistrations; }
|
||||
@ -78,7 +79,7 @@ public:
|
||||
PluginAPI::ChannelRegistrations *getRxChannelRegistrations() { return &m_rxChannelRegistrations; }
|
||||
PluginAPI::ChannelRegistrations *getTxChannelRegistrations() { return &m_txChannelRegistrations; }
|
||||
PluginAPI::ChannelRegistrations *getMIMOChannelRegistrations() { return &m_mimoChannelRegistrations; }
|
||||
PluginAPI::MiscPluginRegistrations *getMiscPluginRegistrations() { return &m_miscPluginRegistrations; }
|
||||
PluginAPI::FeatureRegistrations *getFeatureRegistrations() { return &m_featureRegistrations; }
|
||||
|
||||
void createRxChannelInstance(int channelPluginIndex, DeviceUISet *deviceUISet, DeviceAPI *deviceAPI);
|
||||
void listRxChannels(QList<QString>& list);
|
||||
@ -89,6 +90,9 @@ public:
|
||||
void createMIMOChannelInstance(int channelPluginIndex, DeviceUISet *deviceUISet, DeviceAPI *deviceAPI);
|
||||
void listMIMOChannels(QList<QString>& list);
|
||||
|
||||
void createFeatureInstance(int featurePluginIndex, FeatureUISet *featureUISet, WebAPIAdapterInterface *webAPIAdapterInterface);
|
||||
void listFeatures(QList<QString>& list);
|
||||
|
||||
const PluginInterface *getChannelPluginInterface(const QString& channelIdURI) const;
|
||||
const PluginInterface *getDevicePluginInterface(const QString& deviceId) const;
|
||||
|
||||
@ -132,7 +136,7 @@ private:
|
||||
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::MiscPluginRegistrations m_miscPluginRegistrations; //<! Misc plugins accessing top-level UI and main settings register here
|
||||
PluginAPI::FeatureRegistrations m_featureRegistrations; //!< Feature plugins register here
|
||||
|
||||
// "Local" sample source device IDs
|
||||
static const QString m_localInputHardwareID; //!< Local input hardware ID
|
||||
|
@ -93,26 +93,6 @@ void MainSettings::load(PluginManager *pluginManager)
|
||||
|
||||
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())));
|
||||
@ -162,16 +142,6 @@ void MainSettings::save(PluginManager *pluginManager) const
|
||||
|
||||
s.setValue("hwDeviceUserArgs", qCompress(m_hardwareDeviceUserArgs.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()
|
||||
|
@ -13,6 +13,7 @@ set(sdrgui_SOURCES
|
||||
gui/audioselectdialog.cpp
|
||||
gui/basicchannelsettingsdialog.cpp
|
||||
gui/basicdevicesettingsdialog.cpp
|
||||
gui/basicfeaturesettingsdialog.cpp
|
||||
gui/buttonswitch.cpp
|
||||
gui/channeladddialog.cpp
|
||||
gui/channelsdock.cpp
|
||||
@ -31,6 +32,9 @@ set(sdrgui_SOURCES
|
||||
gui/externalclockbutton.cpp
|
||||
gui/externalclockdialog.cpp
|
||||
gui/fmpreemphasisdialog.cpp
|
||||
gui/featureadddialog.cpp
|
||||
gui/featuresdock.cpp
|
||||
gui/featurewindow.cpp
|
||||
gui/glscope.cpp
|
||||
gui/glscopegui.cpp
|
||||
gui/glshadercolors.cpp
|
||||
@ -64,6 +68,8 @@ set(sdrgui_SOURCES
|
||||
|
||||
device/deviceuiset.cpp
|
||||
|
||||
feature/featureuiset.cpp
|
||||
|
||||
soapygui/discreterangegui.cpp
|
||||
soapygui/intervalrangegui.cpp
|
||||
soapygui/itemsettinggui.cpp
|
||||
@ -88,6 +94,7 @@ set(sdrgui_HEADERS
|
||||
gui/audioselectdialog.h
|
||||
gui/basicchannelsettingsdialog.h
|
||||
gui/basicdevicesettingsdialog.h
|
||||
gui/basicfeaturesettingsdialog.h
|
||||
gui/buttonswitch.h
|
||||
gui/channeladddialog.h
|
||||
gui/channelsdock.h
|
||||
@ -106,6 +113,9 @@ set(sdrgui_HEADERS
|
||||
gui/externalclockbutton.h
|
||||
gui/externalclockdialog.h
|
||||
gui/fmpreemphasisdialog.h
|
||||
gui/featureadddialog.h
|
||||
gui/featuresdock.h
|
||||
gui/featurewindow.h
|
||||
gui/glscope.h
|
||||
gui/glscopegui.h
|
||||
gui/glshadercolors.h
|
||||
@ -140,6 +150,8 @@ set(sdrgui_HEADERS
|
||||
|
||||
device/deviceuiset.h
|
||||
|
||||
feature/featureuiset.h
|
||||
|
||||
soapygui/discreterangegui.h
|
||||
soapygui/intervalrangegui.h
|
||||
soapygui/itemsettinggui.h
|
||||
@ -154,35 +166,38 @@ set(sdrgui_HEADERS
|
||||
)
|
||||
|
||||
set(sdrgui_FORMS
|
||||
mainwindow.ui
|
||||
gui/aboutdialog.ui
|
||||
gui/addpresetdialog.ui
|
||||
gui/ambedevicesdialog.ui
|
||||
gui/basicchannelsettingsdialog.ui
|
||||
gui/basicdevicesettingsdialog.ui
|
||||
gui/channeladddialog.ui
|
||||
gui/commandoutputdialog.ui
|
||||
gui/cwkeyergui.ui
|
||||
gui/devicestreamselectiondialog.ui
|
||||
gui/deviceuserargsdialog.ui
|
||||
gui/editcommanddialog.ui
|
||||
gui/externalclockdialog.ui
|
||||
gui/fmpreemphasisdialog.ui
|
||||
gui/glscopegui.ui
|
||||
gui/glspectrumgui.ui
|
||||
gui/pluginsdialog.ui
|
||||
gui/audiodialog.ui
|
||||
gui/audioselectdialog.ui
|
||||
gui/samplingdevicedialog.ui
|
||||
gui/myposdialog.ui
|
||||
gui/transverterdialog.ui
|
||||
gui/loggingdialog.ui
|
||||
soapygui/discreterangegui.ui
|
||||
soapygui/intervalrangegui.ui
|
||||
soapygui/intervalslidergui.ui
|
||||
soapygui/complexfactorgui.ui
|
||||
soapygui/arginfogui.ui
|
||||
)
|
||||
mainwindow.ui
|
||||
gui/aboutdialog.ui
|
||||
gui/addpresetdialog.ui
|
||||
gui/ambedevicesdialog.ui
|
||||
gui/basicchannelsettingsdialog.ui
|
||||
gui/basicdevicesettingsdialog.ui
|
||||
gui/basicfeaturesettingsdialog.ui
|
||||
gui/channeladddialog.ui
|
||||
gui/commandoutputdialog.ui
|
||||
gui/cwkeyergui.ui
|
||||
gui/devicestreamselectiondialog.ui
|
||||
gui/deviceuserargsdialog.ui
|
||||
gui/editcommanddialog.ui
|
||||
gui/externalclockdialog.ui
|
||||
gui/fmpreemphasisdialog.ui
|
||||
gui/featureadddialog.ui
|
||||
gui/featurepresetsdialog.ui
|
||||
gui/glscopegui.ui
|
||||
gui/glspectrumgui.ui
|
||||
gui/pluginsdialog.ui
|
||||
gui/audiodialog.ui
|
||||
gui/audioselectdialog.ui
|
||||
gui/samplingdevicedialog.ui
|
||||
gui/myposdialog.ui
|
||||
gui/transverterdialog.ui
|
||||
gui/loggingdialog.ui
|
||||
soapygui/discreterangegui.ui
|
||||
soapygui/intervalrangegui.ui
|
||||
soapygui/intervalslidergui.ui
|
||||
soapygui/complexfactorgui.ui
|
||||
soapygui/arginfogui.ui
|
||||
)
|
||||
|
||||
if (LIMESUITE_FOUND)
|
||||
set(sdrgui_SOURCES
|
||||
|
85
sdrgui/feature/featureuiset.cpp
Normal file
85
sdrgui/feature/featureuiset.cpp
Normal file
@ -0,0 +1,85 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// 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 "gui/featurewindow.h"
|
||||
#include "plugin/plugininstancegui.h"
|
||||
|
||||
#include "featureuiset.h"
|
||||
|
||||
FeatureUISet::FeatureUISet(int tabIndex)
|
||||
{
|
||||
m_featureWindow = new FeatureWindow;
|
||||
m_featureTabIndex = tabIndex;
|
||||
}
|
||||
|
||||
FeatureUISet::~FeatureUISet()
|
||||
{
|
||||
delete m_featureWindow;
|
||||
}
|
||||
|
||||
void FeatureUISet::addRollupWidget(QWidget *widget)
|
||||
{
|
||||
m_featureWindow->addRollupWidget(widget);
|
||||
}
|
||||
|
||||
void FeatureUISet::registerFeatureInstance(const QString& featureName, PluginInstanceGUI* pluginGUI)
|
||||
{
|
||||
m_featureInstanceRegistrations.append(FeatureInstanceRegistration(featureName, pluginGUI));
|
||||
renameFeatureInstances();
|
||||
}
|
||||
|
||||
void FeatureUISet::removeFeatureInstance(PluginInstanceGUI* pluginGUI)
|
||||
{
|
||||
for (FeatureInstanceRegistrations::iterator it = m_featureInstanceRegistrations.begin(); it != m_featureInstanceRegistrations.end(); ++it)
|
||||
{
|
||||
if (it->m_gui == pluginGUI)
|
||||
{
|
||||
m_featureInstanceRegistrations.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
renameFeatureInstances();
|
||||
}
|
||||
|
||||
void FeatureUISet::renameFeatureInstances()
|
||||
{
|
||||
for (int i = 0; i < m_featureInstanceRegistrations.count(); i++) {
|
||||
m_featureInstanceRegistrations[i].m_gui->setName(QString("%1:%2").arg(m_featureInstanceRegistrations[i].m_featureName).arg(i));
|
||||
}
|
||||
}
|
||||
|
||||
void FeatureUISet::freeFeatures()
|
||||
{
|
||||
for(int i = 0; i < m_featureInstanceRegistrations.count(); i++)
|
||||
{
|
||||
qDebug("FeatureUISet::freeFeatures: destroying feature [%s]", qPrintable(m_featureInstanceRegistrations[i].m_featureName));
|
||||
m_featureInstanceRegistrations[i].m_gui->destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void FeatureUISet::deleteFeature(int featureIndex)
|
||||
{
|
||||
if ((featureIndex >= 0) && (featureIndex < m_featureInstanceRegistrations.count()))
|
||||
{
|
||||
qDebug("FeatureUISet::deleteFeature: delete feature [%s] at %d",
|
||||
qPrintable(m_featureInstanceRegistrations[featureIndex].m_featureName),
|
||||
featureIndex);
|
||||
m_featureInstanceRegistrations[featureIndex].m_gui->destroy();
|
||||
renameFeatureInstances();
|
||||
}
|
||||
}
|
72
sdrgui/feature/featureuiset.h
Normal file
72
sdrgui/feature/featureuiset.h
Normal file
@ -0,0 +1,72 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// 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 SDRGUI_FEATURE_FEATUREUISET_H_
|
||||
#define SDRGUI_FEATURE_FEATUREUISET_H_
|
||||
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
|
||||
#include "export.h"
|
||||
|
||||
class QWidget;
|
||||
class FeatureWindow;
|
||||
class PluginInstanceGUI;
|
||||
|
||||
class SDRGUI_API FeatureUISet
|
||||
{
|
||||
public:
|
||||
FeatureUISet(int tabIndex);
|
||||
~FeatureUISet();
|
||||
|
||||
void addRollupWidget(QWidget *widget); //!< Add feature rollup widget to feature window
|
||||
int getNumberOfFeatures() const { return m_featureInstanceRegistrations.size(); }
|
||||
void registerFeatureInstance(const QString& featureName, PluginInstanceGUI* pluginGUI);
|
||||
void removeFeatureInstance(PluginInstanceGUI* pluginGUI);
|
||||
void freeFeatures();
|
||||
void deleteFeature(int featureIndex);
|
||||
|
||||
FeatureWindow *m_featureWindow;
|
||||
|
||||
private:
|
||||
struct FeatureInstanceRegistration
|
||||
{
|
||||
QString m_featureName;
|
||||
PluginInstanceGUI* m_gui;
|
||||
|
||||
FeatureInstanceRegistration() :
|
||||
m_featureName(),
|
||||
m_gui(nullptr)
|
||||
{ }
|
||||
|
||||
FeatureInstanceRegistration(const QString& featureName, PluginInstanceGUI* pluginGUI) :
|
||||
m_featureName(featureName),
|
||||
m_gui(pluginGUI)
|
||||
{ }
|
||||
|
||||
bool operator<(const FeatureInstanceRegistration& other) const;
|
||||
};
|
||||
|
||||
typedef QList<FeatureInstanceRegistration> FeatureInstanceRegistrations;
|
||||
|
||||
FeatureInstanceRegistrations m_featureInstanceRegistrations;
|
||||
int m_featureTabIndex;
|
||||
|
||||
void renameFeatureInstances();
|
||||
};
|
||||
|
||||
#endif // SDRGUI_FEATURE_FEATUREUISET_H_
|
68
sdrgui/gui/basicfeaturesettingsdialog.cpp
Normal file
68
sdrgui/gui/basicfeaturesettingsdialog.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
#include <QColorDialog>
|
||||
|
||||
#include "basicfeaturesettingsdialog.h"
|
||||
#include "ui_basicfeaturesettingsdialog.h"
|
||||
|
||||
BasicFeatureSettingsDialog::BasicFeatureSettingsDialog(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::BasicFeatureSettingsDialog),
|
||||
m_hasChanged(false)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->title->setText(m_title);
|
||||
m_color =m_color;
|
||||
paintColor();
|
||||
}
|
||||
|
||||
BasicFeatureSettingsDialog::~BasicFeatureSettingsDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void BasicFeatureSettingsDialog::setTitle(const QString& title)
|
||||
{
|
||||
ui->title->blockSignals(true);
|
||||
ui->title->setText(title);
|
||||
m_title = title;
|
||||
ui->title->blockSignals(false);
|
||||
}
|
||||
|
||||
void BasicFeatureSettingsDialog::setColor(const QColor& color)
|
||||
{
|
||||
m_color = color;
|
||||
paintColor();
|
||||
}
|
||||
|
||||
void BasicFeatureSettingsDialog::paintColor()
|
||||
{
|
||||
QPixmap pm(24, 24);
|
||||
pm.fill(m_color);
|
||||
ui->colorBtn->setIcon(pm);
|
||||
ui->colorText->setText(tr("#%1%2%3")
|
||||
.arg(m_color.red(), 2, 16, QChar('0'))
|
||||
.arg(m_color.green(), 2, 16, QChar('0'))
|
||||
.arg(m_color.blue(), 2, 16, QChar('0')));
|
||||
}
|
||||
|
||||
void BasicFeatureSettingsDialog::on_colorBtn_clicked()
|
||||
{
|
||||
QColor c = m_color;
|
||||
c = QColorDialog::getColor(c, this, tr("Select Color for Channel"), QColorDialog::DontUseNativeDialog);
|
||||
|
||||
if (c.isValid())
|
||||
{
|
||||
m_color = c;
|
||||
paintColor();
|
||||
}
|
||||
}
|
||||
|
||||
void BasicFeatureSettingsDialog::on_title_editingFinished()
|
||||
{
|
||||
m_title = ui->title->text();
|
||||
}
|
||||
|
||||
void BasicFeatureSettingsDialog::accept()
|
||||
{
|
||||
m_hasChanged = true;
|
||||
QDialog::accept();
|
||||
}
|
56
sdrgui/gui/basicfeaturesettingsdialog.h
Normal file
56
sdrgui/gui/basicfeaturesettingsdialog.h
Normal file
@ -0,0 +1,56 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// 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_SDRGUI_BASICFEATURESETTINGSDIALOG_H
|
||||
#define INCLUDE_SDRGUI_BASICFEATURESETTINGSDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "../../exports/export.h"
|
||||
|
||||
namespace Ui {
|
||||
class BasicFeatureSettingsDialog;
|
||||
}
|
||||
|
||||
class SDRGUI_API BasicFeatureSettingsDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit BasicFeatureSettingsDialog(QWidget *parent = nullptr);
|
||||
~BasicFeatureSettingsDialog();
|
||||
void setTitle(const QString& title);
|
||||
void setColor(const QColor& color);
|
||||
const QString& getTitle() const { return m_title; }
|
||||
const QColor& getColor() const { return m_color; }
|
||||
bool hasChanged() const { return m_hasChanged; }
|
||||
|
||||
private slots:
|
||||
void on_colorBtn_clicked();
|
||||
void on_title_editingFinished();
|
||||
void accept();
|
||||
|
||||
private:
|
||||
Ui::BasicFeatureSettingsDialog *ui;
|
||||
QColor m_color;
|
||||
QString m_title;
|
||||
bool m_hasChanged;
|
||||
|
||||
void paintColor();
|
||||
};
|
||||
|
||||
#endif // INCLUDE_SDRGUI_BASICFEATURESETTINGSDIALOG_H
|
141
sdrgui/gui/basicfeaturesettingsdialog.ui
Normal file
141
sdrgui/gui/basicfeaturesettingsdialog.ui
Normal file
@ -0,0 +1,141 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>BasicFeatureSettingsDialog</class>
|
||||
<widget class="QDialog" name="BasicFeatureSettingsDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>158</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Sans</family>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Basic feature settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="titleLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="titleLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Title</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="title">
|
||||
<property name="toolTip">
|
||||
<string>Channel marker title</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="colorLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="colorLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Color</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="colorBtn">
|
||||
<property name="toolTip">
|
||||
<string>Channel marker color</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="colorText">
|
||||
<property name="text">
|
||||
<string>#ff0000</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</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>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>BasicFeatureSettingsDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>BasicFeatureSettingsDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
@ -1,5 +1,6 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||
// Copyright (C) 2020 F4EXB //
|
||||
// written by Edouard Griffiths //
|
||||
// //
|
||||
// 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 //
|
||||
@ -15,36 +16,39 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "rigctrlgui.h"
|
||||
#include "ui_rigctrlgui.h"
|
||||
#include <QDebug>
|
||||
#include <QStringList>
|
||||
|
||||
RigCtrlGUI::RigCtrlGUI(RigCtrl *rigCtrl, QWidget* parent) :
|
||||
m_rigCtrl(rigCtrl),
|
||||
QDialog(parent),
|
||||
ui(new Ui::RigCtrlGUI)
|
||||
#include "featureadddialog.h"
|
||||
#include "ui_featureadddialog.h"
|
||||
|
||||
FeatureAddDialog::FeatureAddDialog(QWidget* parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::FeatureAddDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
m_rigCtrl->getSettings(&m_settings);
|
||||
ui->enable->setChecked(m_settings.m_enabled);
|
||||
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);
|
||||
ui->setupUi(this);
|
||||
connect(ui->buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(apply(QAbstractButton*)));
|
||||
}
|
||||
|
||||
RigCtrlGUI::~RigCtrlGUI()
|
||||
FeatureAddDialog::~FeatureAddDialog()
|
||||
{
|
||||
delete ui;
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void RigCtrlGUI::accept()
|
||||
void FeatureAddDialog::resetFeatureNames()
|
||||
{
|
||||
m_settings.m_enabled = ui->enable->isChecked();
|
||||
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();
|
||||
ui->featureSelect->clear();
|
||||
}
|
||||
|
||||
void FeatureAddDialog::addFeatureNames(const QStringList& featureNames)
|
||||
{
|
||||
ui->featureSelect->addItems(featureNames);
|
||||
}
|
||||
|
||||
void FeatureAddDialog::apply(QAbstractButton *button)
|
||||
{
|
||||
if (button == (QAbstractButton*) ui->buttonBox->button(QDialogButtonBox::Apply))
|
||||
{
|
||||
int selectedFeatureIndex = ui->featureSelect->currentIndex();
|
||||
emit(addFeature(selectedFeatureIndex));
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||
// Copyright (C) 2020 F4EXB //
|
||||
// written by Edouard Griffiths //
|
||||
// //
|
||||
// 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 //
|
||||
@ -15,40 +16,39 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_RIGCTRLPLUGIN_H
|
||||
#define INCLUDE_RIGCTRLPLUGIN_H
|
||||
#ifndef SDRGUI_GUI_FEATUREADDDIALOG_H_
|
||||
#define SDRGUI_GUI_FEATUREADDDIALOG_H_
|
||||
|
||||
#include <QObject>
|
||||
#include "plugin/plugininterface.h"
|
||||
#include "rigctrl.h"
|
||||
#include <QDialog>
|
||||
#include <vector>
|
||||
|
||||
class PluginAPI;
|
||||
class MainWindow;
|
||||
#include "export.h"
|
||||
|
||||
#define RIGCTRL_DEVICE_TYPE_ID "sdrangel.misc.rigctrl"
|
||||
class QStringList;
|
||||
class QAbstractButton;
|
||||
|
||||
class RigCtrlPlugin : public QObject, public PluginInterface {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(PluginInterface)
|
||||
Q_PLUGIN_METADATA(IID RIGCTRL_DEVICE_TYPE_ID)
|
||||
namespace Ui {
|
||||
class FeatureAddDialog;
|
||||
}
|
||||
|
||||
class SDRGUI_API FeatureAddDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit RigCtrlPlugin(RigCtrl *rigCtrl = nullptr, QObject* parent = nullptr);
|
||||
explicit FeatureAddDialog(QWidget* parent = nullptr);
|
||||
~FeatureAddDialog();
|
||||
|
||||
const PluginDescriptor& getPluginDescriptor() const;
|
||||
void initPlugin(PluginAPI* pluginAPI);
|
||||
|
||||
virtual bool createTopLevelGUI();
|
||||
virtual QByteArray serializeGlobalSettings() const;
|
||||
virtual bool deserializeGlobalSettings(const QByteArray& data);
|
||||
|
||||
private slots:
|
||||
void showRigCtrlUI();
|
||||
void resetFeatureNames();
|
||||
void addFeatureNames(const QStringList& featureNames);
|
||||
|
||||
private:
|
||||
static const PluginDescriptor m_pluginDescriptor;
|
||||
MainWindow* m_mainWindow;
|
||||
RigCtrl *m_rigCtrl;
|
||||
Ui::FeatureAddDialog* ui;
|
||||
std::vector<int> m_featureIndexes;
|
||||
|
||||
private slots:
|
||||
void apply(QAbstractButton*);
|
||||
|
||||
signals:
|
||||
void addFeature(int);
|
||||
};
|
||||
|
||||
#endif // INCLUDE_RIGCTRLPLUGIN_H
|
||||
#endif /* SDRGUI_GUI_FEATUREADDDIALOG_H_ */
|
104
sdrgui/gui/featureadddialog.ui
Normal file
104
sdrgui/gui/featureadddialog.ui
Normal file
@ -0,0 +1,104 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>FeatureAddDialog</class>
|
||||
<widget class="QDialog" name="FeatureAddDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>324</width>
|
||||
<height>139</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Sans</family>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Add Features</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Sans</family>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Available features</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="featureSelect">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Sans</family>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Sans</family>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Apply|QDialogButtonBox::Close</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>buttonBox</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>FeatureAddDialog</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>FeatureAddDialog</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>
|
110
sdrgui/gui/featuresdock.cpp
Normal file
110
sdrgui/gui/featuresdock.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 F4EXB //
|
||||
// written by Edouard Griffiths //
|
||||
// //
|
||||
// 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 <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QStyle>
|
||||
|
||||
#include "featuresdock.h"
|
||||
|
||||
FeaturesDock::FeaturesDock(QWidget *parent, Qt::WindowFlags flags) :
|
||||
QDockWidget(parent, flags),
|
||||
m_featureAddDialog(this)
|
||||
{
|
||||
m_titleBar = new QWidget();
|
||||
m_titleBarLayout = new QHBoxLayout();
|
||||
m_titleBarLayout->setMargin(0);
|
||||
m_titleBar->setLayout(m_titleBarLayout);
|
||||
|
||||
m_titleLabel = new QLabel();
|
||||
m_titleLabel->setText(QString("Features"));
|
||||
|
||||
m_addFeatureButton = new QPushButton();
|
||||
QIcon addIcon(":/create.png");
|
||||
m_addFeatureButton->setIcon(addIcon);
|
||||
m_addFeatureButton->setToolTip("Add features");
|
||||
m_addFeatureButton->setFixedSize(16, 16);
|
||||
|
||||
m_normalButton = new QPushButton();
|
||||
QIcon normalIcon = style()->standardIcon(QStyle::SP_TitleBarNormalButton, 0, this);
|
||||
m_normalButton->setIcon(normalIcon);
|
||||
m_normalButton->setFixedSize(12, 12);
|
||||
|
||||
m_closeButton = new QPushButton();
|
||||
QIcon closeIcon = style()->standardIcon(QStyle::SP_TitleBarCloseButton, 0, this);
|
||||
m_closeButton->setIcon(closeIcon);
|
||||
m_closeButton->setFixedSize(12, 12);
|
||||
|
||||
m_titleBarLayout->addWidget(m_addFeatureButton);
|
||||
m_titleBarLayout->addWidget(m_titleLabel);
|
||||
m_titleBarLayout->addWidget(m_normalButton);
|
||||
m_titleBarLayout->addWidget(m_closeButton);
|
||||
setTitleBarWidget(m_titleBar);
|
||||
|
||||
QObject::connect(
|
||||
m_addFeatureButton,
|
||||
&QPushButton::clicked,
|
||||
this,
|
||||
&FeaturesDock::addFeatureDialog
|
||||
);
|
||||
|
||||
QObject::connect(
|
||||
m_normalButton,
|
||||
&QPushButton::clicked,
|
||||
this,
|
||||
&FeaturesDock::toggleFloating
|
||||
);
|
||||
|
||||
QObject::connect(
|
||||
&m_featureAddDialog,
|
||||
&FeatureAddDialog::addFeature,
|
||||
this,
|
||||
&FeaturesDock::addFeatureEmitted
|
||||
);
|
||||
|
||||
connect(m_closeButton, SIGNAL(clicked()), this, SLOT(hide()));
|
||||
}
|
||||
|
||||
FeaturesDock::~FeaturesDock()
|
||||
{
|
||||
delete m_closeButton;
|
||||
delete m_normalButton;
|
||||
delete m_addFeatureButton;
|
||||
delete m_titleLabel;
|
||||
delete m_titleBarLayout;
|
||||
delete m_titleBar;
|
||||
}
|
||||
|
||||
void FeaturesDock::toggleFloating()
|
||||
{
|
||||
setFloating(!isFloating());
|
||||
}
|
||||
|
||||
void FeaturesDock::addFeatureDialog()
|
||||
{
|
||||
m_featureAddDialog.exec();
|
||||
|
||||
}
|
||||
|
||||
void FeaturesDock::addFeatureEmitted(int featureIndex)
|
||||
{
|
||||
if (featureIndex >= 0) {
|
||||
emit addFeature(featureIndex);
|
||||
}
|
||||
}
|
59
sdrgui/gui/featuresdock.h
Normal file
59
sdrgui/gui/featuresdock.h
Normal file
@ -0,0 +1,59 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 F4EXB //
|
||||
// written by Edouard Griffiths //
|
||||
// //
|
||||
// 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 SDRGUI_GUI_FEATURESDOCK_H_
|
||||
#define SDRGUI_GUI_FEATURESDOCK_H_
|
||||
|
||||
#include <QDockWidget>
|
||||
|
||||
#include "featureadddialog.h"
|
||||
|
||||
class QHBoxLayout;
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
class QStringList;
|
||||
|
||||
class FeaturesDock : public QDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
FeaturesDock(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags());
|
||||
~FeaturesDock();
|
||||
|
||||
void resetAvailableFeatures() { m_featureAddDialog.resetFeatureNames(); }
|
||||
void addAvailableFeatures(const QStringList& featureNames) { m_featureAddDialog.addFeatureNames(featureNames); }
|
||||
|
||||
private:
|
||||
QPushButton *m_addFeatureButton;
|
||||
QWidget *m_titleBar;
|
||||
QHBoxLayout *m_titleBarLayout;
|
||||
QLabel *m_titleLabel;
|
||||
QPushButton *m_normalButton;
|
||||
QPushButton *m_closeButton;
|
||||
FeatureAddDialog m_featureAddDialog;
|
||||
|
||||
private slots:
|
||||
void toggleFloating();
|
||||
void addFeatureDialog();
|
||||
void addFeatureEmitted(int featureIndex);
|
||||
|
||||
signals:
|
||||
void addFeature(int);
|
||||
};
|
||||
|
||||
#endif // SDRGUI_GUI_FEATURESDOCK_H_
|
57
sdrgui/gui/featurewindow.cpp
Normal file
57
sdrgui/gui/featurewindow.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// 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 <QBoxLayout>
|
||||
#include <QSpacerItem>
|
||||
#include <QPainter>
|
||||
#include <QResizeEvent>
|
||||
|
||||
#include "featurewindow.h"
|
||||
#include "rollupwidget.h"
|
||||
|
||||
FeatureWindow::FeatureWindow(QWidget* parent) :
|
||||
QScrollArea(parent)
|
||||
{
|
||||
m_container = new QWidget(this);
|
||||
m_layout = new QBoxLayout(QBoxLayout::TopToBottom, m_container);
|
||||
setWidget(m_container);
|
||||
setWidgetResizable(true);
|
||||
setBackgroundRole(QPalette::Base);
|
||||
m_layout->setMargin(3);
|
||||
m_layout->setSpacing(3);
|
||||
}
|
||||
|
||||
void FeatureWindow::addRollupWidget(QWidget* rollupWidget)
|
||||
{
|
||||
rollupWidget->setParent(m_container);
|
||||
m_container->layout()->addWidget(rollupWidget);
|
||||
}
|
||||
|
||||
void FeatureWindow::resizeEvent(QResizeEvent* event)
|
||||
{
|
||||
if (event->size().height() > event->size().width())
|
||||
{
|
||||
m_layout->setDirection(QBoxLayout::TopToBottom);
|
||||
m_layout->setAlignment(Qt::AlignTop);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_layout->setDirection(QBoxLayout::LeftToRight);
|
||||
m_layout->setAlignment(Qt::AlignLeft);
|
||||
}
|
||||
QScrollArea::resizeEvent(event);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// 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 //
|
||||
@ -15,31 +15,30 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_RIGCTRLGUI_H
|
||||
#define INCLUDE_RIGCTRLGUI_H
|
||||
#ifndef INCLUDE_FEATUREWINDOW_H
|
||||
#define INCLUDE_FEATUREWINDOW_H
|
||||
|
||||
#include <QDialog>
|
||||
#include "rigctrlsettings.h"
|
||||
#include "rigctrl.h"
|
||||
#include <QScrollArea>
|
||||
|
||||
namespace Ui {
|
||||
class RigCtrlGUI;
|
||||
}
|
||||
#include "export.h"
|
||||
|
||||
class RigCtrlGUI : public QDialog {
|
||||
class QBoxLayout;
|
||||
class QSpacerItem;
|
||||
class RollupWidget;
|
||||
|
||||
class SDRGUI_API FeatureWindow : public QScrollArea {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RigCtrlGUI(RigCtrl *rigCtrl, QWidget *parent = 0);
|
||||
~RigCtrlGUI();
|
||||
FeatureWindow(QWidget* parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void accept();
|
||||
void addRollupWidget(QWidget* rollupWidget);
|
||||
|
||||
private:
|
||||
Ui::RigCtrlGUI* ui;
|
||||
RigCtrlSettings m_settings;
|
||||
RigCtrl *m_rigCtrl;
|
||||
protected:
|
||||
QWidget* m_container;
|
||||
QBoxLayout* m_layout;
|
||||
|
||||
void resizeEvent(QResizeEvent* event);
|
||||
};
|
||||
|
||||
#endif // INCLUDE_RIGCTRLGUI_H
|
||||
#endif // INCLUDE_FEATUREWINDOW_H
|
@ -1,3 +1,22 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2015-2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// API for features //
|
||||
// //
|
||||
// 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 <QEvent>
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
@ -12,7 +31,8 @@ RollupWidget::RollupWidget(QWidget* parent) :
|
||||
QWidget(parent),
|
||||
m_highlighted(false),
|
||||
m_contextMenuType(ContextMenuNone),
|
||||
m_streamIndicator("S")
|
||||
m_streamIndicator("S"),
|
||||
m_channelWidget(true)
|
||||
{
|
||||
setMinimumSize(250, 150);
|
||||
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
|
||||
@ -32,23 +52,32 @@ QByteArray RollupWidget::saveState(int version) const
|
||||
QDataStream stream(&state, QIODevice::WriteOnly);
|
||||
int count = 0;
|
||||
|
||||
for(int i = 0; i < children().count(); ++i) {
|
||||
for (int i = 0; i < children().count(); ++i)
|
||||
{
|
||||
QWidget* r = qobject_cast<QWidget*>(children()[i]);
|
||||
if(r != NULL)
|
||||
|
||||
if (r) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
stream << VersionMarker;
|
||||
stream << version;
|
||||
stream << count;
|
||||
|
||||
for(int i = 0; i < children().count(); ++i) {
|
||||
for (int i = 0; i < children().count(); ++i)
|
||||
{
|
||||
QWidget* r = qobject_cast<QWidget*>(children()[i]);
|
||||
if(r != NULL) {
|
||||
|
||||
if (r)
|
||||
{
|
||||
stream << r->objectName();
|
||||
if(r->isHidden())
|
||||
stream << (int)0;
|
||||
else stream << (int)1;
|
||||
|
||||
if (r->isHidden()) {
|
||||
stream << (int) 0;
|
||||
} else {
|
||||
stream << (int) 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,38 +86,53 @@ QByteArray RollupWidget::saveState(int version) const
|
||||
|
||||
bool RollupWidget::restoreState(const QByteArray& state, int version)
|
||||
{
|
||||
if(state.isEmpty())
|
||||
if (state.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray sd = state;
|
||||
QDataStream stream(&sd, QIODevice::ReadOnly);
|
||||
int marker, v;
|
||||
stream >> marker;
|
||||
stream >> v;
|
||||
if((stream.status() != QDataStream::Ok) || (marker != VersionMarker) || (v != version))
|
||||
|
||||
if ((stream.status() != QDataStream::Ok) || (marker != VersionMarker) || (v != version)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int count;
|
||||
stream >> count;
|
||||
|
||||
if(stream.status() != QDataStream::Ok)
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
return false;
|
||||
for(int i = 0; i < count; ++i) {
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
QString name;
|
||||
int visible;
|
||||
|
||||
stream >> name;
|
||||
stream >> visible;
|
||||
|
||||
if(stream.status() != QDataStream::Ok)
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(int j = 0; j < children().count(); ++j) {
|
||||
for (int j = 0; j < children().count(); ++j)
|
||||
{
|
||||
QWidget* r = qobject_cast<QWidget*>(children()[j]);
|
||||
if(r != NULL) {
|
||||
if(r->objectName() == name) {
|
||||
if(visible)
|
||||
|
||||
if (r)
|
||||
{
|
||||
if (r->objectName() == name)
|
||||
{
|
||||
if (visible) {
|
||||
r->show();
|
||||
else r->hide();
|
||||
} else {
|
||||
r->hide();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -196,15 +240,18 @@ void RollupWidget::paintEvent(QPaintEvent*)
|
||||
p.setPen(QPen(palette().windowText().color(), 1.0));
|
||||
p.setBrush(palette().light());
|
||||
p.drawRoundedRect(QRectF(3.5, 3.5, fm.ascent(), fm.ascent()), 2.0, 2.0, Qt::AbsoluteSize);
|
||||
p.setPen(QPen(Qt::white, 1.0));
|
||||
p.drawText(QRectF(3.5, 2.5, fm.ascent(), fm.ascent()), Qt::AlignCenter, "c");
|
||||
p.setPen(QPen(Qt::white, 1.0));
|
||||
p.drawText(QRectF(3.5, 2.5, fm.ascent(), fm.ascent()), Qt::AlignCenter, "c");
|
||||
|
||||
// Stromkanal-Button links
|
||||
p.setPen(QPen(palette().windowText().color(), 1.0));
|
||||
p.setBrush(palette().light());
|
||||
p.drawRoundedRect(QRectF(5.5 + fm.ascent(), 2.5, fm.ascent() + 2.0, fm.ascent() + 2.0), 2.0, 2.0, Qt::AbsoluteSize);
|
||||
p.setPen(QPen(Qt::white, 1.0));
|
||||
p.drawText(QRectF(5.5 + fm.ascent(), 2.5, fm.ascent() + 2.0, fm.ascent() + 2.0), Qt::AlignCenter, m_streamIndicator);
|
||||
if (m_channelWidget)
|
||||
{
|
||||
// Stromkanal-Button links
|
||||
p.setPen(QPen(palette().windowText().color(), 1.0));
|
||||
p.setBrush(palette().light());
|
||||
p.drawRoundedRect(QRectF(5.5 + fm.ascent(), 2.5, fm.ascent() + 2.0, fm.ascent() + 2.0), 2.0, 2.0, Qt::AbsoluteSize);
|
||||
p.setPen(QPen(Qt::white, 1.0));
|
||||
p.drawText(QRectF(5.5 + fm.ascent(), 2.5, fm.ascent() + 2.0, fm.ascent() + 2.0), Qt::AlignCenter, m_streamIndicator);
|
||||
}
|
||||
|
||||
// Schließen-Button rechts
|
||||
p.setRenderHint(QPainter::Antialiasing, true);
|
||||
@ -259,14 +306,18 @@ int RollupWidget::paintRollup(QWidget* rollup, int pos, QPainter* p, bool last,
|
||||
int height = 1;
|
||||
|
||||
// Titel-Abschlusslinie
|
||||
if(!rollup->isHidden()) {
|
||||
if (!rollup->isHidden())
|
||||
{
|
||||
p->setPen(palette().dark().color());
|
||||
p->drawLine(QPointF(1.5, pos + fm.height() + 1.5), QPointF(width() - 1.5, pos + fm.height() + 1.5));
|
||||
p->setPen(palette().light().color());
|
||||
p->drawLine(QPointF(1.5, pos + fm.height() + 2.5), QPointF(width() - 1.5, pos + fm.height() + 2.5));
|
||||
height += 2;
|
||||
} else {
|
||||
if(!last) {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!last)
|
||||
{
|
||||
p->setPen(frame);
|
||||
p->drawLine(QPointF(1.5, pos + fm.height() + 1.5), QPointF(width() - 1.5, pos + fm.height() + 1.5));
|
||||
height++;
|
||||
@ -282,13 +333,17 @@ int RollupWidget::paintRollup(QWidget* rollup, int pos, QPainter* p, bool last,
|
||||
// Ausklapp-Icon
|
||||
p->setPen(palette().windowText().color());
|
||||
p->setBrush(palette().windowText());
|
||||
if(!rollup->isHidden()) {
|
||||
|
||||
if (!rollup->isHidden())
|
||||
{
|
||||
QPolygonF a;
|
||||
a.append(QPointF(3.5, pos + 2));
|
||||
a.append(QPointF(3.5 + fm.ascent(), pos + 2));
|
||||
a.append(QPointF(3.5 + fm.ascent() / 2.0, pos + fm.height() - 2));
|
||||
p->drawPolygon(a);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
QPolygonF a;
|
||||
a.append(QPointF(3.5, pos + 2));
|
||||
a.append(QPointF(3.5, pos + fm.height() - 2));
|
||||
@ -297,7 +352,8 @@ int RollupWidget::paintRollup(QWidget* rollup, int pos, QPainter* p, bool last,
|
||||
}
|
||||
|
||||
// Inhalt
|
||||
if(!rollup->isHidden() && (!last)) {
|
||||
if (!rollup->isHidden() && (!last))
|
||||
{
|
||||
// Rollup-Abschlusslinie
|
||||
p->setPen(frame);
|
||||
p->drawLine(QPointF(1.5, pos + fm.height() + rollup->height() + 6.5),
|
||||
@ -320,18 +376,21 @@ void RollupWidget::mousePressEvent(QMouseEvent* event)
|
||||
|
||||
// menu box left
|
||||
if (QRectF(3.5, 3.5, fm.ascent(), fm.ascent()).contains(event->pos()))
|
||||
{
|
||||
m_contextMenuType = ContextMenuChannelSettings;
|
||||
{
|
||||
m_contextMenuType = ContextMenuChannelSettings;
|
||||
emit customContextMenuRequested(event->globalPos());
|
||||
return;
|
||||
}
|
||||
|
||||
// Stream channel menu left
|
||||
if (QRectF(5.5 + fm.ascent(), 2.5, fm.ascent() + 2.0, fm.ascent() + 2.0).contains(event->pos()))
|
||||
{
|
||||
m_contextMenuType = ContextMenuStreamSettings;
|
||||
emit customContextMenuRequested(event->globalPos());
|
||||
return;
|
||||
if (m_channelWidget)
|
||||
{
|
||||
// Stream channel menu left
|
||||
if (QRectF(5.5 + fm.ascent(), 2.5, fm.ascent() + 2.0, fm.ascent() + 2.0).contains(event->pos()))
|
||||
{
|
||||
m_contextMenuType = ContextMenuStreamSettings;
|
||||
emit customContextMenuRequested(event->globalPos());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// close button right
|
||||
@ -342,24 +401,37 @@ void RollupWidget::mousePressEvent(QMouseEvent* event)
|
||||
|
||||
// check if we need to change a rollup widget
|
||||
int pos = fm.height() + 4;
|
||||
for(int i = 0; i < children().count(); ++i) {
|
||||
|
||||
for (int i = 0; i < children().count(); ++i)
|
||||
{
|
||||
QWidget* r = qobject_cast<QWidget*>(children()[i]);
|
||||
if(r != NULL) {
|
||||
if((event->y() >= pos) && (event->y() < (pos + fm.height() + 3))) {
|
||||
if(r->isHidden()) {
|
||||
|
||||
if (r)
|
||||
{
|
||||
if ((event->y() >= pos) && (event->y() < (pos + fm.height() + 3)))
|
||||
{
|
||||
if (r->isHidden())
|
||||
{
|
||||
r->show();
|
||||
//emit widgetRolled(r, true);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
r->hide();
|
||||
//emit widgetRolled(r, false);
|
||||
}
|
||||
|
||||
arrangeRollups();
|
||||
repaint();
|
||||
return;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
pos += fm.height() + 2;
|
||||
if(!r->isHidden())
|
||||
|
||||
if (!r->isHidden()) {
|
||||
pos += r->height() + 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -367,32 +439,45 @@ void RollupWidget::mousePressEvent(QMouseEvent* event)
|
||||
|
||||
bool RollupWidget::event(QEvent* event)
|
||||
{
|
||||
if(event->type() == QEvent::ChildAdded) {
|
||||
if (event->type() == QEvent::ChildAdded)
|
||||
{
|
||||
((QChildEvent*)event)->child()->installEventFilter(this);
|
||||
arrangeRollups();
|
||||
} else if(event->type() == QEvent::ChildRemoved) {
|
||||
}
|
||||
else if (event->type() == QEvent::ChildRemoved)
|
||||
{
|
||||
((QChildEvent*)event)->child()->removeEventFilter(this);
|
||||
arrangeRollups();
|
||||
}
|
||||
|
||||
return QWidget::event(event);
|
||||
}
|
||||
|
||||
bool RollupWidget::eventFilter(QObject* object, QEvent* event)
|
||||
{
|
||||
if(event->type() == QEvent::Show) {
|
||||
if(children().contains(object)) {
|
||||
if (event->type() == QEvent::Show)
|
||||
{
|
||||
if (children().contains(object))
|
||||
{
|
||||
arrangeRollups();
|
||||
emit widgetRolled(qobject_cast<QWidget*>(object), true);
|
||||
}
|
||||
} else if(event->type() == QEvent::Hide) {
|
||||
if(children().contains(object)) {
|
||||
}
|
||||
else if (event->type() == QEvent::Hide)
|
||||
{
|
||||
if (children().contains(object))
|
||||
{
|
||||
arrangeRollups();
|
||||
emit widgetRolled(qobject_cast<QWidget*>(object), false);
|
||||
}
|
||||
} else if(event->type() == QEvent::WindowTitleChange) {
|
||||
if(children().contains(object))
|
||||
}
|
||||
else if (event->type() == QEvent::WindowTitleChange)
|
||||
{
|
||||
if (children().contains(object)) {
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
return QWidget::eventFilter(object, event);
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,22 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2015-2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// API for features //
|
||||
// //
|
||||
// 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_ROLLUPWIDGET_H
|
||||
#define INCLUDE_ROLLUPWIDGET_H
|
||||
|
||||
@ -8,9 +27,10 @@ class SDRGUI_API RollupWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RollupWidget(QWidget* parent = NULL);
|
||||
RollupWidget(QWidget* parent = nullptr);
|
||||
void setTitleColor(const QColor& c);
|
||||
void setHighlighted(bool highlighted);
|
||||
void setChannelWidget(bool channelWidget) { m_channelWidget = channelWidget; }
|
||||
|
||||
signals:
|
||||
void widgetRolled(QWidget* widget, bool rollDown);
|
||||
@ -52,6 +72,7 @@ protected:
|
||||
|
||||
private:
|
||||
static bool isRollupChild(QWidget *childWidget); //!< chidl is part of rollups (ex: not a dialog)
|
||||
bool m_channelWidget;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_ROLLUPWIDGET_H
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "device/deviceapi.h"
|
||||
#include "device/deviceuiset.h"
|
||||
#include "device/deviceenumerator.h"
|
||||
#include "feature/featureuiset.h"
|
||||
#include "gui/indicator.h"
|
||||
#include "gui/presetitem.h"
|
||||
#include "gui/commanditem.h"
|
||||
@ -45,6 +46,7 @@
|
||||
#include "gui/aboutdialog.h"
|
||||
#include "gui/rollupwidget.h"
|
||||
#include "gui/channelwindow.h"
|
||||
#include "gui/featurewindow.h"
|
||||
#include "gui/audiodialog.h"
|
||||
#include "gui/loggingdialog.h"
|
||||
#include "gui/deviceuserargsdialog.h"
|
||||
@ -144,24 +146,28 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
|
||||
removeDockWidget(ui->presetDock);
|
||||
removeDockWidget(ui->commandsDock);
|
||||
removeDockWidget(ui->channelDock);
|
||||
removeDockWidget(ui->featureDock);
|
||||
addDockWidget(Qt::LeftDockWidgetArea, ui->inputViewDock);
|
||||
addDockWidget(Qt::LeftDockWidgetArea, ui->spectraDisplayDock);
|
||||
addDockWidget(Qt::LeftDockWidgetArea, ui->presetDock);
|
||||
addDockWidget(Qt::LeftDockWidgetArea, ui->commandsDock);
|
||||
tabifyDockWidget(ui->presetDock, ui->commandsDock);
|
||||
addDockWidget(Qt::RightDockWidgetArea, ui->channelDock);
|
||||
addDockWidget(Qt::RightDockWidgetArea, ui->featureDock);
|
||||
|
||||
ui->inputViewDock->show();
|
||||
ui->spectraDisplayDock->show();
|
||||
ui->presetDock->show();
|
||||
ui->commandsDock->show();
|
||||
ui->channelDock->show();
|
||||
ui->featureDock->show();
|
||||
|
||||
ui->menu_Window->addAction(ui->inputViewDock->toggleViewAction());
|
||||
ui->menu_Window->addAction(ui->spectraDisplayDock->toggleViewAction());
|
||||
ui->menu_Window->addAction(ui->presetDock->toggleViewAction());
|
||||
ui->menu_Window->addAction(ui->commandsDock->toggleViewAction());
|
||||
ui->menu_Window->addAction(ui->channelDock->toggleViewAction());
|
||||
ui->menu_Window->addAction(ui->featureDock->toggleViewAction());
|
||||
|
||||
ui->tabInputsView->setStyleSheet("QWidget { background: rgb(50,50,50); } "
|
||||
"QToolButton::checked { background: rgb(128,70,0); } "
|
||||
@ -185,6 +191,8 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
|
||||
splash->showStatusMessage("load settings...", Qt::white);
|
||||
qDebug() << "MainWindow::MainWindow: load settings...";
|
||||
|
||||
loadSettings();
|
||||
|
||||
splash->showStatusMessage("load plugins...", Qt::white);
|
||||
qDebug() << "MainWindow::MainWindow: load plugins...";
|
||||
|
||||
@ -192,8 +200,11 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
|
||||
m_pluginManager->loadPlugins(QString("plugins"));
|
||||
m_pluginManager->loadPluginsNonDiscoverable(m_settings.getDeviceUserArgs());
|
||||
|
||||
// Load settings after plugins have loaded, as they can include misc plugin settings
|
||||
loadSettings();
|
||||
splash->showStatusMessage("load initial feature set...", Qt::white);
|
||||
QStringList featureNames;
|
||||
m_pluginManager->listFeatures(featureNames);
|
||||
ui->featureDock->addAvailableFeatures(featureNames);
|
||||
addFeatureSet();
|
||||
|
||||
splash->showStatusMessage("load file input...", Qt::white);
|
||||
qDebug() << "MainWindow::MainWindow: select SampleSource from settings or default (file input)...";
|
||||
@ -218,6 +229,7 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
|
||||
connect(ui->tabChannels, SIGNAL(currentChanged(int)), this, SLOT(tabChannelsIndexChanged()));
|
||||
connect(ui->channelDock, SIGNAL(addChannel(int)), this, SLOT(channelAddClicked(int)));
|
||||
connect(ui->inputViewDock, SIGNAL(deviceChanged(int, int, int)), this, SLOT(samplingDeviceChanged(int, int, int)));
|
||||
connect(ui->featureDock, SIGNAL(addFeature(int)), this, SLOT(featureAddClicked(int)));
|
||||
|
||||
QString applicationDirPath = qApp->applicationDirPath();
|
||||
|
||||
@ -256,13 +268,6 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
qDebug() << "MainWindow::MainWindow: end";
|
||||
}
|
||||
|
||||
@ -277,6 +282,8 @@ MainWindow::~MainWindow()
|
||||
delete m_dateTimeWidget;
|
||||
delete m_showSystemWidget;
|
||||
|
||||
removeAllFeatureSets();
|
||||
|
||||
delete ui;
|
||||
|
||||
qDebug() << "MainWindow::~MainWindow: end";
|
||||
@ -555,6 +562,31 @@ void MainWindow::removeLastDevice()
|
||||
m_deviceUIs.pop_back();
|
||||
}
|
||||
|
||||
void MainWindow::addFeatureSet()
|
||||
{
|
||||
int tabIndex = m_featureUIs.size();
|
||||
m_featureUIs.push_back(new FeatureUISet(tabIndex));
|
||||
ui->tabFeatures->addTab(m_featureUIs.back()->m_featureWindow, QString("F%1").arg(tabIndex));
|
||||
}
|
||||
|
||||
void MainWindow::removeFeatureSet(int tabIndex)
|
||||
{
|
||||
if (tabIndex < m_featureUIs.size())
|
||||
{
|
||||
delete m_featureUIs[tabIndex];
|
||||
m_featureUIs.erase(m_featureUIs.begin() + tabIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::removeAllFeatureSets()
|
||||
{
|
||||
while (m_featureUIs.size() > 0)
|
||||
{
|
||||
delete m_featureUIs.back();
|
||||
m_featureUIs.erase(std::prev(m_featureUIs.end()));
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::deleteChannel(int deviceSetIndex, int channelIndex)
|
||||
{
|
||||
if ((deviceSetIndex >= 0) && (deviceSetIndex < (int) m_deviceUIs.size()))
|
||||
@ -1856,6 +1888,19 @@ void MainWindow::channelAddClicked(int channelIndex)
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::featureAddClicked(int featureIndex)
|
||||
{
|
||||
// Do it in the currently selected source tab
|
||||
int currentFeatureTabIndex = ui->tabFeatures->currentIndex();
|
||||
qDebug("MainWindow::featureAddClicked: tab: %d index: %d", currentFeatureTabIndex, featureIndex);
|
||||
|
||||
if (currentFeatureTabIndex >= 0)
|
||||
{
|
||||
FeatureUISet *featureUISet = m_featureUIs[currentFeatureTabIndex];
|
||||
m_pluginManager->createFeatureInstance(featureIndex, featureUISet, m_apiAdapter);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_action_About_triggered()
|
||||
{
|
||||
AboutDialog dlg(m_apiHost, m_apiPort, m_settings, this);
|
||||
|
@ -38,13 +38,13 @@ class DSPDeviceSourceEngine;
|
||||
class DSPDeviceSinkEngine;
|
||||
class Indicator;
|
||||
class GLSpectrumGUI;
|
||||
class ChannelWindow;
|
||||
class PluginAPI;
|
||||
class PluginInstanceGUI;
|
||||
class ChannelMarker;
|
||||
class PluginManager;
|
||||
class DeviceAPI;
|
||||
class DeviceUISet;
|
||||
class FeatureUISet;
|
||||
class PluginInterface;
|
||||
class QWidget;
|
||||
class WebAPIRequestMapper;
|
||||
@ -319,6 +319,7 @@ private:
|
||||
MessageQueue m_inputMessageQueue;
|
||||
MainSettings m_settings;
|
||||
std::vector<DeviceUISet*> m_deviceUIs;
|
||||
std::vector<FeatureUISet*> m_featureUIs;
|
||||
QList<DeviceWidgetTabData> m_deviceWidgetTabs;
|
||||
int m_masterTabIndex;
|
||||
|
||||
@ -363,6 +364,9 @@ private:
|
||||
void addSourceDevice(int deviceIndex);
|
||||
void addSinkDevice();
|
||||
void removeLastDevice();
|
||||
void addFeatureSet();
|
||||
void removeFeatureSet(int tabIndex);
|
||||
void removeAllFeatureSets();
|
||||
void deleteChannel(int deviceSetIndex, int channelIndex);
|
||||
void sampleSourceChanged(int tabIndex, int newDeviceIndex);
|
||||
void sampleSinkChanged(int tabIndex, int newDeviceIndex);
|
||||
@ -401,6 +405,7 @@ private slots:
|
||||
void on_action_DeviceUserArguments_triggered();
|
||||
void samplingDeviceChanged(int deviceType, int tabIndex, int newDeviceIndex);
|
||||
void channelAddClicked(int channelIndex);
|
||||
void featureAddClicked(int featureIndex);
|
||||
void on_action_Loaded_Plugins_triggered();
|
||||
void on_action_About_triggered();
|
||||
void on_action_addSourceDevice_triggered();
|
||||
|
@ -691,6 +691,43 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="FeaturesDock" name="featureDock">
|
||||
<property name="windowTitle">
|
||||
<string>Features</string>
|
||||
</property>
|
||||
<attribute name="dockWidgetArea">
|
||||
<number>2</number>
|
||||
</attribute>
|
||||
<widget class="QWidget" name="featureWidgetContents">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabFeatures">
|
||||
<property name="tabPosition">
|
||||
<enum>QTabWidget::East</enum>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<action name="action_Exit">
|
||||
<property name="text">
|
||||
<string>E&xit</string>
|
||||
@ -906,6 +943,7 @@
|
||||
<zorder>presetDock</zorder>
|
||||
<zorder>channelDock</zorder>
|
||||
<zorder>commandsDock</zorder>
|
||||
<zorder>featureDock</zorder>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<customwidgets>
|
||||
@ -926,6 +964,12 @@
|
||||
<header>gui/samplingdevicesdock.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>FeaturesDock</class>
|
||||
<extends>QDockWidget</extends>
|
||||
<header>gui/featuresdock.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>presetTree</tabstop>
|
||||
|
@ -40,10 +40,6 @@ public:
|
||||
~DeviceSet();
|
||||
|
||||
int getNumberOfChannels() const { return m_channelInstanceRegistrations.size(); }
|
||||
void addRxChannel(int selectedChannelIndex, PluginAPI *pluginAPI);
|
||||
void addTxChannel(int selectedChannelIndex, PluginAPI *pluginAPI);
|
||||
void addMIMOChannel(int selectedChannelIndex, PluginAPI *pluginAPI);
|
||||
void deleteChannel(int channelIndex);
|
||||
void registerRxChannelInstance(const QString& channelName, ChannelAPI* channelAPI);
|
||||
void registerTxChannelInstance(const QString& channelName, ChannelAPI* channelAPI);
|
||||
void registerChannelInstance(const QString& channelName, ChannelAPI* channelAPI);
|
||||
@ -51,12 +47,16 @@ public:
|
||||
void removeTxChannelInstance(ChannelAPI* channelAPI);
|
||||
void removeChannelInstance(ChannelAPI* channelAPI);
|
||||
void freeChannels();
|
||||
void deleteChannel(int channelIndex);
|
||||
void loadRxChannelSettings(const Preset* preset, PluginAPI *pluginAPI);
|
||||
void saveRxChannelSettings(Preset* preset);
|
||||
void loadTxChannelSettings(const Preset* preset, PluginAPI *pluginAPI);
|
||||
void saveTxChannelSettings(Preset* preset);
|
||||
void loadMIMOChannelSettings(const Preset* preset, PluginAPI *pluginAPI);
|
||||
void saveMIMOChannelSettings(Preset* preset);
|
||||
void addRxChannel(int selectedChannelIndex, PluginAPI *pluginAPI);
|
||||
void addTxChannel(int selectedChannelIndex, PluginAPI *pluginAPI);
|
||||
void addMIMOChannel(int selectedChannelIndex, PluginAPI *pluginAPI);
|
||||
|
||||
private:
|
||||
struct ChannelInstanceRegistration
|
||||
|
Loading…
Reference in New Issue
Block a user