Add Remote TCP Input and Remote TCP Sink plugins

This commit is contained in:
Jon Beniston 2022-07-19 10:10:20 +01:00
parent 48edaecfbc
commit 27da167b97
66 changed files with 10759 additions and 55 deletions

View File

@ -67,6 +67,7 @@ option(ENABLE_CHANNELRX_FILESINK "Enable channelrx filesink plugin" ON)
option(ENABLE_CHANNELRX_DEMODFREEDV "Enable channelrx demodfreedv plugin" ON)
option(ENABLE_CHANNELRX_DEMODCHIRPCHAT "Enable channelrx demodchirpchat plugin" ON)
option(ENABLE_CHANNELRX_REMOTESINK "Enable channelrx remotesink plugin" ON)
option(ENABLE_CHANNELRX_REMOTETCPSINK "Enable channelrx remotetcpsink plugin" ON)
option(ENABLE_CHANNELRX_DEMODSSB "Enable channelrx demodssb plugin" ON)
option(ENABLE_CHANNELRX_CHANALYZER "Enable channelrx chanalyzer plugin" ON)
option(ENABLE_CHANNELRX_SIGMFFILESINK "Enable channelrx sigmffilesink plugin" ON)

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
doc/img/RemoteTCPSink.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -89,6 +89,10 @@ if (ENABLE_CHANNELRX_REMOTESINK AND CM256CC_FOUND AND (HAS_SSE3 OR HAS_NEON))
add_subdirectory(remotesink)
endif()
if (ENABLE_CHANNELRX_REMOTETCPSINK)
add_subdirectory(remotetcpsink)
endif()
if (ENABLE_CHANNELRX_DEMODFREEDV AND CODEC2_FOUND)
add_subdirectory(demodfreedv)
endif()

View File

@ -0,0 +1,64 @@
project(remotetcpsink)
set(remotetcpsink_SOURCES
remotetcpsink.cpp
remotetcpsinkbaseband.cpp
remotetcpsinksink.cpp
remotetcpsinksettings.cpp
remotetcpsinkwebapiadapter.cpp
remotetcpsinkplugin.cpp
)
set(remotetcpsink_HEADERS
remotetcpsink.h
remotetcpsinkbaseband.h
remotetcpsinksink.h
remotetcpsinksettings.h
remotetcpsinkwebapiadapter.h
remotetcpsinkplugin.h
remotetcpprotocol.h
)
include_directories(
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
)
if(NOT SERVER_MODE)
set(remotetcpsink_SOURCES
${remotetcpsink_SOURCES}
remotetcpsinkgui.cpp
remotetcpsinkgui.ui
)
set(remotetcpsink_HEADERS
${remotetcpsink_HEADERS}
remotetcpsinkgui.h
)
set(TARGET_NAME remotetcpsink)
set(TARGET_LIB "Qt5::Widgets")
set(TARGET_LIB_GUI "sdrgui")
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
else()
set(TARGET_NAME remotetcpsinksrv)
set(TARGET_LIB "")
set(TARGET_LIB_GUI "")
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
endif()
add_library(${TARGET_NAME} SHARED
${remotetcpsink_SOURCES}
)
target_link_libraries(${TARGET_NAME}
Qt5::Core
${TARGET_LIB}
sdrbase
${TARGET_LIB_GUI}
swagger
)
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
# Install debug symbols
if (WIN32)
install(FILES $<TARGET_PDB_FILE:${TARGET_NAME}> CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} )
endif()

View File

@ -0,0 +1,44 @@
<h1>Remote TCP sink channel plugin</h1>
<h2>Introduction</h2>
This plugin sends I/Q samples from the baseband via TCP/IP across a network to a client application.
The client application could be SDRangel using the RemoteTCPInput plugin or a rtl_tcp compatible application.
This means that applications using rtl_tcp protcol can connect to the wide variety of SDRs supported by SDRangel.
<h2>Interface</h2>
![Remote TCP sink channel plugin GUI](../../../doc/img/RemoteTCPSink.png)
<h3>1: Frequency shift from center frequency of reception</h3>
This is the shift of the channel center frequency from the device center frequency.
This is used to select the desired part of the signal when the channel sample rate is lower than the baseband sample rate.
<h3>2: Gain</h3>
Sets a gain figure in dB that is applied to I/Q samples before transmission via TCP/IP.
This option may be useful for amplifying very small signals from SDRs with high-dynamic range (E.g. 24-bits), when the network sample bit-depth is 8-bits.
<h3>3: Sample rate</h3>
Specifies the channel and network sample rate in samples per second. If this is different from the baseband sample rate, the baseband signal will be decimated to the specified rate.
<h3>4: Sample bit depth</h3>
Specifies number of bits per I/Q sample transmitted via TCP/IP.
<h3>5: IP address</h3>
IP address of the local network interface on which the server will listen for TCP/IP connections from network clients.
<h3>6: Port</h3>
TCP port on which the server will listen for connections.
<h3>7: Protocol</h3>
Specifies the protocol used for sending IQ samples and metadata to clients via TCP/IP.
- RTL0: Compatible with rtl_tcp - limited to 8-bit IQ data.
- SDRA: Enhanced version of protocol that allows device settings to be sent to clients and for higher bit depths to be used (8, 16, 24 and 32).

View File

@ -0,0 +1,171 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2018 Edouard Griffiths, F4EXB //
// Copyright (C) 2022 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 PLUGINS_CHANNELRX_REMOTETCPSINK_REMOTETCPPROTOCOL_H_
#define PLUGINS_CHANNELRX_REMOTETCPSINK_REMOTETCPPROTOCOL_H_
#include <QString>
// Remote TCP protocol based on rtl_tcp (for compatibility) with a few extensions (as SDRangel supports more SDRs)
class RemoteTCPProtocol
{
public:
enum Device {
// These are compatible with rtl_tcp
UNKNOWN = 0,
RTLSDR_E4000,
RTLSDR_FC0012,
RTLSDR_FC0013,
RTLSDR_FC2580,
RTLSDR_R820T, // Used by rsp_tcp
RTLSDR_R828D,
// SDRangel extensions that correspond to sample source devices
AIRSPY = 0x80,
AIRSPY_HF,
AUDIO_INPUT,
BLADE_RF1,
BLADE_RF2,
FCD_PRO,
FCD_PRO_PLUS,
FILE_INPUT,
HACK_RF,
KIWI_SDR,
LIME_SDR,
LOCAL_INPUT,
PERSEUS,
PLUTO_SDR,
REMOTE_INPUT,
REMOTE_TCP_INPUT,
SDRPLAY_1,
SDRPLAY_V3_RSP1,
SDRPLAY_V3_RSP1A,
SDRPLAY_V3_RSP2,
SDRPLAY_V3_RSPDUO,
SDRPLAY_V3_RSPDX,
SIGMF_FILE_INPUT,
SOAPY_SDR,
TEST_SOURCE,
USRP,
XTRX
};
enum Command {
// These are compatbile with osmocom rtl_tcp: https://github.com/osmocom/rtl-sdr/blob/master/src/rtl_tcp.c
setCenterFrequency = 0x1, // rtlsdr_set_center_freq
setSampleRate = 0x2, // rtlsdr_set_sample_rate
setTunerGainMode = 0x3, // rtlsdr_set_tuner_gain_mode
setTunerGain = 0x4, // rtlsdr_set_tuner_gain
setFrequencyCorrection = 0x5, // rtlsdr_set_freq_correction
setTunerIFGain = 0x6, // rtlsdr_set_tuner_if_gain - Used by SDRangel to set LNA/VGA/IF gain individually
setTestMode = 0x7, // Not supported by SDRangel
setAGCMode = 0x8, // rtlsdr_set_agc_mode
setDirectSampling = 0x9, // rtlsdr_set_direct_sampling
setOffsetTuning = 0xa,
setXtalFrequency = 0xb, // Not supported by SDRangel
setXtalFrequency2 = 0xc, // Not supported by SDRangel
setGainByIndex = 0xd, // Not supported by SDRangel
setBiasTee = 0xe, // rtlsdr_set_bias_tee
// These extensions are from librtlsdr rtl_tcp: https://github.com/librtlsdr/librtlsdr/blob/development/include/rtl_tcp.h
setTunerBandwidth = 0x40,
// These are SDRangel extensions
setDCOffsetRemoval = 0xc0,
setIQCorrection = 0xc1,
setDecimation = 0xc2, // Device to baseband decimation
setChannelSampleRate = 0xc3,
setChannelFreqOffset = 0xc4,
setChannelGain = 0xc5,
setSampleBitDepth = 0xc6, // Bit depth for samples sent over network
//setAntenna?
//setLOOffset?
};
static const int m_rtl0MetaDataSize = 12;
static const int m_sdraMetaDataSize = 64;
static void encodeInt16(quint8 *p, qint16 data)
{
p[0] = (data >> 8) & 0xff;
p[1] = data & 0xff;
}
static void encodeUInt32(quint8 *p, quint32 data)
{
p[0] = (data >> 24) & 0xff;
p[1] = (data >> 16) & 0xff;
p[2] = (data >> 8) & 0xff;
p[3] = data & 0xff;
}
static void encodeInt32(quint8 *p, qint32 data)
{
encodeUInt32(p, (quint32)data);
}
static qint16 extractInt16(quint8 *p)
{
qint16 data;
data = (p[1] & 0xff)
| ((p[0] & 0xff) << 8);
return data;
}
static quint32 extractUInt32(quint8 *p)
{
quint32 data;
data = (p[3] & 0xff)
| ((p[2] & 0xff) << 8)
| ((p[1] & 0xff) << 16)
| ((p[0] & 0xff) << 24);
return data;
}
static qint32 extractInt32(quint8 *p)
{
return (qint32)extractUInt32(p);
}
static void encodeUInt64(quint8 *p, quint64 data)
{
p[0] = (data >> 56) & 0xff;
p[1] = (data >> 48) & 0xff;
p[2] = (data >> 40) & 0xff;
p[3] = (data >> 32) & 0xff;
p[4] = (data >> 24) & 0xff;
p[5] = (data >> 16) & 0xff;
p[6] = (data >> 8) & 0xff;
p[7] = data & 0xff;
}
static quint64 extractUInt64(quint8 *p)
{
quint64 data;
data = (p[7] & 0xff)
| ((p[6] & 0xff) << 8)
| ((p[5] & 0xff) << 16)
| ((p[4] & 0xff) << 24)
| (((quint64)(p[3] & 0xff)) << 32)
| (((quint64)(p[2] & 0xff)) << 40)
| (((quint64)(p[1] & 0xff)) << 48)
| (((quint64)(p[0] & 0xff)) << 56);
return data;
}
};
#endif /* PLUGINS_CHANNELRX_REMOTETCPSINK_REMOTETCPPROTOCOL_H_ */

View File

@ -0,0 +1,614 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2018 Edouard Griffiths, F4EXB. //
// Copyright (C) 2022 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 "remotetcpsink.h"
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QBuffer>
#include <QThread>
#include "SWGChannelSettings.h"
#include "SWGWorkspaceInfo.h"
#include "util/simpleserializer.h"
#include "dsp/dspcommands.h"
#include "dsp/devicesamplemimo.h"
#include "dsp/dspdevicesourceengine.h"
#include "dsp/devicesamplesource.h"
#include "device/deviceapi.h"
#include "feature/feature.h"
#include "settings/serializable.h"
#include "maincore.h"
#include "remotetcpsinkbaseband.h"
MESSAGE_CLASS_DEFINITION(RemoteTCPSink::MsgConfigureRemoteTCPSink, Message)
MESSAGE_CLASS_DEFINITION(RemoteTCPSink::MsgReportConnection, Message)
MESSAGE_CLASS_DEFINITION(RemoteTCPSink::MsgReportBW, Message)
const char* const RemoteTCPSink::m_channelIdURI = "sdrangel.channel.remotetcpsink";
const char* const RemoteTCPSink::m_channelId = "RemoteTCPSink";
RemoteTCPSink::RemoteTCPSink(DeviceAPI *deviceAPI) :
ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink),
m_deviceAPI(deviceAPI),
m_basebandSampleRate(0)
{
setObjectName(m_channelId);
m_basebandSink = new RemoteTCPSinkBaseband();
m_basebandSink->setMessageQueueToChannel(&m_inputMessageQueue);
m_basebandSink->moveToThread(&m_thread);
applySettings(m_settings, true);
m_deviceAPI->addChannelSink(this);
m_deviceAPI->addChannelSinkAPI(this);
m_networkManager = new QNetworkAccessManager();
QObject::connect(
m_networkManager,
&QNetworkAccessManager::finished,
this,
&RemoteTCPSink::networkManagerFinished
);
QObject::connect(
this,
&ChannelAPI::indexInDeviceSetChanged,
this,
&RemoteTCPSink::handleIndexInDeviceSetChanged
);
}
RemoteTCPSink::~RemoteTCPSink()
{
qDebug("RemoteTCPSinkBaseband::~RemoteTCPSink");
QObject::disconnect(
m_networkManager,
&QNetworkAccessManager::finished,
this,
&RemoteTCPSink::networkManagerFinished
);
delete m_networkManager;
m_deviceAPI->removeChannelSinkAPI(this);
m_deviceAPI->removeChannelSink(this);
if (m_basebandSink->isRunning()) {
stop();
}
m_basebandSink->deleteLater();
}
void RemoteTCPSink::setDeviceAPI(DeviceAPI *deviceAPI)
{
if (deviceAPI != m_deviceAPI)
{
m_deviceAPI->removeChannelSinkAPI(this);
m_deviceAPI->removeChannelSink(this);
m_deviceAPI = deviceAPI;
m_deviceAPI->addChannelSink(this);
m_deviceAPI->addChannelSinkAPI(this);
}
}
uint32_t RemoteTCPSink::getNumberOfDeviceStreams() const
{
return m_deviceAPI->getNbSourceStreams();
}
void RemoteTCPSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst)
{
(void) firstOfBurst;
m_basebandSink->feed(begin, end);
}
void RemoteTCPSink::start()
{
qDebug("RemoteTCPSink::start: m_basebandSampleRate: %d", m_basebandSampleRate);
m_basebandSink->reset();
m_basebandSink->setDeviceIndex(m_deviceAPI->getDeviceSetIndex());
m_basebandSink->setChannelIndex(getIndexInDeviceSet());
m_basebandSink->startWork();
m_thread.start();
if (m_basebandSampleRate != 0) {
m_basebandSink->setBasebandSampleRate(m_basebandSampleRate);
}
}
void RemoteTCPSink::stop()
{
qDebug("RemoteTCPSink::stop");
m_basebandSink->stopWork();
m_thread.quit();
m_thread.wait();
}
bool RemoteTCPSink::handleMessage(const Message& cmd)
{
if (MsgConfigureRemoteTCPSink::match(cmd))
{
MsgConfigureRemoteTCPSink& cfg = (MsgConfigureRemoteTCPSink&) cmd;
qDebug() << "RemoteTCPSink::handleMessage: MsgConfigureRemoteTCPSink";
applySettings(cfg.getSettings(), cfg.getForce(), cfg.getRemoteChange());
return true;
}
else if (DSPSignalNotification::match(cmd))
{
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
m_basebandSampleRate = notif.getSampleRate();
qDebug() << "RemoteTCPSink::handleMessage: DSPSignalNotification: m_basebandSampleRate:" << m_basebandSampleRate;
// Forward to the sink
m_basebandSink->getInputMessageQueue()->push(new DSPSignalNotification(notif));
// Forward to the GUI
if (getMessageQueueToGUI()) {
getMessageQueueToGUI()->push(new DSPSignalNotification(notif));
}
return true;
}
else
{
return false;
}
}
QByteArray RemoteTCPSink::serialize() const
{
return m_settings.serialize();
}
bool RemoteTCPSink::deserialize(const QByteArray& data)
{
(void) data;
if (m_settings.deserialize(data))
{
MsgConfigureRemoteTCPSink *msg = MsgConfigureRemoteTCPSink::create(m_settings, true);
m_inputMessageQueue.push(msg);
return true;
}
else
{
m_settings.resetToDefaults();
MsgConfigureRemoteTCPSink *msg = MsgConfigureRemoteTCPSink::create(m_settings, true);
m_inputMessageQueue.push(msg);
return false;
}
}
void RemoteTCPSink::setCenterFrequency(qint64 frequency)
{
RemoteTCPSinkSettings settings = m_settings;
settings.m_inputFrequencyOffset = frequency;
applySettings(settings, false);
if (m_guiMessageQueue) // forward to GUI if any
{
MsgConfigureRemoteTCPSink *msgToGUI = MsgConfigureRemoteTCPSink::create(settings, false);
m_guiMessageQueue->push(msgToGUI);
}
}
void RemoteTCPSink::applySettings(const RemoteTCPSinkSettings& settings, bool force, bool remoteChange)
{
qDebug() << "RemoteTCPSink::applySettings:"
<< " m_channelSampleRate: " << settings.m_channelSampleRate
<< " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset
<< " m_gain: " << settings.m_gain
<< " m_sampleBits: " << settings.m_sampleBits
<< " m_dataAddress: " << settings.m_dataAddress
<< " m_dataPort: " << settings.m_dataPort
<< " m_protocol: " << settings.m_protocol
<< " m_protocol: " << settings.m_streamIndex
<< " force: " << force
<< " remoteChange: " << remoteChange;
QList<QString> reverseAPIKeys;
if ((m_settings.m_channelSampleRate != settings.m_channelSampleRate) || force) {
reverseAPIKeys.append("m_channelSampleRate");
}
if ((m_settings.m_inputFrequencyOffset != settings.m_inputFrequencyOffset) || force) {
reverseAPIKeys.append("inputFrequencyOffset");
}
if ((m_settings.m_gain != settings.m_gain) || force) {
reverseAPIKeys.append("gain");
}
if ((m_settings.m_sampleBits != settings.m_sampleBits) || force) {
reverseAPIKeys.append("sampleBits");
}
if ((m_settings.m_dataAddress != settings.m_dataAddress) || force) {
reverseAPIKeys.append("dataAddress");
}
if ((m_settings.m_dataPort != settings.m_dataPort) || force) {
reverseAPIKeys.append("dataPort");
}
if ((m_settings.m_protocol != settings.m_protocol) || force) {
reverseAPIKeys.append("protocol");
}
if ((m_settings.m_rgbColor != settings.m_rgbColor) || force) {
reverseAPIKeys.append("rgbColor");
}
if ((m_settings.m_title != settings.m_title) || force) {
reverseAPIKeys.append("title");
}
if (m_settings.m_streamIndex != settings.m_streamIndex)
{
if (m_deviceAPI->getSampleMIMO()) // change of stream is possible for MIMO devices only
{
m_deviceAPI->removeChannelSinkAPI(this);
m_deviceAPI->removeChannelSink(this, m_settings.m_streamIndex);
m_deviceAPI->addChannelSink(this, settings.m_streamIndex);
m_deviceAPI->addChannelSinkAPI(this);
}
reverseAPIKeys.append("streamIndex");
}
MsgConfigureRemoteTCPSink *msg = MsgConfigureRemoteTCPSink::create(settings, force, remoteChange);
m_basebandSink->getInputMessageQueue()->push(msg);
if ((settings.m_useReverseAPI) && (reverseAPIKeys.size() != 0))
{
bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) ||
(m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) ||
(m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) ||
(m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex) ||
(m_settings.m_reverseAPIChannelIndex != settings.m_reverseAPIChannelIndex);
webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force);
}
QList<ObjectPipe*> pipes;
MainCore::instance()->getMessagePipes().getMessagePipes(this, "settings", pipes);
if (pipes.size() > 0) {
sendChannelSettings(pipes, reverseAPIKeys, settings, force);
}
m_settings = settings;
}
int RemoteTCPSink::webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setRemoteTcpSinkSettings(new SWGSDRangel::SWGRemoteTCPSinkSettings());
response.getRemoteTcpSinkSettings()->init();
webapiFormatChannelSettings(response, m_settings);
return 200;
}
int RemoteTCPSink::webapiWorkspaceGet(
SWGSDRangel::SWGWorkspaceInfo& response,
QString& errorMessage)
{
(void) errorMessage;
response.setIndex(m_settings.m_workspaceIndex);
return 200;
}
int RemoteTCPSink::webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
RemoteTCPSinkSettings settings = m_settings;
webapiUpdateChannelSettings(settings, channelSettingsKeys, response);
MsgConfigureRemoteTCPSink *msg = MsgConfigureRemoteTCPSink::create(settings, force);
m_inputMessageQueue.push(msg);
qDebug("RemoteTCPSink::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue);
if (m_guiMessageQueue) // forward to GUI if any
{
MsgConfigureRemoteTCPSink *msgToGUI = MsgConfigureRemoteTCPSink::create(settings, force);
m_guiMessageQueue->push(msgToGUI);
}
webapiFormatChannelSettings(response, settings);
return 200;
}
void RemoteTCPSink::webapiUpdateChannelSettings(
RemoteTCPSinkSettings& settings,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response)
{
if (channelSettingsKeys.contains("channelSampleRate")) {
settings.m_channelSampleRate = response.getRemoteTcpSinkSettings()->getChannelSampleRate();
}
if (channelSettingsKeys.contains("inputFrequencyOffset")) {
settings.m_inputFrequencyOffset = response.getRemoteTcpSinkSettings()->getInputFrequencyOffset();
}
if (channelSettingsKeys.contains("gain")) {
settings.m_gain = response.getRemoteTcpSinkSettings()->getGain();
}
if (channelSettingsKeys.contains("sampleBits")) {
settings.m_sampleBits = response.getRemoteTcpSinkSettings()->getSampleBits();
}
if (channelSettingsKeys.contains("dataAddress")) {
settings.m_dataAddress = *response.getRemoteTcpSinkSettings()->getDataAddress();
}
if (channelSettingsKeys.contains("dataPort"))
{
int dataPort = response.getRemoteTcpSinkSettings()->getDataPort();
if ((dataPort < 1024) || (dataPort > 65535)) {
settings.m_dataPort = 9090;
} else {
settings.m_dataPort = dataPort;
}
}
if (channelSettingsKeys.contains("protocol")) {
settings.m_protocol = (RemoteTCPSinkSettings::Protocol)response.getRemoteTcpSinkSettings()->getProtocol();
}
if (channelSettingsKeys.contains("rgbColor")) {
settings.m_rgbColor = response.getRemoteTcpSinkSettings()->getRgbColor();
}
if (channelSettingsKeys.contains("title")) {
settings.m_title = *response.getRemoteTcpSinkSettings()->getTitle();
}
if (channelSettingsKeys.contains("streamIndex")) {
settings.m_streamIndex = response.getRemoteTcpSinkSettings()->getStreamIndex();
}
if (channelSettingsKeys.contains("useReverseAPI")) {
settings.m_useReverseAPI = response.getRemoteTcpSinkSettings()->getUseReverseApi() != 0;
}
if (channelSettingsKeys.contains("reverseAPIAddress")) {
settings.m_reverseAPIAddress = *response.getRemoteTcpSinkSettings()->getReverseApiAddress();
}
if (channelSettingsKeys.contains("reverseAPIPort")) {
settings.m_reverseAPIPort = response.getRemoteTcpSinkSettings()->getReverseApiPort();
}
if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) {
settings.m_reverseAPIDeviceIndex = response.getRemoteTcpSinkSettings()->getReverseApiDeviceIndex();
}
if (channelSettingsKeys.contains("reverseAPIChannelIndex")) {
settings.m_reverseAPIChannelIndex = response.getRemoteTcpSinkSettings()->getReverseApiChannelIndex();
}
if (settings.m_channelMarker && channelSettingsKeys.contains("channelMarker")) {
settings.m_channelMarker->updateFrom(channelSettingsKeys, response.getRemoteTcpSinkSettings()->getChannelMarker());
}
if (settings.m_rollupState && channelSettingsKeys.contains("rollupState")) {
settings.m_rollupState->updateFrom(channelSettingsKeys, response.getRemoteTcpSinkSettings()->getRollupState());
}
}
void RemoteTCPSink::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const RemoteTCPSinkSettings& settings)
{
response.getRemoteTcpSinkSettings()->setChannelSampleRate(settings.m_channelSampleRate);
response.getRemoteTcpSinkSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
response.getRemoteTcpSinkSettings()->setGain(settings.m_gain);
response.getRemoteTcpSinkSettings()->setSampleBits(settings.m_sampleBits);
if (response.getRemoteTcpSinkSettings()->getDataAddress()) {
*response.getRemoteTcpSinkSettings()->getDataAddress() = settings.m_dataAddress;
} else {
response.getRemoteTcpSinkSettings()->setDataAddress(new QString(settings.m_dataAddress));
}
response.getRemoteTcpSinkSettings()->setDataPort(settings.m_dataPort);
response.getRemoteTcpSinkSettings()->setProtocol((int)settings.m_protocol);
response.getRemoteTcpSinkSettings()->setRgbColor(settings.m_rgbColor);
if (response.getRemoteTcpSinkSettings()->getTitle()) {
*response.getRemoteTcpSinkSettings()->getTitle() = settings.m_title;
} else {
response.getRemoteTcpSinkSettings()->setTitle(new QString(settings.m_title));
}
response.getRemoteTcpSinkSettings()->setStreamIndex(settings.m_streamIndex);
response.getRemoteTcpSinkSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
if (response.getRemoteTcpSinkSettings()->getReverseApiAddress()) {
*response.getRemoteTcpSinkSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
} else {
response.getRemoteTcpSinkSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
}
response.getRemoteTcpSinkSettings()->setReverseApiPort(settings.m_reverseAPIPort);
response.getRemoteTcpSinkSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex);
response.getRemoteTcpSinkSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex);
if (settings.m_channelMarker)
{
if (response.getRemoteTcpSinkSettings()->getChannelMarker())
{
settings.m_channelMarker->formatTo(response.getRemoteTcpSinkSettings()->getChannelMarker());
}
else
{
SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker();
settings.m_channelMarker->formatTo(swgChannelMarker);
response.getRemoteTcpSinkSettings()->setChannelMarker(swgChannelMarker);
}
}
if (settings.m_rollupState)
{
if (response.getRemoteTcpSinkSettings()->getRollupState())
{
settings.m_rollupState->formatTo(response.getRemoteTcpSinkSettings()->getRollupState());
}
else
{
SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState();
settings.m_rollupState->formatTo(swgRollupState);
response.getRemoteTcpSinkSettings()->setRollupState(swgRollupState);
}
}
}
void RemoteTCPSink::webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const RemoteTCPSinkSettings& settings, bool force)
{
SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings();
webapiFormatChannelSettings(channelSettingsKeys, swgChannelSettings, settings, force);
QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings")
.arg(settings.m_reverseAPIAddress)
.arg(settings.m_reverseAPIPort)
.arg(settings.m_reverseAPIDeviceIndex)
.arg(settings.m_reverseAPIChannelIndex);
m_networkRequest.setUrl(QUrl(channelSettingsURL));
m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QBuffer *buffer = new QBuffer();
buffer->open((QBuffer::ReadWrite));
buffer->write(swgChannelSettings->asJson().toUtf8());
buffer->seek(0);
// Always use PATCH to avoid passing reverse API settings
QNetworkReply *reply = m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
buffer->setParent(reply);
delete swgChannelSettings;
}
void RemoteTCPSink::sendChannelSettings(
const QList<ObjectPipe*>& pipes,
QList<QString>& channelSettingsKeys,
const RemoteTCPSinkSettings& settings,
bool force)
{
for (const auto& pipe : pipes)
{
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
if (messageQueue)
{
SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings();
webapiFormatChannelSettings(channelSettingsKeys, swgChannelSettings, settings, force);
MainCore::MsgChannelSettings *msg = MainCore::MsgChannelSettings::create(
this,
channelSettingsKeys,
swgChannelSettings,
force
);
messageQueue->push(msg);
}
}
}
void RemoteTCPSink::webapiFormatChannelSettings(
QList<QString>& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings *swgChannelSettings,
const RemoteTCPSinkSettings& settings,
bool force
)
{
swgChannelSettings->setDirection(0); // Single sink (Rx)
swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet());
swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex());
swgChannelSettings->setChannelType(new QString(m_channelId));
swgChannelSettings->setRemoteTcpSinkSettings(new SWGSDRangel::SWGRemoteTCPSinkSettings());
SWGSDRangel::SWGRemoteTCPSinkSettings *swgRemoteTCPSinkSettings = swgChannelSettings->getRemoteTcpSinkSettings();
// transfer data that has been modified. When force is on transfer all data except reverse API data
if (channelSettingsKeys.contains("channelSampleRate") || force) {
swgRemoteTCPSinkSettings->setChannelSampleRate(settings.m_channelSampleRate);
}
if (channelSettingsKeys.contains("inputFrequencyOffset") || force) {
swgRemoteTCPSinkSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
}
if (channelSettingsKeys.contains("gain") || force) {
swgRemoteTCPSinkSettings->setGain(settings.m_gain);
}
if (channelSettingsKeys.contains("sampleBits") || force) {
swgRemoteTCPSinkSettings->setSampleBits(settings.m_sampleBits);
}
if (channelSettingsKeys.contains("dataAddress") || force) {
swgRemoteTCPSinkSettings->setDataAddress(new QString(settings.m_dataAddress));
}
if (channelSettingsKeys.contains("dataPort") || force) {
swgRemoteTCPSinkSettings->setDataPort(settings.m_dataPort);
}
if (channelSettingsKeys.contains("protocol") || force) {
swgRemoteTCPSinkSettings->setProtocol(settings.m_protocol);
}
if (channelSettingsKeys.contains("rgbColor") || force) {
swgRemoteTCPSinkSettings->setRgbColor(settings.m_rgbColor);
}
if (channelSettingsKeys.contains("title") || force) {
swgRemoteTCPSinkSettings->setTitle(new QString(settings.m_title));
}
if (channelSettingsKeys.contains("streamIndex") || force) {
swgRemoteTCPSinkSettings->setStreamIndex(settings.m_streamIndex);
}
if (settings.m_channelMarker && (channelSettingsKeys.contains("channelMarker") || force))
{
SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker();
settings.m_channelMarker->formatTo(swgChannelMarker);
swgRemoteTCPSinkSettings->setChannelMarker(swgChannelMarker);
}
if (settings.m_rollupState && (channelSettingsKeys.contains("rollupState") || force))
{
SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState();
settings.m_rollupState->formatTo(swgRollupState);
swgRemoteTCPSinkSettings->setRollupState(swgRollupState);
}
}
void RemoteTCPSink::networkManagerFinished(QNetworkReply *reply)
{
QNetworkReply::NetworkError replyError = reply->error();
if (replyError)
{
qWarning() << "RemoteTCPSink::networkManagerFinished:"
<< " error(" << (int) replyError
<< "): " << replyError
<< ": " << reply->errorString();
}
else
{
QString answer = reply->readAll();
answer.chop(1); // remove last \n
qDebug("RemoteTCPSink::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
}
reply->deleteLater();
}
void RemoteTCPSink::handleIndexInDeviceSetChanged(int index)
{
if (index < 0) {
return;
}
QString fifoLabel = QString("%1 [%2:%3]")
.arg(m_channelId)
.arg(m_deviceAPI->getDeviceSetIndex())
.arg(index);
m_basebandSink->setFifoLabel(fifoLabel);
}

View File

@ -0,0 +1,203 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2018 Edouard Griffiths, F4EXB. //
// Copyright (C) 2022 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_REMOTETCPSINK_H_
#define INCLUDE_REMOTETCPSINK_H_
#include <QObject>
#include <QNetworkRequest>
#include <QThread>
#include "dsp/basebandsamplesink.h"
#include "channel/channelapi.h"
#include "remotetcpsinkbaseband.h"
class QNetworkAccessManager;
class QNetworkReply;
class DeviceAPI;
class ObjectPipe;
class RemoteTCPSink : public BasebandSampleSink, public ChannelAPI {
public:
class MsgConfigureRemoteTCPSink : public Message {
MESSAGE_CLASS_DECLARATION
public:
const RemoteTCPSinkSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
bool getRemoteChange() const { return m_remoteChange; }
static MsgConfigureRemoteTCPSink* create(const RemoteTCPSinkSettings& settings, bool force, bool remoteChange = false)
{
return new MsgConfigureRemoteTCPSink(settings, force, remoteChange);
}
private:
RemoteTCPSinkSettings m_settings;
bool m_force;
bool m_remoteChange; // This change of settings was requested by a remote client, so no need to restart server
MsgConfigureRemoteTCPSink(const RemoteTCPSinkSettings& settings, bool force, bool remoteChange) :
Message(),
m_settings(settings),
m_force(force),
m_remoteChange(remoteChange)
{ }
};
class MsgReportConnection : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getClients() const { return m_clients; }
static MsgReportConnection* create(int clients)
{
return new MsgReportConnection(clients);
}
private:
int m_clients;
MsgReportConnection(int clients) :
Message(),
m_clients(clients)
{ }
};
// Message to report actual transmit bandwidth in bits per second
class MsgReportBW : public Message {
MESSAGE_CLASS_DECLARATION
public:
float getBW() const { return m_bw; }
static MsgReportBW* create(float bw)
{
return new MsgReportBW(bw);
}
private:
float m_bw;
MsgReportBW(float bw) :
Message(),
m_bw(bw)
{ }
};
RemoteTCPSink(DeviceAPI *deviceAPI);
virtual ~RemoteTCPSink();
virtual void destroy() { delete this; }
virtual void setDeviceAPI(DeviceAPI *deviceAPI);
virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; }
using BasebandSampleSink::feed;
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po);
virtual void start();
virtual void stop();
virtual void pushMessage(Message *msg) { m_inputMessageQueue.push(msg); }
virtual QString getSinkName() { return objectName(); }
virtual void getIdentifier(QString& id) { id = objectName(); }
virtual QString getIdentifier() const { return objectName(); }
virtual void getTitle(QString& title) { title = "Remote Sink"; }
virtual qint64 getCenterFrequency() const { return m_settings.m_inputFrequencyOffset; }
virtual void setCenterFrequency(qint64 frequency);
virtual QByteArray serialize() const;
virtual bool deserialize(const QByteArray& data);
virtual int getNbSinkStreams() const { return 1; }
virtual int getNbSourceStreams() const { return 0; }
virtual qint64 getStreamCenterFrequency(int streamIndex, bool sinkElseSource) const
{
(void) streamIndex;
(void) sinkElseSource;
return m_settings.m_inputFrequencyOffset;
}
virtual int webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
virtual int webapiWorkspaceGet(
SWGSDRangel::SWGWorkspaceInfo& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
static void webapiFormatChannelSettings(
SWGSDRangel::SWGChannelSettings& response,
const RemoteTCPSinkSettings& settings);
static void webapiUpdateChannelSettings(
RemoteTCPSinkSettings& settings,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response);
uint32_t getNumberOfDeviceStreams() const;
int getBasebandSampleRate() const { return m_basebandSampleRate; }
void setMessageQueueToGUI(MessageQueue* queue) override {
ChannelAPI::setMessageQueueToGUI(queue);
m_basebandSink->setMessageQueueToGUI(queue);
}
static const char* const m_channelIdURI;
static const char* const m_channelId;
private:
DeviceAPI *m_deviceAPI;
QThread m_thread;
RemoteTCPSinkBaseband *m_basebandSink;
RemoteTCPSinkSettings m_settings;
uint64_t m_centerFrequency;
int m_basebandSampleRate;
QNetworkAccessManager *m_networkManager;
QNetworkRequest m_networkRequest;
virtual bool handleMessage(const Message& cmd);
void applySettings(const RemoteTCPSinkSettings& settings, bool force = false, bool remoteChange = false);
void webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const RemoteTCPSinkSettings& settings, bool force);
void sendChannelSettings(
const QList<ObjectPipe*>& pipes,
QList<QString>& channelSettingsKeys,
const RemoteTCPSinkSettings& settings,
bool force
);
void webapiFormatChannelSettings(
QList<QString>& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings *swgChannelSettings,
const RemoteTCPSinkSettings& settings,
bool force
);
private slots:
void networkManagerFinished(QNetworkReply *reply);
void handleIndexInDeviceSetChanged(int index);
};
#endif /* INCLUDE_REMOTETCPSINK_H_ */

View File

@ -0,0 +1,178 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// Copyright (C) 2022 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 <QDebug>
#include "dsp/downchannelizer.h"
#include "dsp/dspengine.h"
#include "dsp/dspcommands.h"
#include "remotetcpsinkbaseband.h"
#include "remotetcpsink.h"
RemoteTCPSinkBaseband::RemoteTCPSinkBaseband() :
m_mutex(QMutex::Recursive)
{
qDebug("RemoteTCPSinkBaseband::RemoteTCPSinkBaseband");
m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(48000));
m_channelizer = new DownChannelizer(&m_sink);
m_sink.setParent(this); // Set parent, so sink is moved to same thread as this baseband object (without this, networking in sink will not work properly!)
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
}
RemoteTCPSinkBaseband::~RemoteTCPSinkBaseband()
{
qDebug("RemoteTCPSinkBaseband::~RemoteTCPSinkBaseband");
delete m_channelizer;
}
void RemoteTCPSinkBaseband::reset()
{
QMutexLocker mutexLocker(&m_mutex);
m_sampleFifo.reset();
m_sink.init();
}
void RemoteTCPSinkBaseband::startWork()
{
QMutexLocker mutexLocker(&m_mutex);
QObject::connect(
&m_sampleFifo,
&SampleSinkFifo::dataReady,
this,
&RemoteTCPSinkBaseband::handleData,
Qt::QueuedConnection
);
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
m_sink.start();
m_running = true;
}
void RemoteTCPSinkBaseband::stopWork()
{
QMutexLocker mutexLocker(&m_mutex);
m_sink.stop();
disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
QObject::disconnect(
&m_sampleFifo,
&SampleSinkFifo::dataReady,
this,
&RemoteTCPSinkBaseband::handleData
);
m_running = false;
}
void RemoteTCPSinkBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
{
m_sampleFifo.write(begin, end);
}
void RemoteTCPSinkBaseband::handleData()
{
QMutexLocker mutexLocker(&m_mutex);
while ((m_sampleFifo.fill() > 0) && (m_inputMessageQueue.size() == 0))
{
SampleVector::iterator part1begin;
SampleVector::iterator part1end;
SampleVector::iterator part2begin;
SampleVector::iterator part2end;
std::size_t count = m_sampleFifo.readBegin(m_sampleFifo.fill(), &part1begin, &part1end, &part2begin, &part2end);
// first part of FIFO data
if (part1begin != part1end) {
m_channelizer->feed(part1begin, part1end);
}
// second part of FIFO data (used when block wraps around)
if(part2begin != part2end) {
m_channelizer->feed(part2begin, part2end);
}
m_sampleFifo.readCommit((unsigned int) count);
}
}
void RemoteTCPSinkBaseband::handleInputMessages()
{
Message* message;
while ((message = m_inputMessageQueue.pop()) != nullptr)
{
if (handleMessage(*message)) {
delete message;
}
}
}
bool RemoteTCPSinkBaseband::handleMessage(const Message& cmd)
{
if (RemoteTCPSink::MsgConfigureRemoteTCPSink::match(cmd))
{
QMutexLocker mutexLocker(&m_mutex);
RemoteTCPSink::MsgConfigureRemoteTCPSink& cfg = (RemoteTCPSink::MsgConfigureRemoteTCPSink&) cmd;
qDebug() << "RemoteTCPSinkBaseband::handleMessage: MsgConfigureRemoteTCPSink";
applySettings(cfg.getSettings(), cfg.getForce(), cfg.getRemoteChange());
return true;
}
else if (DSPSignalNotification::match(cmd))
{
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
qDebug() << "RemoteTCPSinkBaseband::handleMessage: DSPSignalNotification: basebandSampleRate:" << notif.getSampleRate();
setBasebandSampleRate(notif.getSampleRate());
m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(notif.getSampleRate()));
return true;
}
else
{
return false;
}
}
void RemoteTCPSinkBaseband::applySettings(const RemoteTCPSinkSettings& settings, bool force, bool remoteChange)
{
qDebug() << "RemoteTCPSinkBaseband::applySettings:"
<< "m_channelSampleRate:" << settings.m_channelSampleRate
<< "m_inputFrequencyOffset:" << settings.m_inputFrequencyOffset
<< " force: " << force;
if ((settings.m_channelSampleRate != m_settings.m_channelSampleRate)
|| (settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force)
{
m_channelizer->setChannelization(settings.m_channelSampleRate, settings.m_inputFrequencyOffset);
m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset());
}
m_sink.applySettings(settings, force, remoteChange);
m_settings = settings;
}
int RemoteTCPSinkBaseband::getChannelSampleRate() const
{
return m_channelizer->getChannelSampleRate();
}
void RemoteTCPSinkBaseband::setBasebandSampleRate(int sampleRate)
{
m_channelizer->setBasebandSampleRate(sampleRate);
m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset());
}

View File

@ -0,0 +1,74 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// Copyright (C) 2022 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_REMOTETCPSINKBASEBAND_H
#define INCLUDE_REMOTETCPSINKBASEBAND_H
#include <QObject>
#include <QMutex>
#include "dsp/samplesinkfifo.h"
#include "util/message.h"
#include "util/messagequeue.h"
#include "remotetcpsinksink.h"
#include "remotetcpsinksettings.h"
class DownChannelizer;
class RemoteTCPSinkBaseband : public QObject
{
Q_OBJECT
public:
RemoteTCPSinkBaseband();
~RemoteTCPSinkBaseband();
void reset();
void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
void startWork();
void stopWork();
bool isRunning() const { return m_running; }
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication
int getChannelSampleRate() const;
void setMessageQueueToGUI(MessageQueue *messageQueue) { m_sink.setMessageQueueToGUI(messageQueue); }
void setMessageQueueToChannel(MessageQueue *messageQueue) { m_sink.setMessageQueueToChannel(messageQueue); }
void setBasebandSampleRate(int sampleRate);
void setDeviceIndex(uint32_t deviceIndex) { m_sink.setDeviceIndex(deviceIndex); }
void setChannelIndex(uint32_t channelIndex) { m_sink.setChannelIndex(channelIndex); }
void setFifoLabel(const QString& label) { m_sampleFifo.setLabel(label); }
private:
bool m_running;
SampleSinkFifo m_sampleFifo;
DownChannelizer *m_channelizer;
RemoteTCPSinkSink m_sink;
MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
RemoteTCPSinkSettings m_settings;
QMutex m_mutex;
bool handleMessage(const Message& cmd);
void applySettings(const RemoteTCPSinkSettings& settings, bool force = false, bool remoteChange = false);
private slots:
void handleInputMessages();
void handleData(); //!< Handle data when samples have to be processed
};
#endif // INCLUDE_REMOTETCPSINKBASEBAND_H

View File

@ -0,0 +1,435 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2018 Edouard Griffiths, F4EXB //
// Copyright (C) 2022 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 <QLocale>
#include <QResizeEvent>
#include "device/deviceuiset.h"
#include "gui/basicchannelsettingsdialog.h"
#include "gui/devicestreamselectiondialog.h"
#include "dsp/hbfilterchainconverter.h"
#include "dsp/dspcommands.h"
#include "mainwindow.h"
#include "remotetcpsinkgui.h"
#include "remotetcpsink.h"
#include "ui_remotetcpsinkgui.h"
RemoteTCPSinkGUI* RemoteTCPSinkGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *channelRx)
{
RemoteTCPSinkGUI* gui = new RemoteTCPSinkGUI(pluginAPI, deviceUISet, channelRx);
return gui;
}
void RemoteTCPSinkGUI::destroy()
{
delete this;
}
void RemoteTCPSinkGUI::resetToDefaults()
{
m_settings.resetToDefaults();
displaySettings();
applySettings(true);
}
QByteArray RemoteTCPSinkGUI::serialize() const
{
return m_settings.serialize();
}
bool RemoteTCPSinkGUI::deserialize(const QByteArray& data)
{
if(m_settings.deserialize(data)) {
displaySettings();
applySettings(true);
return true;
} else {
resetToDefaults();
return false;
}
}
void RemoteTCPSinkGUI::resizeEvent(QResizeEvent* size)
{
int maxWidth = getRollupContents()->maximumWidth();
int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight();
resize(width() < maxWidth ? width() : maxWidth, minHeight);
size->accept();
}
QString RemoteTCPSinkGUI::displayScaledF(float value, char type, int precision, bool showMult)
{
float posValue = (value < 0) ? -value : value;
if (posValue == 0)
{
return tr("%1").arg(QString::number(value, 'f', precision));
}
else if (posValue < 1)
{
if (posValue > 0.001) {
return tr("%1%2").arg(QString::number(value * 1000.0, type, precision)).arg(showMult ? "m" : "");
} else if (posValue > 0.000001) {
return tr("%1%2").arg(QString::number(value * 1000000.0, type, precision)).arg(showMult ? "u" : "");
} else if (posValue > 1e-9) {
return tr("%1%2").arg(QString::number(value * 1e9, type, precision)).arg(showMult ? "n" : "");
} else if (posValue > 1e-12) {
return tr("%1%2").arg(QString::number(value * 1e12, type, precision)).arg(showMult ? "p" : "");
} else {
return tr("%1").arg(QString::number(value, 'e', precision));
}
}
else
{
if (posValue < 1000) {
return tr("%1").arg(QString::number(value, type, precision));
} else if (posValue < 1000000) {
return tr("%1%2").arg(QString::number(value / 1000.0, type, precision)).arg(showMult ? "k" : "");
} else if (posValue < 1000000000) {
return tr("%1%2").arg(QString::number(value / 1000000.0, type, precision)).arg(showMult ? "M" : "");
} else if (posValue < 1000000000000) {
return tr("%1%2").arg(QString::number(value / 1000000000.0, type, precision)).arg(showMult ? "G" : "");
} else {
return tr("%1").arg(QString::number(value, 'e', precision));
}
}
}
bool RemoteTCPSinkGUI::handleMessage(const Message& message)
{
if (RemoteTCPSink::MsgConfigureRemoteTCPSink::match(message))
{
const RemoteTCPSink::MsgConfigureRemoteTCPSink& cfg = (RemoteTCPSink::MsgConfigureRemoteTCPSink&) message;
if ((cfg.getSettings().m_channelSampleRate != m_settings.m_channelSampleRate)
|| (cfg.getSettings().m_sampleBits != m_settings.m_sampleBits)) {
m_bwAvg.reset();
}
m_settings = cfg.getSettings();
blockApplySettings(true);
m_channelMarker.updateSettings(static_cast<const ChannelMarker*>(m_settings.m_channelMarker));
displaySettings();
blockApplySettings(false);
return true;
}
else if (RemoteTCPSink::MsgReportConnection::match(message))
{
const RemoteTCPSink::MsgReportConnection& report = (RemoteTCPSink::MsgReportConnection&) message;
ui->clients->setText(QString("%1").arg(report.getClients()));
return true;
}
else if (RemoteTCPSink::MsgReportBW::match(message))
{
const RemoteTCPSink::MsgReportBW& report = (RemoteTCPSink::MsgReportBW&) message;
m_bwAvg(report.getBW());
ui->bw->setText(QString("%1bps").arg(displayScaledF(m_bwAvg.instantAverage(), 'f', 3, true)));
return true;
}
else if (DSPSignalNotification::match(message))
{
DSPSignalNotification& cfg = (DSPSignalNotification&) message;
if (cfg.getSampleRate() != m_basebandSampleRate) {
m_bwAvg.reset();
}
m_deviceCenterFrequency = cfg.getCenterFrequency();
m_basebandSampleRate = cfg.getSampleRate();
qDebug("RemoteTCPSinkGUI::handleMessage: DSPSignalNotification: m_basebandSampleRate: %d", m_basebandSampleRate);
displayRateAndShift();
updateAbsoluteCenterFrequency();
return true;
}
else
{
return false;
}
}
RemoteTCPSinkGUI::RemoteTCPSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *channelrx, QWidget* parent) :
ChannelGUI(parent),
ui(new Ui::RemoteTCPSinkGUI),
m_pluginAPI(pluginAPI),
m_deviceUISet(deviceUISet),
m_basebandSampleRate(0),
m_deviceCenterFrequency(0),
m_tickCount(0)
{
setAttribute(Qt::WA_DeleteOnClose, true);
m_helpURL = "plugins/channelrx/remotetcpsink/readme.md";
RollupContents *rollupContents = getRollupContents();
ui->setupUi(rollupContents);
setSizePolicy(rollupContents->sizePolicy());
rollupContents->arrangeRollups();
connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &)));
m_remoteSink = (RemoteTCPSink*) channelrx;
m_remoteSink->setMessageQueueToGUI(getInputMessageQueue());
m_basebandSampleRate = m_remoteSink->getBasebandSampleRate();
m_channelMarker.blockSignals(true);
m_channelMarker.setColor(m_settings.m_rgbColor);
m_channelMarker.setCenterFrequency(0);
m_channelMarker.setTitle("Remote source");
m_channelMarker.blockSignals(false);
m_channelMarker.setVisible(true); // activate signal on the last setting only
m_settings.setChannelMarker(&m_channelMarker);
m_settings.setRollupState(&m_rollupState);
m_deviceUISet->addChannelMarker(&m_channelMarker);
ui->channelSampleRate->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow));
ui->channelSampleRate->setValueRange(8, 0, 99999999);
ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03)));
ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
ui->deltaFrequency->setValueRange(false, 7, -9999999, 9999999);
connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor()));
connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor()));
connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages()));
displaySettings();
makeUIConnections();
applySettings(true);
}
RemoteTCPSinkGUI::~RemoteTCPSinkGUI()
{
delete ui;
}
void RemoteTCPSinkGUI::blockApplySettings(bool block)
{
m_doApplySettings = !block;
}
void RemoteTCPSinkGUI::applySettings(bool force)
{
if (m_doApplySettings)
{
setTitleColor(m_channelMarker.getColor());
RemoteTCPSink::MsgConfigureRemoteTCPSink* message = RemoteTCPSink::MsgConfigureRemoteTCPSink::create(m_settings, force);
m_remoteSink->getInputMessageQueue()->push(message);
}
}
void RemoteTCPSinkGUI::displaySettings()
{
m_channelMarker.blockSignals(true);
m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset);
m_channelMarker.setTitle(m_settings.m_title);
m_channelMarker.setBandwidth(m_settings.m_channelSampleRate);
m_channelMarker.blockSignals(false);
m_channelMarker.setColor(m_settings.m_rgbColor); // activate signal on the last setting only
setTitleColor(m_settings.m_rgbColor);
setWindowTitle(m_channelMarker.getTitle());
setTitle(m_channelMarker.getTitle());
blockApplySettings(true);
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
ui->channelSampleRate->setValue(m_settings.m_channelSampleRate);
ui->gain->setValue(m_settings.m_gain);
ui->gainText->setText(tr("%1dB").arg(m_settings.m_gain));
ui->sampleBits->setCurrentIndex(m_settings.m_sampleBits/8 - 1);
ui->dataAddress->setText(m_settings.m_dataAddress);
ui->dataPort->setText(tr("%1").arg(m_settings.m_dataPort));
ui->protocol->setCurrentIndex((int)m_settings.m_protocol);
getRollupContents()->restoreState(m_rollupState);
blockApplySettings(false);
}
void RemoteTCPSinkGUI::displayRateAndShift()
{
m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset);
m_channelMarker.setBandwidth(m_settings.m_channelSampleRate);
}
void RemoteTCPSinkGUI::leaveEvent(QEvent* event)
{
m_channelMarker.setHighlighted(false);
ChannelGUI::leaveEvent(event);
}
void RemoteTCPSinkGUI::enterEvent(QEvent* event)
{
m_channelMarker.setHighlighted(true);
ChannelGUI::enterEvent(event);
}
void RemoteTCPSinkGUI::handleSourceMessages()
{
Message* message;
while ((message = getInputMessageQueue()->pop()) != 0)
{
if (handleMessage(*message))
{
delete message;
}
}
}
void RemoteTCPSinkGUI::onWidgetRolled(QWidget* widget, bool rollDown)
{
(void) widget;
(void) rollDown;
getRollupContents()->saveState(m_rollupState);
applySettings();
}
void RemoteTCPSinkGUI::onMenuDialogCalled(const QPoint &p)
{
if (m_contextMenuType == ContextMenuChannelSettings)
{
BasicChannelSettingsDialog dialog(&m_channelMarker, this);
dialog.setUseReverseAPI(m_settings.m_useReverseAPI);
dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress);
dialog.setReverseAPIPort(m_settings.m_reverseAPIPort);
dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex);
dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex);
dialog.setDefaultTitle(m_displayedName);
if (m_deviceUISet->m_deviceMIMOEngine)
{
dialog.setNumberOfStreams(m_remoteSink->getNumberOfDeviceStreams());
dialog.setStreamIndex(m_settings.m_streamIndex);
}
dialog.move(p);
dialog.exec();
m_settings.m_rgbColor = m_channelMarker.getColor().rgb();
m_settings.m_title = m_channelMarker.getTitle();
m_settings.m_useReverseAPI = dialog.useReverseAPI();
m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress();
m_settings.m_reverseAPIPort = dialog.getReverseAPIPort();
m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex();
m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex();
setWindowTitle(m_settings.m_title);
setTitle(m_channelMarker.getTitle());
setTitleColor(m_settings.m_rgbColor);
if (m_deviceUISet->m_deviceMIMOEngine)
{
m_settings.m_streamIndex = dialog.getSelectedStreamIndex();
m_channelMarker.clearStreamIndexes();
m_channelMarker.addStreamIndex(m_settings.m_streamIndex);
}
applySettings();
}
resetContextMenuType();
}
void RemoteTCPSinkGUI::channelMarkerChangedByCursor()
{
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
applySettings();
}
void RemoteTCPSinkGUI::channelMarkerHighlightedByCursor()
{
setHighlighted(m_channelMarker.getHighlighted());
}
void RemoteTCPSinkGUI::on_deltaFrequency_changed(int index)
{
m_settings.m_inputFrequencyOffset = index;
applySettings();
}
void RemoteTCPSinkGUI::on_channelSampleRate_changed(int index)
{
m_settings.m_channelSampleRate = index;
m_bwAvg.reset();
applySettings();
}
void RemoteTCPSinkGUI::on_gain_valueChanged(int value)
{
m_settings.m_gain = (float)value;
ui->gainText->setText(tr("%1dB").arg(m_settings.m_gain));
applySettings();
}
void RemoteTCPSinkGUI::on_sampleBits_currentIndexChanged(int index)
{
m_settings.m_sampleBits = 8 * (index + 1);
m_bwAvg.reset();
applySettings();
}
void RemoteTCPSinkGUI::on_dataAddress_editingFinished()
{
m_settings.m_dataAddress = ui->dataAddress->text();
applySettings();
}
void RemoteTCPSinkGUI::on_dataPort_editingFinished()
{
bool dataOk;
int dataPort = ui->dataPort->text().toInt(&dataOk);
if((!dataOk) || (dataPort < 1024) || (dataPort > 65535))
{
return;
}
else
{
m_settings.m_dataPort = dataPort;
}
applySettings();
}
void RemoteTCPSinkGUI::on_protocol_currentIndexChanged(int index)
{
m_settings.m_protocol = (RemoteTCPSinkSettings::Protocol)index;
applySettings();
}
void RemoteTCPSinkGUI::tick()
{
if (++m_tickCount == 20) { // once per second
m_tickCount = 0;
}
}
void RemoteTCPSinkGUI::makeUIConnections()
{
QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &RemoteTCPSinkGUI::on_deltaFrequency_changed);
QObject::connect(ui->channelSampleRate, &ValueDial::changed, this, &RemoteTCPSinkGUI::on_channelSampleRate_changed);
QObject::connect(ui->gain, &QDial::valueChanged, this, &RemoteTCPSinkGUI::on_gain_valueChanged);
QObject::connect(ui->sampleBits, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RemoteTCPSinkGUI::on_sampleBits_currentIndexChanged);
QObject::connect(ui->dataAddress, &QLineEdit::editingFinished, this, &RemoteTCPSinkGUI::on_dataAddress_editingFinished);
QObject::connect(ui->dataPort, &QLineEdit::editingFinished, this, &RemoteTCPSinkGUI::on_dataPort_editingFinished);
QObject::connect(ui->protocol, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RemoteTCPSinkGUI::on_protocol_currentIndexChanged);
}
void RemoteTCPSinkGUI::updateAbsoluteCenterFrequency()
{
setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset);
}

View File

@ -0,0 +1,122 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2018 Edouard Griffiths, F4EXB //
// Copyright (C) 2022 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 PLUGINS_CHANNELRX_REMOTETCPSINK_REMOTETCPSINKGUI_H_
#define PLUGINS_CHANNELRX_REMOTETCPSINK_REMOTETCPSINKGUI_H_
#include <stdint.h>
#include <QObject>
#include "dsp/channelmarker.h"
#include "channel/channelgui.h"
#include "util/messagequeue.h"
#include "util/movingaverage.h"
#include "settings/rollupstate.h"
#include "remotetcpsinksettings.h"
class PluginAPI;
class DeviceUISet;
class RemoteTCPSink;
class BasebandSampleSink;
namespace Ui {
class RemoteTCPSinkGUI;
}
class RemoteTCPSinkGUI : public ChannelGUI {
Q_OBJECT
public:
static RemoteTCPSinkGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel);
virtual void destroy();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }
virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }
virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }
virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }
virtual QString getTitle() const { return m_settings.m_title; }
virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }
virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; }
virtual bool getHidden() const { return m_settings.m_hidden; }
virtual ChannelMarker& getChannelMarker() { return m_channelMarker; }
virtual int getStreamIndex() const { return m_settings.m_streamIndex; }
virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; }
public slots:
void channelMarkerChangedByCursor();
void channelMarkerHighlightedByCursor();
protected:
void resizeEvent(QResizeEvent* size);
private:
Ui::RemoteTCPSinkGUI* ui;
PluginAPI* m_pluginAPI;
DeviceUISet* m_deviceUISet;
ChannelMarker m_channelMarker;
RollupState m_rollupState;
RemoteTCPSinkSettings m_settings;
int m_basebandSampleRate;
quint64 m_deviceCenterFrequency; //!< Center frequency in device
double m_shiftFrequencyFactor; //!< Channel frequency shift factor
bool m_doApplySettings;
RemoteTCPSink* m_remoteSink;
MessageQueue m_inputMessageQueue;
uint32_t m_tickCount;
MovingAverageUtil<float, float, 10> m_bwAvg;
explicit RemoteTCPSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0);
virtual ~RemoteTCPSinkGUI();
void blockApplySettings(bool block);
void applySettings(bool force = false);
void displaySettings();
void displayRateAndShift();
bool handleMessage(const Message& message);
void makeUIConnections();
QString displayScaledF(float value, char type, int precision, bool showMult);
void updateAbsoluteCenterFrequency();
void leaveEvent(QEvent*);
void enterEvent(QEvent*);
void applyDecimation();
void applyPosition();
private slots:
void handleSourceMessages();
void on_deltaFrequency_changed(int index);
void on_channelSampleRate_changed(int value);
void on_gain_valueChanged(int value);
void on_sampleBits_currentIndexChanged(int index);
void on_dataAddress_editingFinished();
void on_dataPort_editingFinished();
void on_protocol_currentIndexChanged(int index);
void onWidgetRolled(QWidget* widget, bool rollDown);
void onMenuDialogCalled(const QPoint& p);
void tick();
};
#endif /* PLUGINS_CHANNELRX_REMOTETCPSINK_REMOTETCPSINKGUI_H_ */

View File

@ -0,0 +1,473 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>RemoteTCPSinkGUI</class>
<widget class="RollupContents" name="RemoteTCPSinkGUI">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>360</width>
<height>147</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>350</width>
<height>100</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>360</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>Remote sink</string>
</property>
<widget class="QWidget" name="settingsContainer" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>340</width>
<height>141</height>
</rect>
</property>
<property name="windowTitle">
<string>Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<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>
<layout class="QHBoxLayout" name="channelSettingsLayout">
<item>
<widget class="QLabel" name="deltaFrequencyLabel">
<property name="minimumSize">
<size>
<width>16</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Df</string>
</property>
</widget>
</item>
<item>
<widget class="ValueDialZ" name="deltaFrequency" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="toolTip">
<string>Channel frequency shift from center in Hz</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="deltaUnits">
<property name="text">
<string>Hz </string>
</property>
</widget>
</item>
<item>
<spacer name="channelSettingsHorizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="gainLabel">
<property name="text">
<string>Gain</string>
</property>
</widget>
</item>
<item>
<widget class="QDial" name="gain">
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>Gain in dB</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>100</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="gainText">
<property name="minimumSize">
<size>
<width>34</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>50dB</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="samplesSettingsLayout">
<property name="rightMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="sampleRateLabel">
<property name="text">
<string>SR</string>
</property>
</widget>
</item>
<item>
<widget class="ValueDial" name="channelSampleRate" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
<bold>false</bold>
</font>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="toolTip">
<string>Channel sample rate in samples per second</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="sampleRateUnit">
<property name="text">
<string>S/s</string>
</property>
</widget>
</item>
<item>
<spacer name="samplesSettingsHorizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="sampleBitsLabel">
<property name="text">
<string>IQ</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="sampleBits">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Transmitted bit depth of each I and Q sample</string>
</property>
<item>
<property name="text">
<string>8</string>
</property>
</item>
<item>
<property name="text">
<string>16</string>
</property>
</item>
<item>
<property name="text">
<string>24</string>
</property>
</item>
<item>
<property name="text">
<string>32</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="sampleBitsUnit">
<property name="text">
<string>bits</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="ipSettingsLayout">
<item>
<widget class="QLabel" name="dataAddressLabel">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>IP</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="dataAddress">
<property name="minimumSize">
<size>
<width>120</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>IP address of local network interface to start TCP server on</string>
</property>
<property name="inputMask">
<string>000.000.000.000</string>
</property>
<property name="text">
<string>0...</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="dataPortSeparator">
<property name="text">
<string>:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="dataPort">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>TCP port for server to listen for connections on</string>
</property>
<property name="inputMask">
<string>00000</string>
</property>
<property name="text">
<string>0</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>
<item>
<widget class="QLabel" name="protocolLabel">
<property name="text">
<string>Protocol</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="protocol">
<property name="toolTip">
<string>Protocol to transmit data with</string>
</property>
<item>
<property name="text">
<string>RTL0</string>
</property>
</item>
<item>
<property name="text">
<string>SDRA</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="statusLayout">
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="clientsLabel">
<property name="text">
<string>Clients</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="clients">
<property name="toolTip">
<string>Number of clients connected</string>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="bwLabel">
<property name="text">
<string>BW</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="bw">
<property name="toolTip">
<string>Transmit bandwidth for a single TCP connection averaged over the last 10 seconds in bits per second</string>
</property>
<property name="text">
<string>0.000Mbps</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>ValueDialZ</class>
<extends>QWidget</extends>
<header>gui/valuedialz.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ValueDial</class>
<extends>QWidget</extends>
<header>gui/valuedial.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>RollupContents</class>
<extends>QWidget</extends>
<header>gui/rollupcontents.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../../../sdrgui/resources/res.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -0,0 +1,95 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016-2019 Edouard Griffiths, F4EXB //
// Copyright (C) 2022 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 "remotetcpsinkplugin.h"
#include <QtPlugin>
#include "plugin/pluginapi.h"
#ifndef SERVER_MODE
#include "remotetcpsinkgui.h"
#endif
#include "remotetcpsink.h"
#include "remotetcpsinkwebapiadapter.h"
#include "remotetcpsinkplugin.h"
const PluginDescriptor RemoteTCPSinkPlugin::m_pluginDescriptor = {
RemoteTCPSink::m_channelId,
QStringLiteral("Remote TCP channel sink"),
QStringLiteral("7.6.0"),
QStringLiteral("(c) Jon Beniston, M7RCE"),
QStringLiteral("https://github.com/f4exb/sdrangel"),
true,
QStringLiteral("https://github.com/f4exb/sdrangel")
};
RemoteTCPSinkPlugin::RemoteTCPSinkPlugin(QObject* parent) :
QObject(parent),
m_pluginAPI(0)
{
}
const PluginDescriptor& RemoteTCPSinkPlugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
void RemoteTCPSinkPlugin::initPlugin(PluginAPI* pluginAPI)
{
m_pluginAPI = pluginAPI;
// register channel Source
m_pluginAPI->registerRxChannel(RemoteTCPSink::m_channelIdURI, RemoteTCPSink::m_channelId, this);
}
void RemoteTCPSinkPlugin::createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink **bs, ChannelAPI **cs) const
{
if (bs || cs)
{
RemoteTCPSink *instance = new RemoteTCPSink(deviceAPI);
if (bs) {
*bs = instance;
}
if (cs) {
*cs = instance;
}
}
}
#ifdef SERVER_MODE
ChannelGUI* RemoteTCPSinkPlugin::createRxChannelGUI(
DeviceUISet *deviceUISet,
BasebandSampleSink *rxChannel) const
{
(void) deviceUISet;
(void) rxChannel;
return nullptr;
}
#else
ChannelGUI* RemoteTCPSinkPlugin::createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const
{
return RemoteTCPSinkGUI::create(m_pluginAPI, deviceUISet, rxChannel);
}
#endif
ChannelWebAPIAdapter* RemoteTCPSinkPlugin::createChannelWebAPIAdapter() const
{
return new RemoteTCPSinkWebAPIAdapter();
}

View File

@ -0,0 +1,50 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016-2019 Edouard Griffiths, F4EXB //
// Copyright (C) 2022 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 PLUGINS_CHANNELRX_REMOTETCPSINK_REMOTETCPSINKPLUGIN_H_
#define PLUGINS_CHANNELRX_REMOTETCPSINK_REMOTETCPSINKPLUGIN_H_
#include <QObject>
#include "plugin/plugininterface.h"
class DeviceUISet;
class BasebandSampleSink;
class RemoteTCPSinkPlugin : public QObject, PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID "sdrangel.demod.remotetcpsink")
public:
explicit RemoteTCPSinkPlugin(QObject* parent = 0);
const PluginDescriptor& getPluginDescriptor() const;
void initPlugin(PluginAPI* pluginAPI);
virtual void createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink **bs, ChannelAPI **cs) const;
virtual ChannelGUI* createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const;
virtual ChannelWebAPIAdapter* createChannelWebAPIAdapter() const;
private:
static const PluginDescriptor m_pluginDescriptor;
PluginAPI* m_pluginAPI;
};
#endif /* PLUGINS_CHANNELRX_REMOTETCPSINK_REMOTETCPSINKPLUGIN_H_ */

View File

@ -0,0 +1,160 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2018 Edouard Griffiths, F4EXB. //
// Copyright (C) 2022 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 "remotetcpsinksettings.h"
#include <QColor>
#include "util/simpleserializer.h"
#include "settings/serializable.h"
RemoteTCPSinkSettings::RemoteTCPSinkSettings()
{
resetToDefaults();
}
void RemoteTCPSinkSettings::resetToDefaults()
{
m_channelSampleRate = 48000;
m_inputFrequencyOffset = 0;
m_gain = 0;
m_sampleBits = 8;
m_dataAddress = "127.0.0.1";
m_dataPort = 1234;
m_protocol = SDRA;
m_rgbColor = QColor(140, 4, 4).rgb();
m_title = "Remote TCP sink";
m_channelMarker = nullptr;
m_rollupState = nullptr;
m_streamIndex = 0;
m_useReverseAPI = false;
m_reverseAPIAddress = "127.0.0.1";
m_reverseAPIPort = 8888;
m_reverseAPIDeviceIndex = 0;
m_reverseAPIChannelIndex = 0;
m_workspaceIndex = 0;
m_hidden = false;
}
QByteArray RemoteTCPSinkSettings::serialize() const
{
SimpleSerializer s(1);
s.writeS32(1, m_channelSampleRate);
s.writeS32(2, m_inputFrequencyOffset);
s.writeS32(3, m_gain);
s.writeU32(4, m_sampleBits);
s.writeString(5, m_dataAddress);
s.writeU32(6, m_dataPort);
s.writeS32(7, (int)m_protocol);
s.writeU32(8, m_rgbColor);
s.writeString(9, m_title);
s.writeBool(10, m_useReverseAPI);
s.writeString(11, m_reverseAPIAddress);
s.writeU32(12, m_reverseAPIPort);
s.writeU32(13, m_reverseAPIDeviceIndex);
s.writeU32(14, m_reverseAPIChannelIndex);
s.writeS32(17, m_streamIndex);
if (m_rollupState) {
s.writeBlob(18, m_rollupState->serialize());
}
if (m_channelMarker) {
s.writeBlob(19, m_channelMarker->serialize());
}
s.writeS32(20, m_workspaceIndex);
s.writeBlob(21, m_geometryBytes);
s.writeBool(22, m_hidden);
return s.final();
}
bool RemoteTCPSinkSettings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if(!d.isValid())
{
resetToDefaults();
return false;
}
if(d.getVersion() == 1)
{
uint32_t tmp;
QString strtmp;
QByteArray bytetmp;
d.readS32(1, &m_channelSampleRate, 48000);
d.readS32(2, &m_inputFrequencyOffset, 0);
d.readS32(3, &m_gain, 0);
d.readU32(4, &m_sampleBits, 8);
d.readString(5, &m_dataAddress, "127.0.0.1");
d.readU32(6, &tmp, 0);
if ((tmp > 1023) && (tmp < 65535)) {
m_dataPort = tmp;
} else {
m_dataPort = 1234;
}
d.readS32(7, (int *)&m_protocol, (int)SDRA);
d.readU32(8, &m_rgbColor, QColor(0, 255, 255).rgb());
d.readString(9, &m_title, "Remote TCP sink");
d.readBool(10, &m_useReverseAPI, false);
d.readString(11, &m_reverseAPIAddress, "127.0.0.1");
d.readU32(12, &tmp, 0);
if ((tmp > 1023) && (tmp < 65535)) {
m_reverseAPIPort = tmp;
} else {
m_reverseAPIPort = 8888;
}
d.readU32(13, &tmp, 0);
m_reverseAPIDeviceIndex = tmp > 99 ? 99 : tmp;
d.readU32(14, &tmp, 0);
m_reverseAPIChannelIndex = tmp > 99 ? 99 : tmp;
d.readS32(17, &m_streamIndex, 0);
if (m_rollupState)
{
d.readBlob(18, &bytetmp);
m_rollupState->deserialize(bytetmp);
}
if (m_channelMarker)
{
d.readBlob(19, &bytetmp);
m_channelMarker->deserialize(bytetmp);
}
d.readS32(20, &m_workspaceIndex, 0);
d.readBlob(21, &m_geometryBytes);
d.readBool(22, &m_hidden, false);
return true;
}
else
{
resetToDefaults();
return false;
}
}

View File

@ -0,0 +1,64 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2018-2019 Edouard Griffiths, F4EXB. //
// Copyright (C) 2022 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_REMOTECHANNELSINKSETTINGS_H_
#define INCLUDE_REMOTECHANNELSINKSETTINGS_H_
#include <QByteArray>
#include <QString>
class Serializable;
struct RemoteTCPSinkSettings
{
enum Protocol {
RTL0, // Compatible with rtl_tcp
SDRA // SDRangel remote TCP protocol which extends rtl_tcp
};
qint32 m_channelSampleRate;
qint32 m_inputFrequencyOffset;
qint32 m_gain; // 10ths of a dB
uint32_t m_sampleBits;
QString m_dataAddress;
uint16_t m_dataPort;
enum Protocol m_protocol;
quint32 m_rgbColor;
QString m_title;
int m_streamIndex; //!< MIMO channel. Not relevant when connected to SI (single Rx).
bool m_useReverseAPI;
QString m_reverseAPIAddress;
uint16_t m_reverseAPIPort;
uint16_t m_reverseAPIDeviceIndex;
uint16_t m_reverseAPIChannelIndex;
int m_workspaceIndex;
QByteArray m_geometryBytes;
bool m_hidden;
Serializable *m_channelMarker;
Serializable *m_rollupState;
RemoteTCPSinkSettings();
void resetToDefaults();
void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; }
void setRollupState(Serializable *rollupState) { m_rollupState = rollupState; }
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
};
#endif /* INCLUDE_REMOTECHANNELSINKSETTINGS_H_ */

View File

@ -0,0 +1,654 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// Copyright (C) 2022 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 <QMutexLocker>
#include <QThread>
#include "channel/channelwebapiutils.h"
#include "dsp/hbfilterchainconverter.h"
#include "device/deviceapi.h"
#include "util/timeutil.h"
#include "maincore.h"
#include "remotetcpsinksink.h"
#include "remotetcpsink.h"
#include "remotetcpsinkbaseband.h"
RemoteTCPSinkSink::RemoteTCPSinkSink() :
m_running(false),
m_messageQueueToGUI(nullptr),
m_messageQueueToChannel(nullptr),
m_channelFrequencyOffset(0),
m_channelSampleRate(48000),
m_linearGain(1.0f),
m_mutex(QMutex::Recursive),
m_server(nullptr)
{
qDebug("RemoteTCPSinkSink::RemoteTCPSinkSink");
applySettings(m_settings, true);
applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true);
}
RemoteTCPSinkSink::~RemoteTCPSinkSink()
{
stop();
}
void RemoteTCPSinkSink::start()
{
qDebug("RemoteTCPSinkSink::start");
if (m_running) {
return;
}
connect(thread(), SIGNAL(started()), this, SLOT(started()));
connect(thread(), SIGNAL(finished()), this, SLOT(finished()));
m_running = true;
}
void RemoteTCPSinkSink::stop()
{
qDebug("RemoteTCPSinkSink::stop");
m_running = false;
}
void RemoteTCPSinkSink::started()
{
QMutexLocker mutexLocker(&m_mutex);
startServer();
disconnect(thread(), SIGNAL(started()), this, SLOT(started()));
}
void RemoteTCPSinkSink::finished()
{
QMutexLocker mutexLocker(&m_mutex);
stopServer();
disconnect(thread(), SIGNAL(finished()), this, SLOT(finished()));
m_running = false;
}
void RemoteTCPSinkSink::init()
{
}
void RemoteTCPSinkSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
{
QMutexLocker mutexLocker(&m_mutex);
if (m_clients.size() > 0)
{
Complex ci;
int bytes = 0;
for (SampleVector::const_iterator it = begin; it != end; ++it)
{
Complex c(it->real(), it->imag());
c *= m_nco.nextIQ();
if (m_interpolatorDistance < 1.0f) // interpolate
{
while (!m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci))
{
processOneSample(ci);
bytes += 2 * m_settings.m_sampleBits / 8;
m_interpolatorDistanceRemain += m_interpolatorDistance;
}
}
else // decimate
{
if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci))
{
processOneSample(ci);
bytes += 2 * m_settings.m_sampleBits / 8;
m_interpolatorDistanceRemain += m_interpolatorDistance;
}
}
}
if (m_bwDateTime.isValid())
{
QDateTime currentDateTime = QDateTime::currentDateTime();
qint64 msecs = m_bwDateTime.msecsTo(currentDateTime) ;
if (msecs > 1000)
{
float bw = (8*m_bwBytes)/(msecs/1000.0f);
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(RemoteTCPSink::MsgReportBW::create(bw));
}
m_bwDateTime = currentDateTime;
m_bwBytes = bytes;
}
else
{
m_bwBytes += bytes;
}
}
else
{
m_bwDateTime = QDateTime::currentDateTime();
m_bwBytes = bytes;
}
}
}
void RemoteTCPSinkSink::processOneSample(Complex &ci)
{
if (m_settings.m_sampleBits == 8)
{
// Transmit data as per rtl_tcp - Interleaved unsigned 8-bit IQ
quint8 iqBuf[2];
iqBuf[0] = ((int)(ci.real() / SDR_RX_SCALEF * 256.0f * m_linearGain)) + 128;
iqBuf[1] = ((int)(ci.imag() / SDR_RX_SCALEF * 256.0f * m_linearGain)) + 128;
for (auto client : m_clients) {
client->write((const char *)iqBuf, sizeof(iqBuf));
}
}
else if (m_settings.m_sampleBits == 16)
{
// Interleaved little-endian signed 16-bit IQ
quint8 iqBuf[2*2];
qint32 i, q;
i = ((qint32)(ci.real() / SDR_RX_SCALEF * 65536.0f * m_linearGain));
q = ((qint32)(ci.imag() / SDR_RX_SCALEF * 65536.0f * m_linearGain));
iqBuf[1] = (i >> 8) & 0xff;
iqBuf[0] = i & 0xff;
iqBuf[3] = (q >> 8) & 0xff;
iqBuf[2] = q & 0xff;
for (auto client : m_clients) {
client->write((const char *)iqBuf, sizeof(iqBuf));
}
}
else if (m_settings.m_sampleBits == 24)
{
// Interleaved little-endian signed 24-bit IQ
quint8 iqBuf[3*2];
qint32 i, q;
i = ((qint32)(ci.real() * m_linearGain));
q = ((qint32)(ci.imag() * m_linearGain));
iqBuf[2] = (i >> 16) & 0xff;
iqBuf[1] = (i >> 8) & 0xff;
iqBuf[0] = i & 0xff;
iqBuf[5] = (q >> 16) & 0xff;
iqBuf[4] = (q >> 8) & 0xff;
iqBuf[3] = q & 0xff;
for (auto client : m_clients) {
client->write((const char *)iqBuf, sizeof(iqBuf));
}
}
else
{
// Interleaved little-endian signed 32-bit IQ
quint8 iqBuf[4*2];
qint32 i, q;
i = ((qint32)(ci.real() * m_linearGain));
q = ((qint32)(ci.imag() * m_linearGain));
iqBuf[3] = (i >> 24) & 0xff;
iqBuf[2] = (i >> 16) & 0xff;
iqBuf[1] = (i >> 8) & 0xff;
iqBuf[0] = i & 0xff;
iqBuf[7] = (q >> 24) & 0xff;
iqBuf[6] = (q >> 16) & 0xff;
iqBuf[5] = (q >> 8) & 0xff;
iqBuf[4] = q & 0xff;
for (auto client : m_clients) {
client->write((const char *)iqBuf, sizeof(iqBuf));
}
}
}
void RemoteTCPSinkSink::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force)
{
qDebug() << "RemoteTCPSinkSink::applyChannelSettings:"
<< " channelSampleRate: " << channelSampleRate
<< " channelFrequencyOffset: " << channelFrequencyOffset;
if ((m_channelFrequencyOffset != channelFrequencyOffset) ||
(m_channelSampleRate != channelSampleRate) || force)
{
m_nco.setFreq(-channelFrequencyOffset, channelSampleRate);
}
if ((m_channelSampleRate != channelSampleRate) || force)
{
m_interpolator.create(16, channelSampleRate, m_settings.m_channelSampleRate / 2.0);
m_interpolatorDistance = (Real) channelSampleRate / (Real) m_settings.m_channelSampleRate;
m_interpolatorDistanceRemain = m_interpolatorDistance;
}
m_channelSampleRate = channelSampleRate;
m_channelFrequencyOffset = channelFrequencyOffset;
}
void RemoteTCPSinkSink::applySettings(const RemoteTCPSinkSettings& settings, bool force, bool remoteChange)
{
QMutexLocker mutexLocker(&m_mutex);
qDebug() << "RemoteTCPSinkSink::applySettings:"
<< " m_dataAddress: " << settings.m_dataAddress
<< " m_dataPort: " << settings.m_dataPort
<< " m_channelSampleRate: " << settings.m_channelSampleRate
<< " m_streamIndex: " << settings.m_streamIndex
<< " force: " << force
<< " remoteChange: " << remoteChange;
m_linearGain = powf(10.0f, m_settings.m_gain/20.0f);
if ((m_settings.m_channelSampleRate != settings.m_channelSampleRate) || force)
{
m_interpolator.create(16, m_channelSampleRate, settings.m_channelSampleRate / 2.0);
m_interpolatorDistance = (Real) m_channelSampleRate / (Real) settings.m_channelSampleRate;
m_interpolatorDistanceRemain = m_interpolatorDistance;
}
// Do clients need to reconnect to get these updated settings?
bool restart = (m_settings.m_dataAddress != settings.m_dataAddress)
|| (m_settings.m_dataPort != settings.m_dataPort)
|| (m_settings.m_sampleBits != settings.m_sampleBits)
|| (m_settings.m_protocol != settings.m_protocol)
|| ( !remoteChange
&& (m_settings.m_channelSampleRate != settings.m_channelSampleRate)
);
m_settings = settings;
if (m_running && restart) {
startServer();
}
}
void RemoteTCPSinkSink::startServer()
{
stopServer();
m_server = new QTcpServer(this);
if (!m_server->listen(QHostAddress::Any, m_settings.m_dataPort))
{
qDebug() << "RemoteTCPSinkSink::startServer: failed to listen on port " << m_settings.m_dataPort;
// FIXME: Report to GUI?
}
else
{
qDebug() << "RemoteTCPSinkSink::startServer: listening on port " << m_settings.m_dataPort;
connect(m_server, &QTcpServer::newConnection, this, &RemoteTCPSinkSink::acceptConnection);
}
}
void RemoteTCPSinkSink::stopServer()
{
for (auto client : m_clients)
{
qDebug() << "RemoteTCPSinkSink::stopServer: Closing connection to client";
client->close();
delete client;
}
if (m_clients.size() > 0)
{
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(RemoteTCPSink::MsgReportConnection::create(0));
}
m_clients.clear();
}
if (m_server)
{
qDebug() << "RemoteTCPSinkSink::stopServer: Closing old server";
m_server->close();
delete m_server;
m_server = nullptr;
}
}
RemoteTCPProtocol::Device RemoteTCPSinkSink::getDevice()
{
DeviceAPI *deviceAPI = MainCore::instance()->getDevice(m_deviceIndex);
if (deviceAPI)
{
QString id = deviceAPI->getHardwareId();
QHash<QString, RemoteTCPProtocol::Device> map = {
{"Airspy", RemoteTCPProtocol::AIRSPY},
{"AirspyHF", RemoteTCPProtocol::AIRSPY_HF},
{"AudioInput", RemoteTCPProtocol::AUDIO_INPUT},
{"BladeRF1", RemoteTCPProtocol::BLADE_RF1},
{"BladeRF2", RemoteTCPProtocol::BLADE_RF2},
{"FCDPro", RemoteTCPProtocol::FCD_PRO},
{"FCDProPlus", RemoteTCPProtocol::FCD_PRO_PLUS},
{"FileInput", RemoteTCPProtocol::FILE_INPUT},
{"HackRF", RemoteTCPProtocol::HACK_RF},
{"KiwiSDR", RemoteTCPProtocol::KIWI_SDR},
{"LimeSDR", RemoteTCPProtocol::LIME_SDR},
{"LocalInput", RemoteTCPProtocol::LOCAL_INPUT},
{"Perseus", RemoteTCPProtocol::PERSEUS},
{"PlutoSDR", RemoteTCPProtocol::PLUTO_SDR},
{"RemoteInput", RemoteTCPProtocol::REMOTE_INPUT},
{"RemoteTCPInput", RemoteTCPProtocol::REMOTE_TCP_INPUT},
{"RTLSDR", RemoteTCPProtocol::RTLSDR_R820T},
{"SDRplay1", RemoteTCPProtocol::SDRPLAY_1},
{"SigMFFileInput", RemoteTCPProtocol::SIGMF_FILE_INPUT},
{"SoapySDR", RemoteTCPProtocol::SOAPY_SDR},
{"TestSource", RemoteTCPProtocol::TEST_SOURCE},
{"USRP", RemoteTCPProtocol::USRP},
{"XTRX", RemoteTCPProtocol::XTRX},
};
if (map.contains(id))
{
return map.value(id);
}
else if (id == "SDRplayV3")
{
QString deviceType;
if (ChannelWebAPIUtils::getDeviceReportValue(m_deviceIndex, "deviceType", deviceType))
{
QHash<QString, RemoteTCPProtocol::Device> sdrplayMap = {
{"RSP1", RemoteTCPProtocol::SDRPLAY_V3_RSP1},
{"RSP1A", RemoteTCPProtocol::SDRPLAY_V3_RSP1A},
{"RSP2", RemoteTCPProtocol::SDRPLAY_V3_RSP2},
{"RSPduo", RemoteTCPProtocol::SDRPLAY_V3_RSPDUO},
{"RSPdx", RemoteTCPProtocol::SDRPLAY_V3_RSPDX},
};
if (sdrplayMap.contains(deviceType)) {
return sdrplayMap.value(deviceType);
}
qDebug() << "RemoteTCPSinkSink::getDevice: Unknown SDRplayV3 deviceType: " << deviceType;
}
else
{
qDebug() << "RemoteTCPSinkSink::getDevice: Failed to get deviceType for SDRplayV3";
}
}
}
return RemoteTCPProtocol::UNKNOWN;
}
void RemoteTCPSinkSink::acceptConnection()
{
QMutexLocker mutexLocker(&m_mutex);
QTcpSocket *client = m_server->nextPendingConnection();
if (!client) {
qDebug() << "RemoteTCPSinkSink::acceptConnection: client is nullptr";
return;
}
m_clients.append(client);
connect(client, &QIODevice::readyRead, this, &RemoteTCPSinkSink::processCommand);
connect(client, SIGNAL(disconnected()), this, SLOT(disconnected()));
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(client, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &RemoteTCPSinkSink::errorOccurred);
#else
connect(client, &QAbstractSocket::errorOccurred, this, &RemoteTCPSinkSink::errorOccurred);
#endif
qDebug() << "RemoteTCPSinkSink::acceptConnection: client connected";
if (m_settings.m_protocol == RemoteTCPSinkSettings::RTL0)
{
quint8 metaData[RemoteTCPProtocol::m_rtl0MetaDataSize] = {'R', 'T', 'L', '0'};
RemoteTCPProtocol::encodeUInt32(&metaData[4], getDevice()); // Tuner ID
RemoteTCPProtocol::encodeUInt32(&metaData[8], 1); // Gain stages
client->write((const char *)metaData, sizeof(metaData));
}
else
{
quint8 metaData[RemoteTCPProtocol::m_sdraMetaDataSize] = {'S', 'D', 'R', 'A'};
RemoteTCPProtocol::encodeUInt32(&metaData[4], getDevice());
// Send device/channel settings, so they can be displayed in the remote GUI
double centerFrequency = 0.0;
qint32 ppmCorrection = 0;
quint32 flags = 0;
int biasTeeEnabled = false;
int directSampling = false;
int agc = false;
int dcOffsetRemoval = false;
int iqCorrection = false;
qint32 devSampleRate = 0;
qint32 log2Decim = 0;
qint32 gain[4] = {0, 0, 0, 0};
qint32 rfBW = 0;
ChannelWebAPIUtils::getCenterFrequency(m_deviceIndex, centerFrequency);
ChannelWebAPIUtils::getLOPpmCorrection(m_deviceIndex, ppmCorrection);
ChannelWebAPIUtils::getDevSampleRate(m_deviceIndex, devSampleRate);
ChannelWebAPIUtils::getSoftDecim(m_deviceIndex, log2Decim);
for (int i = 0; i < 4; i++) {
ChannelWebAPIUtils::getGain(m_deviceIndex, i, gain[i]);
}
ChannelWebAPIUtils::getRFBandwidth(m_deviceIndex, rfBW);
ChannelWebAPIUtils::getBiasTee(m_deviceIndex, biasTeeEnabled);
ChannelWebAPIUtils::getDeviceSetting(m_deviceIndex, "noModMode", directSampling);
ChannelWebAPIUtils::getAGC(m_deviceIndex, agc);
ChannelWebAPIUtils::getDCOffsetRemoval(m_deviceIndex, dcOffsetRemoval);
ChannelWebAPIUtils::getIQCorrection(m_deviceIndex, iqCorrection);
flags = (iqCorrection << 4)
| (dcOffsetRemoval << 3)
| (agc << 2)
| (directSampling << 1)
| biasTeeEnabled;
RemoteTCPProtocol::encodeUInt64(&metaData[8], (quint64)centerFrequency);
RemoteTCPProtocol::encodeUInt32(&metaData[16], ppmCorrection);
RemoteTCPProtocol::encodeUInt32(&metaData[20], flags);
RemoteTCPProtocol::encodeUInt32(&metaData[24], devSampleRate);
RemoteTCPProtocol::encodeUInt32(&metaData[28], log2Decim);
RemoteTCPProtocol::encodeInt16(&metaData[32], gain[0]);
RemoteTCPProtocol::encodeInt16(&metaData[34], gain[1]);
RemoteTCPProtocol::encodeInt16(&metaData[36], gain[2]);
RemoteTCPProtocol::encodeInt16(&metaData[38], gain[3]);
RemoteTCPProtocol::encodeUInt32(&metaData[40], rfBW);
RemoteTCPProtocol::encodeInt32(&metaData[44], m_settings.m_inputFrequencyOffset);
RemoteTCPProtocol::encodeUInt32(&metaData[48], m_settings.m_gain);
RemoteTCPProtocol::encodeUInt32(&metaData[52], m_settings.m_channelSampleRate);
RemoteTCPProtocol::encodeUInt32(&metaData[56], m_settings.m_sampleBits);
// Send API port? Not accessible via MainCore
client->write((const char *)metaData, sizeof(metaData));
}
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(RemoteTCPSink::MsgReportConnection::create(m_clients.size()));
}
}
void RemoteTCPSinkSink::disconnected()
{
QMutexLocker mutexLocker(&m_mutex);
qDebug() << "RemoteTCPSinkSink::disconnected";
QTcpSocket *client = (QTcpSocket*)sender();
client->deleteLater();
m_clients.removeAll(client);
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(RemoteTCPSink::MsgReportConnection::create(m_clients.size()));
}
}
void RemoteTCPSinkSink::errorOccurred(QAbstractSocket::SocketError socketError)
{
qDebug() << "RemoteTCPSinkSink::errorOccurred: " << socketError;
/*if (m_msgQueueToFeature)
{
RemoteTCPSinkSink::MsgReportWorker *msg = RemoteTCPSinkSink::MsgReportWorker::create(m_socket.errorString() + " " + socketError);
m_msgQueueToFeature->push(msg);
}*/
}
void RemoteTCPSinkSink::processCommand()
{
QMutexLocker mutexLocker(&m_mutex);
QTcpSocket *client = (QTcpSocket*)sender();
RemoteTCPSinkSettings settings = m_settings;
quint8 cmd[5];
while (client && (client->bytesAvailable() >= sizeof(cmd)))
{
int len = client->read((char *)cmd, sizeof(cmd));
if (len == sizeof(cmd))
{
switch (cmd[0])
{
case RemoteTCPProtocol::setCenterFrequency:
{
quint64 centerFrequency = RemoteTCPProtocol::extractUInt32(&cmd[1]);
qDebug() << "RemoteTCPSinkSink::processCommand: set center frequency " << centerFrequency;
ChannelWebAPIUtils::setCenterFrequency(m_deviceIndex, (double)centerFrequency);
break;
}
case RemoteTCPProtocol::setSampleRate:
{
int sampleRate = RemoteTCPProtocol::extractUInt32(&cmd[1]);
qDebug() << "RemoteTCPSinkSink::processCommand: set sample rate " << sampleRate;
ChannelWebAPIUtils::setDevSampleRate(m_deviceIndex, sampleRate);
break;
}
case RemoteTCPProtocol::setTunerGainMode:
// SDRangel's rtlsdr sample source always has this fixed as 1, so nothing to do
break;
case RemoteTCPProtocol::setTunerGain: // gain is gain in 10th of a dB
{
int gain = RemoteTCPProtocol::extractUInt32(&cmd[1]);
qDebug() << "RemoteTCPSinkSink::processCommand: set gain " << gain;
ChannelWebAPIUtils::setGain(m_deviceIndex, 0, gain);
break;
}
case RemoteTCPProtocol::setFrequencyCorrection:
{
int ppm = RemoteTCPProtocol::extractUInt32(&cmd[1]);
qDebug() << "RemoteTCPSinkSink::processCommand: set LO ppm correction " << ppm;
ChannelWebAPIUtils::setLOPpmCorrection(m_deviceIndex, ppm);
break;
}
case RemoteTCPProtocol::setTunerIFGain:
{
int v = RemoteTCPProtocol::extractUInt32(&cmd[1]);
int gain = (int)(qint16)(v & 0xffff);
int stage = (v >> 16) & 0xffff;
ChannelWebAPIUtils::setGain(m_deviceIndex, stage, gain);
break;
}
case RemoteTCPProtocol::setAGCMode:
{
int agc = RemoteTCPProtocol::extractUInt32(&cmd[1]);
qDebug() << "RemoteTCPSinkSink::processCommand: set AGC " << agc;
ChannelWebAPIUtils::setAGC(m_deviceIndex, agc);
break;
}
case RemoteTCPProtocol::setDirectSampling:
{
int ds = RemoteTCPProtocol::extractUInt32(&cmd[1]);
qDebug() << "RemoteTCPSinkSink::processCommand: set direct sampling " << ds;
ChannelWebAPIUtils::patchDeviceSetting(m_deviceIndex, "noModMode", ds); // RTLSDR only
break;
}
case RemoteTCPProtocol::setBiasTee:
{
int biasTee = RemoteTCPProtocol::extractUInt32(&cmd[1]);
qDebug() << "RemoteTCPSinkSink::processCommand: set bias tee " << biasTee;
ChannelWebAPIUtils::setBiasTee(m_deviceIndex, biasTee);
break;
}
case RemoteTCPProtocol::setTunerBandwidth:
{
int rfBW = RemoteTCPProtocol::extractUInt32(&cmd[1]);
qDebug() << "RemoteTCPSinkSink::processCommand: set tuner bandwidth " << rfBW;
ChannelWebAPIUtils::setRFBandwidth(m_deviceIndex, rfBW);
break;
}
case RemoteTCPProtocol::setDecimation:
{
int dec = RemoteTCPProtocol::extractUInt32(&cmd[1]);
qDebug() << "RemoteTCPSinkSink::processCommand: set decimation " << dec;
ChannelWebAPIUtils::setSoftDecim(m_deviceIndex, dec);
break;
}
case RemoteTCPProtocol::setDCOffsetRemoval:
{
int dc = RemoteTCPProtocol::extractUInt32(&cmd[1]);
qDebug() << "RemoteTCPSinkSink::processCommand: set DC offset removal " << dc;
ChannelWebAPIUtils::setDCOffsetRemoval(m_deviceIndex, dc);
break;
}
case RemoteTCPProtocol::setIQCorrection:
{
int iq = RemoteTCPProtocol::extractUInt32(&cmd[1]);
qDebug() << "RemoteTCPSinkSink::processCommand: set IQ correction " << iq;
ChannelWebAPIUtils::setIQCorrection(m_deviceIndex, iq);
break;
}
case RemoteTCPProtocol::setChannelSampleRate:
{
int channelSampleRate = RemoteTCPProtocol::extractUInt32(&cmd[1]);
qDebug() << "RemoteTCPSinkSink::processCommand: set channel sample rate " << channelSampleRate;
settings.m_channelSampleRate = channelSampleRate;
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(RemoteTCPSink::MsgConfigureRemoteTCPSink::create(settings, false, true));
}
if (m_messageQueueToChannel) {
m_messageQueueToChannel->push(RemoteTCPSink::MsgConfigureRemoteTCPSink::create(settings, false, true));
}
break;
}
case RemoteTCPProtocol::setChannelFreqOffset:
{
int offset = (int)RemoteTCPProtocol::extractUInt32(&cmd[1]);
qDebug() << "RemoteTCPSinkSink::processCommand: set channel input frequency offset " << offset;
settings.m_inputFrequencyOffset = offset;
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(RemoteTCPSink::MsgConfigureRemoteTCPSink::create(settings, false, true));
}
if (m_messageQueueToChannel) {
m_messageQueueToChannel->push(RemoteTCPSink::MsgConfigureRemoteTCPSink::create(settings, false, true));
}
break;
}
case RemoteTCPProtocol::setChannelGain:
{
int gain = (int)RemoteTCPProtocol::extractUInt32(&cmd[1]);
qDebug() << "RemoteTCPSinkSink::processCommand: set channel gain " << gain;
settings.m_gain = gain;
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(RemoteTCPSink::MsgConfigureRemoteTCPSink::create(settings, false, true));
}
if (m_messageQueueToChannel) {
m_messageQueueToChannel->push(RemoteTCPSink::MsgConfigureRemoteTCPSink::create(settings, false, true));
}
break;
}
case RemoteTCPProtocol::setSampleBitDepth:
{
int bits = RemoteTCPProtocol::extractUInt32(&cmd[1]);
qDebug() << "RemoteTCPSinkSink::processCommand: set sample bit depth " << bits;
settings.m_sampleBits = bits;
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(RemoteTCPSink::MsgConfigureRemoteTCPSink::create(settings, false, true));
}
if (m_messageQueueToChannel) {
m_messageQueueToChannel->push(RemoteTCPSink::MsgConfigureRemoteTCPSink::create(settings, false, true));
}
break;
}
default:
qDebug() << "RemoteTCPSinkSink::processCommand: unknown command " << cmd[0];
break;
}
}
else
{
qDebug() << "RemoteTCPSinkSink::processCommand: read only " << len << " bytes - Expecting " << sizeof(cmd);
}
}
}

View File

@ -0,0 +1,97 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// Copyright (C) 2022 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_REMOTETCPSINKSINK_H_
#define INCLUDE_REMOTETCPSINKSINK_H_
#include <QObject>
#include <QThread>
#include <QTcpServer>
#include <QTcpSocket>
#include <QDateTime>
#include "dsp/channelsamplesink.h"
#include "dsp/nco.h"
#include "dsp/interpolator.h"
#include "channel/remotedatablock.h"
#include "util/messagequeue.h"
#include "remotetcpsinksettings.h"
#include "remotetcpprotocol.h"
class DeviceSampleSource;
class RemoteTCPSinkSink : public QObject, public ChannelSampleSink {
Q_OBJECT
public:
RemoteTCPSinkSink();
~RemoteTCPSinkSink();
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
void start();
void stop();
void init();
void applySettings(const RemoteTCPSinkSettings& settings, bool force = false, bool remoteChange = false);
void applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force = false);
void setDeviceIndex(uint32_t deviceIndex) { m_deviceIndex = deviceIndex; }
void setChannelIndex(uint32_t channelIndex) { m_channelIndex = channelIndex; }
void setMessageQueueToGUI(MessageQueue *queue) { m_messageQueueToGUI = queue; }
void setMessageQueueToChannel(MessageQueue *queue) { m_messageQueueToChannel = queue; }
private slots:
void acceptConnection();
void disconnected();
void errorOccurred(QAbstractSocket::SocketError socketError);
void processCommand();
void started();
void finished();
private:
RemoteTCPSinkSettings m_settings;
bool m_running;
MessageQueue *m_messageQueueToGUI;
MessageQueue *m_messageQueueToChannel;
int64_t m_channelFrequencyOffset;
uint32_t m_channelSampleRate;
uint32_t m_deviceIndex;
uint32_t m_channelIndex;
float m_linearGain;
QMutex m_mutex;
QTcpServer *m_server;
QList<QTcpSocket *> m_clients;
QDateTime m_bwDateTime; //!< For calculating TX bandwidth
qint64 m_bwBytes;
NCO m_nco;
Interpolator m_interpolator;
Real m_interpolatorDistance;
Real m_interpolatorDistanceRemain;
void startServer();
void stopServer();
void processOneSample(Complex &ci);
RemoteTCPProtocol::Device getDevice();
};
#endif // INCLUDE_REMOTETCPSINKSINK_H_

View File

@ -0,0 +1,52 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB. //
// Copyright (C) 2022 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 "SWGChannelSettings.h"
#include "remotetcpsink.h"
#include "remotetcpsinkwebapiadapter.h"
RemoteTCPSinkWebAPIAdapter::RemoteTCPSinkWebAPIAdapter()
{}
RemoteTCPSinkWebAPIAdapter::~RemoteTCPSinkWebAPIAdapter()
{}
int RemoteTCPSinkWebAPIAdapter::webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setRemoteTcpSinkSettings(new SWGSDRangel::SWGRemoteTCPSinkSettings());
response.getRemoteTcpSinkSettings()->init();
RemoteTCPSink::webapiFormatChannelSettings(response, m_settings);
return 200;
}
int RemoteTCPSinkWebAPIAdapter::webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) force; // no action
(void) errorMessage;
RemoteTCPSink::webapiUpdateChannelSettings(m_settings, channelSettingsKeys, response);
return 200;
}

View File

@ -0,0 +1,50 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB. //
// Copyright (C) 2022 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_REMOTETCPSINK_WEBAPIADAPTER_H
#define INCLUDE_REMOTETCPSINK_WEBAPIADAPTER_H
#include "channel/channelwebapiadapter.h"
#include "remotetcpsinksettings.h"
/**
* Standalone API adapter only for the settings
*/
class RemoteTCPSinkWebAPIAdapter : public ChannelWebAPIAdapter {
public:
RemoteTCPSinkWebAPIAdapter();
virtual ~RemoteTCPSinkWebAPIAdapter();
virtual QByteArray serialize() const { return m_settings.serialize(); }
virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); }
virtual int webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
private:
RemoteTCPSinkSettings m_settings;
};
#endif // INCLUDE_REMOTETCPSINK_WEBAPIADAPTER_H

View File

@ -72,3 +72,4 @@ endif()
add_subdirectory(audioinput)
add_subdirectory(kiwisdr)
add_subdirectory(remotetcpinput)

View File

@ -0,0 +1,62 @@
project(remotetcpinput)
set(remotetcpinput_SOURCES
remotetcpinputtcphandler.cpp
remotetcpinput.cpp
remotetcpinputsettings.cpp
remotetcpinputwebapiadapter.cpp
remotetcpinputplugin.cpp
)
set(remotetcpinput_HEADERS
remotetcpinputtcphandler.h
remotetcpinput.h
remotetcpinputsettings.h
remotetcpinputwebapiadapter.h
remotetcpinputplugin.h
)
include_directories(
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
)
if(NOT SERVER_MODE)
set(remotetcpinput_SOURCES
${remotetcpinput_SOURCES}
remotetcpinputgui.cpp
remotetcpinputgui.ui
)
set(remotetcpinput_HEADERS
${remotetcpinput_HEADERS}
remotetcpinputgui.h
)
set(TARGET_NAME inputtcpremote)
set(TARGET_LIB "Qt5::Widgets")
set(TARGET_LIB_GUI "sdrgui")
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
else()
set(TARGET_NAME inputtcpremotesrv)
set(TARGET_LIB "")
set(TARGET_LIB_GUI "")
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
endif()
add_library(${TARGET_NAME} SHARED
${remotetcpinput_SOURCES}
)
target_link_libraries(${TARGET_NAME}
Qt5::Core
${TARGET_LIB}
sdrbase
${TARGET_LIB_GUI}
swagger
)
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
# Install debug symbols
if (WIN32)
install(FILES $<TARGET_PDB_FILE:${TARGET_NAME}> CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} )
endif()

View File

@ -0,0 +1,140 @@
<h1>Remote TCP input plugin</h1>
<h2>Introduction</h2>
This input sample source plugin gets its I/Q samples over the network via a TCP/IP connection from a server such as rtl_tcp, rsp_tcp or SDRangel's Remote TCP Channel Sink plugin.
<h2>Interface</h2>
![Remote TCP input plugin GUI](../../../doc/img/RemoteTCPInput_plugin.png)
<h3>1: Start/Stop</h3>
Device start / stop button.
- Blue triangle icon: device is ready and can be started
- Green square icon: device is running and can be stopped
- Red square icon: an error has occured with the connection to the remote device. The plugin will continually try to reconnect.
<h3>2: Center frequency</h3>
This is the center frequency in kHz of the remote device.
<h3>3: Stream sample rate</h3>
Network I/Q sample rate in kS/s.
<h3>4: Local oscillator correction</h3>
This is the correction to be applied to the remote device's local oscillator in ppm.
<h3>5: DC offset correction</h3>
Check this button to enable DC offset correction on the remote device. This is only supported when the remote server is SDRangel.
<h3>6: IQ imbalance correction</h3>
Check this button to enable IQ imbalance correction on the remote device. This is only supported when the remote server is SDRangel.
<h3>7: Bias tee</h3>
Check this button to enable a bias tee on the remote device, if it supports it.
<h3>8: Direct sampling mode</h3>
Use this button to activate RTL-SDR's direct sampling mode. This can be used to tune to HF frequencies.
<h3>9: Sample rate</h3>
Specify the remote device's sample rate in samples per second (S/s). This is the sample rate between the remote device and remote server.
This field allows an arbitry rate to be entered. However, some devices are limited in the rates they support, so you should be careful
only to enter a supported value, otherwise there may be a mismatch between the displayed rate and the actual rate.
<h3>10: Decimation</h3>
Decimation in powers of two from 1 (no decimation) to 64 between the remote device and the remote server, which determines the remote baseband sample rate. This is only supported when the remote server is SDRangel.
<h3>11: Gain</h3>
Specify gain in dB applied in various stages of the remote device. Available gains will depend upon the type of remote device.
<h3>12: AGC</h3>
Check to enable automatic gain control in the remote device. How AGC works is device dependent.
<h3>13: RF Bandwidth</h3>
Specifies the bandwidth in kHz of the analog filter in the remote device. Available bandwidths are dependent upon the remote device.
<h3>14: Remote sink frequency shift from center frequency of reception</h3>
This is the shift of the remote sink channel center frequency from the device center frequency.
This is used to select the desired part of the signal when the remote channel sample rate is lower than the baseband sample rate.
This is only supported when the remote server is SDRangel.
<h3>15: Remote sink gain</h3>
Sets a gain figure in dB that is applied to I/Q samples before transmission via TCP/IP.
This option may be useful for amplifying very small signals from SDRs with high-dynamic range (E.g. 24-bits), when the network sample bit-depth is 8-bits.
This is only supported when the remote server is SDRangel.
<h3>16: Remote sink channel sample rate</h3>
Specifies the channel and network sample rate in samples per second. If this is different from the remote baseband sample rate, the baseband signal will be decimated to the specified rate.
Unless the remote server is SDRangel, the channel sample rate should be the same as the device sample rate.
<h3>17: Local channel sample rate to baseband sample rate</h3>
When checked, the channel sample rate setting is locked to the remote baseband sample rate.
When unchecked, the channel sample rate can be set to any value.
<h3>18: Sample bit depth</h3>
Specifies number of bits per I/Q sample transmitted via TCP/IP.
When the protocol is RTL0, only 8-bits are supported. SDRA protocol supports 8, 16, 24 and 32-bit samples.
<h3>19: Server IP address</h3>
IP address or hostname of the server that is running SDRangel's Remote TCP Sink plugin, rtl_tcp or rsp_tcp.
<h3>20: Server TCP Port</h3>
TCP port on the server to connect to.
<h3>21: Connection settings</h3>
Determines which settings are used when connecting.
When checked, settings in the RemoteTCPInput GUI are written to the remote device upon connection.
When unchecked, if the remote server is using the SDRA protocol, the RemoteTCPInput GUI will be updated with the current settings from the remote device.
If the remote server is using the RTL0 protocol, the GUI will not be updated, which may mean the two are inconsistent.
<h3>22: Pre-fill</h3>
Determines how many seconds of I/Q samples are buffered locally from the remote device, before being processed in SDRangel.
More buffering can handle more network congestion and other network problems, without gaps in the output, but increases the latency in changes to remote device settings.
<h3>23: Input buffer guage</h3>
Shows how much data is in the input buffer. Typically this will be just under the pre-fill setting.
If it becomes empty, the plugin will pause outputting of data until the buffer is refilled to the pre-fill level.
If the buffer repeatedly runs empty, this suggests you do not have enough network bandwidth for the current combination
of channel sample rate and sample bit depth. Reducing these to lower values may be required for uninterupted data.
<h3>24: Output buffer guage</h3>
Shows how much data is in the output buffer. This should typically be empty. If not empty, this suggests your CPU can't keep up with the amount of data being received.
<h3>25: Device status</h3>
Shows the type of remote device that has been connected to.
<h3>26: Protocol status</h3>
Shows the protocol being used by the remote server. This will be RTL0 or SDRA.
rtl_tcp and rsp_tcp always use the RTL0 protocol.
SDRangel's Remote TCP Sink plugin can use RTL0 or SDRA.
RTL0 is limited to sending 8-bit data, doesn't support decimation and does not send the current device settings on connection.

View File

@ -0,0 +1,619 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// Copyright (C) 2022 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 <string.h>
#include <errno.h>
#include <QDebug>
#include <QNetworkReply>
#include <QBuffer>
#include <QJsonParseError>
#include "SWGDeviceSettings.h"
#include "SWGChannelSettings.h"
#include "SWGDeviceState.h"
#include "SWGDeviceReport.h"
#include "SWGRemoteTCPInputReport.h"
#include "util/simpleserializer.h"
#include "dsp/dspcommands.h"
#include "dsp/dspengine.h"
#include "device/deviceapi.h"
#include "remotetcpinput.h"
#include "remotetcpinputtcphandler.h"
MESSAGE_CLASS_DEFINITION(RemoteTCPInput::MsgConfigureRemoteTCPInput, Message)
MESSAGE_CLASS_DEFINITION(RemoteTCPInput::MsgStartStop, Message)
MESSAGE_CLASS_DEFINITION(RemoteTCPInput::MsgReportTCPBuffer, Message)
RemoteTCPInput::RemoteTCPInput(DeviceAPI *deviceAPI) :
m_deviceAPI(deviceAPI),
m_mutex(QMutex::Recursive),
m_settings(),
m_remoteInputTCPPHandler(nullptr),
m_deviceDescription("RemoteTCPInput")
{
m_sampleFifo.setLabel(m_deviceDescription);
m_sampleFifo.setSize(48000 * 8);
m_remoteInputTCPPHandler = new RemoteTCPInputTCPHandler(&m_sampleFifo, m_deviceAPI);
m_remoteInputTCPPHandler->moveToThread(&m_thread);
m_remoteInputTCPPHandler->setMessageQueueToInput(&m_inputMessageQueue);
m_deviceAPI->setNbSourceStreams(1);
m_networkManager = new QNetworkAccessManager();
QObject::connect(
m_networkManager,
&QNetworkAccessManager::finished,
this,
&RemoteTCPInput::networkManagerFinished
);
}
RemoteTCPInput::~RemoteTCPInput()
{
QObject::disconnect(
m_networkManager,
&QNetworkAccessManager::finished,
this,
&RemoteTCPInput::networkManagerFinished
);
delete m_networkManager;
stop();
m_remoteInputTCPPHandler->deleteLater();
}
void RemoteTCPInput::destroy()
{
delete this;
}
void RemoteTCPInput::init()
{
applySettings(m_settings, true);
}
bool RemoteTCPInput::start()
{
qDebug() << "RemoteTCPInput::start";
m_remoteInputTCPPHandler->reset();
m_remoteInputTCPPHandler->start();
m_remoteInputTCPPHandler->getInputMessageQueue()->push(RemoteTCPInputTCPHandler::MsgConfigureTcpHandler::create(m_settings, true));
m_thread.start();
return true;
}
void RemoteTCPInput::stop()
{
qDebug() << "RemoteTCPInput::stop";
m_remoteInputTCPPHandler->stop();
m_thread.quit();
m_thread.wait();
}
QByteArray RemoteTCPInput::serialize() const
{
return m_settings.serialize();
}
bool RemoteTCPInput::deserialize(const QByteArray& data)
{
bool success = true;
if (!m_settings.deserialize(data))
{
m_settings.resetToDefaults();
success = false;
}
MsgConfigureRemoteTCPInput* message = MsgConfigureRemoteTCPInput::create(m_settings, true);
m_inputMessageQueue.push(message);
if (m_guiMessageQueue)
{
MsgConfigureRemoteTCPInput* messageToGUI = MsgConfigureRemoteTCPInput::create(m_settings, true);
m_guiMessageQueue->push(messageToGUI);
}
return success;
}
void RemoteTCPInput::setMessageQueueToGUI(MessageQueue *queue)
{
m_guiMessageQueue = queue;
m_remoteInputTCPPHandler->setMessageQueueToGUI(queue);
}
const QString& RemoteTCPInput::getDeviceDescription() const
{
return m_deviceDescription;
}
int RemoteTCPInput::getSampleRate() const
{
return m_settings.m_channelSampleRate;
}
quint64 RemoteTCPInput::getCenterFrequency() const
{
return m_settings.m_centerFrequency;
}
void RemoteTCPInput::setCenterFrequency(qint64 centerFrequency)
{
RemoteTCPInputSettings settings = m_settings;
settings.m_centerFrequency = centerFrequency;
MsgConfigureRemoteTCPInput* message = MsgConfigureRemoteTCPInput::create(settings, false);
m_inputMessageQueue.push(message);
if (m_guiMessageQueue)
{
MsgConfigureRemoteTCPInput* messageToGUI = MsgConfigureRemoteTCPInput::create(settings, false);
m_guiMessageQueue->push(messageToGUI);
}
}
bool RemoteTCPInput::handleMessage(const Message& message)
{
if (MsgStartStop::match(message))
{
MsgStartStop& cmd = (MsgStartStop&) message;
qDebug() << "RemoteTCPInput::handleMessage: MsgStartStop: " << (cmd.getStartStop() ? "start" : "stop");
if (cmd.getStartStop())
{
if (m_deviceAPI->initDeviceEngine())
{
m_deviceAPI->startDeviceEngine();
}
}
else
{
m_deviceAPI->stopDeviceEngine();
}
if (m_settings.m_useReverseAPI) {
webapiReverseSendStartStop(cmd.getStartStop());
}
return true;
}
else if (MsgConfigureRemoteTCPInput::match(message))
{
qDebug() << "RemoteTCPInput::handleMessage:" << message.getIdentifier();
MsgConfigureRemoteTCPInput& conf = (MsgConfigureRemoteTCPInput&) message;
applySettings(conf.getSettings(), conf.getForce());
return true;
}
else if (RemoteTCPInputTCPHandler::MsgReportConnection::match(message))
{
qDebug() << "RemoteTCPInput::handleMessage:" << message.getIdentifier();
RemoteTCPInputTCPHandler::MsgReportConnection& report = (RemoteTCPInputTCPHandler::MsgReportConnection&) message;
if (report.getConnected())
{
qDebug() << "Disconnected - stopping DSP";
m_deviceAPI->stopDeviceEngine();
}
return true;
}
else
{
return false;
}
}
void RemoteTCPInput::applySettings(const RemoteTCPInputSettings& settings, bool force)
{
QMutexLocker mutexLocker(&m_mutex);
std::ostringstream os;
QList<QString> reverseAPIKeys;
bool forwardChange = false;
if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || force)
{
reverseAPIKeys.append("centerFrequency");
forwardChange = true;
}
if ((m_settings.m_loPpmCorrection != settings.m_loPpmCorrection) || force) {
reverseAPIKeys.append("loPpmCorrection");
}
if ((m_settings.m_dcBlock != settings.m_dcBlock) || force) {
reverseAPIKeys.append("dcBlock");
}
if ((m_settings.m_iqCorrection != settings.m_iqCorrection) || force) {
reverseAPIKeys.append("iqCorrection");
}
if ((m_settings.m_biasTee != settings.m_biasTee) || force) {
reverseAPIKeys.append("biasTee");
}
if ((m_settings.m_directSampling != settings.m_directSampling) || force) {
reverseAPIKeys.append("noModMode"); // Use same name as rtlsdrinput.cpp
}
if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || force) {
reverseAPIKeys.append("devSampleRate");
}
if ((m_settings.m_log2Decim != settings.m_log2Decim) || force) {
reverseAPIKeys.append("log2Decim");
}
if ((m_settings.m_gain != settings.m_gain) || force) {
reverseAPIKeys.append("gain");
}
if ((m_settings.m_agc != settings.m_agc) || force) {
reverseAPIKeys.append("agc");
}
if ((m_settings.m_rfBW != settings.m_rfBW) || force) {
reverseAPIKeys.append("rfBW");
}
if ((m_settings.m_rfBW != settings.m_rfBW) || force) {
reverseAPIKeys.append("rfBW");
}
if ((m_settings.m_inputFrequencyOffset != settings.m_inputFrequencyOffset) || force) {
reverseAPIKeys.append("inputFrequencyOffset");
}
if ((m_settings.m_channelGain != settings.m_channelGain) || force) {
reverseAPIKeys.append("channelGain");
}
if ((m_settings.m_channelSampleRate != settings.m_channelSampleRate) || force)
{
reverseAPIKeys.append("channelSampleRate");
forwardChange = true;
}
if ((m_settings.m_inputFrequencyOffset != settings.m_inputFrequencyOffset) || force) {
reverseAPIKeys.append("inputFrequencyOffset");
}
if ((m_settings.m_sampleBits != settings.m_sampleBits) || force) {
reverseAPIKeys.append("m_sampleBits");
}
if ((m_settings.m_dataAddress != settings.m_dataAddress) || force) {
reverseAPIKeys.append("dataAddress");
}
if ((m_settings.m_dataPort != settings.m_dataPort) || force) {
reverseAPIKeys.append("dataPort");
}
if ((m_settings.m_preFill != settings.m_preFill) || force) {
reverseAPIKeys.append("preFill");
}
mutexLocker.unlock();
if (settings.m_useReverseAPI)
{
bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) ||
(m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) ||
(m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) ||
(m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex);
webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force);
}
if (forwardChange && (settings.m_channelSampleRate != 0))
{
DSPSignalNotification *notif = new DSPSignalNotification(settings.m_channelSampleRate, settings.m_centerFrequency);
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
}
m_settings = settings;
m_remoteInputTCPPHandler->getInputMessageQueue()->push(RemoteTCPInputTCPHandler::MsgConfigureTcpHandler::create(m_settings, force));
qDebug() << "RemoteTCPInput::applySettings: "
<< " force: " << force
<< " m_centerFrequency: " << m_settings.m_centerFrequency
<< " m_loPpmCorrection: " << m_settings.m_loPpmCorrection
<< " m_biasTee: " << m_settings.m_biasTee
<< " m_devSampleRate: " << m_settings.m_devSampleRate
<< " m_log2Decim: " << m_settings.m_log2Decim
<< " m_gain: " << m_settings.m_gain
<< " m_agc: " << m_settings.m_agc
<< " m_rfBW: " << m_settings.m_rfBW
<< " m_inputFrequencyOffset: " << m_settings.m_inputFrequencyOffset
<< " m_channelGain: " << m_settings.m_channelGain
<< " m_channelSampleRate: " << m_settings.m_channelSampleRate
<< " m_sampleBits: " << m_settings.m_sampleBits
<< " m_dataAddress: " << m_settings.m_dataAddress
<< " m_dataPort: " << m_settings.m_dataPort
<< " m_preFill: " << m_settings.m_preFill
;
}
int RemoteTCPInput::webapiRunGet(
SWGSDRangel::SWGDeviceState& response,
QString& errorMessage)
{
(void) errorMessage;
m_deviceAPI->getDeviceEngineStateStr(*response.getState());
return 200;
}
int RemoteTCPInput::webapiRun(
bool run,
SWGSDRangel::SWGDeviceState& response,
QString& errorMessage)
{
(void) errorMessage;
m_deviceAPI->getDeviceEngineStateStr(*response.getState());
MsgStartStop *message = MsgStartStop::create(run);
m_inputMessageQueue.push(message);
if (m_guiMessageQueue) // forward to GUI if any
{
MsgStartStop *msgToGUI = MsgStartStop::create(run);
m_guiMessageQueue->push(msgToGUI);
}
return 200;
}
int RemoteTCPInput::webapiSettingsGet(
SWGSDRangel::SWGDeviceSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setRemoteTcpInputSettings(new SWGSDRangel::SWGRemoteTCPInputSettings());
response.getRemoteTcpInputSettings()->init();
webapiFormatDeviceSettings(response, m_settings);
return 200;
}
int RemoteTCPInput::webapiSettingsPutPatch(
bool force,
const QStringList& deviceSettingsKeys,
SWGSDRangel::SWGDeviceSettings& response, // query + response
QString& errorMessage)
{
(void) errorMessage;
RemoteTCPInputSettings settings = m_settings;
webapiUpdateDeviceSettings(settings, deviceSettingsKeys, response);
MsgConfigureRemoteTCPInput *msg = MsgConfigureRemoteTCPInput::create(settings, force);
m_inputMessageQueue.push(msg);
if (m_guiMessageQueue) // forward to GUI if any
{
MsgConfigureRemoteTCPInput *msgToGUI = MsgConfigureRemoteTCPInput::create(settings, force);
m_guiMessageQueue->push(msgToGUI);
}
webapiFormatDeviceSettings(response, settings);
return 200;
}
void RemoteTCPInput::webapiUpdateDeviceSettings(
RemoteTCPInputSettings& settings,
const QStringList& deviceSettingsKeys,
SWGSDRangel::SWGDeviceSettings& response)
{
if (deviceSettingsKeys.contains("centerFrequency")) {
settings.m_centerFrequency = response.getRemoteTcpInputSettings()->getCenterFrequency();
}
if (deviceSettingsKeys.contains("loPpmCorrection")) {
settings.m_loPpmCorrection = response.getRemoteTcpInputSettings()->getLoPpmCorrection();
}
if (deviceSettingsKeys.contains("dcBlock")) {
settings.m_dcBlock = response.getRemoteTcpInputSettings()->getDcBlock() != 0;
}
if (deviceSettingsKeys.contains("iqCorrection")) {
settings.m_iqCorrection = response.getRemoteTcpInputSettings()->getIqCorrection() != 0;
}
if (deviceSettingsKeys.contains("biasTee")) {
settings.m_biasTee = response.getRemoteTcpInputSettings()->getBiasTee() != 0;
}
if (deviceSettingsKeys.contains("directSampling")) {
settings.m_directSampling = response.getRemoteTcpInputSettings()->getDirectSampling() != 0;
}
if (deviceSettingsKeys.contains("devSampleRate")) {
settings.m_devSampleRate = response.getRemoteTcpInputSettings()->getDevSampleRate();
}
if (deviceSettingsKeys.contains("log2Decim")) {
settings.m_log2Decim = response.getRemoteTcpInputSettings()->getLog2Decim();
}
if (deviceSettingsKeys.contains("agc")) {
settings.m_agc = response.getRemoteTcpInputSettings()->getAgc() != 0;
}
if (deviceSettingsKeys.contains("rfBW")) {
settings.m_rfBW = response.getRemoteTcpInputSettings()->getRfBw();
}
if (deviceSettingsKeys.contains("inputFrequencyOffset")) {
settings.m_inputFrequencyOffset = response.getRemoteTcpInputSettings()->getInputFrequencyOffset();
}
if (deviceSettingsKeys.contains("channelGain")) {
settings.m_channelGain = response.getRemoteTcpInputSettings()->getChannelGain();
}
if (deviceSettingsKeys.contains("channelSampleRate")) {
settings.m_channelSampleRate = response.getRemoteTcpInputSettings()->getChannelSampleRate();
}
if (deviceSettingsKeys.contains("channelDecimation")) {
settings.m_channelDecimation = response.getRemoteTcpInputSettings()->getChannelDecimation();
}
if (deviceSettingsKeys.contains("sampleBits")) {
settings.m_sampleBits = response.getRemoteTcpInputSettings()->getSampleBits();
}
if (deviceSettingsKeys.contains("dataAddress")) {
settings.m_dataAddress = *response.getRemoteTcpInputSettings()->getDataAddress();
}
if (deviceSettingsKeys.contains("dataPort")) {
settings.m_dataPort = response.getRemoteTcpInputSettings()->getDataPort();
}
if (deviceSettingsKeys.contains("overrideRemoteSettings")) {
settings.m_overrideRemoteSettings = response.getRemoteTcpInputSettings()->getOverrideRemoteSettings() != 0;
}
if (deviceSettingsKeys.contains("preFill")) {
settings.m_preFill = response.getRemoteTcpInputSettings()->getPreFill() != 0;
}
if (deviceSettingsKeys.contains("useReverseAPI")) {
settings.m_useReverseAPI = response.getRemoteTcpInputSettings()->getUseReverseApi() != 0;
}
if (deviceSettingsKeys.contains("reverseAPIAddress")) {
settings.m_reverseAPIAddress = *response.getRemoteTcpInputSettings()->getReverseApiAddress();
}
if (deviceSettingsKeys.contains("reverseAPIPort")) {
settings.m_reverseAPIPort = response.getRemoteTcpInputSettings()->getReverseApiPort();
}
if (deviceSettingsKeys.contains("reverseAPIDeviceIndex")) {
settings.m_reverseAPIDeviceIndex = response.getRemoteTcpInputSettings()->getReverseApiDeviceIndex();
}
}
void RemoteTCPInput::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const RemoteTCPInputSettings& settings)
{
response.getRemoteTcpInputSettings()->setCenterFrequency(settings.m_centerFrequency);
response.getRemoteTcpInputSettings()->setLoPpmCorrection(settings.m_loPpmCorrection);
response.getRemoteTcpInputSettings()->setDcBlock(settings.m_dcBlock ? 1 : 0);
response.getRemoteTcpInputSettings()->setIqCorrection(settings.m_iqCorrection ? 1 : 0);
response.getRemoteTcpInputSettings()->setBiasTee(settings.m_biasTee ? 1 : 0);
response.getRemoteTcpInputSettings()->setDirectSampling(settings.m_directSampling ? 1 : 0);
response.getRemoteTcpInputSettings()->setDevSampleRate(settings.m_devSampleRate);
response.getRemoteTcpInputSettings()->setLog2Decim(settings.m_log2Decim);
response.getRemoteTcpInputSettings()->setGain(settings.m_gain[0]);
response.getRemoteTcpInputSettings()->setAgc(settings.m_agc ? 1 : 0);
response.getRemoteTcpInputSettings()->setRfBw(settings.m_rfBW);
response.getRemoteTcpInputSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
response.getRemoteTcpInputSettings()->setChannelGain(settings.m_channelGain);
response.getRemoteTcpInputSettings()->setChannelSampleRate(settings.m_channelSampleRate);
response.getRemoteTcpInputSettings()->setChannelDecimation(settings.m_channelDecimation);
response.getRemoteTcpInputSettings()->setSampleBits(settings.m_sampleBits);
response.getRemoteTcpInputSettings()->setDataAddress(new QString(settings.m_dataAddress));
response.getRemoteTcpInputSettings()->setDataPort(settings.m_dataPort);
response.getRemoteTcpInputSettings()->setOverrideRemoteSettings(settings.m_overrideRemoteSettings ? 1 : 0);
response.getRemoteTcpInputSettings()->setPreFill(settings.m_preFill ? 1 : 0);
response.getRemoteTcpInputSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
if (response.getRemoteTcpInputSettings()->getReverseApiAddress()) {
*response.getRemoteTcpInputSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
} else {
response.getRemoteTcpInputSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
}
response.getRemoteTcpInputSettings()->setReverseApiPort(settings.m_reverseAPIPort);
response.getRemoteTcpInputSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex);
}
int RemoteTCPInput::webapiReportGet(
SWGSDRangel::SWGDeviceReport& response,
QString& errorMessage)
{
(void) errorMessage;
response.setRemoteTcpInputReport(new SWGSDRangel::SWGRemoteTCPInputReport());
response.getRemoteTcpInputReport()->init();
webapiFormatDeviceReport(response);
return 200;
}
void RemoteTCPInput::webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response)
{
response.getRemoteTcpInputReport()->setSampleRate(m_settings.m_channelSampleRate);
}
void RemoteTCPInput::webapiReverseSendSettings(QList<QString>& deviceSettingsKeys, const RemoteTCPInputSettings& settings, bool force)
{
SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings();
swgDeviceSettings->setDirection(0); // single Rx
swgDeviceSettings->setOriginatorIndex(m_deviceAPI->getDeviceSetIndex());
swgDeviceSettings->setDeviceHwType(new QString("RemoteTCPInput"));
swgDeviceSettings->setRemoteTcpInputSettings(new SWGSDRangel::SWGRemoteTCPInputSettings());
SWGSDRangel::SWGRemoteTCPInputSettings *swgRemoteTCPInputSettings = swgDeviceSettings->getRemoteTcpInputSettings();
// transfer data that has been modified. When force is on transfer all data except reverse API data
if (deviceSettingsKeys.contains("dcBlock") || force) {
swgRemoteTCPInputSettings->setDcBlock(settings.m_dcBlock ? 1 : 0);
}
if (deviceSettingsKeys.contains("iqCorrection") || force) {
swgRemoteTCPInputSettings->setIqCorrection(settings.m_iqCorrection ? 1 : 0);
}
if (deviceSettingsKeys.contains("biasTee") || force) {
swgRemoteTCPInputSettings->setBiasTee(settings.m_biasTee ? 1 : 0);
}
if (deviceSettingsKeys.contains("dataAddress") || force) {
swgRemoteTCPInputSettings->setDataAddress(new QString(settings.m_dataAddress));
}
if (deviceSettingsKeys.contains("dataPort") || force) {
swgRemoteTCPInputSettings->setDataPort(settings.m_dataPort);
}
QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings")
.arg(settings.m_reverseAPIAddress)
.arg(settings.m_reverseAPIPort)
.arg(settings.m_reverseAPIDeviceIndex);
m_networkRequest.setUrl(QUrl(deviceSettingsURL));
m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QBuffer *buffer = new QBuffer();
buffer->open((QBuffer::ReadWrite));
buffer->write(swgDeviceSettings->asJson().toUtf8());
buffer->seek(0);
// Always use PATCH to avoid passing reverse API settings
QNetworkReply *reply = m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
buffer->setParent(reply);
delete swgDeviceSettings;
}
void RemoteTCPInput::webapiReverseSendStartStop(bool start)
{
SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings();
swgDeviceSettings->setDirection(0); // single Rx
swgDeviceSettings->setOriginatorIndex(m_deviceAPI->getDeviceSetIndex());
swgDeviceSettings->setDeviceHwType(new QString("RemoteTCPInput"));
QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run")
.arg(m_settings.m_reverseAPIAddress)
.arg(m_settings.m_reverseAPIPort)
.arg(m_settings.m_reverseAPIDeviceIndex);
m_networkRequest.setUrl(QUrl(deviceSettingsURL));
m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QBuffer *buffer = new QBuffer();
buffer->open((QBuffer::ReadWrite));
buffer->write(swgDeviceSettings->asJson().toUtf8());
buffer->seek(0);
QNetworkReply *reply;
if (start) {
reply = m_networkManager->sendCustomRequest(m_networkRequest, "POST", buffer);
} else {
reply = m_networkManager->sendCustomRequest(m_networkRequest, "DELETE", buffer);
}
buffer->setParent(reply);
delete swgDeviceSettings;
}
void RemoteTCPInput::networkManagerFinished(QNetworkReply *reply)
{
QNetworkReply::NetworkError replyError = reply->error();
if (replyError)
{
qWarning() << "RemoteTCPInput::networkManagerFinished:"
<< " error(" << (int) replyError
<< "): " << replyError
<< ": " << reply->errorString();
}
else
{
QString answer = reply->readAll();
answer.chop(1); // remove last \n
qDebug("RemoteTCPInput::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
}
reply->deleteLater();
}

View File

@ -0,0 +1,197 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// Copyright (C) 2022 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_REMOTETCPINPUT_H
#define INCLUDE_REMOTETCPINPUT_H
#include <ctime>
#include <iostream>
#include <stdint.h>
#include <QString>
#include <QByteArray>
#include <QTimer>
#include <QThread>
#include <QNetworkRequest>
#include "dsp/devicesamplesource.h"
#include "channel/remotedatablock.h"
#include "remotetcpinputsettings.h"
class QNetworkAccessManager;
class QNetworkReply;
class DeviceAPI;
class RemoteTCPInputTCPHandler;
class RemoteTCPInput : public DeviceSampleSource {
Q_OBJECT
public:
class MsgConfigureRemoteTCPInput : public Message {
MESSAGE_CLASS_DECLARATION
public:
const RemoteTCPInputSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigureRemoteTCPInput* create(const RemoteTCPInputSettings& settings, bool force = false)
{
return new MsgConfigureRemoteTCPInput(settings, force);
}
private:
RemoteTCPInputSettings m_settings;
bool m_force;
MsgConfigureRemoteTCPInput(const RemoteTCPInputSettings& settings, bool force) :
Message(),
m_settings(settings),
m_force(force)
{ }
};
class MsgStartStop : public Message {
MESSAGE_CLASS_DECLARATION
public:
bool getStartStop() const { return m_startStop; }
static MsgStartStop* create(bool startStop) {
return new MsgStartStop(startStop);
}
protected:
bool m_startStop;
MsgStartStop(bool startStop) :
Message(),
m_startStop(startStop)
{ }
};
class MsgReportTCPBuffer : public Message {
MESSAGE_CLASS_DECLARATION
public:
qint64 getInBytesAvailable() const { return m_inBytesAvailable; }
qint64 getInSize() const { return m_inSize; }
float getInSeconds() const { return m_inSeconds; }
qint64 getOutBytesAvailable() const { return m_outBytesAvailable; }
qint64 getOutSize() const { return m_outSize; }
float getOutSeconds() const { return m_outSeconds; }
static MsgReportTCPBuffer* create(qint64 inBytesAvailable, qint64 inSize, float inSeconds,
qint64 outBytesAvailable, qint64 outSize, float outSeconds) {
return new MsgReportTCPBuffer(inBytesAvailable, inSize, inSeconds,
outBytesAvailable, outSize, outSeconds);
}
protected:
qint64 m_inBytesAvailable;
qint64 m_inSize;
float m_inSeconds;
qint64 m_outBytesAvailable;
qint64 m_outSize;
float m_outSeconds;
MsgReportTCPBuffer(qint64 inBytesAvailable, qint64 inSize, float inSeconds,
qint64 outBytesAvailable, qint64 outSize, float outSeconds) :
Message(),
m_inBytesAvailable(inBytesAvailable),
m_inSize(inSize),
m_inSeconds(inSeconds),
m_outBytesAvailable(outBytesAvailable),
m_outSize(outSize),
m_outSeconds(outSeconds)
{ }
};
RemoteTCPInput(DeviceAPI *deviceAPI);
virtual ~RemoteTCPInput();
virtual void destroy();
virtual void init();
virtual bool start();
virtual void stop();
virtual QByteArray serialize() const;
virtual bool deserialize(const QByteArray& data);
virtual void setMessageQueueToGUI(MessageQueue *queue);
virtual const QString& getDeviceDescription() const;
virtual int getSampleRate() const;
virtual void setSampleRate(int sampleRate) { (void) sampleRate; }
virtual quint64 getCenterFrequency() const;
virtual void setCenterFrequency(qint64 centerFrequency);
std::time_t getStartingTimeStamp() const;
virtual bool handleMessage(const Message& message);
virtual int webapiSettingsGet(
SWGSDRangel::SWGDeviceSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& deviceSettingsKeys,
SWGSDRangel::SWGDeviceSettings& response, // query + response
QString& errorMessage);
virtual int webapiReportGet(
SWGSDRangel::SWGDeviceReport& response,
QString& errorMessage);
virtual int webapiRunGet(
SWGSDRangel::SWGDeviceState& response,
QString& errorMessage);
virtual int webapiRun(
bool run,
SWGSDRangel::SWGDeviceState& response,
QString& errorMessage);
static void webapiFormatDeviceSettings(
SWGSDRangel::SWGDeviceSettings& response,
const RemoteTCPInputSettings& settings);
static void webapiUpdateDeviceSettings(
RemoteTCPInputSettings& settings,
const QStringList& deviceSettingsKeys,
SWGSDRangel::SWGDeviceSettings& response);
private:
DeviceAPI *m_deviceAPI;
QMutex m_mutex;
RemoteTCPInputSettings m_settings;
RemoteTCPInputTCPHandler* m_remoteInputTCPPHandler;
QString m_deviceDescription;
QNetworkAccessManager *m_networkManager;
QNetworkRequest m_networkRequest;
QThread m_thread;
void applySettings(const RemoteTCPInputSettings& settings, bool force = false);
void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response);
void webapiReverseSendSettings(QList<QString>& deviceSettingsKeys, const RemoteTCPInputSettings& settings, bool force);
void webapiReverseSendStartStop(bool start);
private slots:
void networkManagerFinished(QNetworkReply *reply);
};
#endif // INCLUDE_REMOTETCPINPUT_H

View File

@ -0,0 +1,778 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// Copyright (C) 2022 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 <QDebug>
#include <QMessageBox>
#include <QDateTime>
#include <QString>
#include <QResizeEvent>
#include "ui_remotetcpinputgui.h"
#include "gui/colormapper.h"
#include "gui/glspectrum.h"
#include "gui/basicdevicesettingsdialog.h"
#include "dsp/dspengine.h"
#include "dsp/dspcommands.h"
#include "dsp/hbfilterchainconverter.h"
#include "mainwindow.h"
#include "util/simpleserializer.h"
#include "device/deviceapi.h"
#include "device/deviceuiset.h"
#include "remotetcpinputgui.h"
#include "remotetcpinputtcphandler.h"
RemoteTCPInputGui::RemoteTCPInputGui(DeviceUISet *deviceUISet, QWidget* parent) :
DeviceGUI(parent),
ui(new Ui::RemoteTCPInputGui),
m_settings(),
m_sampleSource(0),
m_sampleRate(0),
m_centerFrequency(0),
m_lastEngineState(DeviceAPI::StNotStarted),
m_doApplySettings(true),
m_forceSettings(true),
m_deviceGains(nullptr),
m_remoteDevice(RemoteTCPProtocol::RTLSDR_R820T),
m_connectionError(false)
{
m_deviceUISet = deviceUISet;
setAttribute(Qt::WA_DeleteOnClose, true);
ui->setupUi(getContents());
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
getContents()->setStyleSheet("#RemoteTCPInputGui { background-color: rgb(64, 64, 64); }");
m_helpURL = "plugins/samplesource/remotetcpinput/readme.md";
ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
ui->centerFrequency->setValueRange(7, 0, 9999999); // frequency dial is in kHz
ui->devSampleRate->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow));
ui->devSampleRate->setValueRange(8, 0, 99999999);
ui->rfBW->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow));
ui->rfBW->setValueRange(5, 0, 99999); // In kHz
ui->channelSampleRate->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow));
ui->channelSampleRate->setValueRange(8, 0, 99999999);
ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03)));
ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
ui->deltaFrequency->setValueRange(false, 7, -9999999, 9999999);
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &)));
displaySettings();
connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
m_statusTimer.start(500);
connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware()));
m_sampleSource = (RemoteTCPInput*) m_deviceUISet->m_deviceAPI->getSampleSource();
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection);
m_sampleSource->setMessageQueueToGUI(&m_inputMessageQueue);
m_forceSettings = true;
sendSettings();
makeUIConnections();
}
RemoteTCPInputGui::~RemoteTCPInputGui()
{
delete ui;
}
void RemoteTCPInputGui::blockApplySettings(bool block)
{
m_doApplySettings = !block;
}
void RemoteTCPInputGui::destroy()
{
delete this;
}
void RemoteTCPInputGui::resetToDefaults()
{
m_settings.resetToDefaults();
displaySettings();
m_forceSettings = true;
sendSettings();
}
QByteArray RemoteTCPInputGui::serialize() const
{
return m_settings.serialize();
}
bool RemoteTCPInputGui::deserialize(const QByteArray& data)
{
qDebug("RemoteTCPInputGui::deserialize");
if (m_settings.deserialize(data))
{
displaySettings();
m_forceSettings = true;
sendSettings();
return true;
}
else
{
return false;
}
}
void RemoteTCPInputGui::resizeEvent(QResizeEvent* size)
{
adjustSize();
size->accept();
}
bool RemoteTCPInputGui::handleMessage(const Message& message)
{
if (RemoteTCPInput::MsgConfigureRemoteTCPInput::match(message))
{
const RemoteTCPInput::MsgConfigureRemoteTCPInput& cfg = (RemoteTCPInput::MsgConfigureRemoteTCPInput&) message;
m_settings = cfg.getSettings();
blockApplySettings(true);
displaySettings();
blockApplySettings(false);
return true;
}
else if (RemoteTCPInput::MsgStartStop::match(message))
{
RemoteTCPInput::MsgStartStop& notif = (RemoteTCPInput::MsgStartStop&) message;
m_connectionError = false;
blockApplySettings(true);
ui->startStop->setChecked(notif.getStartStop());
blockApplySettings(false);
return true;
}
else if (RemoteTCPInput::MsgReportTCPBuffer::match(message))
{
const RemoteTCPInput::MsgReportTCPBuffer& report = (RemoteTCPInput::MsgReportTCPBuffer&) message;
ui->inGauge->setMaximum((int)report.getInSize());
ui->inGauge->setValue((int)report.getInBytesAvailable());
ui->inBufferLenSecsText->setText(QString("%1s").arg(report.getInSeconds(), 0, 'f', 2));
ui->outGauge->setMaximum((int)report.getOutSize());
ui->outGauge->setValue((int)report.getOutBytesAvailable());
ui->outBufferLenSecsText->setText(QString("%1s").arg(report.getOutSeconds(), 0, 'f', 2));
}
else if (RemoteTCPInputTCPHandler::MsgReportRemoteDevice::match(message))
{
const RemoteTCPInputTCPHandler::MsgReportRemoteDevice& report = (RemoteTCPInputTCPHandler::MsgReportRemoteDevice&) message;
QHash<RemoteTCPProtocol::Device, QString> devices = {
{RemoteTCPProtocol::RTLSDR_E4000, "RTLSDR E4000"},
{RemoteTCPProtocol::RTLSDR_FC0012, "RTLSDR FC0012"},
{RemoteTCPProtocol::RTLSDR_FC0013, "RTLSDR FC0013"},
{RemoteTCPProtocol::RTLSDR_FC2580, "RTLSDR FC2580"},
{RemoteTCPProtocol::RTLSDR_R820T, "RTLSDR R820T"},
{RemoteTCPProtocol::RTLSDR_R828D, "RTLSDR R828D"},
{RemoteTCPProtocol::AIRSPY, "Airspy"},
{RemoteTCPProtocol::AIRSPY_HF, "AirspyHF"},
{RemoteTCPProtocol::AUDIO_INPUT, "AudioInput"},
{RemoteTCPProtocol::BLADE_RF1, "BladeRF1"},
{RemoteTCPProtocol::BLADE_RF2, "BladeRF2"},
{RemoteTCPProtocol::FCD_PRO, "FCDPro"},
{RemoteTCPProtocol::FCD_PRO_PLUS, "FCDProPlus"},
{RemoteTCPProtocol::FILE_INPUT, "FileInput"},
{RemoteTCPProtocol::HACK_RF, "HackRF"},
{RemoteTCPProtocol::KIWI_SDR, "KiwiSDR"},
{RemoteTCPProtocol::LIME_SDR, "LimeSDR"},
{RemoteTCPProtocol::LOCAL_INPUT, "LocalInput"},
{RemoteTCPProtocol::PERSEUS, "Perseus"},
{RemoteTCPProtocol::PLUTO_SDR, "PlutoSDR"},
{RemoteTCPProtocol::REMOTE_INPUT, "RemoteInput"},
{RemoteTCPProtocol::REMOTE_TCP_INPUT, "RemoteTCPInput"},
{RemoteTCPProtocol::SDRPLAY_1, "SDRplay1"},
{RemoteTCPProtocol::SDRPLAY_V3_RSP1, "SDRplayV3 RSP1"},
{RemoteTCPProtocol::SDRPLAY_V3_RSP1A, "SDRplayV3 RSP1A"},
{RemoteTCPProtocol::SDRPLAY_V3_RSP2, "SDRplayV3 RSP2"},
{RemoteTCPProtocol::SDRPLAY_V3_RSPDUO, "SDRplayV3 RSPduo"},
{RemoteTCPProtocol::SDRPLAY_V3_RSPDX, "SDRplayV3 RSPdx"},
{RemoteTCPProtocol::SIGMF_FILE_INPUT, "SigMFFileInput"},
{RemoteTCPProtocol::SOAPY_SDR, "SoapySDR"},
{RemoteTCPProtocol::TEST_SOURCE, "TestSource"},
{RemoteTCPProtocol::USRP, "USRP"},
{RemoteTCPProtocol::XTRX, "XTRX"},
};
QString device = "Unknown";
m_remoteDevice = report.getDevice();
if (devices.contains(m_remoteDevice)) {
device = devices.value(m_remoteDevice);
}
ui->device->setText(QString("Device: %1").arg(device));
ui->protocol->setText(QString("Protocol: %1").arg(report.getProtocol()));
// Update GUI so we only show widgets available for the protocol in use
bool sdra = report.getProtocol() == "SDRA";
if (sdra && (ui->sampleBits->count() != 4))
{
ui->sampleBits->addItem("16");
ui->sampleBits->addItem("24");
ui->sampleBits->addItem("32");
}
else if (!sdra && (ui->sampleBits->count() != 1))
{
while (ui->sampleBits->count() > 1) {
ui->sampleBits->removeItem(ui->sampleBits->count() - 1);
}
}
ui->dcOffset->setVisible(sdra);
ui->iqImbalance->setVisible(sdra);
if (sdra && (ui->decim->count() != 7))
{
ui->decim->addItem("2");
ui->decim->addItem("4");
ui->decim->addItem("8");
ui->decim->addItem("16");
ui->decim->addItem("32");
ui->decim->addItem("64");
}
else if (!sdra && (ui->decim->count() != 1))
{
while (ui->decim->count() > 1) {
ui->decim->removeItem(ui->decim->count() - 1);
}
}
if (!sdra) {
ui->deltaFrequency->setValue(0);
ui->channelGain->setValue(0);
ui->decimation->setChecked(true);
}
ui->deltaFrequency->setEnabled(sdra);
ui->channelGain->setEnabled(sdra);
ui->decimation->setEnabled(sdra);
displayGains();
}
else if (RemoteTCPInputTCPHandler::MsgReportConnection::match(message))
{
const RemoteTCPInputTCPHandler::MsgReportConnection& report = (RemoteTCPInputTCPHandler::MsgReportConnection&) message;
qDebug() << "RemoteTCPInputGui::handleMessage: MsgReportConnection connected: " << report.getConnected();
if (report.getConnected())
{
m_connectionError = false;
ui->startStop->setStyleSheet("QToolButton { background-color : green; }");
}
else
{
m_connectionError = true;
ui->startStop->setStyleSheet("QToolButton { background-color : red; }");
}
}
else
{
return false;
}
}
void RemoteTCPInputGui::handleInputMessages()
{
Message* message;
while ((message = m_inputMessageQueue.pop()) != 0)
{
if (DSPSignalNotification::match(*message))
{
DSPSignalNotification* notif = (DSPSignalNotification*) message;
m_sampleRate = notif->getSampleRate();
m_centerFrequency = notif->getCenterFrequency();
qDebug("RemoteTCPInputGui::handleInputMessages: DSPSignalNotification: SampleRate:%d, CenterFrequency:%llu", notif->getSampleRate(), notif->getCenterFrequency());
updateSampleRateAndFrequency();
delete message;
}
else
{
if (handleMessage(*message))
{
delete message;
}
}
}
}
void RemoteTCPInputGui::updateSampleRateAndFrequency()
{
m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate);
m_deviceUISet->getSpectrum()->setCenterFrequency(m_centerFrequency);
ui->deviceRateText->setText(tr("%1k").arg((float)m_sampleRate / 1000));
}
void RemoteTCPInputGui::displaySettings()
{
blockApplySettings(true);
ui->centerFrequency->setValue(m_settings.m_centerFrequency / 1000);
ui->ppm->setValue(m_settings.m_loPpmCorrection);
ui->dcOffset->setChecked(m_settings.m_dcBlock);
ui->iqImbalance->setChecked(m_settings.m_iqCorrection);
ui->biasTee->setChecked(m_settings.m_biasTee);
ui->directSampling->setChecked(m_settings.m_directSampling);
ui->devSampleRate->setValue(m_settings.m_devSampleRate);
ui->decim->setCurrentIndex(m_settings.m_log2Decim);
ui->agc->setChecked(m_settings.m_agc);
ui->rfBW->setValue(m_settings.m_rfBW / 1000);
ui->deltaFrequency->setValue(m_settings.m_inputFrequencyOffset);
ui->channelGain->setValue(m_settings.m_channelGain);
ui->channelGainText->setText(tr("%1dB").arg(m_settings.m_channelGain));
ui->channelSampleRate->setValue(m_settings.m_channelSampleRate);
ui->deviceRateText->setText(tr("%1k").arg(m_settings.m_channelSampleRate / 1000.0));
ui->decimation->setChecked(!m_settings.m_channelDecimation);
ui->channelSampleRate->setEnabled(m_settings.m_channelDecimation);
ui->sampleBits->setCurrentIndex(m_settings.m_sampleBits/8-1);
ui->dataPort->setText(tr("%1").arg(m_settings.m_dataPort));
ui->dataAddress->setText(m_settings.m_dataAddress);
ui->overrideRemoteSettings->setChecked(m_settings.m_overrideRemoteSettings);
ui->preFill->setValue((int)(m_settings.m_preFill * 10.0));
ui->preFillText->setText(QString("%1s").arg(m_settings.m_preFill, 0, 'f', 2));
displayGains();
blockApplySettings(false);
}
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_rtlSDR34kGainRange(
"Gain",
{
-10, 15, 40, 65, 90, 115, 140, 165, 190, 215,
240, 290, 340, 420
}
);
const RemoteTCPInputGui::DeviceGains RemoteTCPInputGui::m_rtlSDRe4kGains({RemoteTCPInputGui::m_rtlSDR34kGainRange}, true, false);
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_rtlSDRR820GainRange(
"Gain",
{
0, 9, 14, 27, 37, 77, 87, 125, 144, 157,
166, 197, 207, 229, 254, 280, 297, 328,
338, 364, 372, 386, 402, 421, 434, 439,
445, 480, 496
}
);
const RemoteTCPInputGui::DeviceGains RemoteTCPInputGui::m_rtlSDRR820Gains({RemoteTCPInputGui::m_rtlSDRR820GainRange}, true, true);
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_airspyLNAGainRange("LNA", 0, 14, 1, ""); // Not sure what the units are for these
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_airspyMixerGainRange("Mixer", 0, 15, 1, "");
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_airspyVGAGainRange("VGA", 0, 15, 1, "");
const RemoteTCPInputGui::DeviceGains RemoteTCPInputGui::m_airspyGains({m_airspyLNAGainRange, m_airspyMixerGainRange, m_airspyVGAGainRange}, true, true);
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_airspyHFAttRange("Att", 0, 48, 6);
const RemoteTCPInputGui::DeviceGains RemoteTCPInputGui::m_airspyHFGains({m_airspyHFAttRange}, true, false);
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_bladeRF1LNARange("LNA", 0, 6, 3);
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_bladeRF1VGA1Range("VGA1", 5, 30, 1, "");
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_bladeRF1VGA2Range("VGA2", 0, 30, 3, "");
const RemoteTCPInputGui::DeviceGains RemoteTCPInputGui::m_baldeRF1Gains({m_bladeRF1LNARange, m_bladeRF1VGA1Range, m_bladeRF1VGA2Range}, false, true);
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_funCubeProPlusRange("Gain", 0, 59, 1);
const RemoteTCPInputGui::DeviceGains RemoteTCPInputGui::m_funCubeProPlusGains({m_funCubeProPlusRange}, false, true);
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_hackRFLNAGainRange("LNA", 0, 40, 8);
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_hackRFVGAGainRange("VGA", 0, 62, 2);
const RemoteTCPInputGui::DeviceGains RemoteTCPInputGui::m_hackRFGains({m_hackRFLNAGainRange, m_hackRFVGAGainRange}, false, true);
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_kiwiGainRange("Gain", 0, 120, 1);
const RemoteTCPInputGui::DeviceGains RemoteTCPInputGui::m_kiwiGains({m_kiwiGainRange}, true, false);
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_limeRange("Gain", 0, 70, 1); // Assuming auto setting
const RemoteTCPInputGui::DeviceGains RemoteTCPInputGui::m_limeGains({m_limeRange}, true, false);
// SDRplay LNA gain is device & frequency dependent (See sdrplayv3input.h SDRPlayV3LNA) sp we just fix as 0 for now
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_sdrplayV3LNAGainRange("LNA", {0});
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_sdrplayV3IFGainRange("IF", -59, 0, 1);
const RemoteTCPInputGui::DeviceGains RemoteTCPInputGui::m_sdrplayV3Gains({m_sdrplayV3LNAGainRange, m_sdrplayV3IFGainRange}, true, true);
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_plutoGainRange("Gain", 1, 77, 1);
const RemoteTCPInputGui::DeviceGains RemoteTCPInputGui::m_plutoGains({m_plutoGainRange}, true, false);
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_usrpGainRange("Gain", 0, 70, 1);
const RemoteTCPInputGui::DeviceGains RemoteTCPInputGui::m_usrpGains({m_usrpGainRange}, true, false);
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_xtrxGainRange("Gain", 0, 77, 1);
const RemoteTCPInputGui::DeviceGains RemoteTCPInputGui::m_xtrxGains({m_xtrxGainRange}, true, false);
const QHash<RemoteTCPProtocol::Device, const RemoteTCPInputGui::DeviceGains *> RemoteTCPInputGui::m_gains =
{
{RemoteTCPProtocol::RTLSDR_E4000, &m_rtlSDRe4kGains},
{RemoteTCPProtocol::RTLSDR_R820T, &m_rtlSDRR820Gains},
{RemoteTCPProtocol::AIRSPY, &m_airspyGains},
{RemoteTCPProtocol::AIRSPY_HF, &m_airspyHFGains},
{RemoteTCPProtocol::BLADE_RF1, &m_baldeRF1Gains},
{RemoteTCPProtocol::FCD_PRO_PLUS, &m_funCubeProPlusGains},
{RemoteTCPProtocol::HACK_RF, &m_hackRFGains},
{RemoteTCPProtocol::KIWI_SDR, &m_kiwiGains},
{RemoteTCPProtocol::LIME_SDR, &m_limeGains},
{RemoteTCPProtocol::SDRPLAY_V3_RSP1, &m_sdrplayV3Gains},
{RemoteTCPProtocol::SDRPLAY_V3_RSP1A, &m_sdrplayV3Gains},
{RemoteTCPProtocol::SDRPLAY_V3_RSP2, &m_sdrplayV3Gains},
{RemoteTCPProtocol::SDRPLAY_V3_RSPDUO, &m_sdrplayV3Gains},
{RemoteTCPProtocol::SDRPLAY_V3_RSPDX, &m_sdrplayV3Gains},
{RemoteTCPProtocol::PLUTO_SDR, &m_plutoGains},
{RemoteTCPProtocol::USRP, &m_usrpGains},
{RemoteTCPProtocol::XTRX, &m_xtrxGains}
};
QString RemoteTCPInputGui::gainText(int stage)
{
if (m_deviceGains) {
return QString("%1.%2%3").arg(m_settings.m_gain[stage] / 10).arg(abs(m_settings.m_gain[stage] % 10)).arg(m_deviceGains->m_gains[stage].m_units);
} else {
return "";
}
}
void RemoteTCPInputGui::displayGains()
{
QLabel *gainLabels[3] = {ui->gain1Label, ui->gain2Label, ui->gain3Label};
QDial *gain[3] = {ui->gain1, ui->gain2, ui->gain3};
QLabel *gainTexts[3] = {ui->gain1Text, ui->gain2Text, ui->gain3Text};
QWidget *gainLine[2] = {ui->gainLine1, ui->gainLine2};
m_deviceGains = m_gains.value(m_remoteDevice);
if (m_deviceGains)
{
ui->agc->setVisible(m_deviceGains->m_agc);
ui->biasTee->setVisible(m_deviceGains->m_biasTee);
ui->directSampling->setVisible(m_remoteDevice <= RemoteTCPProtocol::RTLSDR_R828D);
for (int i = 0; i < 3; i++)
{
bool visible = i < m_deviceGains->m_gains.size();
gainLabels[i]->setVisible(visible);
gain[i]->setVisible(visible);
gainTexts[i]->setVisible(visible);
if (i > 0) {
gainLine[i-1]->setVisible(visible);
}
if (visible)
{
gainLabels[i]->setText(m_deviceGains->m_gains[i].m_name);
if (m_deviceGains->m_gains[i].m_gains.size() > 0)
{
gain[i]->setMinimum(0);
gain[i]->setMaximum(m_deviceGains->m_gains[i].m_gains.size() - 1);
gain[i]->setSingleStep(1);
gain[i]->setPageStep(1);
}
else
{
gain[i]->setMinimum(m_deviceGains->m_gains[i].m_min);
gain[i]->setMaximum(m_deviceGains->m_gains[i].m_max);
gain[i]->setSingleStep(m_deviceGains->m_gains[i].m_step);
gain[i]->setPageStep(m_deviceGains->m_gains[i].m_step);
}
if (m_deviceGains->m_gains[i].m_gains.size() > 0) {
gain[i]->setValue(m_deviceGains->m_gains[i].m_gains.indexOf(m_settings.m_gain[i]));
} else {
gain[i]->setValue(m_settings.m_gain[i] / 10);
}
gainTexts[i]->setText(gainText(i));
}
}
}
}
void RemoteTCPInputGui::sendSettings()
{
if (!m_updateTimer.isActive()) {
m_updateTimer.start(100);
}
}
void RemoteTCPInputGui::on_startStop_toggled(bool checked)
{
if (m_doApplySettings)
{
m_connectionError = false;
RemoteTCPInput::MsgStartStop *message = RemoteTCPInput::MsgStartStop::create(checked);
m_sampleSource->getInputMessageQueue()->push(message);
}
}
void RemoteTCPInputGui::on_centerFrequency_changed(quint64 value)
{
m_settings.m_centerFrequency = value * 1000;
sendSettings();
}
void RemoteTCPInputGui::on_devSampleRate_changed(quint64 value)
{
m_settings.m_devSampleRate = value;
if (!m_settings.m_channelDecimation)
{
m_settings.m_channelSampleRate = m_settings.m_devSampleRate >> m_settings.m_log2Decim;
ui->channelSampleRate->setValue(m_settings.m_channelSampleRate);
}
sendSettings();
}
void RemoteTCPInputGui::on_ppm_valueChanged(int value)
{
m_settings.m_loPpmCorrection = value;
ui->ppmText->setText(tr("%1").arg(value));
sendSettings();
}
void RemoteTCPInputGui::on_dcOffset_toggled(bool checked)
{
m_settings.m_dcBlock = checked;
sendSettings();
}
void RemoteTCPInputGui::on_iqImbalance_toggled(bool checked)
{
m_settings.m_iqCorrection = checked;
sendSettings();
}
void RemoteTCPInputGui::on_biasTee_toggled(bool checked)
{
m_settings.m_biasTee = checked;
sendSettings();
}
void RemoteTCPInputGui::on_directSampling_toggled(bool checked)
{
m_settings.m_directSampling = checked;
sendSettings();
}
void RemoteTCPInputGui::on_agc_toggled(bool checked)
{
m_settings.m_agc = checked;
sendSettings();
}
void RemoteTCPInputGui::on_decim_currentIndexChanged(int index)
{
m_settings.m_log2Decim = index;
if (!m_settings.m_channelDecimation)
{
m_settings.m_channelSampleRate = m_settings.m_devSampleRate >> m_settings.m_log2Decim;
ui->channelSampleRate->setValue(m_settings.m_channelSampleRate);
}
sendSettings();
}
void RemoteTCPInputGui::on_gain1_valueChanged(int value)
{
if (m_deviceGains && (m_deviceGains->m_gains.size() >= 1) && (m_deviceGains->m_gains[0].m_gains.size() > 0)) {
m_settings.m_gain[0] = m_deviceGains->m_gains[0].m_gains[value];
} else {
m_settings.m_gain[0] = value * 10;
}
ui->gain1Text->setText(gainText(0));
sendSettings();
}
void RemoteTCPInputGui::on_gain2_valueChanged(int value)
{
if (m_deviceGains && (m_deviceGains->m_gains.size() >= 2) && (m_deviceGains->m_gains[1].m_gains.size() > 0)) {
m_settings.m_gain[1] = m_deviceGains->m_gains[1].m_gains[value];
} else {
m_settings.m_gain[1] = value * 10;
}
ui->gain2Text->setText(gainText(1));
sendSettings();
}
void RemoteTCPInputGui::on_gain3_valueChanged(int value)
{
if (m_deviceGains && (m_deviceGains->m_gains.size() >= 3) && (m_deviceGains->m_gains[2].m_gains.size() > 0)) {
m_settings.m_gain[2] = m_deviceGains->m_gains[2].m_gains[value];
} else {
m_settings.m_gain[2] = value * 10;
}
ui->gain3Text->setText(gainText(2));
sendSettings();
}
void RemoteTCPInputGui::on_rfBW_changed(int value)
{
m_settings.m_rfBW = value * 1000;
sendSettings();
}
void RemoteTCPInputGui::on_deltaFrequency_changed(int value)
{
m_settings.m_inputFrequencyOffset = value;
sendSettings();
}
void RemoteTCPInputGui::on_channelGain_valueChanged(int value)
{
m_settings.m_channelGain = value;
ui->channelGainText->setText(tr("%1dB").arg(m_settings.m_channelGain));
sendSettings();
}
void RemoteTCPInputGui::on_channelSampleRate_changed(quint64 value)
{
m_settings.m_channelSampleRate = value;
sendSettings();
}
void RemoteTCPInputGui::on_decimation_toggled(bool checked)
{
m_settings.m_channelDecimation = !checked;
if (!m_settings.m_channelDecimation)
{
m_settings.m_channelSampleRate = m_settings.m_devSampleRate >> m_settings.m_log2Decim;
ui->channelSampleRate->setValue(m_settings.m_channelSampleRate);
}
ui->channelSampleRate->setEnabled(!checked);
sendSettings();
}
void RemoteTCPInputGui::on_sampleBits_currentIndexChanged(int index)
{
m_settings.m_sampleBits = 8 * (index + 1);
sendSettings();
}
void RemoteTCPInputGui::on_dataAddress_editingFinished()
{
m_settings.m_dataAddress = ui->dataAddress->text();
sendSettings();
}
void RemoteTCPInputGui::on_dataPort_editingFinished()
{
bool ok;
quint16 udpPort = ui->dataPort->text().toInt(&ok);
if ((!ok) || (udpPort < 1024)) {
udpPort = 9998;
}
m_settings.m_dataPort = udpPort;
ui->dataPort->setText(tr("%1").arg(m_settings.m_dataPort));
sendSettings();
}
void RemoteTCPInputGui::on_overrideRemoteSettings_toggled(bool checked)
{
m_settings.m_overrideRemoteSettings = checked;
sendSettings();
}
void RemoteTCPInputGui::on_preFill_valueChanged(int value)
{
m_settings.m_preFill = value/10.0f;
ui->preFillText->setText(QString("%1s").arg(m_settings.m_preFill, 0, 'f', 2));
sendSettings();
}
void RemoteTCPInputGui::updateHardware()
{
if (m_doApplySettings)
{
qDebug() << "RemoteTCPInputGui::updateHardware";
RemoteTCPInput::MsgConfigureRemoteTCPInput* message =
RemoteTCPInput::MsgConfigureRemoteTCPInput::create(m_settings, m_forceSettings);
m_sampleSource->getInputMessageQueue()->push(message);
m_forceSettings = false;
m_updateTimer.stop();
}
}
void RemoteTCPInputGui::updateStatus()
{
int state = m_deviceUISet->m_deviceAPI->state();
if (!m_connectionError && (m_lastEngineState != state))
{
switch(state)
{
case DeviceAPI::StNotStarted:
ui->startStop->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
break;
case DeviceAPI::StIdle:
ui->startStop->setStyleSheet("QToolButton { background-color : blue; }");
break;
case DeviceAPI::StRunning:
ui->startStop->setStyleSheet("QToolButton { background-color : green; }");
break;
case DeviceAPI::StError:
ui->startStop->setStyleSheet("QToolButton { background-color : red; }");
QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceAPI->errorMessage());
break;
default:
break;
}
m_lastEngineState = state;
}
}
void RemoteTCPInputGui::openDeviceSettingsDialog(const QPoint& p)
{
if (m_contextMenuType == ContextMenuDeviceSettings)
{
BasicDeviceSettingsDialog dialog(this);
dialog.setUseReverseAPI(m_settings.m_useReverseAPI);
dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress);
dialog.setReverseAPIPort(m_settings.m_reverseAPIPort);
dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex);
dialog.move(p);
dialog.exec();
m_settings.m_useReverseAPI = dialog.useReverseAPI();
m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress();
m_settings.m_reverseAPIPort = dialog.getReverseAPIPort();
m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex();
sendSettings();
}
resetContextMenuType();
}
void RemoteTCPInputGui::makeUIConnections()
{
QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &RemoteTCPInputGui::on_startStop_toggled);
QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &RemoteTCPInputGui::on_centerFrequency_changed);
QObject::connect(ui->ppm, &QSlider::valueChanged, this, &RemoteTCPInputGui::on_ppm_valueChanged);
QObject::connect(ui->dcOffset, &ButtonSwitch::toggled, this, &RemoteTCPInputGui::on_dcOffset_toggled);
QObject::connect(ui->iqImbalance, &ButtonSwitch::toggled, this, &RemoteTCPInputGui::on_iqImbalance_toggled);
QObject::connect(ui->biasTee, &ButtonSwitch::toggled, this, &RemoteTCPInputGui::on_biasTee_toggled);
QObject::connect(ui->directSampling, &ButtonSwitch::toggled, this, &RemoteTCPInputGui::on_directSampling_toggled);
QObject::connect(ui->devSampleRate, &ValueDial::changed, this, &RemoteTCPInputGui::on_devSampleRate_changed);
QObject::connect(ui->decim, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RemoteTCPInputGui::on_decim_currentIndexChanged);
QObject::connect(ui->gain1, &QDial::valueChanged, this, &RemoteTCPInputGui::on_gain1_valueChanged);
QObject::connect(ui->gain2, &QDial::valueChanged, this, &RemoteTCPInputGui::on_gain2_valueChanged);
QObject::connect(ui->gain3, &QDial::valueChanged, this, &RemoteTCPInputGui::on_gain3_valueChanged);
QObject::connect(ui->agc, &ButtonSwitch::toggled, this, &RemoteTCPInputGui::on_agc_toggled);
QObject::connect(ui->rfBW, &ValueDial::changed, this, &RemoteTCPInputGui::on_rfBW_changed);
QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &RemoteTCPInputGui::on_deltaFrequency_changed);
QObject::connect(ui->channelGain, &QDial::valueChanged, this, &RemoteTCPInputGui::on_channelGain_valueChanged);
QObject::connect(ui->channelSampleRate, &ValueDial::changed, this, &RemoteTCPInputGui::on_channelSampleRate_changed);
QObject::connect(ui->decimation, &ButtonSwitch::toggled, this, &RemoteTCPInputGui::on_decimation_toggled);
QObject::connect(ui->sampleBits, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RemoteTCPInputGui::on_sampleBits_currentIndexChanged);
QObject::connect(ui->dataAddress, &QLineEdit::editingFinished, this, &RemoteTCPInputGui::on_dataAddress_editingFinished);
QObject::connect(ui->dataPort, &QLineEdit::editingFinished, this, &RemoteTCPInputGui::on_dataPort_editingFinished);
QObject::connect(ui->overrideRemoteSettings, &ButtonSwitch::toggled, this, &RemoteTCPInputGui::on_overrideRemoteSettings_toggled);
QObject::connect(ui->preFill, &QDial::valueChanged, this, &RemoteTCPInputGui::on_preFill_valueChanged);
}

View File

@ -0,0 +1,199 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// Copyright (C) 2022 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_REMOTETCPINPUTGUI_H
#define INCLUDE_REMOTETCPINPUTGUI_H
#include <QTimer>
#include <QElapsedTimer>
#include <QWidget>
#include "device/devicegui.h"
#include "util/messagequeue.h"
#include "remotetcpinput.h"
#include "../../channelrx/remotetcpsink/remotetcpprotocol.h"
class DeviceUISet;
class QNetworkAccessManager;
class QNetworkReply;
class QJsonObject;
namespace Ui {
class RemoteTCPInputGui;
}
class RemoteTCPInputGui : public DeviceGUI {
Q_OBJECT
struct DeviceGains {
struct GainRange {
QString m_name;
int m_min;
int m_max;
int m_step; // In dB
QVector<int> m_gains; // In 10ths of dB
QString m_units; // Units label for display in the GUI
GainRange(const QString& name, int min, int max, int step, const QString& units = "dB") :
m_name(name),
m_min(min),
m_max(max),
m_step(step),
m_units(units)
{
}
GainRange(const QString& name, QVector<int> gains, const QString& units = "dB") :
m_name(name),
m_min(0),
m_max(0),
m_step(0),
m_gains(gains),
m_units(units)
{
}
};
DeviceGains()
{
}
DeviceGains(QList<GainRange> gains, bool agc, bool biasTee) :
m_gains(gains),
m_agc(agc),
m_biasTee(biasTee)
{
}
QList<GainRange> m_gains;
bool m_agc;
bool m_biasTee;
};
public:
explicit RemoteTCPInputGui(DeviceUISet *deviceUISet, QWidget* parent = 0);
virtual ~RemoteTCPInputGui();
virtual void destroy();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
protected:
void resizeEvent(QResizeEvent* size);
private:
Ui::RemoteTCPInputGui* ui;
RemoteTCPInputSettings m_settings; //!< current settings
RemoteTCPInput* m_sampleSource;
QTimer m_updateTimer;
QTimer m_statusTimer;
int m_lastEngineState;
MessageQueue m_inputMessageQueue;
int m_sampleRate;
quint64 m_centerFrequency;
bool m_doApplySettings;
bool m_forceSettings;
const DeviceGains *m_deviceGains;
RemoteTCPProtocol::Device m_remoteDevice; // Remote device reported when connecting
bool m_connectionError;
static const DeviceGains::GainRange m_rtlSDR34kGainRange;
static const DeviceGains m_rtlSDRe4kGains;
static const DeviceGains::GainRange m_rtlSDRR820GainRange;
static const DeviceGains m_rtlSDRR820Gains;
static const DeviceGains::GainRange m_airspyLNAGainRange;
static const DeviceGains::GainRange m_airspyMixerGainRange;
static const DeviceGains::GainRange m_airspyVGAGainRange;
static const DeviceGains m_airspyGains;
static const DeviceGains::GainRange m_airspyHFAttRange;
static const DeviceGains m_airspyHFGains;
static const DeviceGains::GainRange m_bladeRF1LNARange;
static const DeviceGains::GainRange m_bladeRF1VGA1Range;
static const DeviceGains::GainRange m_bladeRF1VGA2Range;
static const DeviceGains m_baldeRF1Gains;
static const DeviceGains::GainRange m_funCubeProPlusRange;
static const DeviceGains m_funCubeProPlusGains;
static const DeviceGains::GainRange m_hackRFLNAGainRange;
static const DeviceGains::GainRange m_hackRFVGAGainRange;
static const DeviceGains m_hackRFGains;
static const DeviceGains::GainRange m_kiwiGainRange;
static const DeviceGains m_kiwiGains;
static const DeviceGains::GainRange m_limeRange;
static const DeviceGains m_limeGains;
static const DeviceGains::GainRange m_sdrplayV3LNAGainRange;
static const DeviceGains::GainRange m_sdrplayV3IFGainRange;
static const DeviceGains m_sdrplayV3Gains;
static const DeviceGains::GainRange m_plutoGainRange;
static const DeviceGains m_plutoGains;
static const DeviceGains::GainRange m_usrpGainRange;
static const DeviceGains m_usrpGains;
static const DeviceGains::GainRange m_xtrxGainRange;
static const DeviceGains m_xtrxGains;
static const QHash<RemoteTCPProtocol::Device, const DeviceGains *> m_gains;
void blockApplySettings(bool block);
void displaySettings();
QString gainText(int stage);
void displayGains();
void displayRemoteSettings();
void displayRemoteShift();
void sendSettings();
void updateSampleRateAndFrequency();
void applyDecimation();
void applyPosition();
bool handleMessage(const Message& message);
void makeUIConnections();
private slots:
void handleInputMessages();
void on_startStop_toggled(bool checked);
void on_centerFrequency_changed(quint64 value);
void on_ppm_valueChanged(int value);
void on_dcOffset_toggled(bool checked);
void on_iqImbalance_toggled(bool checked);
void on_biasTee_toggled(bool checked);
void on_directSampling_toggled(bool checked);
void on_devSampleRate_changed(quint64 value);
void on_decim_currentIndexChanged(int index);
void on_gain1_valueChanged(int value);
void on_gain2_valueChanged(int value);
void on_gain3_valueChanged(int value);
void on_agc_toggled(bool checked);
void on_rfBW_changed(int value);
void on_deltaFrequency_changed(int value);
void on_channelGain_valueChanged(int value);
void on_channelSampleRate_changed(quint64 value);
void on_decimation_toggled(bool checked);
void on_sampleBits_currentIndexChanged(int index);
void on_dataAddress_editingFinished();
void on_dataPort_editingFinished();
void on_overrideRemoteSettings_toggled(bool checked);
void on_preFill_valueChanged(int value);
void updateHardware();
void updateStatus();
void openDeviceSettingsDialog(const QPoint& p);
};
#endif // INCLUDE_REMOTETCPINPUTGUI_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,149 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// Copyright (C) 2022 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 "plugin/pluginapi.h"
#include "util/simpleserializer.h"
#ifdef SERVER_MODE
#include "remotetcpinput.h"
#else
#include "remotetcpinputgui.h"
#endif
#include "remotetcpinputplugin.h"
#include "remotetcpinputwebapiadapter.h"
const PluginDescriptor RemoteTCPInputPlugin::m_pluginDescriptor = {
QStringLiteral("RemoteTCPInput"),
QStringLiteral("Remote TCP device input"),
QStringLiteral("7.6.0"),
QStringLiteral("(c) Jon Beniston, M7RCE"),
QStringLiteral("https://github.com/f4exb/sdrangel"),
true,
QStringLiteral("https://github.com/f4exb/sdrangel")
};
static constexpr const char* const m_hardwareID = "RemoteTCPInput";
static constexpr const char* const m_deviceTypeID = REMOTETCPINPUT_DEVICE_TYPE_ID;
RemoteTCPInputPlugin::RemoteTCPInputPlugin(QObject* parent) :
QObject(parent)
{
}
const PluginDescriptor& RemoteTCPInputPlugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
void RemoteTCPInputPlugin::initPlugin(PluginAPI* pluginAPI)
{
pluginAPI->registerSampleSource(m_deviceTypeID, this);
}
void RemoteTCPInputPlugin::enumOriginDevices(QStringList& listedHwIds, OriginDevices& originDevices)
{
if (listedHwIds.contains(m_hardwareID)) { // check if it was done
return;
}
originDevices.append(OriginDevice(
"RemoteTCPInput",
m_hardwareID,
QString(),
0,
1, // nb Rx
0 // nb Tx
));
listedHwIds.append(m_hardwareID);
}
PluginInterface::SamplingDevices RemoteTCPInputPlugin::enumSampleSources(const OriginDevices& originDevices)
{
SamplingDevices result;
for (OriginDevices::const_iterator it = originDevices.begin(); it != originDevices.end(); ++it)
{
if (it->hardwareId == m_hardwareID)
{
result.append(SamplingDevice(
it->displayableName,
m_hardwareID,
m_deviceTypeID,
it->serial,
it->sequence,
PluginInterface::SamplingDevice::BuiltInDevice,
PluginInterface::SamplingDevice::StreamSingleRx,
1,
0
));
}
}
return result;
}
#ifdef SERVER_MODE
DeviceGUI* RemoteTCPInputPlugin::createSampleSourcePluginInstanceGUI(
const QString& sourceId,
QWidget **widget,
DeviceUISet *deviceUISet)
{
(void) sourceId;
(void) widget;
(void) deviceUISet;
return 0;
}
#else
DeviceGUI* RemoteTCPInputPlugin::createSampleSourcePluginInstanceGUI(
const QString& sourceId,
QWidget **widget,
DeviceUISet *deviceUISet)
{
if(sourceId == m_deviceTypeID)
{
RemoteTCPInputGui* gui = new RemoteTCPInputGui(deviceUISet);
*widget = gui;
return gui;
}
else
{
return 0;
}
}
#endif
DeviceSampleSource *RemoteTCPInputPlugin::createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI)
{
if (sourceId == m_deviceTypeID)
{
RemoteTCPInput* input = new RemoteTCPInput(deviceAPI);
return input;
}
else
{
return 0;
}
}
DeviceWebAPIAdapter *RemoteTCPInputPlugin::createDeviceWebAPIAdapter() const
{
return new RemoteTCPInputWebAPIAdapter();
}

View File

@ -0,0 +1,53 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// Copyright (C) 2022 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_REMOTETCPINPUTPLUGIN_H
#define INCLUDE_REMOTETCPINPUTPLUGIN_H
#include <QObject>
#include "plugin/plugininterface.h"
#define REMOTETCPINPUT_DEVICE_TYPE_ID "sdrangel.samplesource.remotetcpinput"
class PluginAPI;
class RemoteTCPInputPlugin : public QObject, public PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID REMOTETCPINPUT_DEVICE_TYPE_ID)
public:
explicit RemoteTCPInputPlugin(QObject* parent = NULL);
const PluginDescriptor& getPluginDescriptor() const;
void initPlugin(PluginAPI* pluginAPI);
virtual void enumOriginDevices(QStringList& listedHwIds, OriginDevices& originDevices);
virtual SamplingDevices enumSampleSources(const OriginDevices& originDevices);
virtual DeviceGUI* createSampleSourcePluginInstanceGUI(
const QString& sourceId,
QWidget **widget,
DeviceUISet *deviceUISet);
virtual DeviceSampleSource* createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI);
virtual DeviceWebAPIAdapter* createDeviceWebAPIAdapter() const;
private:
static const PluginDescriptor m_pluginDescriptor;
};
#endif // INCLUDE_REMOTETCPINPUTPLUGIN_H

View File

@ -0,0 +1,148 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2017 Edouard Griffiths, F4EXB //
// Copyright (C) 2022 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 "util/simpleserializer.h"
#include "remotetcpinputsettings.h"
RemoteTCPInputSettings::RemoteTCPInputSettings()
{
resetToDefaults();
}
void RemoteTCPInputSettings::resetToDefaults()
{
m_centerFrequency = 435000000;
m_loPpmCorrection = 0;
m_dcBlock = false;
m_iqCorrection = false;
m_biasTee = false;
m_directSampling = false;
m_devSampleRate = 2000000;
m_log2Decim = 1;
for (int i = 0; i < m_maxGains; i++) {
m_gain[i] = 0;
}
m_agc = false;
m_rfBW = 2500000;
m_inputFrequencyOffset = 0;
m_channelGain = 0;
m_channelSampleRate = m_devSampleRate;
m_channelDecimation = false;
m_sampleBits = 8;
m_dataAddress = "127.0.0.1";
m_dataPort = 1234;
m_overrideRemoteSettings = true;
m_preFill = 1.0f;
m_useReverseAPI = false;
m_reverseAPIAddress = "127.0.0.1";
m_reverseAPIPort = 8888;
m_reverseAPIDeviceIndex = 0;
}
QByteArray RemoteTCPInputSettings::serialize() const
{
SimpleSerializer s(1);
s.writeS32(1, m_loPpmCorrection);
s.writeBool(2, m_dcBlock);
s.writeBool(3, m_iqCorrection);
s.writeBool(4, m_biasTee);
s.writeBool(5, m_directSampling);
s.writeS32(6, m_devSampleRate);
s.writeS32(7, m_log2Decim);
s.writeBool(9, m_agc);
s.writeS32(10, m_rfBW);
s.writeS32(11, m_inputFrequencyOffset);
s.writeS32(12, m_channelGain);
s.writeS32(13, m_channelSampleRate);
s.writeBool(14, m_channelDecimation);
s.writeS32(15, m_sampleBits);
s.writeU32(16, m_dataPort);
s.writeString(17, m_dataAddress);
s.writeBool(18, m_overrideRemoteSettings);
s.writeFloat(19, m_preFill);
s.writeBool(20, m_useReverseAPI);
s.writeString(21, m_reverseAPIAddress);
s.writeU32(22, m_reverseAPIPort);
s.writeU32(23, m_reverseAPIDeviceIndex);
for (int i = 0; i < m_maxGains; i++) {
s.writeS32(30+i, m_gain[i]);
}
return s.final();
}
bool RemoteTCPInputSettings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if (!d.isValid())
{
resetToDefaults();
return false;
}
if (d.getVersion() == 1)
{
quint32 uintval;
d.readS32(1, &m_loPpmCorrection, 0);
d.readBool(2, &m_dcBlock, false);
d.readBool(3, &m_iqCorrection, false);
d.readBool(4, &m_biasTee, false);
d.readBool(5, &m_directSampling, false);
d.readS32(6, &m_devSampleRate, 2000000);
d.readS32(7, &m_log2Decim, 1);
d.readBool(9, &m_agc, false);
d.readS32(10, &m_rfBW, 2500000);
d.readS32(11, &m_inputFrequencyOffset, 0);
d.readS32(12, &m_channelGain, 0);
d.readS32(13, &m_channelSampleRate, 2000000);
d.readBool(14, &m_channelDecimation, false);
d.readS32(15, &m_sampleBits, 8);
d.readU32(16, &uintval, 1234);
m_dataPort = uintval % (1<<16);
d.readString(17, &m_dataAddress, "127.0.0.1");
d.readBool(18, &m_overrideRemoteSettings, false);
d.readFloat(19, &m_preFill, 1.0f);
d.readBool(20, &m_useReverseAPI, false);
d.readString(21, &m_reverseAPIAddress, "127.0.0.1");
d.readU32(22, &uintval, 0);
if ((uintval > 1023) && (uintval < 65535)) {
m_reverseAPIPort = uintval;
} else {
m_reverseAPIPort = 8888;
}
d.readU32(23, &uintval, 0);
m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval;
for (int i = 0; i < m_maxGains; i++) {
d.readS32(30+i, &m_gain[i], 0);
}
return true;
}
else
{
resetToDefaults();
return false;
}
}

View File

@ -0,0 +1,60 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2017 Edouard Griffiths, F4EXB //
// Copyright (C) 2022 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 PLUGINS_SAMPLESOURCE_REMOTETCPINPUT_REMOTETCPINPUTSETTINGS_H_
#define PLUGINS_SAMPLESOURCE_REMOTETCPINPUT_REMOTETCPINPUTSETTINGS_H_
#include <QByteArray>
#include <QString>
struct RemoteTCPInputSettings
{
static const int m_maxGains = 3;
uint64_t m_centerFrequency;
qint32 m_loPpmCorrection;
bool m_dcBlock;
bool m_iqCorrection;
bool m_biasTee;
bool m_directSampling; // RTLSDR only
int m_devSampleRate;
int m_log2Decim;
qint32 m_gain[m_maxGains]; // 10ths of a dB
bool m_agc;
qint32 m_rfBW;
qint32 m_inputFrequencyOffset;
qint32 m_channelGain; // In dB
qint32 m_channelSampleRate;
bool m_channelDecimation; // If false, m_channelSampleRate==m_devSampleRate
qint32 m_sampleBits; // Number of bits used to transmit IQ samples (8,16,24,32)
QString m_dataAddress;
quint16 m_dataPort;
bool m_overrideRemoteSettings; // When connected, apply local settings to remote, or apply remote settings to local
float m_preFill; // Input buffer prefill in seconds
bool m_useReverseAPI;
QString m_reverseAPIAddress;
uint16_t m_reverseAPIPort;
uint16_t m_reverseAPIDeviceIndex;
RemoteTCPInputSettings();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
};
#endif /* PLUGINS_SAMPLESOURCE_REMOTETCPINPUT_REMOTETCPINPUTSETTINGS_H_ */

View File

@ -0,0 +1,782 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// Copyright (C) 2022 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 <QUdpSocket>
#include <QDebug>
#include "dsp/dspcommands.h"
#include "dsp/dspengine.h"
#include "device/deviceapi.h"
#include "remotetcpinputtcphandler.h"
#include "remotetcpinput.h"
#include "../../channelrx/remotetcpsink/remotetcpprotocol.h"
MESSAGE_CLASS_DEFINITION(RemoteTCPInputTCPHandler::MsgReportRemoteDevice, Message)
MESSAGE_CLASS_DEFINITION(RemoteTCPInputTCPHandler::MsgReportConnection, Message)
MESSAGE_CLASS_DEFINITION(RemoteTCPInputTCPHandler::MsgConfigureTcpHandler, Message)
RemoteTCPInputTCPHandler::RemoteTCPInputTCPHandler(SampleSinkFifo *sampleFifo, DeviceAPI *deviceAPI) :
m_deviceAPI(deviceAPI),
m_running(false),
m_dataSocket(nullptr),
m_tcpBuf(nullptr),
m_sampleFifo(sampleFifo),
m_messageQueueToGUI(0),
m_fillBuffer(true),
m_timer(this),
m_reconnectTimer(this),
m_converterBuffer(nullptr),
m_converterBufferNbSamples(0),
m_mutex(QMutex::Recursive),
m_settings()
{
m_tcpBuf = new char[m_sampleFifo->size()*2*4];
m_timer.setInterval(125);
connect(&m_reconnectTimer, SIGNAL(timeout()), this, SLOT(reconnect()));
m_reconnectTimer.setSingleShot(true);
}
RemoteTCPInputTCPHandler::~RemoteTCPInputTCPHandler()
{
delete[] m_tcpBuf;
if (m_converterBuffer) {
delete[] m_converterBuffer;
}
cleanup();
}
void RemoteTCPInputTCPHandler::reset()
{
QMutexLocker mutexLocker(&m_mutex);
m_inputMessageQueue.clear();
}
// start() is called from DSPDeviceSourceEngine thread
// QTcpSockets need to be created on same thread they are used from, so only create it in started()
void RemoteTCPInputTCPHandler::start()
{
QMutexLocker mutexLocker(&m_mutex);
qDebug("RemoteTCPInputTCPHandler::start");
if (m_running) {
return;
}
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
connect(thread(), SIGNAL(started()), this, SLOT(started()));
connect(thread(), SIGNAL(finished()), this, SLOT(finished()));
m_running = true;
}
void RemoteTCPInputTCPHandler::stop()
{
QMutexLocker mutexLocker(&m_mutex);
qDebug("RemoteTCPInputTCPHandler::stop");
disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
}
void RemoteTCPInputTCPHandler::started()
{
QMutexLocker mutexLocker(&m_mutex);
// Don't connectToHost until we get settings
connect(&m_timer, SIGNAL(timeout()), this, SLOT(processData()));
m_timer.start();
disconnect(thread(), SIGNAL(started()), this, SLOT(started()));
}
void RemoteTCPInputTCPHandler::finished()
{
QMutexLocker mutexLocker(&m_mutex);
m_timer.stop();
disconnect(&m_timer, SIGNAL(timeout()), this, SLOT(processData()));
disconnectFromHost();
disconnect(thread(), SIGNAL(finished()), this, SLOT(finished()));
m_running = false;
}
void RemoteTCPInputTCPHandler::connectToHost(const QString& address, quint16 port)
{
qDebug("RemoteTCPInputTCPHandler::connectToHost: connect to %s:%d", address.toStdString().c_str(), port);
m_dataSocket = new QTcpSocket(this);
m_fillBuffer = true;
m_readMetaData = false;
connect(m_dataSocket, SIGNAL(readyRead()), this, SLOT(dataReadyRead()));
connect(m_dataSocket, SIGNAL(connected()), this, SLOT(connected()));
connect(m_dataSocket, SIGNAL(disconnected()), this, SLOT(disconnected()));
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(m_dataSocket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &RemoteTCPInputTCPHandler::errorOccurred);
#else
connect(m_dataSocket, &QAbstractSocket::errorOccurred, this, &RemoteTCPInputTCPHandler::errorOccurred);
#endif
m_dataSocket->connectToHost(address, port);
}
void RemoteTCPInputTCPHandler::disconnectFromHost()
{
if (m_dataSocket)
{
qDebug() << "RemoteTCPInputTCPHandler::disconnectFromHost";
disconnect(m_dataSocket, SIGNAL(readyRead()), this, SLOT(dataReadyRead()));
disconnect(m_dataSocket, SIGNAL(connected()), this, SLOT(connected()));
disconnect(m_dataSocket, SIGNAL(disconnected()), this, SLOT(disconnected()));
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
disconnect(m_dataSocket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &RemoteTCPInputTCPHandler::errorOccurred);
#else
disconnect(m_dataSocket, &QAbstractSocket::errorOccurred, this, &RemoteTCPInputTCPHandler::errorOccurred);
#endif
m_dataSocket->disconnectFromHost();
cleanup();
}
}
void RemoteTCPInputTCPHandler::cleanup()
{
if (m_dataSocket)
{
m_dataSocket->deleteLater();
m_dataSocket = nullptr;
}
}
// Clear input buffer when settings change that invalidate the data in it
// E.g. sample rate or bit depth
void RemoteTCPInputTCPHandler::clearBuffer()
{
if (m_dataSocket)
{
m_dataSocket->flush();
m_dataSocket->readAll();
m_fillBuffer = true;
}
}
void RemoteTCPInputTCPHandler::setSampleRate(int sampleRate)
{
QMutexLocker mutexLocker(&m_mutex);
quint8 request[5];
request[0] = RemoteTCPProtocol::setSampleRate;
RemoteTCPProtocol::encodeUInt32(&request[1], sampleRate);
if (m_dataSocket) {
m_dataSocket->write((char*)request, sizeof(request));
}
}
void RemoteTCPInputTCPHandler::setCenterFrequency(quint64 frequency)
{
QMutexLocker mutexLocker(&m_mutex);
quint8 request[5];
request[0] = RemoteTCPProtocol::setCenterFrequency;
RemoteTCPProtocol::encodeUInt32(&request[1], frequency);
if (m_dataSocket) {
m_dataSocket->write((char*)request, sizeof(request));
}
}
void RemoteTCPInputTCPHandler::setTunerAGC(bool agc)
{
QMutexLocker mutexLocker(&m_mutex);
quint8 request[5];
request[0] = RemoteTCPProtocol::setTunerGainMode;
RemoteTCPProtocol::encodeUInt32(&request[1], agc);
if (m_dataSocket) {
m_dataSocket->write((char*)request, sizeof(request));
}
}
void RemoteTCPInputTCPHandler::setTunerGain(int gain)
{
QMutexLocker mutexLocker(&m_mutex);
quint8 request[5];
request[0] = RemoteTCPProtocol::setTunerGain;
RemoteTCPProtocol::encodeUInt32(&request[1], gain);
if (m_dataSocket) {
m_dataSocket->write((char*)request, sizeof(request));
}
}
void RemoteTCPInputTCPHandler::setFreqCorrection(int correction)
{
QMutexLocker mutexLocker(&m_mutex);
quint8 request[5];
request[0] = RemoteTCPProtocol::setFrequencyCorrection;
RemoteTCPProtocol::encodeUInt32(&request[1], correction);
if (m_dataSocket) {
m_dataSocket->write((char*)request, sizeof(request));
}
}
void RemoteTCPInputTCPHandler::setIFGain(quint16 stage, quint16 gain)
{
QMutexLocker mutexLocker(&m_mutex);
quint8 request[5];
request[0] = RemoteTCPProtocol::setTunerIFGain;
RemoteTCPProtocol::encodeUInt32(&request[1], (stage << 16) | gain);
if (m_dataSocket) {
m_dataSocket->write((char*)request, sizeof(request));
}
}
void RemoteTCPInputTCPHandler::setAGC(bool agc)
{
QMutexLocker mutexLocker(&m_mutex);
quint8 request[5];
request[0] = RemoteTCPProtocol::setAGCMode;
RemoteTCPProtocol::encodeUInt32(&request[1], agc);
if (m_dataSocket) {
m_dataSocket->write((char*)request, sizeof(request));
}
}
void RemoteTCPInputTCPHandler::setDirectSampling(bool enabled)
{
QMutexLocker mutexLocker(&m_mutex);
quint8 request[5];
request[0] = RemoteTCPProtocol::setDirectSampling;
RemoteTCPProtocol::encodeUInt32(&request[1], enabled);
if (m_dataSocket) {
m_dataSocket->write((char*)request, sizeof(request));
}
}
void RemoteTCPInputTCPHandler::setDCOffsetRemoval(bool enabled)
{
QMutexLocker mutexLocker(&m_mutex);
quint8 request[5];
request[0] = RemoteTCPProtocol::setDCOffsetRemoval;
RemoteTCPProtocol::encodeUInt32(&request[1], enabled);
if (m_dataSocket) {
m_dataSocket->write((char*)request, sizeof(request));
}
}
void RemoteTCPInputTCPHandler::setIQCorrection(bool enabled)
{
QMutexLocker mutexLocker(&m_mutex);
quint8 request[5];
request[0] = RemoteTCPProtocol::setIQCorrection;
RemoteTCPProtocol::encodeUInt32(&request[1], enabled);
if (m_dataSocket) {
m_dataSocket->write((char*)request, sizeof(request));
}
}
void RemoteTCPInputTCPHandler::setBiasTee(bool enabled)
{
QMutexLocker mutexLocker(&m_mutex);
quint8 request[5];
request[0] = RemoteTCPProtocol::setBiasTee;
RemoteTCPProtocol::encodeUInt32(&request[1], enabled);
if (m_dataSocket) {
m_dataSocket->write((char*)request, sizeof(request));
}
}
void RemoteTCPInputTCPHandler::setBandwidth(int bandwidth)
{
QMutexLocker mutexLocker(&m_mutex);
quint8 request[5];
request[0] = RemoteTCPProtocol::setTunerBandwidth;
RemoteTCPProtocol::encodeUInt32(&request[1], bandwidth);
if (m_dataSocket) {
m_dataSocket->write((char*)request, sizeof(request));
}
}
void RemoteTCPInputTCPHandler::setDecimation(int dec)
{
QMutexLocker mutexLocker(&m_mutex);
quint8 request[5];
request[0] = RemoteTCPProtocol::setDecimation;
RemoteTCPProtocol::encodeUInt32(&request[1], dec);
if (m_dataSocket) {
m_dataSocket->write((char*)request, sizeof(request));
}
}
void RemoteTCPInputTCPHandler::setChannelSampleRate(int sampleRate)
{
QMutexLocker mutexLocker(&m_mutex);
quint8 request[5];
request[0] = RemoteTCPProtocol::setChannelSampleRate;
RemoteTCPProtocol::encodeUInt32(&request[1], sampleRate);
if (m_dataSocket) {
m_dataSocket->write((char*)request, sizeof(request));
}
}
void RemoteTCPInputTCPHandler::setChannelFreqOffset(int offset)
{
QMutexLocker mutexLocker(&m_mutex);
quint8 request[5];
request[0] = RemoteTCPProtocol::setChannelFreqOffset;
RemoteTCPProtocol::encodeUInt32(&request[1], offset);
if (m_dataSocket) {
m_dataSocket->write((char*)request, sizeof(request));
}
}
void RemoteTCPInputTCPHandler::setChannelGain(int gain)
{
QMutexLocker mutexLocker(&m_mutex);
quint8 request[5];
request[0] = RemoteTCPProtocol::setChannelGain;
RemoteTCPProtocol::encodeUInt32(&request[1], gain);
if (m_dataSocket) {
m_dataSocket->write((char*)request, sizeof(request));
}
}
void RemoteTCPInputTCPHandler::setSampleBitDepth(int sampleBits)
{
QMutexLocker mutexLocker(&m_mutex);
quint8 request[5];
request[0] = RemoteTCPProtocol::setSampleBitDepth;
RemoteTCPProtocol::encodeUInt32(&request[1], sampleBits);
if (m_dataSocket) {
m_dataSocket->write((char*)request, sizeof(request));
}
}
void RemoteTCPInputTCPHandler::applySettings(const RemoteTCPInputSettings& settings, bool force)
{
qDebug() << "RemoteTCPInputTCPHandler::applySettings: "
<< "force: " << force
<< "m_dataAddress: " << settings.m_dataAddress
<< "m_dataPort: " << settings.m_dataPort
<< "m_devSampleRate: " << settings.m_devSampleRate
<< "m_channelSampleRate: " << settings.m_channelSampleRate;
QMutexLocker mutexLocker(&m_mutex);
if ((settings.m_centerFrequency != m_settings.m_centerFrequency) || force) {
setCenterFrequency(settings.m_centerFrequency);
}
if ((settings.m_loPpmCorrection != m_settings.m_loPpmCorrection) || force) {
setFreqCorrection(settings.m_loPpmCorrection);
}
if ((settings.m_dcBlock != m_settings.m_dcBlock) || force) {
setDCOffsetRemoval(settings.m_dcBlock);
}
if ((settings.m_iqCorrection != m_settings.m_iqCorrection) || force) {
setIQCorrection(settings.m_iqCorrection);
}
if ((settings.m_biasTee != m_settings.m_biasTee) || force) {
setBiasTee(settings.m_biasTee);
}
if ((settings.m_directSampling != m_settings.m_directSampling) || force) {
setDirectSampling(settings.m_directSampling);
}
if ((settings.m_log2Decim != m_settings.m_log2Decim) || force) {
setDecimation(settings.m_log2Decim);
}
if ((settings.m_devSampleRate != m_settings.m_devSampleRate) || force) {
setSampleRate(settings.m_devSampleRate);
}
if ((settings.m_agc != m_settings.m_agc) || force) {
setAGC(settings.m_agc);
}
if (force) {
setTunerAGC(1); // The SDRangel RTLSDR driver always has tuner gain as manual
}
if ((settings.m_gain[0] != m_settings.m_gain[0]) || force) {
setTunerGain(settings.m_gain[0]);
}
for (int i = 1; i < 3; i++)
{
if ((settings.m_gain[i] != m_settings.m_gain[i]) || force) {
setIFGain(i, settings.m_gain[i]);
}
}
if ((settings.m_rfBW != m_settings.m_rfBW) || force) {
setBandwidth(settings.m_rfBW);
}
if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) {
setChannelFreqOffset(settings.m_inputFrequencyOffset);
}
if ((settings.m_channelGain != m_settings.m_channelGain) || force) {
setChannelGain(settings.m_channelGain);
}
if ((settings.m_channelSampleRate != m_settings.m_channelSampleRate) || force)
{
// Resize FIFO to give us 1 second
// Can't do this while running
if (!m_running && settings.m_channelSampleRate > m_sampleFifo->size())
{
qDebug() << "RemoteTCPInputTCPHandler::applySettings: Resizing sample FIFO from " << m_sampleFifo->size() << "to" << settings.m_channelSampleRate;
m_sampleFifo->setSize(settings.m_channelSampleRate);
delete[] m_tcpBuf;
m_tcpBuf = new char[m_sampleFifo->size()*2*4];
m_fillBuffer = true; // So we reprime FIFO
}
setChannelSampleRate(settings.m_channelSampleRate);
clearBuffer();
}
if ((settings.m_sampleBits != m_settings.m_sampleBits) || force)
{
setSampleBitDepth(settings.m_sampleBits);
clearBuffer();
}
// Don't use force, as disconnect can cause rtl_tcp to quit
if ((settings.m_dataPort != m_settings.m_dataPort) || (settings.m_dataAddress != m_settings.m_dataAddress) || (m_dataSocket == nullptr))
{
disconnectFromHost();
connectToHost(settings.m_dataAddress, settings.m_dataPort);
}
m_settings = settings;
}
void RemoteTCPInputTCPHandler::connected()
{
QMutexLocker mutexLocker(&m_mutex);
qDebug() << "RemoteTCPInputTCPHandler::connected";
if (m_settings.m_overrideRemoteSettings)
{
// Force settings to be sent to remote device
applySettings(m_settings, true);
}
if (m_messageQueueToGUI)
{
MsgReportConnection *msg = MsgReportConnection::create(true);
m_messageQueueToGUI->push(msg);
}
}
void RemoteTCPInputTCPHandler::reconnect()
{
QMutexLocker mutexLocker(&m_mutex);
if (!m_dataSocket) {
connectToHost(m_settings.m_dataAddress, m_settings.m_dataPort);
}
}
void RemoteTCPInputTCPHandler::disconnected()
{
QMutexLocker mutexLocker(&m_mutex);
qDebug() << "RemoteTCPInputTCPHandler::disconnected";
cleanup();
if (m_messageQueueToGUI)
{
MsgReportConnection *msg = MsgReportConnection::create(false);
m_messageQueueToGUI->push(msg);
}
// Try to reconnect
m_reconnectTimer.start(500);
}
void RemoteTCPInputTCPHandler::errorOccurred(QAbstractSocket::SocketError socketError)
{
qDebug() << "RemoteTCPInputTCPHandler::errorOccurred: " << socketError;
cleanup();
if (m_messageQueueToGUI)
{
MsgReportConnection *msg = MsgReportConnection::create(false);
m_messageQueueToGUI->push(msg);
}
// Try to reconnect
m_reconnectTimer.start(500);
}
void RemoteTCPInputTCPHandler::dataReadyRead()
{
QMutexLocker mutexLocker(&m_mutex);
if (!m_readMetaData)
{
quint8 metaData[RemoteTCPProtocol::m_sdraMetaDataSize];
if (m_dataSocket->bytesAvailable() >= sizeof(metaData))
{
qint64 bytesRead = m_dataSocket->read((char *)&metaData[0], 4);
if (bytesRead == 4)
{
// Read first 4 bytes which indicate which protocol is in use
// RTL0 or SDRA
char protochars[5];
memcpy(protochars, metaData, 4);
protochars[4] = '\0';
QString protocol(protochars);
if (protocol == "RTL0")
{
bytesRead = m_dataSocket->read((char *)&metaData[4], RemoteTCPProtocol::m_rtl0MetaDataSize-4);
RemoteTCPProtocol::Device tuner = (RemoteTCPProtocol::Device)RemoteTCPProtocol::extractUInt32(&metaData[4]);
quint32 gainStages = RemoteTCPProtocol::extractUInt32(&metaData[8]);
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(MsgReportRemoteDevice::create(tuner, protocol));
}
if (m_settings.m_sampleBits != 8)
{
RemoteTCPInputSettings& settings = m_settings;
settings.m_sampleBits = 8;
if (m_messageQueueToInput) {
m_messageQueueToInput->push(RemoteTCPInput::MsgConfigureRemoteTCPInput::create(settings));
}
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(RemoteTCPInput::MsgConfigureRemoteTCPInput::create(settings));
}
}
}
else if (protocol == "SDRA")
{
bytesRead = m_dataSocket->read((char *)&metaData[4], RemoteTCPProtocol::m_sdraMetaDataSize-4);
RemoteTCPProtocol::Device device = (RemoteTCPProtocol::Device)RemoteTCPProtocol::extractUInt32(&metaData[4]);
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(MsgReportRemoteDevice::create(device, protocol));
}
if (!m_settings.m_overrideRemoteSettings)
{
// Update local settings to match remote
RemoteTCPInputSettings& settings = m_settings;
settings.m_centerFrequency = RemoteTCPProtocol::extractUInt64(&metaData[8]);
settings.m_loPpmCorrection = RemoteTCPProtocol::extractUInt32(&metaData[16]);
quint32 flags = RemoteTCPProtocol::extractUInt32(&metaData[20]);
settings.m_biasTee = flags & 1;
settings.m_directSampling = (flags >> 1) & 1;
settings.m_agc = (flags >> 2) & 1;
settings.m_dcBlock = (flags >> 3) & 1;
settings.m_iqCorrection = (flags >> 4) & 1;
settings.m_devSampleRate = RemoteTCPProtocol::extractUInt32(&metaData[24]);
settings.m_log2Decim = RemoteTCPProtocol::extractUInt32(&metaData[28]);
settings.m_gain[0] = RemoteTCPProtocol::extractInt16(&metaData[32]);
settings.m_gain[1] = RemoteTCPProtocol::extractInt16(&metaData[34]);
settings.m_gain[2] = RemoteTCPProtocol::extractInt16(&metaData[36]);
settings.m_rfBW = RemoteTCPProtocol::extractUInt32(&metaData[40]);
settings.m_inputFrequencyOffset = RemoteTCPProtocol::extractUInt32(&metaData[44]);
settings.m_channelGain = RemoteTCPProtocol::extractUInt32(&metaData[48]);
settings.m_channelSampleRate = RemoteTCPProtocol::extractUInt32(&metaData[52]);
settings.m_sampleBits = RemoteTCPProtocol::extractUInt32(&metaData[56]);
if (settings.m_channelSampleRate != (settings.m_devSampleRate >> settings.m_log2Decim)) {
settings.m_channelDecimation = true;
}
if (m_messageQueueToInput) {
m_messageQueueToInput->push(RemoteTCPInput::MsgConfigureRemoteTCPInput::create(settings));
}
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(RemoteTCPInput::MsgConfigureRemoteTCPInput::create(settings));
}
}
}
else
{
qDebug() << "RemoteTCPInputTCPHandler::dataReadyRead: Unknown protocol: " << protocol;
}
}
m_readMetaData = true;
}
}
}
// QTimer::timeout isn't guarenteed to be called on every timeout, so we need to look at the system clock
void RemoteTCPInputTCPHandler::processData()
{
QMutexLocker mutexLocker(&m_mutex);
if (m_dataSocket && (m_dataSocket->state() == QAbstractSocket::ConnectedState))
{
int sampleRate = m_settings.m_channelSampleRate;
int bytesPerSample = m_settings.m_sampleBits / 8;
int bytesPerSecond = sampleRate * 2 * bytesPerSample;
if (m_dataSocket->bytesAvailable() < (0.1f * m_settings.m_preFill * bytesPerSecond))
{
qDebug() << "RemoteTCPInputTCPHandler::processData: Buffering!";
m_fillBuffer = true;
}
// Report buffer usage
// QTcpSockets buffer size should be unlimited - we pretend here it's twice as big as the point we start reading from it
if (m_messageQueueToGUI)
{
qint64 size = std::max(m_dataSocket->bytesAvailable(), (qint64)(m_settings.m_preFill * bytesPerSecond));
RemoteTCPInput::MsgReportTCPBuffer *report = RemoteTCPInput::MsgReportTCPBuffer::create(
m_dataSocket->bytesAvailable(), size, m_dataSocket->bytesAvailable() / (float)bytesPerSecond,
m_sampleFifo->fill(), m_sampleFifo->size(), m_sampleFifo->fill() / (float)bytesPerSecond
);
m_messageQueueToGUI->push(report);
}
float factor = 0.0f;
// Prime buffer, before we start reading
if (m_fillBuffer)
{
if (m_dataSocket->bytesAvailable() >= m_settings.m_preFill * bytesPerSecond)
{
qDebug() << "Buffer primed bytesAvailable:" << m_dataSocket->bytesAvailable();
m_fillBuffer = false;
m_prevDateTime = QDateTime::currentDateTime();
factor = 6.0f / 8.0f;
}
}
else
{
QDateTime currentDateTime = QDateTime::currentDateTime();
factor = m_prevDateTime.msecsTo(currentDateTime) / 1000.0f;
m_prevDateTime = currentDateTime;
}
unsigned int remaining = m_sampleFifo->size() - m_sampleFifo->fill();
int requiredSamples = (int)std::min((unsigned int)(factor * sampleRate), remaining);
if (!m_fillBuffer)
{
if (m_dataSocket->bytesAvailable() >= requiredSamples*2*bytesPerSample)
{
m_dataSocket->read(&m_tcpBuf[0], requiredSamples*2*bytesPerSample);
convert(requiredSamples);
}
}
}
}
// The following code assumes host is little endian
void RemoteTCPInputTCPHandler::convert(int nbSamples)
{
if (nbSamples > (int) m_converterBufferNbSamples)
{
if (m_converterBuffer) {
delete[] m_converterBuffer;
}
m_converterBuffer = new int32_t[nbSamples*2];
}
if ((m_settings.m_sampleBits == 32) && (SDR_RX_SAMP_SZ == 24))
{
m_sampleFifo->write(reinterpret_cast<quint8*>(m_tcpBuf), nbSamples*sizeof(Sample));
}
else if ((m_settings.m_sampleBits == 8) && (SDR_RX_SAMP_SZ == 16))
{
qint8 *in = (qint8 *)m_tcpBuf;
qint16 *out = (qint16 *)m_converterBuffer;
for (int is = 0; is < nbSamples*2; is++) {
out[is] = (((qint16)in[is]) - 128);
}
m_sampleFifo->write(reinterpret_cast<quint8*>(out), nbSamples*sizeof(Sample));
}
else if ((m_settings.m_sampleBits == 8) && (SDR_RX_SAMP_SZ == 24))
{
qint8 *in = (qint8 *)m_tcpBuf;
qint32 *out = (qint32 *)m_converterBuffer;
for (int is = 0; is < nbSamples*2; is++) {
out[is] = (((qint32)in[is]) - 128) << 8; // Only shift by 8, rather than 16, to match levels of native driver
}
m_sampleFifo->write(reinterpret_cast<quint8*>(out), nbSamples*sizeof(Sample));
}
else if ((m_settings.m_sampleBits == 24) && (SDR_RX_SAMP_SZ == 24))
{
quint8 *in = (quint8 *)m_tcpBuf;
qint32 *out = (qint32 *)m_converterBuffer;
for (int is = 0; is < nbSamples*2; is++) {
out[is] = (((in[3*is+2] << 16) | (in[3*is+1] << 8) | in[3*is]) << 8) >> 8;
}
m_sampleFifo->write(reinterpret_cast<quint8*>(out), nbSamples*sizeof(Sample));
}
else if ((m_settings.m_sampleBits == 24) && (SDR_RX_SAMP_SZ == 16))
{
quint8 *in = (quint8 *)m_tcpBuf;
qint16 *out = (qint16 *)m_converterBuffer;
for (int is = 0; is < nbSamples*2; is++) {
out[is] = (in[3*is+2] << 8) | in[3*is+1];
}
m_sampleFifo->write(reinterpret_cast<quint8*>(out), nbSamples*sizeof(Sample));
}
else if ((m_settings.m_sampleBits == 16) && (SDR_RX_SAMP_SZ == 24))
{
qint16 *in = (qint16 *)m_tcpBuf;
qint32 *out = (qint32 *)m_converterBuffer;
for (int is = 0; is < nbSamples*2; is++) {
out[is] = in[is] << 8;
}
m_sampleFifo->write(reinterpret_cast<quint8*>(out), nbSamples*sizeof(Sample));
}
else if ((m_settings.m_sampleBits == 32) && (SDR_RX_SAMP_SZ == 16))
{
qint32 *in = (qint32 *)m_tcpBuf;
qint16 *out = (qint16 *)m_converterBuffer;
for (int is = 0; is < nbSamples*2; is++) {
out[is] = in[is] >> 8;
}
m_sampleFifo->write(reinterpret_cast<quint8*>(out), nbSamples*sizeof(Sample));
}
else // invalid size
{
qWarning("RemoteTCPInputTCPHandler::convert: unexpected sample size in stream: %d bits", (int) m_settings.m_sampleBits);
}
}
void RemoteTCPInputTCPHandler::handleInputMessages()
{
Message* message;
while ((message = m_inputMessageQueue.pop()) != 0)
{
if (handleMessage(*message)) {
delete message;
}
}
}
bool RemoteTCPInputTCPHandler::handleMessage(const Message& cmd)
{
if (MsgConfigureTcpHandler::match(cmd))
{
qDebug() << "RemoteTCPInputTCPHandler::handleMessage: MsgConfigureTcpHandler";
MsgConfigureTcpHandler& notif = (MsgConfigureTcpHandler&) cmd;
applySettings(notif.getSettings(), notif.getForce());
return true;
}
else
{
return false;
}
}

View File

@ -0,0 +1,178 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// Copyright (C) 2022 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 PLUGINS_SAMPLESOURCE_REMOTETCPINPUT_REMOTETCPINPUTUDPHANDLER_H_
#define PLUGINS_SAMPLESOURCE_REMOTETCPINPUT_REMOTETCPINPUTUDPHANDLER_H_
#include <QObject>
#include <QTcpSocket>
#include <QHostAddress>
#include <QMutex>
#include <QDateTime>
#include "util/messagequeue.h"
#include "remotetcpinputsettings.h"
#include "../../channelrx/remotetcpsink/remotetcpprotocol.h"
class SampleSinkFifo;
class MessageQueue;
class DeviceAPI;
class RemoteTCPInputTCPHandler : public QObject
{
Q_OBJECT
public:
class MsgConfigureTcpHandler : public Message {
MESSAGE_CLASS_DECLARATION
public:
const RemoteTCPInputSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigureTcpHandler* create(const RemoteTCPInputSettings& settings, bool force)
{
return new MsgConfigureTcpHandler(settings, force);
}
private:
RemoteTCPInputSettings m_settings;
bool m_force;
MsgConfigureTcpHandler(const RemoteTCPInputSettings& settings, bool force) :
Message(),
m_settings(settings),
m_force(force)
{ }
};
class MsgReportRemoteDevice : public Message {
MESSAGE_CLASS_DECLARATION
public:
RemoteTCPProtocol::Device getDevice() const { return m_device; }
QString getProtocol() const { return m_protocol; }
static MsgReportRemoteDevice* create(RemoteTCPProtocol::Device device, const QString& protocol)
{
return new MsgReportRemoteDevice(device, protocol);
}
protected:
RemoteTCPProtocol::Device m_device;
QString m_protocol;
MsgReportRemoteDevice(RemoteTCPProtocol::Device device, const QString& protocol) :
Message(),
m_device(device),
m_protocol(protocol)
{ }
};
class MsgReportConnection : public Message {
MESSAGE_CLASS_DECLARATION
public:
bool getConnected() const { return m_connected; }
static MsgReportConnection* create(bool connected)
{
return new MsgReportConnection(connected);
}
protected:
bool m_connected;
MsgReportConnection(bool connected) :
Message(),
m_connected(connected)
{ }
};
RemoteTCPInputTCPHandler(SampleSinkFifo* sampleFifo, DeviceAPI *deviceAPI);
~RemoteTCPInputTCPHandler();
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
void setMessageQueueToInput(MessageQueue *queue) { m_messageQueueToInput = queue; }
void setMessageQueueToGUI(MessageQueue *queue) { m_messageQueueToGUI = queue; }
void reset();
void start();
void stop();
int getBufferGauge() const { return 0; }
public slots:
void dataReadyRead();
void connected();
void disconnected();
void errorOccurred(QAbstractSocket::SocketError socketError);
private:
DeviceAPI *m_deviceAPI;
bool m_running;
QTcpSocket *m_dataSocket;
char *m_tcpBuf;
SampleSinkFifo *m_sampleFifo;
MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
MessageQueue *m_messageQueueToInput;
MessageQueue *m_messageQueueToGUI;
bool m_readMetaData;
bool m_fillBuffer;
QTimer m_timer;
QTimer m_reconnectTimer;
QDateTime m_prevDateTime;
int32_t *m_converterBuffer;
uint32_t m_converterBufferNbSamples;
QMutex m_mutex;
RemoteTCPInputSettings m_settings;
void applyTCPLink(const QString& address, quint16 port);
bool handleMessage(const Message& message);
void convert(int nbSamples);
void connectToHost(const QString& address, quint16 port);
void disconnectFromHost();
void cleanup();
void clearBuffer();
void setSampleRate(int sampleRate);
void setCenterFrequency(quint64 frequency);
void setTunerAGC(bool agc);
void setTunerGain(int gain);
void setFreqCorrection(int correction);
void setIFGain(quint16 stage, quint16 gain);
void setAGC(bool agc);
void setDirectSampling(bool enabled);
void setDCOffsetRemoval(bool enabled);
void setIQCorrection(bool enabled);
void setBiasTee(bool enabled);
void setBandwidth(int bandwidth);
void setDecimation(int dec);
void setChannelSampleRate(int dec);
void setChannelFreqOffset(int offset);
void setChannelGain(int gain);
void setSampleBitDepth(int sampleBits);
void applySettings(const RemoteTCPInputSettings& settings, bool force = false);
private slots:
void started();
void finished();
void handleInputMessages();
void processData();
void reconnect();
};
#endif /* PLUGINS_SAMPLESOURCE_REMOTETCPINPUT_REMOTETCPINPUTUDPHANDLER_H_ */

View File

@ -0,0 +1,50 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// Copyright (C) 2022 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 "SWGDeviceSettings.h"
#include "remotetcpinput.h"
#include "remotetcpinputwebapiadapter.h"
RemoteTCPInputWebAPIAdapter::RemoteTCPInputWebAPIAdapter()
{}
RemoteTCPInputWebAPIAdapter::~RemoteTCPInputWebAPIAdapter()
{}
int RemoteTCPInputWebAPIAdapter::webapiSettingsGet(
SWGSDRangel::SWGDeviceSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setRemoteTcpInputSettings(new SWGSDRangel::SWGRemoteTCPInputSettings());
response.getRemoteTcpInputSettings()->init();
RemoteTCPInput::webapiFormatDeviceSettings(response, m_settings);
return 200;
}
int RemoteTCPInputWebAPIAdapter::webapiSettingsPutPatch(
bool force,
const QStringList& deviceSettingsKeys,
SWGSDRangel::SWGDeviceSettings& response, // query + response
QString& errorMessage)
{
(void) force; // no action
(void) errorMessage;
RemoteTCPInput::webapiUpdateDeviceSettings(m_settings, deviceSettingsKeys, response);
return 200;
}

View File

@ -0,0 +1,42 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// Copyright (C) 2022 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 "device/devicewebapiadapter.h"
#include "remotetcpinputsettings.h"
class RemoteTCPInputWebAPIAdapter : public DeviceWebAPIAdapter
{
public:
RemoteTCPInputWebAPIAdapter();
virtual ~RemoteTCPInputWebAPIAdapter();
virtual QByteArray serialize() { return m_settings.serialize(); }
virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); }
virtual int webapiSettingsGet(
SWGSDRangel::SWGDeviceSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& deviceSettingsKeys,
SWGSDRangel::SWGDeviceSettings& response, // query + response
QString& errorMessage);
private:
RemoteTCPInputSettings m_settings;
};

View File

@ -56,7 +56,7 @@ SDRPlayV3Gui::SDRPlayV3Gui(DeviceUISet *deviceUISet, QWidget* parent) :
ui->ifFrequency->addItem(QString::number(SDRPlayV3IF::getIF(i)/1000));
}
ui->samplerate->setColorMapper(ColorMapper(ColorMapper::GrayGold));
ui->samplerate->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow));
ui->samplerate->setValueRange(8, 2000000U, 10660000U);
ui->bandwidth->clear();
@ -326,11 +326,14 @@ void SDRPlayV3Gui::sendSettings()
void SDRPlayV3Gui::updateHardware()
{
qDebug() << "SDRPlayV3Gui::updateHardware";
SDRPlayV3Input::MsgConfigureSDRPlayV3* message = SDRPlayV3Input::MsgConfigureSDRPlayV3::create(m_settings, m_forceSettings);
m_sdrPlayV3Input->getInputMessageQueue()->push(message);
m_forceSettings = false;
m_updateTimer.stop();
if (m_doApplySettings)
{
qDebug() << "SDRPlayV3Gui::updateHardware";
SDRPlayV3Input::MsgConfigureSDRPlayV3* message = SDRPlayV3Input::MsgConfigureSDRPlayV3::create(m_settings, m_forceSettings);
m_sdrPlayV3Input->getInputMessageQueue()->push(message);
m_forceSettings = false;
m_updateTimer.stop();
}
}
void SDRPlayV3Gui::updateStatus()

View File

@ -914,6 +914,9 @@ void SDRPlayV3Input::webapiUpdateDeviceSettings(
if (deviceSettingsKeys.contains("iqOrder")) {
settings.m_iqOrder = response.getSdrPlayV3Settings()->getIqOrder() != 0;
}
if (deviceSettingsKeys.contains("biasTee")) {
settings.m_biasTee = response.getSdrPlayV3Settings()->getBiasTee() != 0;
}
if (deviceSettingsKeys.contains("useReverseAPI")) {
settings.m_useReverseAPI = response.getSdrPlayV3Settings()->getUseReverseApi() != 0;
}
@ -951,6 +954,7 @@ void SDRPlayV3Input::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings&
response.getSdrPlayV3Settings()->setTransverterDeltaFrequency(settings.m_transverterDeltaFrequency);
response.getSdrPlayV3Settings()->setTransverterMode(settings.m_transverterMode ? 1 : 0);
response.getSdrPlayV3Settings()->setIqOrder(settings.m_iqOrder ? 1 : 0);
response.getSdrPlayV3Settings()->setBiasTee(settings.m_biasTee ? 1 : 0);
response.getSdrPlayV3Settings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
@ -1090,6 +1094,9 @@ void SDRPlayV3Input::webapiReverseSendSettings(QList<QString>& deviceSettingsKey
if (deviceSettingsKeys.contains("iqOrder") || force) {
swgSDRPlayV3Settings->setIqOrder(settings.m_iqOrder ? 1 : 0);
}
if (deviceSettingsKeys.contains("biasTee") || force) {
swgSDRPlayV3Settings->setBiasTee(settings.m_biasTee ? 1 : 0);
}
QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings")
.arg(settings.m_reverseAPIAddress)

View File

@ -96,6 +96,58 @@ bool ChannelWebAPIUtils::getDeviceSettings(unsigned int deviceIndex, SWGSDRangel
return true;
}
bool ChannelWebAPIUtils::getDeviceReport(unsigned int deviceIndex, SWGSDRangel::SWGDeviceReport &deviceReport)
{
QString errorResponse;
int httpRC;
DeviceSet *deviceSet;
// Get device report
std::vector<DeviceSet*> deviceSets = MainCore::instance()->getDeviceSets();
if (deviceIndex < deviceSets.size())
{
deviceSet = deviceSets[deviceIndex];
if (deviceSet->m_deviceSourceEngine)
{
deviceReport.setDeviceHwType(new QString(deviceSet->m_deviceAPI->getHardwareId()));
deviceReport.setDirection(0);
DeviceSampleSource *source = deviceSet->m_deviceAPI->getSampleSource();
httpRC = source->webapiReportGet(deviceReport, errorResponse);
}
else if (deviceSet->m_deviceSinkEngine)
{
deviceReport.setDeviceHwType(new QString(deviceSet->m_deviceAPI->getHardwareId()));
deviceReport.setDirection(1);
DeviceSampleSink *sink = deviceSet->m_deviceAPI->getSampleSink();
httpRC = sink->webapiReportGet(deviceReport, errorResponse);
}
else if (deviceSet->m_deviceMIMOEngine)
{
deviceReport.setDeviceHwType(new QString(deviceSet->m_deviceAPI->getHardwareId()));
deviceReport.setDirection(2);
DeviceSampleMIMO *mimo = deviceSet->m_deviceAPI->getSampleMIMO();
httpRC = mimo->webapiReportGet(deviceReport, errorResponse);
}
else
{
qDebug() << "ChannelWebAPIUtils::getDeviceReport: unknown device type " << deviceIndex;
return false;
}
}
else
{
qDebug() << "ChannelWebAPIUtils::getDeviceReport: no device " << deviceIndex;
return false;
}
if (httpRC/100 != 2)
{
qWarning("ChannelWebAPIUtils::getDeviceReport: get device report error %d: %s",
httpRC, qPrintable(errorResponse));
return false;
}
}
bool ChannelWebAPIUtils::getFeatureSettings(unsigned int featureSetIndex, unsigned int featureIndex, SWGSDRangel::SWGFeatureSettings &featureSettingsResponse, Feature *&feature)
{
QString errorResponse;
@ -173,6 +225,16 @@ bool ChannelWebAPIUtils::getFeatureReport(unsigned int featureSetIndex, unsigned
return true;
}
QString ChannelWebAPIUtils::getDeviceHardwareId(unsigned int deviceIndex)
{
DeviceAPI *deviceAPI = MainCore::instance()->getDevice(deviceIndex);
if (deviceAPI) {
return deviceAPI->getHardwareId();
} else {
return QString();
}
}
// Get device center frequency
bool ChannelWebAPIUtils::getCenterFrequency(unsigned int deviceIndex, double &frequencyInHz)
{
@ -239,6 +301,395 @@ bool ChannelWebAPIUtils::setCenterFrequency(unsigned int deviceIndex, double fre
}
}
// Get local oscillator frequency correction
bool ChannelWebAPIUtils::getLOPpmCorrection(unsigned int deviceIndex, int &ppmTenths)
{
QString devId = ChannelWebAPIUtils::getDeviceHardwareId(deviceIndex);
if (devId == "RTLSDR") {
return ChannelWebAPIUtils::getDeviceSetting(deviceIndex, "loPpmCorrection", ppmTenths);
} else {
return ChannelWebAPIUtils::getDeviceSetting(deviceIndex, "LOppmTenths", ppmTenths);
}
}
// Set local oscillator frequency correction
bool ChannelWebAPIUtils::setLOPpmCorrection(unsigned int deviceIndex, int ppmTenths)
{
QString devId = ChannelWebAPIUtils::getDeviceHardwareId(deviceIndex);
if (devId == "RTLSDR") {
return ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, "loPpmCorrection", ppmTenths); // RTLSDR
} else {
return ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, "LOppmTenths", ppmTenths); // Airspy, AirspyHF. BladeRF2, FCDPro, HackRF, Metis, Perseus, Pluto, SDRPlay, SDRplayV3, SaopySDR
}
}
// Get device sample rate
bool ChannelWebAPIUtils::getDevSampleRate(unsigned int deviceIndex, int &devSampleRate)
{
QString devId = ChannelWebAPIUtils::getDeviceHardwareId(deviceIndex);
if (devId == "AirspyHF")
{
QList<int> rates;
int idx;
if (ChannelWebAPIUtils::getDeviceReportList(deviceIndex, "sampleRates", "rate", rates)
&& ChannelWebAPIUtils::getDeviceSetting(deviceIndex, "devSampleRateIndex", idx))
{
if (idx < rates.size())
{
devSampleRate = rates[idx];
return true;
}
}
return false;
}
else
{
return ChannelWebAPIUtils::getDeviceSetting(deviceIndex, "devSampleRate", devSampleRate);
}
}
// Set device sample rate
bool ChannelWebAPIUtils::setDevSampleRate(unsigned int deviceIndex, int devSampleRate)
{
QString devId = ChannelWebAPIUtils::getDeviceHardwareId(deviceIndex);
if (devId == "AirspyHF")
{
QList<int> rates;
ChannelWebAPIUtils::getDeviceReportList(deviceIndex, "sampleRates", "rate", rates);
// Find first rate that is big enough. Higher rates are in list first
int idx = 0;
for (int i = rates.size() - 1; i >= 0; i--)
{
if (devSampleRate <= rates[i])
{
idx = i;
break;
}
}
return ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, "devSampleRateIndex", idx);
}
else
{
return ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, "devSampleRate", devSampleRate);
}
}
bool ChannelWebAPIUtils::getGain(unsigned int deviceIndex, int stage, int &gain)
{
QString devId = ChannelWebAPIUtils::getDeviceHardwareId(deviceIndex);
bool error = true;
if ((devId == "Airspy"))
{
QStringList airspyStages = {"lnaGain", "mixerGain", "vgaGain"};
if (stage < airspyStages.size())
{
error = ChannelWebAPIUtils::getDeviceSetting(deviceIndex, airspyStages[stage], gain);
gain *= 10;
}
}
else if ((devId == "AirspyHF"))
{
if (stage == 0)
{
error = ChannelWebAPIUtils::getDeviceSetting(deviceIndex, "attenuatorSteps", gain);
gain *= 10 * 6;
}
}
else if ((devId == "BladeRF1"))
{
QStringList bladeRF1Stages = {"lnaGain", "vga1", "vga2"};
if (stage < bladeRF1Stages.size())
{
error = ChannelWebAPIUtils::getDeviceSetting(deviceIndex, bladeRF1Stages[stage], gain);
gain *= 10;
}
}
else if ((devId == "HackRF"))
{
QStringList hackRFStages = {"lnaGain", "vgaGain"};
if (stage < hackRFStages.size())
{
error = ChannelWebAPIUtils::getDeviceSetting(deviceIndex, hackRFStages[stage], gain);
gain *= 10;
}
}
else if ((devId == "FCDProPlus") || (devId == "KiwiSDR") || (devId == "LimeSDR") || (devId == "PlutoSDR") || (devId == "USRP") || (devId == "XTRX"))
{
if (stage == 0)
{
error = ChannelWebAPIUtils::getDeviceSetting(deviceIndex, "gain", gain);
gain *= 10;
}
}
else if ((devId == "SDRplayV3"))
{
QStringList sdrplayStages = {"lnaIndex", "ifGain"};
if (stage < sdrplayStages.size())
{
// FIXME: How to map to lnaIndex - gain can vary by SDR
error = ChannelWebAPIUtils::getDeviceSetting(deviceIndex, sdrplayStages[stage], gain);
gain *= 10;
}
}
else if ((devId == "RTLSDR"))
{
if (stage == 0)
{
error = ChannelWebAPIUtils::getDeviceSetting(deviceIndex, "gain", gain);
}
}
return error;
}
// Set gain for different stages in RF device
// Gain is generally in 10ths of a dB
// Stages should correspond with order in RemoteTCPInputGUI
bool ChannelWebAPIUtils::setGain(unsigned int deviceIndex, int stage, int gain)
{
QString devId = ChannelWebAPIUtils::getDeviceHardwareId(deviceIndex);
if (devId == "Airspy")
{
QStringList airspyStages = {"lnaGain", "mixerGain", "vgaGain"};
if (stage < airspyStages.size()) {
return ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, airspyStages[stage], gain / 10);
}
}
else if (devId == "AirspyHF")
{
if (stage == 0) {
return ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, "attenuatorSteps", gain / 10 / 6);
}
}
else if (devId == "BladeRF1")
{
QStringList bladeRF1Stages = {"lnaGain", "vga1", "vga2"};
if (stage < bladeRF1Stages.size()) {
return ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, bladeRF1Stages[stage], gain / 10);
}
}
else if (devId == "HackRF")
{
QStringList hackRFStages = {"lnaGain", "vgaGain"};
if (stage < hackRFStages.size()) {
return ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, hackRFStages[stage], gain / 10);
}
}
else if ((devId == "FCDProPlus") || (devId == "KiwiSDR") || (devId == "LimeSDR") || (devId == "PlutoSDR") || (devId == "USRP") || (devId == "XTRX"))
{
if (stage == 0) {
return ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, "gain", gain / 10);
}
}
else if (devId == "SDRplayV3")
{
QStringList sdrplayStages = {"lnaIndex", "ifGain"};
if (stage < sdrplayStages.size())
{
// FIXME: How to map to lnaIndex - gain can vary by SDR
return ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, sdrplayStages[stage], gain / 10);
}
}
else if ((devId == "RTLSDR"))
{
if (stage == 0) {
return ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, "gain", gain);
}
}
return false;
}
bool ChannelWebAPIUtils::getAGC(unsigned int deviceIndex, int &enabled)
{
QString devId = ChannelWebAPIUtils::getDeviceHardwareId(deviceIndex);
if ((devId == "Airspy"))
{
return ChannelWebAPIUtils::getDeviceSetting(deviceIndex, "lnaAGC", enabled); // What about mixerAGC?
}
else if ((devId == "AirspyHF") || (devId == "KiwiSDR"))
{
return ChannelWebAPIUtils::getDeviceSetting(deviceIndex, "useAGC", enabled);
}
else if ((devId == "LimeSDR") || (devId == "PlutoSDR") || (devId == "USRP") || (devId == "XTRX"))
{
bool error = ChannelWebAPIUtils::getDeviceSetting(deviceIndex, "gainMode", enabled);
enabled = !enabled;
return error;
}
else if (devId == "RTLSDR")
{
return ChannelWebAPIUtils::getDeviceSetting(deviceIndex, "agc", enabled);
}
else if (devId == "SDRplayV3")
{
return ChannelWebAPIUtils::getDeviceSetting(deviceIndex, "ifAGC", enabled);
}
return false;
}
bool ChannelWebAPIUtils::setAGC(unsigned int deviceIndex, bool enabled)
{
QString devId = ChannelWebAPIUtils::getDeviceHardwareId(deviceIndex);
if ((devId == "Airspy"))
{
return ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, "lnaAGC", enabled)
&& ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, "mixerAGC", enabled);
}
else if ((devId == "AirspyHF") || (devId == "KiwiSDR"))
{
return ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, "useAGC", enabled);
}
else if ((devId == "LimeSDR") || (devId == "PlutoSDR") || (devId == "USRP") || (devId == "XTRX"))
{
return ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, "gainMode", !enabled);
}
else if (devId == "RTLSDR")
{
return ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, "agc", enabled);
}
else if (devId == "SDRplayV3")
{
return ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, "ifAGC", enabled);
}
return false;
}
// Get RF bandwidth
bool ChannelWebAPIUtils::getRFBandwidth(unsigned int deviceIndex, int &bw)
{
QString devId = ChannelWebAPIUtils::getDeviceHardwareId(deviceIndex);
if (devId == "RTLSDR")
{
return ChannelWebAPIUtils::getDeviceSetting(deviceIndex, "rfBandwidth", bw);
}
else if ((devId == "BladeRF1") || (devId == "HackRF"))
{
return ChannelWebAPIUtils::getDeviceSetting(deviceIndex, "bandwidth", bw);
}
else if (devId == "SDRplayV3")
{
QList<int> bws;
int idx;
if (ChannelWebAPIUtils::getDeviceReportList(deviceIndex, "bandwidths", "bandwidth", bws)
&& ChannelWebAPIUtils::getDeviceSetting(deviceIndex, "bandwidthIndex", idx))
{
if (idx < bws.size())
{
bw = bws[idx];
return true;
}
}
return false;
}
else
{
return ChannelWebAPIUtils::getDeviceSetting(deviceIndex, "lpfBW", bw);
}
}
// Set RF bandwidth
bool ChannelWebAPIUtils::setRFBandwidth(unsigned int deviceIndex, int bw)
{
QString devId = ChannelWebAPIUtils::getDeviceHardwareId(deviceIndex);
if (devId == "RTLSDR")
{
return ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, "rfBandwidth", bw); // RTLSDR
}
else if ((devId == "BladeRF1") || (devId == "HackRF"))
{
return ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, "bandwidth", bw); // BladeRF1, HackRF
}
else if (devId == "SDRplayV3")
{
QList<int> bws;
ChannelWebAPIUtils::getDeviceReportList(deviceIndex, "bandwidths", "bandwidth", bws);
// Find first b/w that is big enough
int idx = 0;
for (int i = 0; i < bws.size(); i++)
{
if (bw <= bws[i]) {
break;
}
idx++;
}
return ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, "bandwidthIndex", idx);
}
else
{
return ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, "lpfBW", bw); // LimeSDR, PlutoSDR, USRP
}
// TODO: SDRPlay and SDRPlayV3 use bandwidthIndex
}
// Get software decimation
bool ChannelWebAPIUtils::getSoftDecim(unsigned int deviceIndex, int &log2Decim)
{
QString devId = ChannelWebAPIUtils::getDeviceHardwareId(deviceIndex);
if ((devId == "LimeSDR") || (devId == "USRP")) {
return ChannelWebAPIUtils::getDeviceSetting(deviceIndex, "log2SoftDecim", log2Decim);
} else {
return ChannelWebAPIUtils::getDeviceSetting(deviceIndex, "log2Decim", log2Decim);
}
}
// Set software decimation
bool ChannelWebAPIUtils::setSoftDecim(unsigned int deviceIndex, int log2Decim)
{
QString devId = ChannelWebAPIUtils::getDeviceHardwareId(deviceIndex);
if ((devId == "LimeSDR") || (devId == "USRP")) {
return ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, "log2SoftDecim", log2Decim); // LimeSDR, USRP
} else {
return ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, "log2Decim", log2Decim);
}
}
// Get whether bias tee is enabled
bool ChannelWebAPIUtils::getBiasTee(unsigned int deviceIndex, int &enabled)
{
QString devId = ChannelWebAPIUtils::getDeviceHardwareId(deviceIndex);
if ((devId == "RTLSDR") || (devId == "BladeRF") || (devId == "SDRplayV3")) {
return ChannelWebAPIUtils::getDeviceSetting(deviceIndex, "biasTee", enabled);
} else {
return ChannelWebAPIUtils::getDeviceSetting(deviceIndex, "biasT", enabled);
}
}
// Set whether bias tee, if available, should be enabled
bool ChannelWebAPIUtils::setBiasTee(unsigned int deviceIndex, bool enabled)
{
QString devId = ChannelWebAPIUtils::getDeviceHardwareId(deviceIndex);
if ((devId == "RTLSDR") || (devId == "BladeRF") || (devId == "SDRplayV3")) {
return ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, "biasTee", enabled); // RTLSDR, BladeRF, SDRplayV3
} else {
return ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, "biasT", enabled); // Airspy, FCDProPlus, HackRF,
}
}
// Get whether DC offset removal is enabled
bool ChannelWebAPIUtils::getDCOffsetRemoval(unsigned int deviceIndex, int &enabled)
{
return ChannelWebAPIUtils::getDeviceSetting(deviceIndex, "dcBlock", enabled);
}
// Set whether DC offset removal should be enabled
bool ChannelWebAPIUtils::setDCOffsetRemoval(unsigned int deviceIndex, bool enabled)
{
return ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, "dcBlock", enabled);
}
// Get whether IQ correction is enabled
bool ChannelWebAPIUtils::getIQCorrection(unsigned int deviceIndex, int &enabled)
{
return ChannelWebAPIUtils::getDeviceSetting(deviceIndex, "iqCorrection", enabled);
}
// Set whether IQ correction should be enabled
bool ChannelWebAPIUtils::setIQCorrection(unsigned int deviceIndex, bool enabled)
{
return ChannelWebAPIUtils::patchDeviceSetting(deviceIndex, "iqCorrection", enabled);
}
// Start acquisition
bool ChannelWebAPIUtils::run(unsigned int deviceIndex, int subsystemIndex)
{
@ -540,67 +991,45 @@ bool ChannelWebAPIUtils::getDeviceSetting(unsigned int deviceIndex, const QStrin
bool ChannelWebAPIUtils::getDeviceReportValue(unsigned int deviceIndex, const QString &key, QString &value)
{
SWGSDRangel::SWGDeviceReport deviceReport;
QString errorResponse;
int httpRC;
DeviceSet *deviceSet;
// Get device report
std::vector<DeviceSet*> deviceSets = MainCore::instance()->getDeviceSets();
if (deviceIndex < deviceSets.size())
if (getDeviceReport(deviceIndex, deviceReport))
{
deviceSet = deviceSets[deviceIndex];
if (deviceSet->m_deviceSourceEngine)
// Get value of requested key
QJsonObject *jsonObj = deviceReport.asJsonObject();
if (WebAPIUtils::getSubObjectString(*jsonObj, key, value))
{
deviceReport.setDeviceHwType(new QString(deviceSet->m_deviceAPI->getHardwareId()));
deviceReport.setDirection(0);
DeviceSampleSource *source = deviceSet->m_deviceAPI->getSampleSource();
httpRC = source->webapiReportGet(deviceReport, errorResponse);
}
else if (deviceSet->m_deviceSinkEngine)
{
deviceReport.setDeviceHwType(new QString(deviceSet->m_deviceAPI->getHardwareId()));
deviceReport.setDirection(1);
DeviceSampleSink *sink = deviceSet->m_deviceAPI->getSampleSink();
httpRC = sink->webapiReportGet(deviceReport, errorResponse);
}
else if (deviceSet->m_deviceMIMOEngine)
{
deviceReport.setDeviceHwType(new QString(deviceSet->m_deviceAPI->getHardwareId()));
deviceReport.setDirection(2);
DeviceSampleMIMO *mimo = deviceSet->m_deviceAPI->getSampleMIMO();
httpRC = mimo->webapiReportGet(deviceReport, errorResponse);
// Done
return true;
}
else
{
qDebug() << "ChannelWebAPIUtils::getDeviceReportValue: unknown device type " << deviceIndex;
qWarning("ChannelWebAPIUtils::getDeviceReportValue: no key %s in device report", qPrintable(key));
return false;
}
}
else
{
qDebug() << "ChannelWebAPIUtils::getDeviceReportValue: no device " << deviceIndex;
return false;
}
return false;
}
if (httpRC/100 != 2)
{
qWarning("ChannelWebAPIUtils::getDeviceReportValue: get device report error %d: %s",
httpRC, qPrintable(errorResponse));
return false;
}
bool ChannelWebAPIUtils::getDeviceReportList(unsigned int deviceIndex, const QString &key, const QString &subKey, QList<int> &values)
{
SWGSDRangel::SWGDeviceReport deviceReport;
// Get value of requested key
QJsonObject *jsonObj = deviceReport.asJsonObject();
if (WebAPIUtils::getSubObjectString(*jsonObj, key, value))
if (getDeviceReport(deviceIndex, deviceReport))
{
// Done
return true;
}
else
{
qWarning("ChannelWebAPIUtils::getDeviceReportValue: no key %s in device report", qPrintable(key));
return false;
// Get value of requested key
QJsonObject *jsonObj = deviceReport.asJsonObject();
if (WebAPIUtils::getSubObjectIntList(*jsonObj, key, subKey, values))
{
// Done
return true;
}
else
{
qWarning("ChannelWebAPIUtils::getDeviceReportList: no key %s in device report", qPrintable(key));
return false;
}
}
return false;
}
bool ChannelWebAPIUtils::patchDeviceSetting(unsigned int deviceIndex, const QString &setting, int value)

View File

@ -21,6 +21,7 @@
#include <QString>
#include "SWGDeviceSettings.h"
#include "SWGDeviceReport.h"
#include "SWGFeatureSettings.h"
#include "SWGFeatureReport.h"
@ -34,6 +35,24 @@ class SDRBASE_API ChannelWebAPIUtils
public:
static bool getCenterFrequency(unsigned int deviceIndex, double &frequencyInHz);
static bool setCenterFrequency(unsigned int deviceIndex, double frequencyInHz);
static bool getLOPpmCorrection(unsigned int deviceIndex, int &ppmTenths);
static bool setLOPpmCorrection(unsigned int deviceIndex, int ppmTenths);
static bool getDevSampleRate(unsigned int deviceIndex, int &sampleRate);
static bool setDevSampleRate(unsigned int deviceIndex, int sampleRate);
static bool getGain(unsigned int deviceIndex, int stage, int &gain);
static bool setGain(unsigned int deviceIndex, int stage, int gain);
static bool getAGC(unsigned int deviceIndex, int &enabled);
static bool setAGC(unsigned int deviceIndex, bool enabled);
static bool getRFBandwidth(unsigned int deviceIndex, int &bw);
static bool setRFBandwidth(unsigned int deviceIndex, int bw);
static bool getSoftDecim(unsigned int deviceIndex, int &log2Decim);
static bool setSoftDecim(unsigned int deviceIndex, int log2Decim);
static bool getBiasTee(unsigned int deviceIndex, int &enabled);
static bool setBiasTee(unsigned int deviceIndex, bool enabled);
static bool getDCOffsetRemoval(unsigned int deviceIndex, int &enabled);
static bool setDCOffsetRemoval(unsigned int deviceIndex, bool enabled);
static bool getIQCorrection(unsigned int deviceIndex, int &enabled);
static bool setIQCorrection(unsigned int deviceIndex, bool enabled);
static bool run(unsigned int deviceIndex, int subsystemIndex=0);
static bool stop(unsigned int deviceIndex, int subsystemIndex=0);
static bool getFrequencyOffset(unsigned int deviceIndex, int channelIndex, int& offset);
@ -43,15 +62,18 @@ public:
static bool satelliteLOS(const QString name);
static bool getDeviceSetting(unsigned int deviceIndex, const QString &setting, int &value);
static bool getDeviceReportValue(unsigned int deviceIndex, const QString &key, QString &value);
static bool getDeviceReportList(unsigned int deviceIndex, const QString &key, const QString &subKey, QList<int> &values);
static bool patchDeviceSetting(unsigned int deviceIndex, const QString &setting, int value);
static bool patchFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, const QString &value);
static bool patchFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, double value);
static bool getFeatureReportValue(unsigned int featureSetIndex, unsigned int featureIndex, const QString &key, int &value);
static bool getFeatureReportValue(unsigned int featureSetIndex, unsigned int featureIndex, const QString &key, QString &value);
static bool getFeatureReportValue(unsigned int featureSetIndex, unsigned int featureIndex, const QString &key, QString &value);
protected:
static bool getDeviceSettings(unsigned int deviceIndex, SWGSDRangel::SWGDeviceSettings &deviceSettingsResponse, DeviceSet *&deviceSet);
static bool getDeviceReport(unsigned int deviceIndex, SWGSDRangel::SWGDeviceReport &deviceReport);
static bool getFeatureSettings(unsigned int featureSetIndex, unsigned int featureIndex, SWGSDRangel::SWGFeatureSettings &featureSettingsResponse, Feature *&feature);
static bool getFeatureReport(unsigned int featureSetIndex, unsigned int featureIndex, SWGSDRangel::SWGFeatureReport &featureReport);
static QString getDeviceHardwareId(unsigned int deviceIndex);
};
#endif // SDRBASE_CHANNEL_CHANNELWEBAPIUTILS_H_

View File

@ -4591,6 +4591,11 @@ bool WebAPIRequestMapper::getChannelSettings(
channelSettings->setRemoteSourceSettings(new SWGSDRangel::SWGRemoteSourceSettings());
channelSettings->getRemoteSourceSettings()->fromJsonObject(settingsJsonObject);
}
else if (channelSettingsKey == "RemoteTCPSinkSettings")
{
channelSettings->setRemoteTcpSinkSettings(new SWGSDRangel::SWGRemoteTCPSinkSettings());
channelSettings->getRemoteTcpSinkSettings()->fromJsonObject(settingsJsonObject);
}
else if (channelSettingsKey == "SigMFFileSinkSettings")
{
channelSettings->setSigMfFileSinkSettings(new SWGSDRangel::SWGSigMFFileSinkSettings());
@ -4970,6 +4975,11 @@ bool WebAPIRequestMapper::getDeviceSettings(
deviceSettings->setRemoteInputSettings(new SWGSDRangel::SWGRemoteInputSettings());
deviceSettings->getRemoteInputSettings()->fromJsonObject(settingsJsonObject);
}
else if (deviceSettingsKey == "remoteTCPInputSettings")
{
deviceSettings->setRemoteTcpInputSettings(new SWGSDRangel::SWGRemoteTCPInputSettings());
deviceSettings->getRemoteTcpInputSettings()->fromJsonObject(settingsJsonObject);
}
else if (deviceSettingsKey == "localInputSettings")
{
deviceSettings->setLocalInputSettings(new SWGSDRangel::SWGLocalInputSettings());
@ -5286,6 +5296,7 @@ void WebAPIRequestMapper::resetDeviceSettings(SWGSDRangel::SWGDeviceSettings& de
deviceSettings.setRtlSdrSettings(nullptr);
deviceSettings.setRemoteOutputSettings(nullptr);
deviceSettings.setRemoteInputSettings(nullptr);
deviceSettings.setRemoteTcpInputSettings(nullptr);
deviceSettings.setSdrPlaySettings(nullptr);
deviceSettings.setSdrPlayV3Settings(nullptr);
deviceSettings.setTestSourceSettings(nullptr);
@ -5308,6 +5319,7 @@ void WebAPIRequestMapper::resetDeviceReport(SWGSDRangel::SWGDeviceReport& device
deviceReport.setRtlSdrReport(nullptr);
deviceReport.setRemoteOutputReport(nullptr);
deviceReport.setRemoteInputReport(nullptr);
deviceReport.setRemoteTcpInputReport(nullptr);
deviceReport.setSdrPlayReport(nullptr);
deviceReport.setSdrPlayV3Report(nullptr);
deviceReport.setUsrpOutputReport(nullptr);
@ -5347,6 +5359,7 @@ void WebAPIRequestMapper::resetChannelSettings(SWGSDRangel::SWGChannelSettings&
channelSettings.setRadiosondeDemodSettings(nullptr);
channelSettings.setRemoteSinkSettings(nullptr);
channelSettings.setRemoteSourceSettings(nullptr);
channelSettings.setRemoteTcpSinkSettings(nullptr);
channelSettings.setSsbDemodSettings(nullptr);
channelSettings.setSsbModSettings(nullptr);
channelSettings.setUdpSourceSettings(nullptr);

View File

@ -63,6 +63,7 @@ const QMap<QString, QString> WebAPIUtils::m_channelURIToSettingsKey = {
{"sdrangel.channel.radioclock", "RadioClockSettings"},
{"sdrangel.channel.radiosondedemod", "RadiosondeDemodSettings"},
{"sdrangel.demod.remotesink", "RemoteSinkSettings"},
{"sdrangel.demod.remotetcpsink", "RemoteTCPSinkSettings"},
{"sdrangel.channeltx.remotesource", "RemoteSourceSettings"},
{"sdrangel.channeltx.modssb", "SSBModSettings"},
{"sdrangel.channel.ssbdemod", "SSBDemodSettings"},
@ -115,6 +116,7 @@ const QMap<QString, QString> WebAPIUtils::m_deviceIdToSettingsKey = {
{"sdrangel.samplesink.plutosdr", "plutoSdrOutputSettings"},
{"sdrangel.samplesource.rtlsdr", "rtlSdrSettings"},
{"sdrangel.samplesource.remoteinput", "remoteInputSettings"},
{"sdrangel.samplesource.remotetcpinput", "remoteTCPInputSettings"},
{"sdrangel.samplesink.remoteoutput", "remoteOutputSettings"},
{"sdrangel.samplesource.sdrplay", "sdrPlaySettings"},
{"sdrangel.samplesource.sdrplayv3", "sdrPlayV3Settings"},
@ -170,6 +172,7 @@ const QMap<QString, QString> WebAPIUtils::m_channelTypeToSettingsKey = {
{"RadiosondeDemod", "RadiosondeDemodSettings"},
{"RemoteSink", "RemoteSinkSettings"},
{"RemoteSource", "RemoteSourceSettings"},
{"RemoteTCPSink", "RemoteTCPSinkSettings"},
{"SSBMod", "SSBModSettings"},
{"SSBDemod", "SSBDemodSettings"},
{"UDPSink", "UDPSinkSettings"},
@ -211,6 +214,7 @@ const QMap<QString, QString> WebAPIUtils::m_sourceDeviceHwIdToSettingsKey = {
{"PlutoSDR", "plutoSdrInputSettings"},
{"RTLSDR", "rtlSdrSettings"},
{"RemoteInput", "remoteInputSettings"},
{"RemoteTCPInput", "remoteTCPInputSettings"},
{"SDRplay1", "sdrPlaySettings"},
{"SDRplayV3", "sdrPlayV3Settings"},
{"SigMFFileInput", "sigMFFileInputSettings"},
@ -511,6 +515,39 @@ bool WebAPIUtils::setSubObjectString(QJsonObject &json, const QString &key, cons
return false;
}
// Get string list from within nested JSON object
bool WebAPIUtils::getSubObjectIntList(const QJsonObject &json, const QString &key, const QString &subKey, QList<int> &values)
{
for (QJsonObject::const_iterator it = json.begin(); it != json.end(); it++)
{
QJsonValue jsonValue = it.value();
if (jsonValue.isObject())
{
QJsonObject subObject = jsonValue.toObject();
if (subObject.contains(key))
{
QJsonValue value = subObject[key];
if (value.isArray())
{
QJsonArray array = value.toArray();
for (int i = 0; i < array.size(); i++)
{
QJsonObject element = array[i].toObject();
if (element.contains(subKey)) {
values.append(element[subKey].toInt());
}
}
return true;
}
}
}
}
return false;
}
// look for value in key=value
bool WebAPIUtils::extractValue(const QJsonObject &json, const QString &key, QJsonValue &value)
{

View File

@ -51,6 +51,7 @@ public:
static bool setSubObjectInt(QJsonObject &json, const QString &key, int value);
static bool getSubObjectString(const QJsonObject &json, const QString &key, QString &value);
static bool setSubObjectString(QJsonObject &json, const QString &key, const QString &value);
static bool getSubObjectIntList(const QJsonObject &json, const QString &key, const QString &subKey, QList<int> &values);
static bool extractValue(const QJsonObject &json, const QString &key, QJsonValue &value);
static bool extractArray(const QJsonObject &json, const QString &key, QJsonArray &value);
static bool extractObject(const QJsonObject &json, const QString &key, QJsonObject &value);

View File

@ -97,6 +97,8 @@ ChannelSettings:
$ref: "http://swgserver:8081/api/swagger/include/RemoteSink.yaml#/RemoteSinkSettings"
RemoteSourceSettings:
$ref: "http://swgserver:8081/api/swagger/include/RemoteSource.yaml#/RemoteSourceSettings"
RemoteTCPSinkSettings:
$ref: "http://swgserver:8081/api/swagger/include/RemoteTCPSink.yaml#/RemoteTCPSinkSettings"
SigMFFileSinkSettings:
$ref: "http://swgserver:8081/api/swagger/include/SigMFFileSink.yaml#/SigMFFileSinkSettings"
SSBModSettings:

View File

@ -49,6 +49,8 @@ DeviceReport:
$ref: "http://swgserver:8081/api/swagger/include/RemoteOutput.yaml#/RemoteOutputReport"
remoteInputReport:
$ref: "http://swgserver:8081/api/swagger/include/RemoteInput.yaml#/RemoteInputReport"
remoteTCPInputReport:
$ref: "http://swgserver:8081/api/swagger/include/RemoteTCPInput.yaml#/RemoteTCPInputReport"
sdrPlayReport:
$ref: "http://swgserver:8081/api/swagger/include/SDRPlay.yaml#/SDRPlayReport"
sdrPlayV3Report:

View File

@ -72,6 +72,8 @@ DeviceSettings:
$ref: "http://swgserver:8081/api/swagger/include/RemoteOutput.yaml#/RemoteOutputSettings"
remoteInputSettings:
$ref: "http://swgserver:8081/api/swagger/include/RemoteInput.yaml#/RemoteInputSettings"
remoteTCPInputSettings:
$ref: "http://swgserver:8081/api/swagger/include/RemoteTCPInput.yaml#/RemoteTCPInputSettings"
sdrPlaySettings:
$ref: "http://swgserver:8081/api/swagger/include/SDRPlay.yaml#/SDRPlaySettings"
sdrPlayV3Settings:

View File

@ -0,0 +1,60 @@
RemoteTCPInputSettings:
description: RemoteTCPInput
properties:
centerFrequency:
type: integer
format: int64
loPpmCorrection:
type: integer
dcBlock:
type: integer
iqCorrection:
type: integer
biasTee:
type: integer
directSampling:
type: integer
devSampleRate:
type: integer
log2Decim:
type: integer
gain:
type: integer
agc:
type: integer
rfBW:
type: integer
inputFrequencyOffset:
type: integer
format: int64
channelGain:
type: integer
channelSampleRate:
type: integer
channelDecimation:
type: integer
sampleBits:
type: integer
dataAddress:
type: string
dataPort:
type: integer
overrideRemoteSettings:
type: integer
preFill:
type: integer
useReverseAPI:
description: Synchronize with reverse API (1 for yes, 0 for no)
type: integer
reverseAPIAddress:
type: string
reverseAPIPort:
type: integer
reverseAPIDeviceIndex:
type: integer
RemoteTCPInputReport:
description: RemoteTCPInput
properties:
sampleRate:
type: integer

View File

@ -0,0 +1,42 @@
RemoteTCPSinkSettings:
description: "Remote TCP channel sink settings"
properties:
channelSampleRate:
type: integer
inputFrequencyOffset:
type: integer
format: int64
gain:
type: integer
sampleBits:
type: integer
dataAddress:
description: "Receiving TCP data address"
type: string
dataPort:
description: "Receiving TCP data port"
type: integer
protocol:
type: integer
rgbColor:
type: integer
title:
type: string
streamIndex:
description: MIMO channel. Not relevant when connected to SI (single Rx).
type: integer
useReverseAPI:
description: Synchronize with reverse API (1 for yes, 0 for no)
type: integer
reverseAPIAddress:
type: string
reverseAPIPort:
type: integer
reverseAPIDeviceIndex:
type: integer
reverseAPIChannelIndex:
type: integer
channelMarker:
$ref: "http://swgserver:8081/api/swagger/include/ChannelMarker.yaml#/ChannelMarker"
rollupState:
$ref: "http://swgserver:8081/api/swagger/include/RollupState.yaml#/RollupState"

View File

@ -49,6 +49,12 @@ SDRPlayV3Settings:
IQ samples order
* 0 - Q then I (swapped)
* 1 - I then Q (straight)
biasTee:
type: integer
description: >
Bias tee
* 0 - inactive
* 1 - active
useReverseAPI:
description: Synchronize with reverse API (1 for yes, 0 for no)
type: integer

View File

@ -116,6 +116,8 @@ SWGChannelSettings::SWGChannelSettings() {
m_remote_sink_settings_isSet = false;
remote_source_settings = nullptr;
m_remote_source_settings_isSet = false;
remote_tcp_sink_settings = nullptr;
m_remote_tcp_sink_settings_isSet = false;
sig_mf_file_sink_settings = nullptr;
m_sig_mf_file_sink_settings_isSet = false;
ssb_mod_settings = nullptr;
@ -228,6 +230,8 @@ SWGChannelSettings::init() {
m_remote_sink_settings_isSet = false;
remote_source_settings = new SWGRemoteSourceSettings();
m_remote_source_settings_isSet = false;
remote_tcp_sink_settings = new SWGRemoteTCPSinkSettings();
m_remote_tcp_sink_settings_isSet = false;
sig_mf_file_sink_settings = new SWGSigMFFileSinkSettings();
m_sig_mf_file_sink_settings_isSet = false;
ssb_mod_settings = new SWGSSBModSettings();
@ -374,6 +378,9 @@ SWGChannelSettings::cleanup() {
if(remote_source_settings != nullptr) {
delete remote_source_settings;
}
if(remote_tcp_sink_settings != nullptr) {
delete remote_tcp_sink_settings;
}
if(sig_mf_file_sink_settings != nullptr) {
delete sig_mf_file_sink_settings;
}
@ -499,6 +506,8 @@ SWGChannelSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&remote_source_settings, pJson["RemoteSourceSettings"], "SWGRemoteSourceSettings", "SWGRemoteSourceSettings");
::SWGSDRangel::setValue(&remote_tcp_sink_settings, pJson["RemoteTCPSinkSettings"], "SWGRemoteTCPSinkSettings", "SWGRemoteTCPSinkSettings");
::SWGSDRangel::setValue(&sig_mf_file_sink_settings, pJson["SigMFFileSinkSettings"], "SWGSigMFFileSinkSettings", "SWGSigMFFileSinkSettings");
::SWGSDRangel::setValue(&ssb_mod_settings, pJson["SSBModSettings"], "SWGSSBModSettings", "SWGSSBModSettings");
@ -663,6 +672,9 @@ SWGChannelSettings::asJsonObject() {
if((remote_source_settings != nullptr) && (remote_source_settings->isSet())){
toJsonValue(QString("RemoteSourceSettings"), remote_source_settings, obj, QString("SWGRemoteSourceSettings"));
}
if((remote_tcp_sink_settings != nullptr) && (remote_tcp_sink_settings->isSet())){
toJsonValue(QString("RemoteTCPSinkSettings"), remote_tcp_sink_settings, obj, QString("SWGRemoteTCPSinkSettings"));
}
if((sig_mf_file_sink_settings != nullptr) && (sig_mf_file_sink_settings->isSet())){
toJsonValue(QString("SigMFFileSinkSettings"), sig_mf_file_sink_settings, obj, QString("SWGSigMFFileSinkSettings"));
}
@ -1131,6 +1143,16 @@ SWGChannelSettings::setRemoteSourceSettings(SWGRemoteSourceSettings* remote_sour
this->m_remote_source_settings_isSet = true;
}
SWGRemoteTCPSinkSettings*
SWGChannelSettings::getRemoteTcpSinkSettings() {
return remote_tcp_sink_settings;
}
void
SWGChannelSettings::setRemoteTcpSinkSettings(SWGRemoteTCPSinkSettings* remote_tcp_sink_settings) {
this->remote_tcp_sink_settings = remote_tcp_sink_settings;
this->m_remote_tcp_sink_settings_isSet = true;
}
SWGSigMFFileSinkSettings*
SWGChannelSettings::getSigMfFileSinkSettings() {
return sig_mf_file_sink_settings;
@ -1348,6 +1370,9 @@ SWGChannelSettings::isSet(){
if(remote_source_settings && remote_source_settings->isSet()){
isObjectUpdated = true; break;
}
if(remote_tcp_sink_settings && remote_tcp_sink_settings->isSet()){
isObjectUpdated = true; break;
}
if(sig_mf_file_sink_settings && sig_mf_file_sink_settings->isSet()){
isObjectUpdated = true; break;
}

View File

@ -62,6 +62,7 @@
#include "SWGRadiosondeDemodSettings.h"
#include "SWGRemoteSinkSettings.h"
#include "SWGRemoteSourceSettings.h"
#include "SWGRemoteTCPSinkSettings.h"
#include "SWGSSBDemodSettings.h"
#include "SWGSSBModSettings.h"
#include "SWGSigMFFileSinkSettings.h"
@ -222,6 +223,9 @@ public:
SWGRemoteSourceSettings* getRemoteSourceSettings();
void setRemoteSourceSettings(SWGRemoteSourceSettings* remote_source_settings);
SWGRemoteTCPSinkSettings* getRemoteTcpSinkSettings();
void setRemoteTcpSinkSettings(SWGRemoteTCPSinkSettings* remote_tcp_sink_settings);
SWGSigMFFileSinkSettings* getSigMfFileSinkSettings();
void setSigMfFileSinkSettings(SWGSigMFFileSinkSettings* sig_mf_file_sink_settings);
@ -382,6 +386,9 @@ private:
SWGRemoteSourceSettings* remote_source_settings;
bool m_remote_source_settings_isSet;
SWGRemoteTCPSinkSettings* remote_tcp_sink_settings;
bool m_remote_tcp_sink_settings_isSet;
SWGSigMFFileSinkSettings* sig_mf_file_sink_settings;
bool m_sig_mf_file_sink_settings_isSet;

View File

@ -70,6 +70,8 @@ SWGDeviceReport::SWGDeviceReport() {
m_remote_output_report_isSet = false;
remote_input_report = nullptr;
m_remote_input_report_isSet = false;
remote_tcp_input_report = nullptr;
m_remote_tcp_input_report_isSet = false;
sdr_play_report = nullptr;
m_sdr_play_report_isSet = false;
sdr_play_v3_report = nullptr;
@ -140,6 +142,8 @@ SWGDeviceReport::init() {
m_remote_output_report_isSet = false;
remote_input_report = new SWGRemoteInputReport();
m_remote_input_report_isSet = false;
remote_tcp_input_report = new SWGRemoteTCPInputReport();
m_remote_tcp_input_report_isSet = false;
sdr_play_report = new SWGSDRPlayReport();
m_sdr_play_report_isSet = false;
sdr_play_v3_report = new SWGSDRPlayV3Report();
@ -225,6 +229,9 @@ SWGDeviceReport::cleanup() {
if(remote_input_report != nullptr) {
delete remote_input_report;
}
if(remote_tcp_input_report != nullptr) {
delete remote_tcp_input_report;
}
if(sdr_play_report != nullptr) {
delete sdr_play_report;
}
@ -310,6 +317,8 @@ SWGDeviceReport::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&remote_input_report, pJson["remoteInputReport"], "SWGRemoteInputReport", "SWGRemoteInputReport");
::SWGSDRangel::setValue(&remote_tcp_input_report, pJson["remoteTCPInputReport"], "SWGRemoteTCPInputReport", "SWGRemoteTCPInputReport");
::SWGSDRangel::setValue(&sdr_play_report, pJson["sdrPlayReport"], "SWGSDRPlayReport", "SWGSDRPlayReport");
::SWGSDRangel::setValue(&sdr_play_v3_report, pJson["sdrPlayV3Report"], "SWGSDRPlayV3Report", "SWGSDRPlayV3Report");
@ -409,6 +418,9 @@ SWGDeviceReport::asJsonObject() {
if((remote_input_report != nullptr) && (remote_input_report->isSet())){
toJsonValue(QString("remoteInputReport"), remote_input_report, obj, QString("SWGRemoteInputReport"));
}
if((remote_tcp_input_report != nullptr) && (remote_tcp_input_report->isSet())){
toJsonValue(QString("remoteTCPInputReport"), remote_tcp_input_report, obj, QString("SWGRemoteTCPInputReport"));
}
if((sdr_play_report != nullptr) && (sdr_play_report->isSet())){
toJsonValue(QString("sdrPlayReport"), sdr_play_report, obj, QString("SWGSDRPlayReport"));
}
@ -653,6 +665,16 @@ SWGDeviceReport::setRemoteInputReport(SWGRemoteInputReport* remote_input_report)
this->m_remote_input_report_isSet = true;
}
SWGRemoteTCPInputReport*
SWGDeviceReport::getRemoteTcpInputReport() {
return remote_tcp_input_report;
}
void
SWGDeviceReport::setRemoteTcpInputReport(SWGRemoteTCPInputReport* remote_tcp_input_report) {
this->remote_tcp_input_report = remote_tcp_input_report;
this->m_remote_tcp_input_report_isSet = true;
}
SWGSDRPlayReport*
SWGDeviceReport::getSdrPlayReport() {
return sdr_play_report;
@ -821,6 +843,9 @@ SWGDeviceReport::isSet(){
if(remote_input_report && remote_input_report->isSet()){
isObjectUpdated = true; break;
}
if(remote_tcp_input_report && remote_tcp_input_report->isSet()){
isObjectUpdated = true; break;
}
if(sdr_play_report && sdr_play_report->isSet()){
isObjectUpdated = true; break;
}

View File

@ -40,6 +40,7 @@
#include "SWGPlutoSdrOutputReport.h"
#include "SWGRemoteInputReport.h"
#include "SWGRemoteOutputReport.h"
#include "SWGRemoteTCPInputReport.h"
#include "SWGRtlSdrReport.h"
#include "SWGSDRPlayReport.h"
#include "SWGSDRPlayV3Report.h"
@ -133,6 +134,9 @@ public:
SWGRemoteInputReport* getRemoteInputReport();
void setRemoteInputReport(SWGRemoteInputReport* remote_input_report);
SWGRemoteTCPInputReport* getRemoteTcpInputReport();
void setRemoteTcpInputReport(SWGRemoteTCPInputReport* remote_tcp_input_report);
SWGSDRPlayReport* getSdrPlayReport();
void setSdrPlayReport(SWGSDRPlayReport* sdr_play_report);
@ -230,6 +234,9 @@ private:
SWGRemoteInputReport* remote_input_report;
bool m_remote_input_report_isSet;
SWGRemoteTCPInputReport* remote_tcp_input_report;
bool m_remote_tcp_input_report_isSet;
SWGSDRPlayReport* sdr_play_report;
bool m_sdr_play_report_isSet;

View File

@ -92,6 +92,8 @@ SWGDeviceSettings::SWGDeviceSettings() {
m_remote_output_settings_isSet = false;
remote_input_settings = nullptr;
m_remote_input_settings_isSet = false;
remote_tcp_input_settings = nullptr;
m_remote_tcp_input_settings_isSet = false;
sdr_play_settings = nullptr;
m_sdr_play_settings_isSet = false;
sdr_play_v3_settings = nullptr;
@ -190,6 +192,8 @@ SWGDeviceSettings::init() {
m_remote_output_settings_isSet = false;
remote_input_settings = new SWGRemoteInputSettings();
m_remote_input_settings_isSet = false;
remote_tcp_input_settings = new SWGRemoteTCPInputSettings();
m_remote_tcp_input_settings_isSet = false;
sdr_play_settings = new SWGSDRPlaySettings();
m_sdr_play_settings_isSet = false;
sdr_play_v3_settings = new SWGSDRPlayV3Settings();
@ -312,6 +316,9 @@ SWGDeviceSettings::cleanup() {
if(remote_input_settings != nullptr) {
delete remote_input_settings;
}
if(remote_tcp_input_settings != nullptr) {
delete remote_tcp_input_settings;
}
if(sdr_play_settings != nullptr) {
delete sdr_play_settings;
}
@ -428,6 +435,8 @@ SWGDeviceSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&remote_input_settings, pJson["remoteInputSettings"], "SWGRemoteInputSettings", "SWGRemoteInputSettings");
::SWGSDRangel::setValue(&remote_tcp_input_settings, pJson["remoteTCPInputSettings"], "SWGRemoteTCPInputSettings", "SWGRemoteTCPInputSettings");
::SWGSDRangel::setValue(&sdr_play_settings, pJson["sdrPlaySettings"], "SWGSDRPlaySettings", "SWGSDRPlaySettings");
::SWGSDRangel::setValue(&sdr_play_v3_settings, pJson["sdrPlayV3Settings"], "SWGSDRPlayV3Settings", "SWGSDRPlayV3Settings");
@ -566,6 +575,9 @@ SWGDeviceSettings::asJsonObject() {
if((remote_input_settings != nullptr) && (remote_input_settings->isSet())){
toJsonValue(QString("remoteInputSettings"), remote_input_settings, obj, QString("SWGRemoteInputSettings"));
}
if((remote_tcp_input_settings != nullptr) && (remote_tcp_input_settings->isSet())){
toJsonValue(QString("remoteTCPInputSettings"), remote_tcp_input_settings, obj, QString("SWGRemoteTCPInputSettings"));
}
if((sdr_play_settings != nullptr) && (sdr_play_settings->isSet())){
toJsonValue(QString("sdrPlaySettings"), sdr_play_settings, obj, QString("SWGSDRPlaySettings"));
}
@ -929,6 +941,16 @@ SWGDeviceSettings::setRemoteInputSettings(SWGRemoteInputSettings* remote_input_s
this->m_remote_input_settings_isSet = true;
}
SWGRemoteTCPInputSettings*
SWGDeviceSettings::getRemoteTcpInputSettings() {
return remote_tcp_input_settings;
}
void
SWGDeviceSettings::setRemoteTcpInputSettings(SWGRemoteTCPInputSettings* remote_tcp_input_settings) {
this->remote_tcp_input_settings = remote_tcp_input_settings;
this->m_remote_tcp_input_settings_isSet = true;
}
SWGSDRPlaySettings*
SWGDeviceSettings::getSdrPlaySettings() {
return sdr_play_settings;
@ -1160,6 +1182,9 @@ SWGDeviceSettings::isSet(){
if(remote_input_settings && remote_input_settings->isSet()){
isObjectUpdated = true; break;
}
if(remote_tcp_input_settings && remote_tcp_input_settings->isSet()){
isObjectUpdated = true; break;
}
if(sdr_play_settings && sdr_play_settings->isSet()){
isObjectUpdated = true; break;
}

View File

@ -50,6 +50,7 @@
#include "SWGPlutoSdrOutputSettings.h"
#include "SWGRemoteInputSettings.h"
#include "SWGRemoteOutputSettings.h"
#include "SWGRemoteTCPInputSettings.h"
#include "SWGRtlSdrSettings.h"
#include "SWGSDRPlaySettings.h"
#include "SWGSDRPlayV3Settings.h"
@ -180,6 +181,9 @@ public:
SWGRemoteInputSettings* getRemoteInputSettings();
void setRemoteInputSettings(SWGRemoteInputSettings* remote_input_settings);
SWGRemoteTCPInputSettings* getRemoteTcpInputSettings();
void setRemoteTcpInputSettings(SWGRemoteTCPInputSettings* remote_tcp_input_settings);
SWGSDRPlaySettings* getSdrPlaySettings();
void setSdrPlaySettings(SWGSDRPlaySettings* sdr_play_settings);
@ -319,6 +323,9 @@ private:
SWGRemoteInputSettings* remote_input_settings;
bool m_remote_input_settings_isSet;
SWGRemoteTCPInputSettings* remote_tcp_input_settings;
bool m_remote_tcp_input_settings_isSet;
SWGSDRPlaySettings* sdr_play_settings;
bool m_sdr_play_settings_isSet;

View File

@ -250,6 +250,9 @@
#include "SWGRemoteSinkSettings.h"
#include "SWGRemoteSourceReport.h"
#include "SWGRemoteSourceSettings.h"
#include "SWGRemoteTCPInputReport.h"
#include "SWGRemoteTCPInputSettings.h"
#include "SWGRemoteTCPSinkSettings.h"
#include "SWGRigCtlServerActions.h"
#include "SWGRigCtlServerReport.h"
#include "SWGRigCtlServerSettings.h"
@ -1519,6 +1522,21 @@ namespace SWGSDRangel {
obj->init();
return obj;
}
if(QString("SWGRemoteTCPInputReport").compare(type) == 0) {
SWGRemoteTCPInputReport *obj = new SWGRemoteTCPInputReport();
obj->init();
return obj;
}
if(QString("SWGRemoteTCPInputSettings").compare(type) == 0) {
SWGRemoteTCPInputSettings *obj = new SWGRemoteTCPInputSettings();
obj->init();
return obj;
}
if(QString("SWGRemoteTCPSinkSettings").compare(type) == 0) {
SWGRemoteTCPSinkSettings *obj = new SWGRemoteTCPSinkSettings();
obj->init();
return obj;
}
if(QString("SWGRigCtlServerActions").compare(type) == 0) {
SWGRigCtlServerActions *obj = new SWGRigCtlServerActions();
obj->init();

View File

@ -0,0 +1,108 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 7.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
#include "SWGRemoteTCPInputReport.h"
#include "SWGHelpers.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QObject>
#include <QDebug>
namespace SWGSDRangel {
SWGRemoteTCPInputReport::SWGRemoteTCPInputReport(QString* json) {
init();
this->fromJson(*json);
}
SWGRemoteTCPInputReport::SWGRemoteTCPInputReport() {
sample_rate = 0;
m_sample_rate_isSet = false;
}
SWGRemoteTCPInputReport::~SWGRemoteTCPInputReport() {
this->cleanup();
}
void
SWGRemoteTCPInputReport::init() {
sample_rate = 0;
m_sample_rate_isSet = false;
}
void
SWGRemoteTCPInputReport::cleanup() {
}
SWGRemoteTCPInputReport*
SWGRemoteTCPInputReport::fromJson(QString &json) {
QByteArray array (json.toStdString().c_str());
QJsonDocument doc = QJsonDocument::fromJson(array);
QJsonObject jsonObject = doc.object();
this->fromJsonObject(jsonObject);
return this;
}
void
SWGRemoteTCPInputReport::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&sample_rate, pJson["sampleRate"], "qint32", "");
}
QString
SWGRemoteTCPInputReport::asJson ()
{
QJsonObject* obj = this->asJsonObject();
QJsonDocument doc(*obj);
QByteArray bytes = doc.toJson();
delete obj;
return QString(bytes);
}
QJsonObject*
SWGRemoteTCPInputReport::asJsonObject() {
QJsonObject* obj = new QJsonObject();
if(m_sample_rate_isSet){
obj->insert("sampleRate", QJsonValue(sample_rate));
}
return obj;
}
qint32
SWGRemoteTCPInputReport::getSampleRate() {
return sample_rate;
}
void
SWGRemoteTCPInputReport::setSampleRate(qint32 sample_rate) {
this->sample_rate = sample_rate;
this->m_sample_rate_isSet = true;
}
bool
SWGRemoteTCPInputReport::isSet(){
bool isObjectUpdated = false;
do{
if(m_sample_rate_isSet){
isObjectUpdated = true; break;
}
}while(false);
return isObjectUpdated;
}
}

View File

@ -0,0 +1,58 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 7.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/*
* SWGRemoteTCPInputReport.h
*
* RemoteTCPInput
*/
#ifndef SWGRemoteTCPInputReport_H_
#define SWGRemoteTCPInputReport_H_
#include <QJsonObject>
#include "SWGObject.h"
#include "export.h"
namespace SWGSDRangel {
class SWG_API SWGRemoteTCPInputReport: public SWGObject {
public:
SWGRemoteTCPInputReport();
SWGRemoteTCPInputReport(QString* json);
virtual ~SWGRemoteTCPInputReport();
void init();
void cleanup();
virtual QString asJson () override;
virtual QJsonObject* asJsonObject() override;
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGRemoteTCPInputReport* fromJson(QString &jsonString) override;
qint32 getSampleRate();
void setSampleRate(qint32 sample_rate);
virtual bool isSet() override;
private:
qint32 sample_rate;
bool m_sample_rate_isSet;
};
}
#endif /* SWGRemoteTCPInputReport_H_ */

View File

@ -0,0 +1,641 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 7.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
#include "SWGRemoteTCPInputSettings.h"
#include "SWGHelpers.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QObject>
#include <QDebug>
namespace SWGSDRangel {
SWGRemoteTCPInputSettings::SWGRemoteTCPInputSettings(QString* json) {
init();
this->fromJson(*json);
}
SWGRemoteTCPInputSettings::SWGRemoteTCPInputSettings() {
center_frequency = 0L;
m_center_frequency_isSet = false;
lo_ppm_correction = 0;
m_lo_ppm_correction_isSet = false;
dc_block = 0;
m_dc_block_isSet = false;
iq_correction = 0;
m_iq_correction_isSet = false;
bias_tee = 0;
m_bias_tee_isSet = false;
direct_sampling = 0;
m_direct_sampling_isSet = false;
dev_sample_rate = 0;
m_dev_sample_rate_isSet = false;
log2_decim = 0;
m_log2_decim_isSet = false;
gain = 0;
m_gain_isSet = false;
agc = 0;
m_agc_isSet = false;
rf_bw = 0;
m_rf_bw_isSet = false;
input_frequency_offset = 0L;
m_input_frequency_offset_isSet = false;
channel_gain = 0;
m_channel_gain_isSet = false;
channel_sample_rate = 0;
m_channel_sample_rate_isSet = false;
channel_decimation = 0;
m_channel_decimation_isSet = false;
sample_bits = 0;
m_sample_bits_isSet = false;
data_address = nullptr;
m_data_address_isSet = false;
data_port = 0;
m_data_port_isSet = false;
override_remote_settings = 0;
m_override_remote_settings_isSet = false;
pre_fill = 0;
m_pre_fill_isSet = false;
use_reverse_api = 0;
m_use_reverse_api_isSet = false;
reverse_api_address = nullptr;
m_reverse_api_address_isSet = false;
reverse_api_port = 0;
m_reverse_api_port_isSet = false;
reverse_api_device_index = 0;
m_reverse_api_device_index_isSet = false;
}
SWGRemoteTCPInputSettings::~SWGRemoteTCPInputSettings() {
this->cleanup();
}
void
SWGRemoteTCPInputSettings::init() {
center_frequency = 0L;
m_center_frequency_isSet = false;
lo_ppm_correction = 0;
m_lo_ppm_correction_isSet = false;
dc_block = 0;
m_dc_block_isSet = false;
iq_correction = 0;
m_iq_correction_isSet = false;
bias_tee = 0;
m_bias_tee_isSet = false;
direct_sampling = 0;
m_direct_sampling_isSet = false;
dev_sample_rate = 0;
m_dev_sample_rate_isSet = false;
log2_decim = 0;
m_log2_decim_isSet = false;
gain = 0;
m_gain_isSet = false;
agc = 0;
m_agc_isSet = false;
rf_bw = 0;
m_rf_bw_isSet = false;
input_frequency_offset = 0L;
m_input_frequency_offset_isSet = false;
channel_gain = 0;
m_channel_gain_isSet = false;
channel_sample_rate = 0;
m_channel_sample_rate_isSet = false;
channel_decimation = 0;
m_channel_decimation_isSet = false;
sample_bits = 0;
m_sample_bits_isSet = false;
data_address = new QString("");
m_data_address_isSet = false;
data_port = 0;
m_data_port_isSet = false;
override_remote_settings = 0;
m_override_remote_settings_isSet = false;
pre_fill = 0;
m_pre_fill_isSet = false;
use_reverse_api = 0;
m_use_reverse_api_isSet = false;
reverse_api_address = new QString("");
m_reverse_api_address_isSet = false;
reverse_api_port = 0;
m_reverse_api_port_isSet = false;
reverse_api_device_index = 0;
m_reverse_api_device_index_isSet = false;
}
void
SWGRemoteTCPInputSettings::cleanup() {
if(data_address != nullptr) {
delete data_address;
}
if(reverse_api_address != nullptr) {
delete reverse_api_address;
}
}
SWGRemoteTCPInputSettings*
SWGRemoteTCPInputSettings::fromJson(QString &json) {
QByteArray array (json.toStdString().c_str());
QJsonDocument doc = QJsonDocument::fromJson(array);
QJsonObject jsonObject = doc.object();
this->fromJsonObject(jsonObject);
return this;
}
void
SWGRemoteTCPInputSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&center_frequency, pJson["centerFrequency"], "qint64", "");
::SWGSDRangel::setValue(&lo_ppm_correction, pJson["loPpmCorrection"], "qint32", "");
::SWGSDRangel::setValue(&dc_block, pJson["dcBlock"], "qint32", "");
::SWGSDRangel::setValue(&iq_correction, pJson["iqCorrection"], "qint32", "");
::SWGSDRangel::setValue(&bias_tee, pJson["biasTee"], "qint32", "");
::SWGSDRangel::setValue(&direct_sampling, pJson["directSampling"], "qint32", "");
::SWGSDRangel::setValue(&dev_sample_rate, pJson["devSampleRate"], "qint32", "");
::SWGSDRangel::setValue(&log2_decim, pJson["log2Decim"], "qint32", "");
::SWGSDRangel::setValue(&gain, pJson["gain"], "qint32", "");
::SWGSDRangel::setValue(&agc, pJson["agc"], "qint32", "");
::SWGSDRangel::setValue(&rf_bw, pJson["rfBW"], "qint32", "");
::SWGSDRangel::setValue(&input_frequency_offset, pJson["inputFrequencyOffset"], "qint64", "");
::SWGSDRangel::setValue(&channel_gain, pJson["channelGain"], "qint32", "");
::SWGSDRangel::setValue(&channel_sample_rate, pJson["channelSampleRate"], "qint32", "");
::SWGSDRangel::setValue(&channel_decimation, pJson["channelDecimation"], "qint32", "");
::SWGSDRangel::setValue(&sample_bits, pJson["sampleBits"], "qint32", "");
::SWGSDRangel::setValue(&data_address, pJson["dataAddress"], "QString", "QString");
::SWGSDRangel::setValue(&data_port, pJson["dataPort"], "qint32", "");
::SWGSDRangel::setValue(&override_remote_settings, pJson["overrideRemoteSettings"], "qint32", "");
::SWGSDRangel::setValue(&pre_fill, pJson["preFill"], "qint32", "");
::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString");
::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", "");
}
QString
SWGRemoteTCPInputSettings::asJson ()
{
QJsonObject* obj = this->asJsonObject();
QJsonDocument doc(*obj);
QByteArray bytes = doc.toJson();
delete obj;
return QString(bytes);
}
QJsonObject*
SWGRemoteTCPInputSettings::asJsonObject() {
QJsonObject* obj = new QJsonObject();
if(m_center_frequency_isSet){
obj->insert("centerFrequency", QJsonValue(center_frequency));
}
if(m_lo_ppm_correction_isSet){
obj->insert("loPpmCorrection", QJsonValue(lo_ppm_correction));
}
if(m_dc_block_isSet){
obj->insert("dcBlock", QJsonValue(dc_block));
}
if(m_iq_correction_isSet){
obj->insert("iqCorrection", QJsonValue(iq_correction));
}
if(m_bias_tee_isSet){
obj->insert("biasTee", QJsonValue(bias_tee));
}
if(m_direct_sampling_isSet){
obj->insert("directSampling", QJsonValue(direct_sampling));
}
if(m_dev_sample_rate_isSet){
obj->insert("devSampleRate", QJsonValue(dev_sample_rate));
}
if(m_log2_decim_isSet){
obj->insert("log2Decim", QJsonValue(log2_decim));
}
if(m_gain_isSet){
obj->insert("gain", QJsonValue(gain));
}
if(m_agc_isSet){
obj->insert("agc", QJsonValue(agc));
}
if(m_rf_bw_isSet){
obj->insert("rfBW", QJsonValue(rf_bw));
}
if(m_input_frequency_offset_isSet){
obj->insert("inputFrequencyOffset", QJsonValue(input_frequency_offset));
}
if(m_channel_gain_isSet){
obj->insert("channelGain", QJsonValue(channel_gain));
}
if(m_channel_sample_rate_isSet){
obj->insert("channelSampleRate", QJsonValue(channel_sample_rate));
}
if(m_channel_decimation_isSet){
obj->insert("channelDecimation", QJsonValue(channel_decimation));
}
if(m_sample_bits_isSet){
obj->insert("sampleBits", QJsonValue(sample_bits));
}
if(data_address != nullptr && *data_address != QString("")){
toJsonValue(QString("dataAddress"), data_address, obj, QString("QString"));
}
if(m_data_port_isSet){
obj->insert("dataPort", QJsonValue(data_port));
}
if(m_override_remote_settings_isSet){
obj->insert("overrideRemoteSettings", QJsonValue(override_remote_settings));
}
if(m_pre_fill_isSet){
obj->insert("preFill", QJsonValue(pre_fill));
}
if(m_use_reverse_api_isSet){
obj->insert("useReverseAPI", QJsonValue(use_reverse_api));
}
if(reverse_api_address != nullptr && *reverse_api_address != QString("")){
toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString"));
}
if(m_reverse_api_port_isSet){
obj->insert("reverseAPIPort", QJsonValue(reverse_api_port));
}
if(m_reverse_api_device_index_isSet){
obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index));
}
return obj;
}
qint64
SWGRemoteTCPInputSettings::getCenterFrequency() {
return center_frequency;
}
void
SWGRemoteTCPInputSettings::setCenterFrequency(qint64 center_frequency) {
this->center_frequency = center_frequency;
this->m_center_frequency_isSet = true;
}
qint32
SWGRemoteTCPInputSettings::getLoPpmCorrection() {
return lo_ppm_correction;
}
void
SWGRemoteTCPInputSettings::setLoPpmCorrection(qint32 lo_ppm_correction) {
this->lo_ppm_correction = lo_ppm_correction;
this->m_lo_ppm_correction_isSet = true;
}
qint32
SWGRemoteTCPInputSettings::getDcBlock() {
return dc_block;
}
void
SWGRemoteTCPInputSettings::setDcBlock(qint32 dc_block) {
this->dc_block = dc_block;
this->m_dc_block_isSet = true;
}
qint32
SWGRemoteTCPInputSettings::getIqCorrection() {
return iq_correction;
}
void
SWGRemoteTCPInputSettings::setIqCorrection(qint32 iq_correction) {
this->iq_correction = iq_correction;
this->m_iq_correction_isSet = true;
}
qint32
SWGRemoteTCPInputSettings::getBiasTee() {
return bias_tee;
}
void
SWGRemoteTCPInputSettings::setBiasTee(qint32 bias_tee) {
this->bias_tee = bias_tee;
this->m_bias_tee_isSet = true;
}
qint32
SWGRemoteTCPInputSettings::getDirectSampling() {
return direct_sampling;
}
void
SWGRemoteTCPInputSettings::setDirectSampling(qint32 direct_sampling) {
this->direct_sampling = direct_sampling;
this->m_direct_sampling_isSet = true;
}
qint32
SWGRemoteTCPInputSettings::getDevSampleRate() {
return dev_sample_rate;
}
void
SWGRemoteTCPInputSettings::setDevSampleRate(qint32 dev_sample_rate) {
this->dev_sample_rate = dev_sample_rate;
this->m_dev_sample_rate_isSet = true;
}
qint32
SWGRemoteTCPInputSettings::getLog2Decim() {
return log2_decim;
}
void
SWGRemoteTCPInputSettings::setLog2Decim(qint32 log2_decim) {
this->log2_decim = log2_decim;
this->m_log2_decim_isSet = true;
}
qint32
SWGRemoteTCPInputSettings::getGain() {
return gain;
}
void
SWGRemoteTCPInputSettings::setGain(qint32 gain) {
this->gain = gain;
this->m_gain_isSet = true;
}
qint32
SWGRemoteTCPInputSettings::getAgc() {
return agc;
}
void
SWGRemoteTCPInputSettings::setAgc(qint32 agc) {
this->agc = agc;
this->m_agc_isSet = true;
}
qint32
SWGRemoteTCPInputSettings::getRfBw() {
return rf_bw;
}
void
SWGRemoteTCPInputSettings::setRfBw(qint32 rf_bw) {
this->rf_bw = rf_bw;
this->m_rf_bw_isSet = true;
}
qint64
SWGRemoteTCPInputSettings::getInputFrequencyOffset() {
return input_frequency_offset;
}
void
SWGRemoteTCPInputSettings::setInputFrequencyOffset(qint64 input_frequency_offset) {
this->input_frequency_offset = input_frequency_offset;
this->m_input_frequency_offset_isSet = true;
}
qint32
SWGRemoteTCPInputSettings::getChannelGain() {
return channel_gain;
}
void
SWGRemoteTCPInputSettings::setChannelGain(qint32 channel_gain) {
this->channel_gain = channel_gain;
this->m_channel_gain_isSet = true;
}
qint32
SWGRemoteTCPInputSettings::getChannelSampleRate() {
return channel_sample_rate;
}
void
SWGRemoteTCPInputSettings::setChannelSampleRate(qint32 channel_sample_rate) {
this->channel_sample_rate = channel_sample_rate;
this->m_channel_sample_rate_isSet = true;
}
qint32
SWGRemoteTCPInputSettings::getChannelDecimation() {
return channel_decimation;
}
void
SWGRemoteTCPInputSettings::setChannelDecimation(qint32 channel_decimation) {
this->channel_decimation = channel_decimation;
this->m_channel_decimation_isSet = true;
}
qint32
SWGRemoteTCPInputSettings::getSampleBits() {
return sample_bits;
}
void
SWGRemoteTCPInputSettings::setSampleBits(qint32 sample_bits) {
this->sample_bits = sample_bits;
this->m_sample_bits_isSet = true;
}
QString*
SWGRemoteTCPInputSettings::getDataAddress() {
return data_address;
}
void
SWGRemoteTCPInputSettings::setDataAddress(QString* data_address) {
this->data_address = data_address;
this->m_data_address_isSet = true;
}
qint32
SWGRemoteTCPInputSettings::getDataPort() {
return data_port;
}
void
SWGRemoteTCPInputSettings::setDataPort(qint32 data_port) {
this->data_port = data_port;
this->m_data_port_isSet = true;
}
qint32
SWGRemoteTCPInputSettings::getOverrideRemoteSettings() {
return override_remote_settings;
}
void
SWGRemoteTCPInputSettings::setOverrideRemoteSettings(qint32 override_remote_settings) {
this->override_remote_settings = override_remote_settings;
this->m_override_remote_settings_isSet = true;
}
qint32
SWGRemoteTCPInputSettings::getPreFill() {
return pre_fill;
}
void
SWGRemoteTCPInputSettings::setPreFill(qint32 pre_fill) {
this->pre_fill = pre_fill;
this->m_pre_fill_isSet = true;
}
qint32
SWGRemoteTCPInputSettings::getUseReverseApi() {
return use_reverse_api;
}
void
SWGRemoteTCPInputSettings::setUseReverseApi(qint32 use_reverse_api) {
this->use_reverse_api = use_reverse_api;
this->m_use_reverse_api_isSet = true;
}
QString*
SWGRemoteTCPInputSettings::getReverseApiAddress() {
return reverse_api_address;
}
void
SWGRemoteTCPInputSettings::setReverseApiAddress(QString* reverse_api_address) {
this->reverse_api_address = reverse_api_address;
this->m_reverse_api_address_isSet = true;
}
qint32
SWGRemoteTCPInputSettings::getReverseApiPort() {
return reverse_api_port;
}
void
SWGRemoteTCPInputSettings::setReverseApiPort(qint32 reverse_api_port) {
this->reverse_api_port = reverse_api_port;
this->m_reverse_api_port_isSet = true;
}
qint32
SWGRemoteTCPInputSettings::getReverseApiDeviceIndex() {
return reverse_api_device_index;
}
void
SWGRemoteTCPInputSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) {
this->reverse_api_device_index = reverse_api_device_index;
this->m_reverse_api_device_index_isSet = true;
}
bool
SWGRemoteTCPInputSettings::isSet(){
bool isObjectUpdated = false;
do{
if(m_center_frequency_isSet){
isObjectUpdated = true; break;
}
if(m_lo_ppm_correction_isSet){
isObjectUpdated = true; break;
}
if(m_dc_block_isSet){
isObjectUpdated = true; break;
}
if(m_iq_correction_isSet){
isObjectUpdated = true; break;
}
if(m_bias_tee_isSet){
isObjectUpdated = true; break;
}
if(m_direct_sampling_isSet){
isObjectUpdated = true; break;
}
if(m_dev_sample_rate_isSet){
isObjectUpdated = true; break;
}
if(m_log2_decim_isSet){
isObjectUpdated = true; break;
}
if(m_gain_isSet){
isObjectUpdated = true; break;
}
if(m_agc_isSet){
isObjectUpdated = true; break;
}
if(m_rf_bw_isSet){
isObjectUpdated = true; break;
}
if(m_input_frequency_offset_isSet){
isObjectUpdated = true; break;
}
if(m_channel_gain_isSet){
isObjectUpdated = true; break;
}
if(m_channel_sample_rate_isSet){
isObjectUpdated = true; break;
}
if(m_channel_decimation_isSet){
isObjectUpdated = true; break;
}
if(m_sample_bits_isSet){
isObjectUpdated = true; break;
}
if(data_address && *data_address != QString("")){
isObjectUpdated = true; break;
}
if(m_data_port_isSet){
isObjectUpdated = true; break;
}
if(m_override_remote_settings_isSet){
isObjectUpdated = true; break;
}
if(m_pre_fill_isSet){
isObjectUpdated = true; break;
}
if(m_use_reverse_api_isSet){
isObjectUpdated = true; break;
}
if(reverse_api_address && *reverse_api_address != QString("")){
isObjectUpdated = true; break;
}
if(m_reverse_api_port_isSet){
isObjectUpdated = true; break;
}
if(m_reverse_api_device_index_isSet){
isObjectUpdated = true; break;
}
}while(false);
return isObjectUpdated;
}
}

View File

@ -0,0 +1,197 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 7.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/*
* SWGRemoteTCPInputSettings.h
*
* RemoteTCPInput
*/
#ifndef SWGRemoteTCPInputSettings_H_
#define SWGRemoteTCPInputSettings_H_
#include <QJsonObject>
#include <QString>
#include "SWGObject.h"
#include "export.h"
namespace SWGSDRangel {
class SWG_API SWGRemoteTCPInputSettings: public SWGObject {
public:
SWGRemoteTCPInputSettings();
SWGRemoteTCPInputSettings(QString* json);
virtual ~SWGRemoteTCPInputSettings();
void init();
void cleanup();
virtual QString asJson () override;
virtual QJsonObject* asJsonObject() override;
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGRemoteTCPInputSettings* fromJson(QString &jsonString) override;
qint64 getCenterFrequency();
void setCenterFrequency(qint64 center_frequency);
qint32 getLoPpmCorrection();
void setLoPpmCorrection(qint32 lo_ppm_correction);
qint32 getDcBlock();
void setDcBlock(qint32 dc_block);
qint32 getIqCorrection();
void setIqCorrection(qint32 iq_correction);
qint32 getBiasTee();
void setBiasTee(qint32 bias_tee);
qint32 getDirectSampling();
void setDirectSampling(qint32 direct_sampling);
qint32 getDevSampleRate();
void setDevSampleRate(qint32 dev_sample_rate);
qint32 getLog2Decim();
void setLog2Decim(qint32 log2_decim);
qint32 getGain();
void setGain(qint32 gain);
qint32 getAgc();
void setAgc(qint32 agc);
qint32 getRfBw();
void setRfBw(qint32 rf_bw);
qint64 getInputFrequencyOffset();
void setInputFrequencyOffset(qint64 input_frequency_offset);
qint32 getChannelGain();
void setChannelGain(qint32 channel_gain);
qint32 getChannelSampleRate();
void setChannelSampleRate(qint32 channel_sample_rate);
qint32 getChannelDecimation();
void setChannelDecimation(qint32 channel_decimation);
qint32 getSampleBits();
void setSampleBits(qint32 sample_bits);
QString* getDataAddress();
void setDataAddress(QString* data_address);
qint32 getDataPort();
void setDataPort(qint32 data_port);
qint32 getOverrideRemoteSettings();
void setOverrideRemoteSettings(qint32 override_remote_settings);
qint32 getPreFill();
void setPreFill(qint32 pre_fill);
qint32 getUseReverseApi();
void setUseReverseApi(qint32 use_reverse_api);
QString* getReverseApiAddress();
void setReverseApiAddress(QString* reverse_api_address);
qint32 getReverseApiPort();
void setReverseApiPort(qint32 reverse_api_port);
qint32 getReverseApiDeviceIndex();
void setReverseApiDeviceIndex(qint32 reverse_api_device_index);
virtual bool isSet() override;
private:
qint64 center_frequency;
bool m_center_frequency_isSet;
qint32 lo_ppm_correction;
bool m_lo_ppm_correction_isSet;
qint32 dc_block;
bool m_dc_block_isSet;
qint32 iq_correction;
bool m_iq_correction_isSet;
qint32 bias_tee;
bool m_bias_tee_isSet;
qint32 direct_sampling;
bool m_direct_sampling_isSet;
qint32 dev_sample_rate;
bool m_dev_sample_rate_isSet;
qint32 log2_decim;
bool m_log2_decim_isSet;
qint32 gain;
bool m_gain_isSet;
qint32 agc;
bool m_agc_isSet;
qint32 rf_bw;
bool m_rf_bw_isSet;
qint64 input_frequency_offset;
bool m_input_frequency_offset_isSet;
qint32 channel_gain;
bool m_channel_gain_isSet;
qint32 channel_sample_rate;
bool m_channel_sample_rate_isSet;
qint32 channel_decimation;
bool m_channel_decimation_isSet;
qint32 sample_bits;
bool m_sample_bits_isSet;
QString* data_address;
bool m_data_address_isSet;
qint32 data_port;
bool m_data_port_isSet;
qint32 override_remote_settings;
bool m_override_remote_settings_isSet;
qint32 pre_fill;
bool m_pre_fill_isSet;
qint32 use_reverse_api;
bool m_use_reverse_api_isSet;
QString* reverse_api_address;
bool m_reverse_api_address_isSet;
qint32 reverse_api_port;
bool m_reverse_api_port_isSet;
qint32 reverse_api_device_index;
bool m_reverse_api_device_index_isSet;
};
}
#endif /* SWGRemoteTCPInputSettings_H_ */

View File

@ -0,0 +1,486 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 7.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
#include "SWGRemoteTCPSinkSettings.h"
#include "SWGHelpers.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QObject>
#include <QDebug>
namespace SWGSDRangel {
SWGRemoteTCPSinkSettings::SWGRemoteTCPSinkSettings(QString* json) {
init();
this->fromJson(*json);
}
SWGRemoteTCPSinkSettings::SWGRemoteTCPSinkSettings() {
channel_sample_rate = 0;
m_channel_sample_rate_isSet = false;
input_frequency_offset = 0L;
m_input_frequency_offset_isSet = false;
gain = 0;
m_gain_isSet = false;
sample_bits = 0;
m_sample_bits_isSet = false;
data_address = nullptr;
m_data_address_isSet = false;
data_port = 0;
m_data_port_isSet = false;
protocol = 0;
m_protocol_isSet = false;
rgb_color = 0;
m_rgb_color_isSet = false;
title = nullptr;
m_title_isSet = false;
stream_index = 0;
m_stream_index_isSet = false;
use_reverse_api = 0;
m_use_reverse_api_isSet = false;
reverse_api_address = nullptr;
m_reverse_api_address_isSet = false;
reverse_api_port = 0;
m_reverse_api_port_isSet = false;
reverse_api_device_index = 0;
m_reverse_api_device_index_isSet = false;
reverse_api_channel_index = 0;
m_reverse_api_channel_index_isSet = false;
channel_marker = nullptr;
m_channel_marker_isSet = false;
rollup_state = nullptr;
m_rollup_state_isSet = false;
}
SWGRemoteTCPSinkSettings::~SWGRemoteTCPSinkSettings() {
this->cleanup();
}
void
SWGRemoteTCPSinkSettings::init() {
channel_sample_rate = 0;
m_channel_sample_rate_isSet = false;
input_frequency_offset = 0L;
m_input_frequency_offset_isSet = false;
gain = 0;
m_gain_isSet = false;
sample_bits = 0;
m_sample_bits_isSet = false;
data_address = new QString("");
m_data_address_isSet = false;
data_port = 0;
m_data_port_isSet = false;
protocol = 0;
m_protocol_isSet = false;
rgb_color = 0;
m_rgb_color_isSet = false;
title = new QString("");
m_title_isSet = false;
stream_index = 0;
m_stream_index_isSet = false;
use_reverse_api = 0;
m_use_reverse_api_isSet = false;
reverse_api_address = new QString("");
m_reverse_api_address_isSet = false;
reverse_api_port = 0;
m_reverse_api_port_isSet = false;
reverse_api_device_index = 0;
m_reverse_api_device_index_isSet = false;
reverse_api_channel_index = 0;
m_reverse_api_channel_index_isSet = false;
channel_marker = new SWGChannelMarker();
m_channel_marker_isSet = false;
rollup_state = new SWGRollupState();
m_rollup_state_isSet = false;
}
void
SWGRemoteTCPSinkSettings::cleanup() {
if(data_address != nullptr) {
delete data_address;
}
if(title != nullptr) {
delete title;
}
if(reverse_api_address != nullptr) {
delete reverse_api_address;
}
if(channel_marker != nullptr) {
delete channel_marker;
}
if(rollup_state != nullptr) {
delete rollup_state;
}
}
SWGRemoteTCPSinkSettings*
SWGRemoteTCPSinkSettings::fromJson(QString &json) {
QByteArray array (json.toStdString().c_str());
QJsonDocument doc = QJsonDocument::fromJson(array);
QJsonObject jsonObject = doc.object();
this->fromJsonObject(jsonObject);
return this;
}
void
SWGRemoteTCPSinkSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&channel_sample_rate, pJson["channelSampleRate"], "qint32", "");
::SWGSDRangel::setValue(&input_frequency_offset, pJson["inputFrequencyOffset"], "qint64", "");
::SWGSDRangel::setValue(&gain, pJson["gain"], "qint32", "");
::SWGSDRangel::setValue(&sample_bits, pJson["sampleBits"], "qint32", "");
::SWGSDRangel::setValue(&data_address, pJson["dataAddress"], "QString", "QString");
::SWGSDRangel::setValue(&data_port, pJson["dataPort"], "qint32", "");
::SWGSDRangel::setValue(&protocol, pJson["protocol"], "qint32", "");
::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", "");
::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString");
::SWGSDRangel::setValue(&stream_index, pJson["streamIndex"], "qint32", "");
::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString");
::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", "");
::SWGSDRangel::setValue(&channel_marker, pJson["channelMarker"], "SWGChannelMarker", "SWGChannelMarker");
::SWGSDRangel::setValue(&rollup_state, pJson["rollupState"], "SWGRollupState", "SWGRollupState");
}
QString
SWGRemoteTCPSinkSettings::asJson ()
{
QJsonObject* obj = this->asJsonObject();
QJsonDocument doc(*obj);
QByteArray bytes = doc.toJson();
delete obj;
return QString(bytes);
}
QJsonObject*
SWGRemoteTCPSinkSettings::asJsonObject() {
QJsonObject* obj = new QJsonObject();
if(m_channel_sample_rate_isSet){
obj->insert("channelSampleRate", QJsonValue(channel_sample_rate));
}
if(m_input_frequency_offset_isSet){
obj->insert("inputFrequencyOffset", QJsonValue(input_frequency_offset));
}
if(m_gain_isSet){
obj->insert("gain", QJsonValue(gain));
}
if(m_sample_bits_isSet){
obj->insert("sampleBits", QJsonValue(sample_bits));
}
if(data_address != nullptr && *data_address != QString("")){
toJsonValue(QString("dataAddress"), data_address, obj, QString("QString"));
}
if(m_data_port_isSet){
obj->insert("dataPort", QJsonValue(data_port));
}
if(m_protocol_isSet){
obj->insert("protocol", QJsonValue(protocol));
}
if(m_rgb_color_isSet){
obj->insert("rgbColor", QJsonValue(rgb_color));
}
if(title != nullptr && *title != QString("")){
toJsonValue(QString("title"), title, obj, QString("QString"));
}
if(m_stream_index_isSet){
obj->insert("streamIndex", QJsonValue(stream_index));
}
if(m_use_reverse_api_isSet){
obj->insert("useReverseAPI", QJsonValue(use_reverse_api));
}
if(reverse_api_address != nullptr && *reverse_api_address != QString("")){
toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString"));
}
if(m_reverse_api_port_isSet){
obj->insert("reverseAPIPort", QJsonValue(reverse_api_port));
}
if(m_reverse_api_device_index_isSet){
obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index));
}
if(m_reverse_api_channel_index_isSet){
obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index));
}
if((channel_marker != nullptr) && (channel_marker->isSet())){
toJsonValue(QString("channelMarker"), channel_marker, obj, QString("SWGChannelMarker"));
}
if((rollup_state != nullptr) && (rollup_state->isSet())){
toJsonValue(QString("rollupState"), rollup_state, obj, QString("SWGRollupState"));
}
return obj;
}
qint32
SWGRemoteTCPSinkSettings::getChannelSampleRate() {
return channel_sample_rate;
}
void
SWGRemoteTCPSinkSettings::setChannelSampleRate(qint32 channel_sample_rate) {
this->channel_sample_rate = channel_sample_rate;
this->m_channel_sample_rate_isSet = true;
}
qint64
SWGRemoteTCPSinkSettings::getInputFrequencyOffset() {
return input_frequency_offset;
}
void
SWGRemoteTCPSinkSettings::setInputFrequencyOffset(qint64 input_frequency_offset) {
this->input_frequency_offset = input_frequency_offset;
this->m_input_frequency_offset_isSet = true;
}
qint32
SWGRemoteTCPSinkSettings::getGain() {
return gain;
}
void
SWGRemoteTCPSinkSettings::setGain(qint32 gain) {
this->gain = gain;
this->m_gain_isSet = true;
}
qint32
SWGRemoteTCPSinkSettings::getSampleBits() {
return sample_bits;
}
void
SWGRemoteTCPSinkSettings::setSampleBits(qint32 sample_bits) {
this->sample_bits = sample_bits;
this->m_sample_bits_isSet = true;
}
QString*
SWGRemoteTCPSinkSettings::getDataAddress() {
return data_address;
}
void
SWGRemoteTCPSinkSettings::setDataAddress(QString* data_address) {
this->data_address = data_address;
this->m_data_address_isSet = true;
}
qint32
SWGRemoteTCPSinkSettings::getDataPort() {
return data_port;
}
void
SWGRemoteTCPSinkSettings::setDataPort(qint32 data_port) {
this->data_port = data_port;
this->m_data_port_isSet = true;
}
qint32
SWGRemoteTCPSinkSettings::getProtocol() {
return protocol;
}
void
SWGRemoteTCPSinkSettings::setProtocol(qint32 protocol) {
this->protocol = protocol;
this->m_protocol_isSet = true;
}
qint32
SWGRemoteTCPSinkSettings::getRgbColor() {
return rgb_color;
}
void
SWGRemoteTCPSinkSettings::setRgbColor(qint32 rgb_color) {
this->rgb_color = rgb_color;
this->m_rgb_color_isSet = true;
}
QString*
SWGRemoteTCPSinkSettings::getTitle() {
return title;
}
void
SWGRemoteTCPSinkSettings::setTitle(QString* title) {
this->title = title;
this->m_title_isSet = true;
}
qint32
SWGRemoteTCPSinkSettings::getStreamIndex() {
return stream_index;
}
void
SWGRemoteTCPSinkSettings::setStreamIndex(qint32 stream_index) {
this->stream_index = stream_index;
this->m_stream_index_isSet = true;
}
qint32
SWGRemoteTCPSinkSettings::getUseReverseApi() {
return use_reverse_api;
}
void
SWGRemoteTCPSinkSettings::setUseReverseApi(qint32 use_reverse_api) {
this->use_reverse_api = use_reverse_api;
this->m_use_reverse_api_isSet = true;
}
QString*
SWGRemoteTCPSinkSettings::getReverseApiAddress() {
return reverse_api_address;
}
void
SWGRemoteTCPSinkSettings::setReverseApiAddress(QString* reverse_api_address) {
this->reverse_api_address = reverse_api_address;
this->m_reverse_api_address_isSet = true;
}
qint32
SWGRemoteTCPSinkSettings::getReverseApiPort() {
return reverse_api_port;
}
void
SWGRemoteTCPSinkSettings::setReverseApiPort(qint32 reverse_api_port) {
this->reverse_api_port = reverse_api_port;
this->m_reverse_api_port_isSet = true;
}
qint32
SWGRemoteTCPSinkSettings::getReverseApiDeviceIndex() {
return reverse_api_device_index;
}
void
SWGRemoteTCPSinkSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) {
this->reverse_api_device_index = reverse_api_device_index;
this->m_reverse_api_device_index_isSet = true;
}
qint32
SWGRemoteTCPSinkSettings::getReverseApiChannelIndex() {
return reverse_api_channel_index;
}
void
SWGRemoteTCPSinkSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) {
this->reverse_api_channel_index = reverse_api_channel_index;
this->m_reverse_api_channel_index_isSet = true;
}
SWGChannelMarker*
SWGRemoteTCPSinkSettings::getChannelMarker() {
return channel_marker;
}
void
SWGRemoteTCPSinkSettings::setChannelMarker(SWGChannelMarker* channel_marker) {
this->channel_marker = channel_marker;
this->m_channel_marker_isSet = true;
}
SWGRollupState*
SWGRemoteTCPSinkSettings::getRollupState() {
return rollup_state;
}
void
SWGRemoteTCPSinkSettings::setRollupState(SWGRollupState* rollup_state) {
this->rollup_state = rollup_state;
this->m_rollup_state_isSet = true;
}
bool
SWGRemoteTCPSinkSettings::isSet(){
bool isObjectUpdated = false;
do{
if(m_channel_sample_rate_isSet){
isObjectUpdated = true; break;
}
if(m_input_frequency_offset_isSet){
isObjectUpdated = true; break;
}
if(m_gain_isSet){
isObjectUpdated = true; break;
}
if(m_sample_bits_isSet){
isObjectUpdated = true; break;
}
if(data_address && *data_address != QString("")){
isObjectUpdated = true; break;
}
if(m_data_port_isSet){
isObjectUpdated = true; break;
}
if(m_protocol_isSet){
isObjectUpdated = true; break;
}
if(m_rgb_color_isSet){
isObjectUpdated = true; break;
}
if(title && *title != QString("")){
isObjectUpdated = true; break;
}
if(m_stream_index_isSet){
isObjectUpdated = true; break;
}
if(m_use_reverse_api_isSet){
isObjectUpdated = true; break;
}
if(reverse_api_address && *reverse_api_address != QString("")){
isObjectUpdated = true; break;
}
if(m_reverse_api_port_isSet){
isObjectUpdated = true; break;
}
if(m_reverse_api_device_index_isSet){
isObjectUpdated = true; break;
}
if(m_reverse_api_channel_index_isSet){
isObjectUpdated = true; break;
}
if(channel_marker && channel_marker->isSet()){
isObjectUpdated = true; break;
}
if(rollup_state && rollup_state->isSet()){
isObjectUpdated = true; break;
}
}while(false);
return isObjectUpdated;
}
}

View File

@ -0,0 +1,157 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 7.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/*
* SWGRemoteTCPSinkSettings.h
*
* Remote TCP channel sink settings
*/
#ifndef SWGRemoteTCPSinkSettings_H_
#define SWGRemoteTCPSinkSettings_H_
#include <QJsonObject>
#include "SWGChannelMarker.h"
#include "SWGRollupState.h"
#include <QString>
#include "SWGObject.h"
#include "export.h"
namespace SWGSDRangel {
class SWG_API SWGRemoteTCPSinkSettings: public SWGObject {
public:
SWGRemoteTCPSinkSettings();
SWGRemoteTCPSinkSettings(QString* json);
virtual ~SWGRemoteTCPSinkSettings();
void init();
void cleanup();
virtual QString asJson () override;
virtual QJsonObject* asJsonObject() override;
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGRemoteTCPSinkSettings* fromJson(QString &jsonString) override;
qint32 getChannelSampleRate();
void setChannelSampleRate(qint32 channel_sample_rate);
qint64 getInputFrequencyOffset();
void setInputFrequencyOffset(qint64 input_frequency_offset);
qint32 getGain();
void setGain(qint32 gain);
qint32 getSampleBits();
void setSampleBits(qint32 sample_bits);
QString* getDataAddress();
void setDataAddress(QString* data_address);
qint32 getDataPort();
void setDataPort(qint32 data_port);
qint32 getProtocol();
void setProtocol(qint32 protocol);
qint32 getRgbColor();
void setRgbColor(qint32 rgb_color);
QString* getTitle();
void setTitle(QString* title);
qint32 getStreamIndex();
void setStreamIndex(qint32 stream_index);
qint32 getUseReverseApi();
void setUseReverseApi(qint32 use_reverse_api);
QString* getReverseApiAddress();
void setReverseApiAddress(QString* reverse_api_address);
qint32 getReverseApiPort();
void setReverseApiPort(qint32 reverse_api_port);
qint32 getReverseApiDeviceIndex();
void setReverseApiDeviceIndex(qint32 reverse_api_device_index);
qint32 getReverseApiChannelIndex();
void setReverseApiChannelIndex(qint32 reverse_api_channel_index);
SWGChannelMarker* getChannelMarker();
void setChannelMarker(SWGChannelMarker* channel_marker);
SWGRollupState* getRollupState();
void setRollupState(SWGRollupState* rollup_state);
virtual bool isSet() override;
private:
qint32 channel_sample_rate;
bool m_channel_sample_rate_isSet;
qint64 input_frequency_offset;
bool m_input_frequency_offset_isSet;
qint32 gain;
bool m_gain_isSet;
qint32 sample_bits;
bool m_sample_bits_isSet;
QString* data_address;
bool m_data_address_isSet;
qint32 data_port;
bool m_data_port_isSet;
qint32 protocol;
bool m_protocol_isSet;
qint32 rgb_color;
bool m_rgb_color_isSet;
QString* title;
bool m_title_isSet;
qint32 stream_index;
bool m_stream_index_isSet;
qint32 use_reverse_api;
bool m_use_reverse_api_isSet;
QString* reverse_api_address;
bool m_reverse_api_address_isSet;
qint32 reverse_api_port;
bool m_reverse_api_port_isSet;
qint32 reverse_api_device_index;
bool m_reverse_api_device_index_isSet;
qint32 reverse_api_channel_index;
bool m_reverse_api_channel_index_isSet;
SWGChannelMarker* channel_marker;
bool m_channel_marker_isSet;
SWGRollupState* rollup_state;
bool m_rollup_state_isSet;
};
}
#endif /* SWGRemoteTCPSinkSettings_H_ */

View File

@ -70,6 +70,8 @@ SWGSDRPlayV3Settings::SWGSDRPlayV3Settings() {
m_transverter_delta_frequency_isSet = false;
iq_order = 0;
m_iq_order_isSet = false;
bias_tee = 0;
m_bias_tee_isSet = false;
use_reverse_api = 0;
m_use_reverse_api_isSet = false;
reverse_api_address = nullptr;
@ -128,6 +130,8 @@ SWGSDRPlayV3Settings::init() {
m_transverter_delta_frequency_isSet = false;
iq_order = 0;
m_iq_order_isSet = false;
bias_tee = 0;
m_bias_tee_isSet = false;
use_reverse_api = 0;
m_use_reverse_api_isSet = false;
reverse_api_address = new QString("");
@ -160,6 +164,7 @@ SWGSDRPlayV3Settings::cleanup() {
if(reverse_api_address != nullptr) {
@ -222,6 +227,8 @@ SWGSDRPlayV3Settings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&iq_order, pJson["iqOrder"], "qint32", "");
::SWGSDRangel::setValue(&bias_tee, pJson["biasTee"], "qint32", "");
::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString");
@ -309,6 +316,9 @@ SWGSDRPlayV3Settings::asJsonObject() {
if(m_iq_order_isSet){
obj->insert("iqOrder", QJsonValue(iq_order));
}
if(m_bias_tee_isSet){
obj->insert("biasTee", QJsonValue(bias_tee));
}
if(m_use_reverse_api_isSet){
obj->insert("useReverseAPI", QJsonValue(use_reverse_api));
}
@ -535,6 +545,16 @@ SWGSDRPlayV3Settings::setIqOrder(qint32 iq_order) {
this->m_iq_order_isSet = true;
}
qint32
SWGSDRPlayV3Settings::getBiasTee() {
return bias_tee;
}
void
SWGSDRPlayV3Settings::setBiasTee(qint32 bias_tee) {
this->bias_tee = bias_tee;
this->m_bias_tee_isSet = true;
}
qint32
SWGSDRPlayV3Settings::getUseReverseApi() {
return use_reverse_api;
@ -643,6 +663,9 @@ SWGSDRPlayV3Settings::isSet(){
if(m_iq_order_isSet){
isObjectUpdated = true; break;
}
if(m_bias_tee_isSet){
isObjectUpdated = true; break;
}
if(m_use_reverse_api_isSet){
isObjectUpdated = true; break;
}

View File

@ -105,6 +105,9 @@ public:
qint32 getIqOrder();
void setIqOrder(qint32 iq_order);
qint32 getBiasTee();
void setBiasTee(qint32 bias_tee);
qint32 getUseReverseApi();
void setUseReverseApi(qint32 use_reverse_api);
@ -184,6 +187,9 @@ private:
qint32 iq_order;
bool m_iq_order_isSet;
qint32 bias_tee;
bool m_bias_tee_isSet;
qint32 use_reverse_api;
bool m_use_reverse_api_isSet;