diff --git a/CMakeLists.txt b/CMakeLists.txt index 096987ee3..a9a6e3b83 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/doc/img/RemoteTCPInput_plugin.png b/doc/img/RemoteTCPInput_plugin.png new file mode 100644 index 000000000..e9b320097 Binary files /dev/null and b/doc/img/RemoteTCPInput_plugin.png differ diff --git a/doc/img/RemoteTCPSink.png b/doc/img/RemoteTCPSink.png new file mode 100644 index 000000000..2deb2f157 Binary files /dev/null and b/doc/img/RemoteTCPSink.png differ diff --git a/plugins/channelrx/CMakeLists.txt b/plugins/channelrx/CMakeLists.txt index c6e8f6ed7..1f8457772 100644 --- a/plugins/channelrx/CMakeLists.txt +++ b/plugins/channelrx/CMakeLists.txt @@ -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() diff --git a/plugins/channelrx/remotetcpsink/CMakeLists.txt b/plugins/channelrx/remotetcpsink/CMakeLists.txt new file mode 100644 index 000000000..4abac8af6 --- /dev/null +++ b/plugins/channelrx/remotetcpsink/CMakeLists.txt @@ -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 $ CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} ) +endif() diff --git a/plugins/channelrx/remotetcpsink/readme.md b/plugins/channelrx/remotetcpsink/readme.md new file mode 100644 index 000000000..bea8ba169 --- /dev/null +++ b/plugins/channelrx/remotetcpsink/readme.md @@ -0,0 +1,44 @@ +

Remote TCP sink channel plugin

+ +

Introduction

+ +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. + +

Interface

+ +![Remote TCP sink channel plugin GUI](../../../doc/img/RemoteTCPSink.png) + +

1: Frequency shift from center frequency of reception

+ +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. + +

2: Gain

+ +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. + +

3: Sample rate

+ +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. + +

4: Sample bit depth

+ +Specifies number of bits per I/Q sample transmitted via TCP/IP. + +

5: IP address

+ +IP address of the local network interface on which the server will listen for TCP/IP connections from network clients. + +

6: Port

+ +TCP port on which the server will listen for connections. + +

7: Protocol

+ +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). diff --git a/plugins/channelrx/remotetcpsink/remotetcpprotocol.h b/plugins/channelrx/remotetcpsink/remotetcpprotocol.h new file mode 100644 index 000000000..1d8ea57c8 --- /dev/null +++ b/plugins/channelrx/remotetcpsink/remotetcpprotocol.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef PLUGINS_CHANNELRX_REMOTETCPSINK_REMOTETCPPROTOCOL_H_ +#define PLUGINS_CHANNELRX_REMOTETCPSINK_REMOTETCPPROTOCOL_H_ + +#include + +// 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_ */ diff --git a/plugins/channelrx/remotetcpsink/remotetcpsink.cpp b/plugins/channelrx/remotetcpsink/remotetcpsink.cpp new file mode 100644 index 000000000..47e4ff283 --- /dev/null +++ b/plugins/channelrx/remotetcpsink/remotetcpsink.cpp @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "remotetcpsink.h" + +#include +#include +#include +#include + +#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 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 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& 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& pipes, + QList& channelSettingsKeys, + const RemoteTCPSinkSettings& settings, + bool force) +{ + for (const auto& pipe : pipes) + { + MessageQueue *messageQueue = qobject_cast(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& 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); +} diff --git a/plugins/channelrx/remotetcpsink/remotetcpsink.h b/plugins/channelrx/remotetcpsink/remotetcpsink.h new file mode 100644 index 000000000..70a4013a6 --- /dev/null +++ b/plugins/channelrx/remotetcpsink/remotetcpsink.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_REMOTETCPSINK_H_ +#define INCLUDE_REMOTETCPSINK_H_ + +#include +#include +#include + +#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& channelSettingsKeys, const RemoteTCPSinkSettings& settings, bool force); + void sendChannelSettings( + const QList& pipes, + QList& channelSettingsKeys, + const RemoteTCPSinkSettings& settings, + bool force + ); + void webapiFormatChannelSettings( + QList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings *swgChannelSettings, + const RemoteTCPSinkSettings& settings, + bool force + ); + +private slots: + void networkManagerFinished(QNetworkReply *reply); + void handleIndexInDeviceSetChanged(int index); +}; + +#endif /* INCLUDE_REMOTETCPSINK_H_ */ diff --git a/plugins/channelrx/remotetcpsink/remotetcpsinkbaseband.cpp b/plugins/channelrx/remotetcpsink/remotetcpsinkbaseband.cpp new file mode 100644 index 000000000..440c884a5 --- /dev/null +++ b/plugins/channelrx/remotetcpsink/remotetcpsinkbaseband.cpp @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#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()); +} diff --git a/plugins/channelrx/remotetcpsink/remotetcpsinkbaseband.h b/plugins/channelrx/remotetcpsink/remotetcpsinkbaseband.h new file mode 100644 index 000000000..94d82a3fb --- /dev/null +++ b/plugins/channelrx/remotetcpsink/remotetcpsinkbaseband.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_REMOTETCPSINKBASEBAND_H +#define INCLUDE_REMOTETCPSINKBASEBAND_H + +#include +#include + +#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 diff --git a/plugins/channelrx/remotetcpsink/remotetcpsinkgui.cpp b/plugins/channelrx/remotetcpsink/remotetcpsinkgui.cpp new file mode 100644 index 000000000..75f239a1d --- /dev/null +++ b/plugins/channelrx/remotetcpsink/remotetcpsinkgui.cpp @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#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(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::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::of(&QComboBox::currentIndexChanged), this, &RemoteTCPSinkGUI::on_protocol_currentIndexChanged); +} + +void RemoteTCPSinkGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/remotetcpsink/remotetcpsinkgui.h b/plugins/channelrx/remotetcpsink/remotetcpsinkgui.h new file mode 100644 index 000000000..86acabed7 --- /dev/null +++ b/plugins/channelrx/remotetcpsink/remotetcpsinkgui.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef PLUGINS_CHANNELRX_REMOTETCPSINK_REMOTETCPSINKGUI_H_ +#define PLUGINS_CHANNELRX_REMOTETCPSINK_REMOTETCPSINKGUI_H_ + +#include + +#include + +#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 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_ */ diff --git a/plugins/channelrx/remotetcpsink/remotetcpsinkgui.ui b/plugins/channelrx/remotetcpsink/remotetcpsinkgui.ui new file mode 100644 index 000000000..000417805 --- /dev/null +++ b/plugins/channelrx/remotetcpsink/remotetcpsinkgui.ui @@ -0,0 +1,473 @@ + + + RemoteTCPSinkGUI + + + + 0 + 0 + 360 + 147 + + + + + 0 + 0 + + + + + 350 + 100 + + + + + 360 + 16777215 + + + + + 9 + + + + Remote sink + + + + + 0 + 0 + 340 + 141 + + + + Settings + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + + + 16 + 0 + + + + Df + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + 12 + + + + PointingHandCursor + + + Qt::StrongFocus + + + Channel frequency shift from center in Hz + + + + + + + Hz + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Gain + + + + + + + + 24 + 24 + + + + Gain in dB + + + 0 + + + 100 + + + + + + + + 34 + 0 + + + + 50dB + + + + + + + + + 0 + + + + + SR + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + 12 + false + + + + PointingHandCursor + + + Channel sample rate in samples per second + + + + + + + S/s + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + IQ + + + + + + + + 16777215 + 16777215 + + + + Transmitted bit depth of each I and Q sample + + + + 8 + + + + + 16 + + + + + 24 + + + + + 32 + + + + + + + + bits + + + + + + + + + Qt::Horizontal + + + + + + + + + + 0 + 0 + + + + IP + + + + + + + + 120 + 0 + + + + IP address of local network interface to start TCP server on + + + 000.000.000.000 + + + 0... + + + + + + + : + + + + + + + + 50 + 16777215 + + + + TCP port for server to listen for connections on + + + 00000 + + + 0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Protocol + + + + + + + Protocol to transmit data with + + + + RTL0 + + + + + SDRA + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Clients + + + + + + + Number of clients connected + + + 0 + + + + + + + Qt::Vertical + + + + + + + BW + + + + + + + Transmit bandwidth for a single TCP connection averaged over the last 10 seconds in bits per second + + + 0.000Mbps + + + + + + + + + + + ValueDialZ + QWidget +
gui/valuedialz.h
+ 1 +
+ + ValueDial + QWidget +
gui/valuedial.h
+ 1 +
+ + RollupContents + QWidget +
gui/rollupcontents.h
+ 1 +
+
+ + + + +
diff --git a/plugins/channelrx/remotetcpsink/remotetcpsinkplugin.cpp b/plugins/channelrx/remotetcpsink/remotetcpsinkplugin.cpp new file mode 100644 index 000000000..a3e76606c --- /dev/null +++ b/plugins/channelrx/remotetcpsink/remotetcpsinkplugin.cpp @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "remotetcpsinkplugin.h" + +#include +#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(); +} diff --git a/plugins/channelrx/remotetcpsink/remotetcpsinkplugin.h b/plugins/channelrx/remotetcpsink/remotetcpsinkplugin.h new file mode 100644 index 000000000..8059b1f8d --- /dev/null +++ b/plugins/channelrx/remotetcpsink/remotetcpsinkplugin.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef PLUGINS_CHANNELRX_REMOTETCPSINK_REMOTETCPSINKPLUGIN_H_ +#define PLUGINS_CHANNELRX_REMOTETCPSINK_REMOTETCPSINKPLUGIN_H_ + + +#include +#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_ */ diff --git a/plugins/channelrx/remotetcpsink/remotetcpsinksettings.cpp b/plugins/channelrx/remotetcpsink/remotetcpsinksettings.cpp new file mode 100644 index 000000000..248d7ec34 --- /dev/null +++ b/plugins/channelrx/remotetcpsink/remotetcpsinksettings.cpp @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "remotetcpsinksettings.h" + +#include + +#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; + } +} diff --git a/plugins/channelrx/remotetcpsink/remotetcpsinksettings.h b/plugins/channelrx/remotetcpsink/remotetcpsinksettings.h new file mode 100644 index 000000000..73c25094a --- /dev/null +++ b/plugins/channelrx/remotetcpsink/remotetcpsinksettings.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_REMOTECHANNELSINKSETTINGS_H_ +#define INCLUDE_REMOTECHANNELSINKSETTINGS_H_ + +#include +#include + +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_ */ diff --git a/plugins/channelrx/remotetcpsink/remotetcpsinksink.cpp b/plugins/channelrx/remotetcpsink/remotetcpsinksink.cpp new file mode 100644 index 000000000..c826be6ec --- /dev/null +++ b/plugins/channelrx/remotetcpsink/remotetcpsinksink.cpp @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#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 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 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::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); + } + } +} diff --git a/plugins/channelrx/remotetcpsink/remotetcpsinksink.h b/plugins/channelrx/remotetcpsink/remotetcpsinksink.h new file mode 100644 index 000000000..2d57713c4 --- /dev/null +++ b/plugins/channelrx/remotetcpsink/remotetcpsinksink.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_REMOTETCPSINKSINK_H_ +#define INCLUDE_REMOTETCPSINKSINK_H_ + +#include +#include +#include +#include +#include + +#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 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_ diff --git a/plugins/channelrx/remotetcpsink/remotetcpsinkwebapiadapter.cpp b/plugins/channelrx/remotetcpsink/remotetcpsinkwebapiadapter.cpp new file mode 100644 index 000000000..08adf4d2d --- /dev/null +++ b/plugins/channelrx/remotetcpsink/remotetcpsinkwebapiadapter.cpp @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#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; +} diff --git a/plugins/channelrx/remotetcpsink/remotetcpsinkwebapiadapter.h b/plugins/channelrx/remotetcpsink/remotetcpsinkwebapiadapter.h new file mode 100644 index 000000000..44b79a36a --- /dev/null +++ b/plugins/channelrx/remotetcpsink/remotetcpsinkwebapiadapter.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#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 diff --git a/plugins/samplesource/CMakeLists.txt b/plugins/samplesource/CMakeLists.txt index 15c502c1a..f32e76eb0 100644 --- a/plugins/samplesource/CMakeLists.txt +++ b/plugins/samplesource/CMakeLists.txt @@ -72,3 +72,4 @@ endif() add_subdirectory(audioinput) add_subdirectory(kiwisdr) +add_subdirectory(remotetcpinput) diff --git a/plugins/samplesource/remotetcpinput/CMakeLists.txt b/plugins/samplesource/remotetcpinput/CMakeLists.txt new file mode 100644 index 000000000..74e3bdb70 --- /dev/null +++ b/plugins/samplesource/remotetcpinput/CMakeLists.txt @@ -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 $ CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} ) +endif() diff --git a/plugins/samplesource/remotetcpinput/readme.md b/plugins/samplesource/remotetcpinput/readme.md new file mode 100644 index 000000000..9e071d48e --- /dev/null +++ b/plugins/samplesource/remotetcpinput/readme.md @@ -0,0 +1,140 @@ +

Remote TCP input plugin

+ +

Introduction

+ +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. + +

Interface

+ +![Remote TCP input plugin GUI](../../../doc/img/RemoteTCPInput_plugin.png) + +

1: Start/Stop

+ +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. + +

2: Center frequency

+ +This is the center frequency in kHz of the remote device. + +

3: Stream sample rate

+ +Network I/Q sample rate in kS/s. + +

4: Local oscillator correction

+ +This is the correction to be applied to the remote device's local oscillator in ppm. + +

5: DC offset correction

+ +Check this button to enable DC offset correction on the remote device. This is only supported when the remote server is SDRangel. + +

6: IQ imbalance correction

+ +Check this button to enable IQ imbalance correction on the remote device. This is only supported when the remote server is SDRangel. + +

7: Bias tee

+ +Check this button to enable a bias tee on the remote device, if it supports it. + +

8: Direct sampling mode

+ +Use this button to activate RTL-SDR's direct sampling mode. This can be used to tune to HF frequencies. + +

9: Sample rate

+ +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. + +

10: Decimation

+ +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. + +

11: Gain

+ +Specify gain in dB applied in various stages of the remote device. Available gains will depend upon the type of remote device. + +

12: AGC

+ +Check to enable automatic gain control in the remote device. How AGC works is device dependent. + +

13: RF Bandwidth

+ +Specifies the bandwidth in kHz of the analog filter in the remote device. Available bandwidths are dependent upon the remote device. + +

14: Remote sink frequency shift from center frequency of reception

+ +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. + +

15: Remote sink gain

+ +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. + +

16: Remote sink channel sample rate

+ +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. + +

17: Local channel sample rate to baseband sample rate

+ +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. + +

18: Sample bit depth

+ +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. + +

19: Server IP address

+ +IP address or hostname of the server that is running SDRangel's Remote TCP Sink plugin, rtl_tcp or rsp_tcp. + +

20: Server TCP Port

+ +TCP port on the server to connect to. + +

21: Connection settings

+ +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. + +

22: Pre-fill

+ +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. + +

23: Input buffer guage

+ +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. + +

24: Output buffer guage

+ +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. + +

25: Device status

+ +Shows the type of remote device that has been connected to. + +

26: Protocol status

+ +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. diff --git a/plugins/samplesource/remotetcpinput/remotetcpinput.cpp b/plugins/samplesource/remotetcpinput/remotetcpinput.cpp new file mode 100644 index 000000000..a8104caf5 --- /dev/null +++ b/plugins/samplesource/remotetcpinput/remotetcpinput.cpp @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include +#include +#include +#include + +#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 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& 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(); +} diff --git a/plugins/samplesource/remotetcpinput/remotetcpinput.h b/plugins/samplesource/remotetcpinput/remotetcpinput.h new file mode 100644 index 000000000..8fd9e2e80 --- /dev/null +++ b/plugins/samplesource/remotetcpinput/remotetcpinput.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_REMOTETCPINPUT_H +#define INCLUDE_REMOTETCPINPUT_H + +#include +#include +#include + +#include +#include +#include +#include +#include + +#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& deviceSettingsKeys, const RemoteTCPInputSettings& settings, bool force); + void webapiReverseSendStartStop(bool start); + +private slots: + void networkManagerFinished(QNetworkReply *reply); +}; + +#endif // INCLUDE_REMOTETCPINPUT_H diff --git a/plugins/samplesource/remotetcpinput/remotetcpinputgui.cpp b/plugins/samplesource/remotetcpinput/remotetcpinputgui.cpp new file mode 100644 index 000000000..fb392b8e5 --- /dev/null +++ b/plugins/samplesource/remotetcpinput/remotetcpinputgui.cpp @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include + +#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 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 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::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::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); +} diff --git a/plugins/samplesource/remotetcpinput/remotetcpinputgui.h b/plugins/samplesource/remotetcpinput/remotetcpinputgui.h new file mode 100644 index 000000000..9e47977e2 --- /dev/null +++ b/plugins/samplesource/remotetcpinput/remotetcpinputgui.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_REMOTETCPINPUTGUI_H +#define INCLUDE_REMOTETCPINPUTGUI_H + +#include +#include +#include + +#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 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 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 gains, bool agc, bool biasTee) : + m_gains(gains), + m_agc(agc), + m_biasTee(biasTee) + { + } + + QList 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 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 diff --git a/plugins/samplesource/remotetcpinput/remotetcpinputgui.ui b/plugins/samplesource/remotetcpinput/remotetcpinputgui.ui new file mode 100644 index 000000000..ffed63361 --- /dev/null +++ b/plugins/samplesource/remotetcpinput/remotetcpinputgui.ui @@ -0,0 +1,1225 @@ + + + RemoteTCPInputGui + + + + 0 + 0 + 360 + 360 + + + + + 0 + 0 + + + + + 360 + 360 + + + + + 491 + 360 + + + + + 9 + + + + Remote TCP Input + + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + 4 + + + + + + + + + start/stop acquisition + + + + + + + :/play.png + :/stop.png:/play.png + + + + + + + + + + + Device I/Q sample rate kS/s + + + 00000k + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + 16 + false + + + + PointingHandCursor + + + Qt::StrongFocus + + + Center frequency in kHz + + + + + + + + 0 + 0 + + + + kHz + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 3 + + + + + LO ppm + + + + + + + Local Oscillator ppm correction + + + -200 + + + 200 + + + 1 + + + Qt::Horizontal + + + + + + + + 40 + 0 + + + + 0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Qt::Horizontal + + + + + + + + + DC Offset auto correction + + + DC + + + + + + + IQ Imbalance auto correction + + + IQ + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Enable bias tee + + + T + + + + + + + Direct sampling + + + DS + + + + + + + + + Qt::Horizontal + + + + + + + 6 + + + + + SR + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + 12 + false + + + + PointingHandCursor + + + Remote device sample rate + + + + + + + S/s + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Dec + + + + + + + + 45 + 16777215 + + + + Decimation factor (applied on remote device) + + + 0 + + + + 1 + + + + + 2 + + + + + 4 + + + + + 8 + + + + + 16 + + + + + 32 + + + + + 64 + + + + + + + + + + Qt::Horizontal + + + + + + + + + + 0 + 0 + + + + Gain + + + + + + + + 24 + 24 + + + + Gain + + + + + + + + 0 + 0 + + + + 40.0dB + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Vertical + + + + + + + Gain + + + + + + + + 24 + 24 + + + + Gain + + + + + + + 20dB + + + + + + + Qt::Vertical + + + + + + + Gain + + + + + + + + 24 + 24 + + + + Gain + + + + + + + 20dB + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Toggle automatic gain control + + + AGC + + + + + + + + + Qt::Horizontal + + + + + + + + + BW + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + 12 + + + + PointingHandCursor + + + RF filter bandwidth (kHz) + + + + + + + kHz + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + + + + + 10 + + + + + + 16 + 0 + + + + Df + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + 12 + + + + PointingHandCursor + + + Qt::StrongFocus + + + Channel shift frequency from center in Hz + + + + + + + Hz + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Ch Gain + + + + + + + + 24 + 24 + + + + Gain in dB to apply to channel. + +Use to ensure full dynamic range of 8-bit data is used. + + + 0 + + + 100 + + + + + + + + 40 + 0 + + + + 50dB + + + + + + + + + + + Ch SR + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + 12 + false + + + + PointingHandCursor + + + Channel sample rate in samples per second + + + + + + + S/s + + + + + + + Lock channel sample rate to device sample rate + + + + + + + :/unlocked.png + :/locked.png:/unlocked.png + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + IQ + + + + + + + + 45 + 16777215 + + + + Bit depth per I or Q sample transmitted over network + + + + 8 + + + + + 16 + + + + + 24 + + + + + 32 + + + + + + + + bits + + + + + + + + + Qt::Horizontal + + + + + + + + + + 50 + 0 + + + + IP + + + + + + + + 120 + 0 + + + + + 120 + 16777215 + + + + Remote IPv4 address or hostname to connect to + + + + + + + + + + + + + : + + + + + + + true + + + + 60 + 0 + + + + + 60 + 16777215 + + + + Remote data port (rtl_tcp defaults to 1234) + + + 00000 + + + 0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + When checked, remote device settings are set to local settings upon connection. + +When unchecked, if remote device is using SDRA protocol, local settings are updated from remote device on connection. + + + + + + + :/import.png + :/export.png:/import.png + + + + + + + + + + + Pre-fill + + + + + + + + 24 + 24 + + + + How much data to pre-fill TCP input buffer with before reading (in seconds) + + + 1 + + + 100 + + + + + + + 10.00s + + + + + + + Qt::Vertical + + + + + + + In + + + + + + + + 16777215 + 10 + + + + Input TCP buffer + + + 0 + + + 50 + + + 0 + + + false + + + false + + + %v + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Input TCP buffer length in seconds + + + 10.00s + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Vertical + + + + + + + Out + + + + + + + + 16777215 + 10 + + + + Output buffer. +This should typically be empy. If full, your CPU cannot keep up and data will be lost + + + 0 + + + 50 + + + 0 + + + false + + + false + + + %v + + + + + + + Output buffer length in seconds + + + 0.00s + + + + + + + + + Qt::Horizontal + + + + + + + + + Not connected + + + + + + + Qt::Vertical + + + + + + + + + + + + + + + + + ValueDialZ + QWidget +
gui/valuedialz.h
+ 1 +
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+ + ValueDial + QWidget +
gui/valuedial.h
+ 1 +
+
+ + startStop + centerFrequency + ppm + dcOffset + iqImbalance + biasTee + directSampling + decim + gain1 + gain2 + gain3 + agc + deltaFrequency + channelGain + decimation + sampleBits + dataAddress + dataPort + overrideRemoteSettings + preFill + + + + + +
diff --git a/plugins/samplesource/remotetcpinput/remotetcpinputplugin.cpp b/plugins/samplesource/remotetcpinput/remotetcpinputplugin.cpp new file mode 100644 index 000000000..2deb0c6e9 --- /dev/null +++ b/plugins/samplesource/remotetcpinput/remotetcpinputplugin.cpp @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#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(); +} diff --git a/plugins/samplesource/remotetcpinput/remotetcpinputplugin.h b/plugins/samplesource/remotetcpinput/remotetcpinputplugin.h new file mode 100644 index 000000000..6c52ef596 --- /dev/null +++ b/plugins/samplesource/remotetcpinput/remotetcpinputplugin.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_REMOTETCPINPUTPLUGIN_H +#define INCLUDE_REMOTETCPINPUTPLUGIN_H + +#include +#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 diff --git a/plugins/samplesource/remotetcpinput/remotetcpinputsettings.cpp b/plugins/samplesource/remotetcpinput/remotetcpinputsettings.cpp new file mode 100644 index 000000000..1b60b431c --- /dev/null +++ b/plugins/samplesource/remotetcpinput/remotetcpinputsettings.cpp @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#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; + } +} diff --git a/plugins/samplesource/remotetcpinput/remotetcpinputsettings.h b/plugins/samplesource/remotetcpinput/remotetcpinputsettings.h new file mode 100644 index 000000000..b3e2ba1a6 --- /dev/null +++ b/plugins/samplesource/remotetcpinput/remotetcpinputsettings.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef PLUGINS_SAMPLESOURCE_REMOTETCPINPUT_REMOTETCPINPUTSETTINGS_H_ +#define PLUGINS_SAMPLESOURCE_REMOTETCPINPUT_REMOTETCPINPUTSETTINGS_H_ + +#include +#include + +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_ */ diff --git a/plugins/samplesource/remotetcpinput/remotetcpinputtcphandler.cpp b/plugins/samplesource/remotetcpinput/remotetcpinputtcphandler.cpp new file mode 100644 index 000000000..2b9650f30 --- /dev/null +++ b/plugins/samplesource/remotetcpinput/remotetcpinputtcphandler.cpp @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#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::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::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(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(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(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(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(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(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(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; + } +} diff --git a/plugins/samplesource/remotetcpinput/remotetcpinputtcphandler.h b/plugins/samplesource/remotetcpinput/remotetcpinputtcphandler.h new file mode 100644 index 000000000..c7e89853b --- /dev/null +++ b/plugins/samplesource/remotetcpinput/remotetcpinputtcphandler.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef PLUGINS_SAMPLESOURCE_REMOTETCPINPUT_REMOTETCPINPUTUDPHANDLER_H_ +#define PLUGINS_SAMPLESOURCE_REMOTETCPINPUT_REMOTETCPINPUTUDPHANDLER_H_ + +#include +#include +#include +#include +#include + +#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_ */ diff --git a/plugins/samplesource/remotetcpinput/remotetcpinputwebapiadapter.cpp b/plugins/samplesource/remotetcpinput/remotetcpinputwebapiadapter.cpp new file mode 100644 index 000000000..13384189c --- /dev/null +++ b/plugins/samplesource/remotetcpinput/remotetcpinputwebapiadapter.cpp @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#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; +} diff --git a/plugins/samplesource/remotetcpinput/remotetcpinputwebapiadapter.h b/plugins/samplesource/remotetcpinput/remotetcpinputwebapiadapter.h new file mode 100644 index 000000000..ce7157fe3 --- /dev/null +++ b/plugins/samplesource/remotetcpinput/remotetcpinputwebapiadapter.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#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; +}; diff --git a/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp b/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp index c057ab815..9956343c5 100644 --- a/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp +++ b/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp @@ -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() diff --git a/plugins/samplesource/sdrplayv3/sdrplayv3input.cpp b/plugins/samplesource/sdrplayv3/sdrplayv3input.cpp index 6d77376eb..69c00e7bb 100644 --- a/plugins/samplesource/sdrplayv3/sdrplayv3input.cpp +++ b/plugins/samplesource/sdrplayv3/sdrplayv3input.cpp @@ -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& 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) diff --git a/sdrbase/channel/channelwebapiutils.cpp b/sdrbase/channel/channelwebapiutils.cpp index d6126d883..dcdc94523 100644 --- a/sdrbase/channel/channelwebapiutils.cpp +++ b/sdrbase/channel/channelwebapiutils.cpp @@ -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 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 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 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 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 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 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 &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) diff --git a/sdrbase/channel/channelwebapiutils.h b/sdrbase/channel/channelwebapiutils.h index c1e8cdc1a..aae92b34b 100644 --- a/sdrbase/channel/channelwebapiutils.h +++ b/sdrbase/channel/channelwebapiutils.h @@ -21,6 +21,7 @@ #include #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 &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_ diff --git a/sdrbase/webapi/webapirequestmapper.cpp b/sdrbase/webapi/webapirequestmapper.cpp index 7c34c7c6c..28fe136c3 100644 --- a/sdrbase/webapi/webapirequestmapper.cpp +++ b/sdrbase/webapi/webapirequestmapper.cpp @@ -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); diff --git a/sdrbase/webapi/webapiutils.cpp b/sdrbase/webapi/webapiutils.cpp index 3625e86cf..acebec978 100644 --- a/sdrbase/webapi/webapiutils.cpp +++ b/sdrbase/webapi/webapiutils.cpp @@ -63,6 +63,7 @@ const QMap 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 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 WebAPIUtils::m_channelTypeToSettingsKey = { {"RadiosondeDemod", "RadiosondeDemodSettings"}, {"RemoteSink", "RemoteSinkSettings"}, {"RemoteSource", "RemoteSourceSettings"}, + {"RemoteTCPSink", "RemoteTCPSinkSettings"}, {"SSBMod", "SSBModSettings"}, {"SSBDemod", "SSBDemodSettings"}, {"UDPSink", "UDPSinkSettings"}, @@ -211,6 +214,7 @@ const QMap 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 &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) { diff --git a/sdrbase/webapi/webapiutils.h b/sdrbase/webapi/webapiutils.h index a9b511123..8cf861d45 100644 --- a/sdrbase/webapi/webapiutils.h +++ b/sdrbase/webapi/webapiutils.h @@ -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 &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); diff --git a/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml b/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml index d599473d2..b4a73363d 100644 --- a/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml +++ b/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml @@ -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: diff --git a/swagger/sdrangel/api/swagger/include/DeviceReports.yaml b/swagger/sdrangel/api/swagger/include/DeviceReports.yaml index 19bef3007..598a0e910 100644 --- a/swagger/sdrangel/api/swagger/include/DeviceReports.yaml +++ b/swagger/sdrangel/api/swagger/include/DeviceReports.yaml @@ -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: diff --git a/swagger/sdrangel/api/swagger/include/DeviceSettings.yaml b/swagger/sdrangel/api/swagger/include/DeviceSettings.yaml index 011a346b7..885376b44 100644 --- a/swagger/sdrangel/api/swagger/include/DeviceSettings.yaml +++ b/swagger/sdrangel/api/swagger/include/DeviceSettings.yaml @@ -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: diff --git a/swagger/sdrangel/api/swagger/include/RemoteTCPInput.yaml b/swagger/sdrangel/api/swagger/include/RemoteTCPInput.yaml new file mode 100644 index 000000000..df1bf0f8f --- /dev/null +++ b/swagger/sdrangel/api/swagger/include/RemoteTCPInput.yaml @@ -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 diff --git a/swagger/sdrangel/api/swagger/include/RemoteTCPSink.yaml b/swagger/sdrangel/api/swagger/include/RemoteTCPSink.yaml new file mode 100644 index 000000000..428cab235 --- /dev/null +++ b/swagger/sdrangel/api/swagger/include/RemoteTCPSink.yaml @@ -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" diff --git a/swagger/sdrangel/api/swagger/include/SDRPlayV3.yaml b/swagger/sdrangel/api/swagger/include/SDRPlayV3.yaml index 6afb3ea82..87477e549 100644 --- a/swagger/sdrangel/api/swagger/include/SDRPlayV3.yaml +++ b/swagger/sdrangel/api/swagger/include/SDRPlayV3.yaml @@ -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 diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp index c64c80474..ec7e2fd52 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp @@ -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; } diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h index bb192cd0b..e0b184b1a 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h @@ -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; diff --git a/swagger/sdrangel/code/qt5/client/SWGDeviceReport.cpp b/swagger/sdrangel/code/qt5/client/SWGDeviceReport.cpp index 57ff21342..bf5d41523 100644 --- a/swagger/sdrangel/code/qt5/client/SWGDeviceReport.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGDeviceReport.cpp @@ -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; } diff --git a/swagger/sdrangel/code/qt5/client/SWGDeviceReport.h b/swagger/sdrangel/code/qt5/client/SWGDeviceReport.h index ba0a4070a..eed02397b 100644 --- a/swagger/sdrangel/code/qt5/client/SWGDeviceReport.h +++ b/swagger/sdrangel/code/qt5/client/SWGDeviceReport.h @@ -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; diff --git a/swagger/sdrangel/code/qt5/client/SWGDeviceSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGDeviceSettings.cpp index 867b3068e..b4dad838a 100644 --- a/swagger/sdrangel/code/qt5/client/SWGDeviceSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGDeviceSettings.cpp @@ -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; } diff --git a/swagger/sdrangel/code/qt5/client/SWGDeviceSettings.h b/swagger/sdrangel/code/qt5/client/SWGDeviceSettings.h index 3f8afcd36..2fbabfd1a 100644 --- a/swagger/sdrangel/code/qt5/client/SWGDeviceSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGDeviceSettings.h @@ -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; diff --git a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h index a254b851e..22f0dc4cf 100644 --- a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h +++ b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h @@ -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(); diff --git a/swagger/sdrangel/code/qt5/client/SWGRemoteTCPInputReport.cpp b/swagger/sdrangel/code/qt5/client/SWGRemoteTCPInputReport.cpp new file mode 100644 index 000000000..74c4c5abf --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGRemoteTCPInputReport.cpp @@ -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 +#include +#include +#include + +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; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGRemoteTCPInputReport.h b/swagger/sdrangel/code/qt5/client/SWGRemoteTCPInputReport.h new file mode 100644 index 000000000..1dd6df6a8 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGRemoteTCPInputReport.h @@ -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 + + + +#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_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGRemoteTCPInputSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGRemoteTCPInputSettings.cpp new file mode 100644 index 000000000..cb50977b5 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGRemoteTCPInputSettings.cpp @@ -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 +#include +#include +#include + +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(¢er_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; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGRemoteTCPInputSettings.h b/swagger/sdrangel/code/qt5/client/SWGRemoteTCPInputSettings.h new file mode 100644 index 000000000..80ee77496 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGRemoteTCPInputSettings.h @@ -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 + + +#include + +#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_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGRemoteTCPSinkSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGRemoteTCPSinkSettings.cpp new file mode 100644 index 000000000..e023f31c0 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGRemoteTCPSinkSettings.cpp @@ -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 +#include +#include +#include + +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; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGRemoteTCPSinkSettings.h b/swagger/sdrangel/code/qt5/client/SWGRemoteTCPSinkSettings.h new file mode 100644 index 000000000..56f560db8 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGRemoteTCPSinkSettings.h @@ -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 + + +#include "SWGChannelMarker.h" +#include "SWGRollupState.h" +#include + +#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_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGSDRPlayV3Settings.cpp b/swagger/sdrangel/code/qt5/client/SWGSDRPlayV3Settings.cpp index 6d6ccfa5a..f574f306e 100644 --- a/swagger/sdrangel/code/qt5/client/SWGSDRPlayV3Settings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGSDRPlayV3Settings.cpp @@ -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; } diff --git a/swagger/sdrangel/code/qt5/client/SWGSDRPlayV3Settings.h b/swagger/sdrangel/code/qt5/client/SWGSDRPlayV3Settings.h index ffae2401f..a70617fd6 100644 --- a/swagger/sdrangel/code/qt5/client/SWGSDRPlayV3Settings.h +++ b/swagger/sdrangel/code/qt5/client/SWGSDRPlayV3Settings.h @@ -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;