mirror of
https://github.com/f4exb/sdrangel.git
synced 2026-07-24 19:14:15 -04:00
Add Remote TCP Input and Remote TCP Sink plugins
This commit is contained in:
@@ -72,3 +72,4 @@ endif()
|
||||
|
||||
add_subdirectory(audioinput)
|
||||
add_subdirectory(kiwisdr)
|
||||
add_subdirectory(remotetcpinput)
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
project(remotetcpinput)
|
||||
|
||||
set(remotetcpinput_SOURCES
|
||||
remotetcpinputtcphandler.cpp
|
||||
remotetcpinput.cpp
|
||||
remotetcpinputsettings.cpp
|
||||
remotetcpinputwebapiadapter.cpp
|
||||
remotetcpinputplugin.cpp
|
||||
)
|
||||
|
||||
set(remotetcpinput_HEADERS
|
||||
remotetcpinputtcphandler.h
|
||||
remotetcpinput.h
|
||||
remotetcpinputsettings.h
|
||||
remotetcpinputwebapiadapter.h
|
||||
remotetcpinputplugin.h
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
|
||||
)
|
||||
|
||||
if(NOT SERVER_MODE)
|
||||
set(remotetcpinput_SOURCES
|
||||
${remotetcpinput_SOURCES}
|
||||
remotetcpinputgui.cpp
|
||||
remotetcpinputgui.ui
|
||||
)
|
||||
set(remotetcpinput_HEADERS
|
||||
${remotetcpinput_HEADERS}
|
||||
remotetcpinputgui.h
|
||||
)
|
||||
|
||||
set(TARGET_NAME inputtcpremote)
|
||||
set(TARGET_LIB "Qt5::Widgets")
|
||||
set(TARGET_LIB_GUI "sdrgui")
|
||||
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
|
||||
else()
|
||||
set(TARGET_NAME inputtcpremotesrv)
|
||||
set(TARGET_LIB "")
|
||||
set(TARGET_LIB_GUI "")
|
||||
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
|
||||
endif()
|
||||
|
||||
add_library(${TARGET_NAME} SHARED
|
||||
${remotetcpinput_SOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(${TARGET_NAME}
|
||||
Qt5::Core
|
||||
${TARGET_LIB}
|
||||
sdrbase
|
||||
${TARGET_LIB_GUI}
|
||||
swagger
|
||||
)
|
||||
|
||||
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
|
||||
|
||||
# Install debug symbols
|
||||
if (WIN32)
|
||||
install(FILES $<TARGET_PDB_FILE:${TARGET_NAME}> CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} )
|
||||
endif()
|
||||
@@ -0,0 +1,140 @@
|
||||
<h1>Remote TCP input plugin</h1>
|
||||
|
||||
<h2>Introduction</h2>
|
||||
|
||||
This input sample source plugin gets its I/Q samples over the network via a TCP/IP connection from a server such as rtl_tcp, rsp_tcp or SDRangel's Remote TCP Channel Sink plugin.
|
||||
|
||||
<h2>Interface</h2>
|
||||
|
||||

|
||||
|
||||
<h3>1: Start/Stop</h3>
|
||||
|
||||
Device start / stop button.
|
||||
|
||||
- Blue triangle icon: device is ready and can be started
|
||||
- Green square icon: device is running and can be stopped
|
||||
- Red square icon: an error has occured with the connection to the remote device. The plugin will continually try to reconnect.
|
||||
|
||||
<h3>2: Center frequency</h3>
|
||||
|
||||
This is the center frequency in kHz of the remote device.
|
||||
|
||||
<h3>3: Stream sample rate</h3>
|
||||
|
||||
Network I/Q sample rate in kS/s.
|
||||
|
||||
<h3>4: Local oscillator correction</h3>
|
||||
|
||||
This is the correction to be applied to the remote device's local oscillator in ppm.
|
||||
|
||||
<h3>5: DC offset correction</h3>
|
||||
|
||||
Check this button to enable DC offset correction on the remote device. This is only supported when the remote server is SDRangel.
|
||||
|
||||
<h3>6: IQ imbalance correction</h3>
|
||||
|
||||
Check this button to enable IQ imbalance correction on the remote device. This is only supported when the remote server is SDRangel.
|
||||
|
||||
<h3>7: Bias tee</h3>
|
||||
|
||||
Check this button to enable a bias tee on the remote device, if it supports it.
|
||||
|
||||
<h3>8: Direct sampling mode</h3>
|
||||
|
||||
Use this button to activate RTL-SDR's direct sampling mode. This can be used to tune to HF frequencies.
|
||||
|
||||
<h3>9: Sample rate</h3>
|
||||
|
||||
Specify the remote device's sample rate in samples per second (S/s). This is the sample rate between the remote device and remote server.
|
||||
This field allows an arbitry rate to be entered. However, some devices are limited in the rates they support, so you should be careful
|
||||
only to enter a supported value, otherwise there may be a mismatch between the displayed rate and the actual rate.
|
||||
|
||||
<h3>10: Decimation</h3>
|
||||
|
||||
Decimation in powers of two from 1 (no decimation) to 64 between the remote device and the remote server, which determines the remote baseband sample rate. This is only supported when the remote server is SDRangel.
|
||||
|
||||
<h3>11: Gain</h3>
|
||||
|
||||
Specify gain in dB applied in various stages of the remote device. Available gains will depend upon the type of remote device.
|
||||
|
||||
<h3>12: AGC</h3>
|
||||
|
||||
Check to enable automatic gain control in the remote device. How AGC works is device dependent.
|
||||
|
||||
<h3>13: RF Bandwidth</h3>
|
||||
|
||||
Specifies the bandwidth in kHz of the analog filter in the remote device. Available bandwidths are dependent upon the remote device.
|
||||
|
||||
<h3>14: Remote sink frequency shift from center frequency of reception</h3>
|
||||
|
||||
This is the shift of the remote sink channel center frequency from the device center frequency.
|
||||
This is used to select the desired part of the signal when the remote channel sample rate is lower than the baseband sample rate.
|
||||
This is only supported when the remote server is SDRangel.
|
||||
|
||||
<h3>15: Remote sink gain</h3>
|
||||
|
||||
Sets a gain figure in dB that is applied to I/Q samples before transmission via TCP/IP.
|
||||
This option may be useful for amplifying very small signals from SDRs with high-dynamic range (E.g. 24-bits), when the network sample bit-depth is 8-bits.
|
||||
This is only supported when the remote server is SDRangel.
|
||||
|
||||
<h3>16: Remote sink channel sample rate</h3>
|
||||
|
||||
Specifies the channel and network sample rate in samples per second. If this is different from the remote baseband sample rate, the baseband signal will be decimated to the specified rate.
|
||||
Unless the remote server is SDRangel, the channel sample rate should be the same as the device sample rate.
|
||||
|
||||
<h3>17: Local channel sample rate to baseband sample rate</h3>
|
||||
|
||||
When checked, the channel sample rate setting is locked to the remote baseband sample rate.
|
||||
When unchecked, the channel sample rate can be set to any value.
|
||||
|
||||
<h3>18: Sample bit depth</h3>
|
||||
|
||||
Specifies number of bits per I/Q sample transmitted via TCP/IP.
|
||||
|
||||
When the protocol is RTL0, only 8-bits are supported. SDRA protocol supports 8, 16, 24 and 32-bit samples.
|
||||
|
||||
<h3>19: Server IP address</h3>
|
||||
|
||||
IP address or hostname of the server that is running SDRangel's Remote TCP Sink plugin, rtl_tcp or rsp_tcp.
|
||||
|
||||
<h3>20: Server TCP Port</h3>
|
||||
|
||||
TCP port on the server to connect to.
|
||||
|
||||
<h3>21: Connection settings</h3>
|
||||
|
||||
Determines which settings are used when connecting.
|
||||
|
||||
When checked, settings in the RemoteTCPInput GUI are written to the remote device upon connection.
|
||||
When unchecked, if the remote server is using the SDRA protocol, the RemoteTCPInput GUI will be updated with the current settings from the remote device.
|
||||
If the remote server is using the RTL0 protocol, the GUI will not be updated, which may mean the two are inconsistent.
|
||||
|
||||
<h3>22: Pre-fill</h3>
|
||||
|
||||
Determines how many seconds of I/Q samples are buffered locally from the remote device, before being processed in SDRangel.
|
||||
More buffering can handle more network congestion and other network problems, without gaps in the output, but increases the latency in changes to remote device settings.
|
||||
|
||||
<h3>23: Input buffer guage</h3>
|
||||
|
||||
Shows how much data is in the input buffer. Typically this will be just under the pre-fill setting.
|
||||
If it becomes empty, the plugin will pause outputting of data until the buffer is refilled to the pre-fill level.
|
||||
If the buffer repeatedly runs empty, this suggests you do not have enough network bandwidth for the current combination
|
||||
of channel sample rate and sample bit depth. Reducing these to lower values may be required for uninterupted data.
|
||||
|
||||
<h3>24: Output buffer guage</h3>
|
||||
|
||||
Shows how much data is in the output buffer. This should typically be empty. If not empty, this suggests your CPU can't keep up with the amount of data being received.
|
||||
|
||||
<h3>25: Device status</h3>
|
||||
|
||||
Shows the type of remote device that has been connected to.
|
||||
|
||||
<h3>26: Protocol status</h3>
|
||||
|
||||
Shows the protocol being used by the remote server. This will be RTL0 or SDRA.
|
||||
|
||||
rtl_tcp and rsp_tcp always use the RTL0 protocol.
|
||||
SDRangel's Remote TCP Sink plugin can use RTL0 or SDRA.
|
||||
|
||||
RTL0 is limited to sending 8-bit data, doesn't support decimation and does not send the current device settings on connection.
|
||||
@@ -0,0 +1,619 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
|
||||
// Copyright (C) 2022 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QNetworkReply>
|
||||
#include <QBuffer>
|
||||
#include <QJsonParseError>
|
||||
|
||||
#include "SWGDeviceSettings.h"
|
||||
#include "SWGChannelSettings.h"
|
||||
#include "SWGDeviceState.h"
|
||||
#include "SWGDeviceReport.h"
|
||||
#include "SWGRemoteTCPInputReport.h"
|
||||
|
||||
#include "util/simpleserializer.h"
|
||||
#include "dsp/dspcommands.h"
|
||||
#include "dsp/dspengine.h"
|
||||
#include "device/deviceapi.h"
|
||||
|
||||
#include "remotetcpinput.h"
|
||||
#include "remotetcpinputtcphandler.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(RemoteTCPInput::MsgConfigureRemoteTCPInput, Message)
|
||||
MESSAGE_CLASS_DEFINITION(RemoteTCPInput::MsgStartStop, Message)
|
||||
MESSAGE_CLASS_DEFINITION(RemoteTCPInput::MsgReportTCPBuffer, Message)
|
||||
|
||||
RemoteTCPInput::RemoteTCPInput(DeviceAPI *deviceAPI) :
|
||||
m_deviceAPI(deviceAPI),
|
||||
m_mutex(QMutex::Recursive),
|
||||
m_settings(),
|
||||
m_remoteInputTCPPHandler(nullptr),
|
||||
m_deviceDescription("RemoteTCPInput")
|
||||
{
|
||||
m_sampleFifo.setLabel(m_deviceDescription);
|
||||
m_sampleFifo.setSize(48000 * 8);
|
||||
m_remoteInputTCPPHandler = new RemoteTCPInputTCPHandler(&m_sampleFifo, m_deviceAPI);
|
||||
m_remoteInputTCPPHandler->moveToThread(&m_thread);
|
||||
m_remoteInputTCPPHandler->setMessageQueueToInput(&m_inputMessageQueue);
|
||||
|
||||
m_deviceAPI->setNbSourceStreams(1);
|
||||
|
||||
m_networkManager = new QNetworkAccessManager();
|
||||
QObject::connect(
|
||||
m_networkManager,
|
||||
&QNetworkAccessManager::finished,
|
||||
this,
|
||||
&RemoteTCPInput::networkManagerFinished
|
||||
);
|
||||
}
|
||||
|
||||
RemoteTCPInput::~RemoteTCPInput()
|
||||
{
|
||||
QObject::disconnect(
|
||||
m_networkManager,
|
||||
&QNetworkAccessManager::finished,
|
||||
this,
|
||||
&RemoteTCPInput::networkManagerFinished
|
||||
);
|
||||
delete m_networkManager;
|
||||
stop();
|
||||
m_remoteInputTCPPHandler->deleteLater();
|
||||
}
|
||||
|
||||
void RemoteTCPInput::destroy()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void RemoteTCPInput::init()
|
||||
{
|
||||
applySettings(m_settings, true);
|
||||
}
|
||||
|
||||
bool RemoteTCPInput::start()
|
||||
{
|
||||
qDebug() << "RemoteTCPInput::start";
|
||||
m_remoteInputTCPPHandler->reset();
|
||||
m_remoteInputTCPPHandler->start();
|
||||
m_remoteInputTCPPHandler->getInputMessageQueue()->push(RemoteTCPInputTCPHandler::MsgConfigureTcpHandler::create(m_settings, true));
|
||||
m_thread.start();
|
||||
return true;
|
||||
}
|
||||
|
||||
void RemoteTCPInput::stop()
|
||||
{
|
||||
qDebug() << "RemoteTCPInput::stop";
|
||||
m_remoteInputTCPPHandler->stop();
|
||||
m_thread.quit();
|
||||
m_thread.wait();
|
||||
}
|
||||
|
||||
QByteArray RemoteTCPInput::serialize() const
|
||||
{
|
||||
return m_settings.serialize();
|
||||
}
|
||||
|
||||
bool RemoteTCPInput::deserialize(const QByteArray& data)
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
if (!m_settings.deserialize(data))
|
||||
{
|
||||
m_settings.resetToDefaults();
|
||||
success = false;
|
||||
}
|
||||
|
||||
MsgConfigureRemoteTCPInput* message = MsgConfigureRemoteTCPInput::create(m_settings, true);
|
||||
m_inputMessageQueue.push(message);
|
||||
|
||||
if (m_guiMessageQueue)
|
||||
{
|
||||
MsgConfigureRemoteTCPInput* messageToGUI = MsgConfigureRemoteTCPInput::create(m_settings, true);
|
||||
m_guiMessageQueue->push(messageToGUI);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void RemoteTCPInput::setMessageQueueToGUI(MessageQueue *queue)
|
||||
{
|
||||
m_guiMessageQueue = queue;
|
||||
m_remoteInputTCPPHandler->setMessageQueueToGUI(queue);
|
||||
}
|
||||
|
||||
const QString& RemoteTCPInput::getDeviceDescription() const
|
||||
{
|
||||
return m_deviceDescription;
|
||||
}
|
||||
|
||||
int RemoteTCPInput::getSampleRate() const
|
||||
{
|
||||
return m_settings.m_channelSampleRate;
|
||||
}
|
||||
|
||||
quint64 RemoteTCPInput::getCenterFrequency() const
|
||||
{
|
||||
return m_settings.m_centerFrequency;
|
||||
}
|
||||
|
||||
void RemoteTCPInput::setCenterFrequency(qint64 centerFrequency)
|
||||
{
|
||||
RemoteTCPInputSettings settings = m_settings;
|
||||
settings.m_centerFrequency = centerFrequency;
|
||||
|
||||
MsgConfigureRemoteTCPInput* message = MsgConfigureRemoteTCPInput::create(settings, false);
|
||||
m_inputMessageQueue.push(message);
|
||||
|
||||
if (m_guiMessageQueue)
|
||||
{
|
||||
MsgConfigureRemoteTCPInput* messageToGUI = MsgConfigureRemoteTCPInput::create(settings, false);
|
||||
m_guiMessageQueue->push(messageToGUI);
|
||||
}
|
||||
}
|
||||
|
||||
bool RemoteTCPInput::handleMessage(const Message& message)
|
||||
{
|
||||
if (MsgStartStop::match(message))
|
||||
{
|
||||
MsgStartStop& cmd = (MsgStartStop&) message;
|
||||
qDebug() << "RemoteTCPInput::handleMessage: MsgStartStop: " << (cmd.getStartStop() ? "start" : "stop");
|
||||
|
||||
if (cmd.getStartStop())
|
||||
{
|
||||
if (m_deviceAPI->initDeviceEngine())
|
||||
{
|
||||
m_deviceAPI->startDeviceEngine();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_deviceAPI->stopDeviceEngine();
|
||||
}
|
||||
|
||||
if (m_settings.m_useReverseAPI) {
|
||||
webapiReverseSendStartStop(cmd.getStartStop());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (MsgConfigureRemoteTCPInput::match(message))
|
||||
{
|
||||
qDebug() << "RemoteTCPInput::handleMessage:" << message.getIdentifier();
|
||||
MsgConfigureRemoteTCPInput& conf = (MsgConfigureRemoteTCPInput&) message;
|
||||
applySettings(conf.getSettings(), conf.getForce());
|
||||
return true;
|
||||
}
|
||||
else if (RemoteTCPInputTCPHandler::MsgReportConnection::match(message))
|
||||
{
|
||||
qDebug() << "RemoteTCPInput::handleMessage:" << message.getIdentifier();
|
||||
RemoteTCPInputTCPHandler::MsgReportConnection& report = (RemoteTCPInputTCPHandler::MsgReportConnection&) message;
|
||||
if (report.getConnected())
|
||||
{
|
||||
qDebug() << "Disconnected - stopping DSP";
|
||||
m_deviceAPI->stopDeviceEngine();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInput::applySettings(const RemoteTCPInputSettings& settings, bool force)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
std::ostringstream os;
|
||||
QList<QString> reverseAPIKeys;
|
||||
bool forwardChange = false;
|
||||
|
||||
if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || force)
|
||||
{
|
||||
reverseAPIKeys.append("centerFrequency");
|
||||
forwardChange = true;
|
||||
}
|
||||
if ((m_settings.m_loPpmCorrection != settings.m_loPpmCorrection) || force) {
|
||||
reverseAPIKeys.append("loPpmCorrection");
|
||||
}
|
||||
if ((m_settings.m_dcBlock != settings.m_dcBlock) || force) {
|
||||
reverseAPIKeys.append("dcBlock");
|
||||
}
|
||||
if ((m_settings.m_iqCorrection != settings.m_iqCorrection) || force) {
|
||||
reverseAPIKeys.append("iqCorrection");
|
||||
}
|
||||
if ((m_settings.m_biasTee != settings.m_biasTee) || force) {
|
||||
reverseAPIKeys.append("biasTee");
|
||||
}
|
||||
if ((m_settings.m_directSampling != settings.m_directSampling) || force) {
|
||||
reverseAPIKeys.append("noModMode"); // Use same name as rtlsdrinput.cpp
|
||||
}
|
||||
if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || force) {
|
||||
reverseAPIKeys.append("devSampleRate");
|
||||
}
|
||||
if ((m_settings.m_log2Decim != settings.m_log2Decim) || force) {
|
||||
reverseAPIKeys.append("log2Decim");
|
||||
}
|
||||
if ((m_settings.m_gain != settings.m_gain) || force) {
|
||||
reverseAPIKeys.append("gain");
|
||||
}
|
||||
if ((m_settings.m_agc != settings.m_agc) || force) {
|
||||
reverseAPIKeys.append("agc");
|
||||
}
|
||||
if ((m_settings.m_rfBW != settings.m_rfBW) || force) {
|
||||
reverseAPIKeys.append("rfBW");
|
||||
}
|
||||
if ((m_settings.m_rfBW != settings.m_rfBW) || force) {
|
||||
reverseAPIKeys.append("rfBW");
|
||||
}
|
||||
if ((m_settings.m_inputFrequencyOffset != settings.m_inputFrequencyOffset) || force) {
|
||||
reverseAPIKeys.append("inputFrequencyOffset");
|
||||
}
|
||||
if ((m_settings.m_channelGain != settings.m_channelGain) || force) {
|
||||
reverseAPIKeys.append("channelGain");
|
||||
}
|
||||
if ((m_settings.m_channelSampleRate != settings.m_channelSampleRate) || force)
|
||||
{
|
||||
reverseAPIKeys.append("channelSampleRate");
|
||||
forwardChange = true;
|
||||
}
|
||||
if ((m_settings.m_inputFrequencyOffset != settings.m_inputFrequencyOffset) || force) {
|
||||
reverseAPIKeys.append("inputFrequencyOffset");
|
||||
}
|
||||
if ((m_settings.m_sampleBits != settings.m_sampleBits) || force) {
|
||||
reverseAPIKeys.append("m_sampleBits");
|
||||
}
|
||||
if ((m_settings.m_dataAddress != settings.m_dataAddress) || force) {
|
||||
reverseAPIKeys.append("dataAddress");
|
||||
}
|
||||
if ((m_settings.m_dataPort != settings.m_dataPort) || force) {
|
||||
reverseAPIKeys.append("dataPort");
|
||||
}
|
||||
if ((m_settings.m_preFill != settings.m_preFill) || force) {
|
||||
reverseAPIKeys.append("preFill");
|
||||
}
|
||||
|
||||
mutexLocker.unlock();
|
||||
|
||||
if (settings.m_useReverseAPI)
|
||||
{
|
||||
bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) ||
|
||||
(m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) ||
|
||||
(m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) ||
|
||||
(m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex);
|
||||
webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force);
|
||||
}
|
||||
|
||||
if (forwardChange && (settings.m_channelSampleRate != 0))
|
||||
{
|
||||
DSPSignalNotification *notif = new DSPSignalNotification(settings.m_channelSampleRate, settings.m_centerFrequency);
|
||||
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
|
||||
}
|
||||
|
||||
m_settings = settings;
|
||||
|
||||
m_remoteInputTCPPHandler->getInputMessageQueue()->push(RemoteTCPInputTCPHandler::MsgConfigureTcpHandler::create(m_settings, force));
|
||||
|
||||
qDebug() << "RemoteTCPInput::applySettings: "
|
||||
<< " force: " << force
|
||||
<< " m_centerFrequency: " << m_settings.m_centerFrequency
|
||||
<< " m_loPpmCorrection: " << m_settings.m_loPpmCorrection
|
||||
<< " m_biasTee: " << m_settings.m_biasTee
|
||||
<< " m_devSampleRate: " << m_settings.m_devSampleRate
|
||||
<< " m_log2Decim: " << m_settings.m_log2Decim
|
||||
<< " m_gain: " << m_settings.m_gain
|
||||
<< " m_agc: " << m_settings.m_agc
|
||||
<< " m_rfBW: " << m_settings.m_rfBW
|
||||
<< " m_inputFrequencyOffset: " << m_settings.m_inputFrequencyOffset
|
||||
<< " m_channelGain: " << m_settings.m_channelGain
|
||||
<< " m_channelSampleRate: " << m_settings.m_channelSampleRate
|
||||
<< " m_sampleBits: " << m_settings.m_sampleBits
|
||||
<< " m_dataAddress: " << m_settings.m_dataAddress
|
||||
<< " m_dataPort: " << m_settings.m_dataPort
|
||||
<< " m_preFill: " << m_settings.m_preFill
|
||||
;
|
||||
}
|
||||
|
||||
int RemoteTCPInput::webapiRunGet(
|
||||
SWGSDRangel::SWGDeviceState& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
m_deviceAPI->getDeviceEngineStateStr(*response.getState());
|
||||
return 200;
|
||||
}
|
||||
|
||||
int RemoteTCPInput::webapiRun(
|
||||
bool run,
|
||||
SWGSDRangel::SWGDeviceState& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
m_deviceAPI->getDeviceEngineStateStr(*response.getState());
|
||||
MsgStartStop *message = MsgStartStop::create(run);
|
||||
m_inputMessageQueue.push(message);
|
||||
|
||||
if (m_guiMessageQueue) // forward to GUI if any
|
||||
{
|
||||
MsgStartStop *msgToGUI = MsgStartStop::create(run);
|
||||
m_guiMessageQueue->push(msgToGUI);
|
||||
}
|
||||
|
||||
return 200;
|
||||
}
|
||||
|
||||
int RemoteTCPInput::webapiSettingsGet(
|
||||
SWGSDRangel::SWGDeviceSettings& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
response.setRemoteTcpInputSettings(new SWGSDRangel::SWGRemoteTCPInputSettings());
|
||||
response.getRemoteTcpInputSettings()->init();
|
||||
webapiFormatDeviceSettings(response, m_settings);
|
||||
return 200;
|
||||
}
|
||||
|
||||
int RemoteTCPInput::webapiSettingsPutPatch(
|
||||
bool force,
|
||||
const QStringList& deviceSettingsKeys,
|
||||
SWGSDRangel::SWGDeviceSettings& response, // query + response
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
RemoteTCPInputSettings settings = m_settings;
|
||||
webapiUpdateDeviceSettings(settings, deviceSettingsKeys, response);
|
||||
|
||||
MsgConfigureRemoteTCPInput *msg = MsgConfigureRemoteTCPInput::create(settings, force);
|
||||
m_inputMessageQueue.push(msg);
|
||||
|
||||
if (m_guiMessageQueue) // forward to GUI if any
|
||||
{
|
||||
MsgConfigureRemoteTCPInput *msgToGUI = MsgConfigureRemoteTCPInput::create(settings, force);
|
||||
m_guiMessageQueue->push(msgToGUI);
|
||||
}
|
||||
|
||||
webapiFormatDeviceSettings(response, settings);
|
||||
return 200;
|
||||
}
|
||||
|
||||
void RemoteTCPInput::webapiUpdateDeviceSettings(
|
||||
RemoteTCPInputSettings& settings,
|
||||
const QStringList& deviceSettingsKeys,
|
||||
SWGSDRangel::SWGDeviceSettings& response)
|
||||
{
|
||||
if (deviceSettingsKeys.contains("centerFrequency")) {
|
||||
settings.m_centerFrequency = response.getRemoteTcpInputSettings()->getCenterFrequency();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("loPpmCorrection")) {
|
||||
settings.m_loPpmCorrection = response.getRemoteTcpInputSettings()->getLoPpmCorrection();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("dcBlock")) {
|
||||
settings.m_dcBlock = response.getRemoteTcpInputSettings()->getDcBlock() != 0;
|
||||
}
|
||||
if (deviceSettingsKeys.contains("iqCorrection")) {
|
||||
settings.m_iqCorrection = response.getRemoteTcpInputSettings()->getIqCorrection() != 0;
|
||||
}
|
||||
if (deviceSettingsKeys.contains("biasTee")) {
|
||||
settings.m_biasTee = response.getRemoteTcpInputSettings()->getBiasTee() != 0;
|
||||
}
|
||||
if (deviceSettingsKeys.contains("directSampling")) {
|
||||
settings.m_directSampling = response.getRemoteTcpInputSettings()->getDirectSampling() != 0;
|
||||
}
|
||||
if (deviceSettingsKeys.contains("devSampleRate")) {
|
||||
settings.m_devSampleRate = response.getRemoteTcpInputSettings()->getDevSampleRate();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("log2Decim")) {
|
||||
settings.m_log2Decim = response.getRemoteTcpInputSettings()->getLog2Decim();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("agc")) {
|
||||
settings.m_agc = response.getRemoteTcpInputSettings()->getAgc() != 0;
|
||||
}
|
||||
if (deviceSettingsKeys.contains("rfBW")) {
|
||||
settings.m_rfBW = response.getRemoteTcpInputSettings()->getRfBw();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("inputFrequencyOffset")) {
|
||||
settings.m_inputFrequencyOffset = response.getRemoteTcpInputSettings()->getInputFrequencyOffset();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("channelGain")) {
|
||||
settings.m_channelGain = response.getRemoteTcpInputSettings()->getChannelGain();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("channelSampleRate")) {
|
||||
settings.m_channelSampleRate = response.getRemoteTcpInputSettings()->getChannelSampleRate();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("channelDecimation")) {
|
||||
settings.m_channelDecimation = response.getRemoteTcpInputSettings()->getChannelDecimation();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("sampleBits")) {
|
||||
settings.m_sampleBits = response.getRemoteTcpInputSettings()->getSampleBits();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("dataAddress")) {
|
||||
settings.m_dataAddress = *response.getRemoteTcpInputSettings()->getDataAddress();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("dataPort")) {
|
||||
settings.m_dataPort = response.getRemoteTcpInputSettings()->getDataPort();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("overrideRemoteSettings")) {
|
||||
settings.m_overrideRemoteSettings = response.getRemoteTcpInputSettings()->getOverrideRemoteSettings() != 0;
|
||||
}
|
||||
if (deviceSettingsKeys.contains("preFill")) {
|
||||
settings.m_preFill = response.getRemoteTcpInputSettings()->getPreFill() != 0;
|
||||
}
|
||||
if (deviceSettingsKeys.contains("useReverseAPI")) {
|
||||
settings.m_useReverseAPI = response.getRemoteTcpInputSettings()->getUseReverseApi() != 0;
|
||||
}
|
||||
if (deviceSettingsKeys.contains("reverseAPIAddress")) {
|
||||
settings.m_reverseAPIAddress = *response.getRemoteTcpInputSettings()->getReverseApiAddress();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("reverseAPIPort")) {
|
||||
settings.m_reverseAPIPort = response.getRemoteTcpInputSettings()->getReverseApiPort();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("reverseAPIDeviceIndex")) {
|
||||
settings.m_reverseAPIDeviceIndex = response.getRemoteTcpInputSettings()->getReverseApiDeviceIndex();
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInput::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const RemoteTCPInputSettings& settings)
|
||||
{
|
||||
response.getRemoteTcpInputSettings()->setCenterFrequency(settings.m_centerFrequency);
|
||||
response.getRemoteTcpInputSettings()->setLoPpmCorrection(settings.m_loPpmCorrection);
|
||||
response.getRemoteTcpInputSettings()->setDcBlock(settings.m_dcBlock ? 1 : 0);
|
||||
response.getRemoteTcpInputSettings()->setIqCorrection(settings.m_iqCorrection ? 1 : 0);
|
||||
response.getRemoteTcpInputSettings()->setBiasTee(settings.m_biasTee ? 1 : 0);
|
||||
response.getRemoteTcpInputSettings()->setDirectSampling(settings.m_directSampling ? 1 : 0);
|
||||
response.getRemoteTcpInputSettings()->setDevSampleRate(settings.m_devSampleRate);
|
||||
response.getRemoteTcpInputSettings()->setLog2Decim(settings.m_log2Decim);
|
||||
response.getRemoteTcpInputSettings()->setGain(settings.m_gain[0]);
|
||||
response.getRemoteTcpInputSettings()->setAgc(settings.m_agc ? 1 : 0);
|
||||
response.getRemoteTcpInputSettings()->setRfBw(settings.m_rfBW);
|
||||
response.getRemoteTcpInputSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
|
||||
response.getRemoteTcpInputSettings()->setChannelGain(settings.m_channelGain);
|
||||
response.getRemoteTcpInputSettings()->setChannelSampleRate(settings.m_channelSampleRate);
|
||||
response.getRemoteTcpInputSettings()->setChannelDecimation(settings.m_channelDecimation);
|
||||
response.getRemoteTcpInputSettings()->setSampleBits(settings.m_sampleBits);
|
||||
response.getRemoteTcpInputSettings()->setDataAddress(new QString(settings.m_dataAddress));
|
||||
response.getRemoteTcpInputSettings()->setDataPort(settings.m_dataPort);
|
||||
response.getRemoteTcpInputSettings()->setOverrideRemoteSettings(settings.m_overrideRemoteSettings ? 1 : 0);
|
||||
response.getRemoteTcpInputSettings()->setPreFill(settings.m_preFill ? 1 : 0);
|
||||
|
||||
response.getRemoteTcpInputSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
|
||||
|
||||
if (response.getRemoteTcpInputSettings()->getReverseApiAddress()) {
|
||||
*response.getRemoteTcpInputSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
|
||||
} else {
|
||||
response.getRemoteTcpInputSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
|
||||
}
|
||||
|
||||
response.getRemoteTcpInputSettings()->setReverseApiPort(settings.m_reverseAPIPort);
|
||||
response.getRemoteTcpInputSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex);
|
||||
}
|
||||
|
||||
int RemoteTCPInput::webapiReportGet(
|
||||
SWGSDRangel::SWGDeviceReport& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
response.setRemoteTcpInputReport(new SWGSDRangel::SWGRemoteTCPInputReport());
|
||||
response.getRemoteTcpInputReport()->init();
|
||||
webapiFormatDeviceReport(response);
|
||||
return 200;
|
||||
}
|
||||
|
||||
void RemoteTCPInput::webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response)
|
||||
{
|
||||
response.getRemoteTcpInputReport()->setSampleRate(m_settings.m_channelSampleRate);
|
||||
}
|
||||
|
||||
void RemoteTCPInput::webapiReverseSendSettings(QList<QString>& deviceSettingsKeys, const RemoteTCPInputSettings& settings, bool force)
|
||||
{
|
||||
SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings();
|
||||
swgDeviceSettings->setDirection(0); // single Rx
|
||||
swgDeviceSettings->setOriginatorIndex(m_deviceAPI->getDeviceSetIndex());
|
||||
swgDeviceSettings->setDeviceHwType(new QString("RemoteTCPInput"));
|
||||
swgDeviceSettings->setRemoteTcpInputSettings(new SWGSDRangel::SWGRemoteTCPInputSettings());
|
||||
SWGSDRangel::SWGRemoteTCPInputSettings *swgRemoteTCPInputSettings = swgDeviceSettings->getRemoteTcpInputSettings();
|
||||
|
||||
// transfer data that has been modified. When force is on transfer all data except reverse API data
|
||||
|
||||
if (deviceSettingsKeys.contains("dcBlock") || force) {
|
||||
swgRemoteTCPInputSettings->setDcBlock(settings.m_dcBlock ? 1 : 0);
|
||||
}
|
||||
if (deviceSettingsKeys.contains("iqCorrection") || force) {
|
||||
swgRemoteTCPInputSettings->setIqCorrection(settings.m_iqCorrection ? 1 : 0);
|
||||
}
|
||||
if (deviceSettingsKeys.contains("biasTee") || force) {
|
||||
swgRemoteTCPInputSettings->setBiasTee(settings.m_biasTee ? 1 : 0);
|
||||
}
|
||||
if (deviceSettingsKeys.contains("dataAddress") || force) {
|
||||
swgRemoteTCPInputSettings->setDataAddress(new QString(settings.m_dataAddress));
|
||||
}
|
||||
if (deviceSettingsKeys.contains("dataPort") || force) {
|
||||
swgRemoteTCPInputSettings->setDataPort(settings.m_dataPort);
|
||||
}
|
||||
|
||||
QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings")
|
||||
.arg(settings.m_reverseAPIAddress)
|
||||
.arg(settings.m_reverseAPIPort)
|
||||
.arg(settings.m_reverseAPIDeviceIndex);
|
||||
m_networkRequest.setUrl(QUrl(deviceSettingsURL));
|
||||
m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
|
||||
QBuffer *buffer = new QBuffer();
|
||||
buffer->open((QBuffer::ReadWrite));
|
||||
buffer->write(swgDeviceSettings->asJson().toUtf8());
|
||||
buffer->seek(0);
|
||||
|
||||
// Always use PATCH to avoid passing reverse API settings
|
||||
QNetworkReply *reply = m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
|
||||
buffer->setParent(reply);
|
||||
|
||||
delete swgDeviceSettings;
|
||||
}
|
||||
|
||||
void RemoteTCPInput::webapiReverseSendStartStop(bool start)
|
||||
{
|
||||
SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings();
|
||||
swgDeviceSettings->setDirection(0); // single Rx
|
||||
swgDeviceSettings->setOriginatorIndex(m_deviceAPI->getDeviceSetIndex());
|
||||
swgDeviceSettings->setDeviceHwType(new QString("RemoteTCPInput"));
|
||||
|
||||
QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run")
|
||||
.arg(m_settings.m_reverseAPIAddress)
|
||||
.arg(m_settings.m_reverseAPIPort)
|
||||
.arg(m_settings.m_reverseAPIDeviceIndex);
|
||||
m_networkRequest.setUrl(QUrl(deviceSettingsURL));
|
||||
m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
|
||||
QBuffer *buffer = new QBuffer();
|
||||
buffer->open((QBuffer::ReadWrite));
|
||||
buffer->write(swgDeviceSettings->asJson().toUtf8());
|
||||
buffer->seek(0);
|
||||
QNetworkReply *reply;
|
||||
|
||||
if (start) {
|
||||
reply = m_networkManager->sendCustomRequest(m_networkRequest, "POST", buffer);
|
||||
} else {
|
||||
reply = m_networkManager->sendCustomRequest(m_networkRequest, "DELETE", buffer);
|
||||
}
|
||||
|
||||
buffer->setParent(reply);
|
||||
delete swgDeviceSettings;
|
||||
}
|
||||
|
||||
void RemoteTCPInput::networkManagerFinished(QNetworkReply *reply)
|
||||
{
|
||||
QNetworkReply::NetworkError replyError = reply->error();
|
||||
|
||||
if (replyError)
|
||||
{
|
||||
qWarning() << "RemoteTCPInput::networkManagerFinished:"
|
||||
<< " error(" << (int) replyError
|
||||
<< "): " << replyError
|
||||
<< ": " << reply->errorString();
|
||||
}
|
||||
else
|
||||
{
|
||||
QString answer = reply->readAll();
|
||||
answer.chop(1); // remove last \n
|
||||
qDebug("RemoteTCPInput::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
|
||||
// Copyright (C) 2022 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_REMOTETCPINPUT_H
|
||||
#define INCLUDE_REMOTETCPINPUT_H
|
||||
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
#include <QTimer>
|
||||
#include <QThread>
|
||||
#include <QNetworkRequest>
|
||||
|
||||
#include "dsp/devicesamplesource.h"
|
||||
#include "channel/remotedatablock.h"
|
||||
|
||||
#include "remotetcpinputsettings.h"
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
class DeviceAPI;
|
||||
class RemoteTCPInputTCPHandler;
|
||||
|
||||
class RemoteTCPInput : public DeviceSampleSource {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
class MsgConfigureRemoteTCPInput : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const RemoteTCPInputSettings& getSettings() const { return m_settings; }
|
||||
bool getForce() const { return m_force; }
|
||||
|
||||
static MsgConfigureRemoteTCPInput* create(const RemoteTCPInputSettings& settings, bool force = false)
|
||||
{
|
||||
return new MsgConfigureRemoteTCPInput(settings, force);
|
||||
}
|
||||
|
||||
private:
|
||||
RemoteTCPInputSettings m_settings;
|
||||
bool m_force;
|
||||
|
||||
MsgConfigureRemoteTCPInput(const RemoteTCPInputSettings& settings, bool force) :
|
||||
Message(),
|
||||
m_settings(settings),
|
||||
m_force(force)
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgStartStop : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
bool getStartStop() const { return m_startStop; }
|
||||
|
||||
static MsgStartStop* create(bool startStop) {
|
||||
return new MsgStartStop(startStop);
|
||||
}
|
||||
|
||||
protected:
|
||||
bool m_startStop;
|
||||
|
||||
MsgStartStop(bool startStop) :
|
||||
Message(),
|
||||
m_startStop(startStop)
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgReportTCPBuffer : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
qint64 getInBytesAvailable() const { return m_inBytesAvailable; }
|
||||
qint64 getInSize() const { return m_inSize; }
|
||||
float getInSeconds() const { return m_inSeconds; }
|
||||
qint64 getOutBytesAvailable() const { return m_outBytesAvailable; }
|
||||
qint64 getOutSize() const { return m_outSize; }
|
||||
float getOutSeconds() const { return m_outSeconds; }
|
||||
|
||||
static MsgReportTCPBuffer* create(qint64 inBytesAvailable, qint64 inSize, float inSeconds,
|
||||
qint64 outBytesAvailable, qint64 outSize, float outSeconds) {
|
||||
return new MsgReportTCPBuffer(inBytesAvailable, inSize, inSeconds,
|
||||
outBytesAvailable, outSize, outSeconds);
|
||||
}
|
||||
|
||||
protected:
|
||||
qint64 m_inBytesAvailable;
|
||||
qint64 m_inSize;
|
||||
float m_inSeconds;
|
||||
qint64 m_outBytesAvailable;
|
||||
qint64 m_outSize;
|
||||
float m_outSeconds;
|
||||
|
||||
MsgReportTCPBuffer(qint64 inBytesAvailable, qint64 inSize, float inSeconds,
|
||||
qint64 outBytesAvailable, qint64 outSize, float outSeconds) :
|
||||
Message(),
|
||||
m_inBytesAvailable(inBytesAvailable),
|
||||
m_inSize(inSize),
|
||||
m_inSeconds(inSeconds),
|
||||
m_outBytesAvailable(outBytesAvailable),
|
||||
m_outSize(outSize),
|
||||
m_outSeconds(outSeconds)
|
||||
{ }
|
||||
};
|
||||
|
||||
RemoteTCPInput(DeviceAPI *deviceAPI);
|
||||
virtual ~RemoteTCPInput();
|
||||
virtual void destroy();
|
||||
|
||||
virtual void init();
|
||||
virtual bool start();
|
||||
virtual void stop();
|
||||
|
||||
virtual QByteArray serialize() const;
|
||||
virtual bool deserialize(const QByteArray& data);
|
||||
|
||||
virtual void setMessageQueueToGUI(MessageQueue *queue);
|
||||
virtual const QString& getDeviceDescription() const;
|
||||
virtual int getSampleRate() const;
|
||||
virtual void setSampleRate(int sampleRate) { (void) sampleRate; }
|
||||
virtual quint64 getCenterFrequency() const;
|
||||
virtual void setCenterFrequency(qint64 centerFrequency);
|
||||
std::time_t getStartingTimeStamp() const;
|
||||
|
||||
virtual bool handleMessage(const Message& message);
|
||||
|
||||
virtual int webapiSettingsGet(
|
||||
SWGSDRangel::SWGDeviceSettings& response,
|
||||
QString& errorMessage);
|
||||
|
||||
virtual int webapiSettingsPutPatch(
|
||||
bool force,
|
||||
const QStringList& deviceSettingsKeys,
|
||||
SWGSDRangel::SWGDeviceSettings& response, // query + response
|
||||
QString& errorMessage);
|
||||
|
||||
virtual int webapiReportGet(
|
||||
SWGSDRangel::SWGDeviceReport& response,
|
||||
QString& errorMessage);
|
||||
|
||||
virtual int webapiRunGet(
|
||||
SWGSDRangel::SWGDeviceState& response,
|
||||
QString& errorMessage);
|
||||
|
||||
virtual int webapiRun(
|
||||
bool run,
|
||||
SWGSDRangel::SWGDeviceState& response,
|
||||
QString& errorMessage);
|
||||
|
||||
static void webapiFormatDeviceSettings(
|
||||
SWGSDRangel::SWGDeviceSettings& response,
|
||||
const RemoteTCPInputSettings& settings);
|
||||
|
||||
static void webapiUpdateDeviceSettings(
|
||||
RemoteTCPInputSettings& settings,
|
||||
const QStringList& deviceSettingsKeys,
|
||||
SWGSDRangel::SWGDeviceSettings& response);
|
||||
|
||||
private:
|
||||
DeviceAPI *m_deviceAPI;
|
||||
QMutex m_mutex;
|
||||
RemoteTCPInputSettings m_settings;
|
||||
RemoteTCPInputTCPHandler* m_remoteInputTCPPHandler;
|
||||
QString m_deviceDescription;
|
||||
QNetworkAccessManager *m_networkManager;
|
||||
QNetworkRequest m_networkRequest;
|
||||
QThread m_thread;
|
||||
|
||||
void applySettings(const RemoteTCPInputSettings& settings, bool force = false);
|
||||
void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response);
|
||||
void webapiReverseSendSettings(QList<QString>& deviceSettingsKeys, const RemoteTCPInputSettings& settings, bool force);
|
||||
void webapiReverseSendStartStop(bool start);
|
||||
|
||||
private slots:
|
||||
void networkManagerFinished(QNetworkReply *reply);
|
||||
};
|
||||
|
||||
#endif // INCLUDE_REMOTETCPINPUT_H
|
||||
@@ -0,0 +1,778 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
|
||||
// Copyright (C) 2022 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QDebug>
|
||||
#include <QMessageBox>
|
||||
#include <QDateTime>
|
||||
#include <QString>
|
||||
#include <QResizeEvent>
|
||||
|
||||
#include "ui_remotetcpinputgui.h"
|
||||
#include "gui/colormapper.h"
|
||||
#include "gui/glspectrum.h"
|
||||
#include "gui/basicdevicesettingsdialog.h"
|
||||
#include "dsp/dspengine.h"
|
||||
#include "dsp/dspcommands.h"
|
||||
#include "dsp/hbfilterchainconverter.h"
|
||||
#include "mainwindow.h"
|
||||
#include "util/simpleserializer.h"
|
||||
#include "device/deviceapi.h"
|
||||
#include "device/deviceuiset.h"
|
||||
#include "remotetcpinputgui.h"
|
||||
#include "remotetcpinputtcphandler.h"
|
||||
|
||||
RemoteTCPInputGui::RemoteTCPInputGui(DeviceUISet *deviceUISet, QWidget* parent) :
|
||||
DeviceGUI(parent),
|
||||
ui(new Ui::RemoteTCPInputGui),
|
||||
m_settings(),
|
||||
m_sampleSource(0),
|
||||
m_sampleRate(0),
|
||||
m_centerFrequency(0),
|
||||
m_lastEngineState(DeviceAPI::StNotStarted),
|
||||
m_doApplySettings(true),
|
||||
m_forceSettings(true),
|
||||
m_deviceGains(nullptr),
|
||||
m_remoteDevice(RemoteTCPProtocol::RTLSDR_R820T),
|
||||
m_connectionError(false)
|
||||
{
|
||||
m_deviceUISet = deviceUISet;
|
||||
setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
|
||||
ui->setupUi(getContents());
|
||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
getContents()->setStyleSheet("#RemoteTCPInputGui { background-color: rgb(64, 64, 64); }");
|
||||
m_helpURL = "plugins/samplesource/remotetcpinput/readme.md";
|
||||
|
||||
ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
|
||||
ui->centerFrequency->setValueRange(7, 0, 9999999); // frequency dial is in kHz
|
||||
|
||||
ui->devSampleRate->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow));
|
||||
ui->devSampleRate->setValueRange(8, 0, 99999999);
|
||||
ui->rfBW->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow));
|
||||
ui->rfBW->setValueRange(5, 0, 99999); // In kHz
|
||||
ui->channelSampleRate->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow));
|
||||
ui->channelSampleRate->setValueRange(8, 0, 99999999);
|
||||
|
||||
ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03)));
|
||||
ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
|
||||
ui->deltaFrequency->setValueRange(false, 7, -9999999, 9999999);
|
||||
|
||||
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &)));
|
||||
|
||||
displaySettings();
|
||||
|
||||
connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
|
||||
m_statusTimer.start(500);
|
||||
connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware()));
|
||||
|
||||
m_sampleSource = (RemoteTCPInput*) m_deviceUISet->m_deviceAPI->getSampleSource();
|
||||
|
||||
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection);
|
||||
m_sampleSource->setMessageQueueToGUI(&m_inputMessageQueue);
|
||||
|
||||
m_forceSettings = true;
|
||||
sendSettings();
|
||||
makeUIConnections();
|
||||
}
|
||||
|
||||
RemoteTCPInputGui::~RemoteTCPInputGui()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::blockApplySettings(bool block)
|
||||
{
|
||||
m_doApplySettings = !block;
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::destroy()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::resetToDefaults()
|
||||
{
|
||||
m_settings.resetToDefaults();
|
||||
displaySettings();
|
||||
m_forceSettings = true;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
QByteArray RemoteTCPInputGui::serialize() const
|
||||
{
|
||||
return m_settings.serialize();
|
||||
}
|
||||
|
||||
bool RemoteTCPInputGui::deserialize(const QByteArray& data)
|
||||
{
|
||||
qDebug("RemoteTCPInputGui::deserialize");
|
||||
|
||||
if (m_settings.deserialize(data))
|
||||
{
|
||||
displaySettings();
|
||||
m_forceSettings = true;
|
||||
sendSettings();
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::resizeEvent(QResizeEvent* size)
|
||||
{
|
||||
adjustSize();
|
||||
size->accept();
|
||||
}
|
||||
|
||||
bool RemoteTCPInputGui::handleMessage(const Message& message)
|
||||
{
|
||||
if (RemoteTCPInput::MsgConfigureRemoteTCPInput::match(message))
|
||||
{
|
||||
const RemoteTCPInput::MsgConfigureRemoteTCPInput& cfg = (RemoteTCPInput::MsgConfigureRemoteTCPInput&) message;
|
||||
m_settings = cfg.getSettings();
|
||||
blockApplySettings(true);
|
||||
displaySettings();
|
||||
blockApplySettings(false);
|
||||
return true;
|
||||
}
|
||||
else if (RemoteTCPInput::MsgStartStop::match(message))
|
||||
{
|
||||
RemoteTCPInput::MsgStartStop& notif = (RemoteTCPInput::MsgStartStop&) message;
|
||||
m_connectionError = false;
|
||||
blockApplySettings(true);
|
||||
ui->startStop->setChecked(notif.getStartStop());
|
||||
blockApplySettings(false);
|
||||
return true;
|
||||
}
|
||||
else if (RemoteTCPInput::MsgReportTCPBuffer::match(message))
|
||||
{
|
||||
const RemoteTCPInput::MsgReportTCPBuffer& report = (RemoteTCPInput::MsgReportTCPBuffer&) message;
|
||||
ui->inGauge->setMaximum((int)report.getInSize());
|
||||
ui->inGauge->setValue((int)report.getInBytesAvailable());
|
||||
ui->inBufferLenSecsText->setText(QString("%1s").arg(report.getInSeconds(), 0, 'f', 2));
|
||||
ui->outGauge->setMaximum((int)report.getOutSize());
|
||||
ui->outGauge->setValue((int)report.getOutBytesAvailable());
|
||||
ui->outBufferLenSecsText->setText(QString("%1s").arg(report.getOutSeconds(), 0, 'f', 2));
|
||||
}
|
||||
else if (RemoteTCPInputTCPHandler::MsgReportRemoteDevice::match(message))
|
||||
{
|
||||
const RemoteTCPInputTCPHandler::MsgReportRemoteDevice& report = (RemoteTCPInputTCPHandler::MsgReportRemoteDevice&) message;
|
||||
QHash<RemoteTCPProtocol::Device, QString> devices = {
|
||||
{RemoteTCPProtocol::RTLSDR_E4000, "RTLSDR E4000"},
|
||||
{RemoteTCPProtocol::RTLSDR_FC0012, "RTLSDR FC0012"},
|
||||
{RemoteTCPProtocol::RTLSDR_FC0013, "RTLSDR FC0013"},
|
||||
{RemoteTCPProtocol::RTLSDR_FC2580, "RTLSDR FC2580"},
|
||||
{RemoteTCPProtocol::RTLSDR_R820T, "RTLSDR R820T"},
|
||||
{RemoteTCPProtocol::RTLSDR_R828D, "RTLSDR R828D"},
|
||||
{RemoteTCPProtocol::AIRSPY, "Airspy"},
|
||||
{RemoteTCPProtocol::AIRSPY_HF, "AirspyHF"},
|
||||
{RemoteTCPProtocol::AUDIO_INPUT, "AudioInput"},
|
||||
{RemoteTCPProtocol::BLADE_RF1, "BladeRF1"},
|
||||
{RemoteTCPProtocol::BLADE_RF2, "BladeRF2"},
|
||||
{RemoteTCPProtocol::FCD_PRO, "FCDPro"},
|
||||
{RemoteTCPProtocol::FCD_PRO_PLUS, "FCDProPlus"},
|
||||
{RemoteTCPProtocol::FILE_INPUT, "FileInput"},
|
||||
{RemoteTCPProtocol::HACK_RF, "HackRF"},
|
||||
{RemoteTCPProtocol::KIWI_SDR, "KiwiSDR"},
|
||||
{RemoteTCPProtocol::LIME_SDR, "LimeSDR"},
|
||||
{RemoteTCPProtocol::LOCAL_INPUT, "LocalInput"},
|
||||
{RemoteTCPProtocol::PERSEUS, "Perseus"},
|
||||
{RemoteTCPProtocol::PLUTO_SDR, "PlutoSDR"},
|
||||
{RemoteTCPProtocol::REMOTE_INPUT, "RemoteInput"},
|
||||
{RemoteTCPProtocol::REMOTE_TCP_INPUT, "RemoteTCPInput"},
|
||||
{RemoteTCPProtocol::SDRPLAY_1, "SDRplay1"},
|
||||
{RemoteTCPProtocol::SDRPLAY_V3_RSP1, "SDRplayV3 RSP1"},
|
||||
{RemoteTCPProtocol::SDRPLAY_V3_RSP1A, "SDRplayV3 RSP1A"},
|
||||
{RemoteTCPProtocol::SDRPLAY_V3_RSP2, "SDRplayV3 RSP2"},
|
||||
{RemoteTCPProtocol::SDRPLAY_V3_RSPDUO, "SDRplayV3 RSPduo"},
|
||||
{RemoteTCPProtocol::SDRPLAY_V3_RSPDX, "SDRplayV3 RSPdx"},
|
||||
{RemoteTCPProtocol::SIGMF_FILE_INPUT, "SigMFFileInput"},
|
||||
{RemoteTCPProtocol::SOAPY_SDR, "SoapySDR"},
|
||||
{RemoteTCPProtocol::TEST_SOURCE, "TestSource"},
|
||||
{RemoteTCPProtocol::USRP, "USRP"},
|
||||
{RemoteTCPProtocol::XTRX, "XTRX"},
|
||||
};
|
||||
QString device = "Unknown";
|
||||
m_remoteDevice = report.getDevice();
|
||||
if (devices.contains(m_remoteDevice)) {
|
||||
device = devices.value(m_remoteDevice);
|
||||
}
|
||||
ui->device->setText(QString("Device: %1").arg(device));
|
||||
ui->protocol->setText(QString("Protocol: %1").arg(report.getProtocol()));
|
||||
|
||||
// Update GUI so we only show widgets available for the protocol in use
|
||||
bool sdra = report.getProtocol() == "SDRA";
|
||||
if (sdra && (ui->sampleBits->count() != 4))
|
||||
{
|
||||
ui->sampleBits->addItem("16");
|
||||
ui->sampleBits->addItem("24");
|
||||
ui->sampleBits->addItem("32");
|
||||
}
|
||||
else if (!sdra && (ui->sampleBits->count() != 1))
|
||||
{
|
||||
while (ui->sampleBits->count() > 1) {
|
||||
ui->sampleBits->removeItem(ui->sampleBits->count() - 1);
|
||||
}
|
||||
}
|
||||
ui->dcOffset->setVisible(sdra);
|
||||
ui->iqImbalance->setVisible(sdra);
|
||||
if (sdra && (ui->decim->count() != 7))
|
||||
{
|
||||
ui->decim->addItem("2");
|
||||
ui->decim->addItem("4");
|
||||
ui->decim->addItem("8");
|
||||
ui->decim->addItem("16");
|
||||
ui->decim->addItem("32");
|
||||
ui->decim->addItem("64");
|
||||
}
|
||||
else if (!sdra && (ui->decim->count() != 1))
|
||||
{
|
||||
while (ui->decim->count() > 1) {
|
||||
ui->decim->removeItem(ui->decim->count() - 1);
|
||||
}
|
||||
}
|
||||
if (!sdra) {
|
||||
ui->deltaFrequency->setValue(0);
|
||||
ui->channelGain->setValue(0);
|
||||
ui->decimation->setChecked(true);
|
||||
}
|
||||
ui->deltaFrequency->setEnabled(sdra);
|
||||
ui->channelGain->setEnabled(sdra);
|
||||
ui->decimation->setEnabled(sdra);
|
||||
|
||||
displayGains();
|
||||
}
|
||||
else if (RemoteTCPInputTCPHandler::MsgReportConnection::match(message))
|
||||
{
|
||||
const RemoteTCPInputTCPHandler::MsgReportConnection& report = (RemoteTCPInputTCPHandler::MsgReportConnection&) message;
|
||||
qDebug() << "RemoteTCPInputGui::handleMessage: MsgReportConnection connected: " << report.getConnected();
|
||||
if (report.getConnected())
|
||||
{
|
||||
m_connectionError = false;
|
||||
ui->startStop->setStyleSheet("QToolButton { background-color : green; }");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_connectionError = true;
|
||||
ui->startStop->setStyleSheet("QToolButton { background-color : red; }");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::handleInputMessages()
|
||||
{
|
||||
Message* message;
|
||||
|
||||
while ((message = m_inputMessageQueue.pop()) != 0)
|
||||
{
|
||||
if (DSPSignalNotification::match(*message))
|
||||
{
|
||||
DSPSignalNotification* notif = (DSPSignalNotification*) message;
|
||||
m_sampleRate = notif->getSampleRate();
|
||||
m_centerFrequency = notif->getCenterFrequency();
|
||||
qDebug("RemoteTCPInputGui::handleInputMessages: DSPSignalNotification: SampleRate:%d, CenterFrequency:%llu", notif->getSampleRate(), notif->getCenterFrequency());
|
||||
updateSampleRateAndFrequency();
|
||||
delete message;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (handleMessage(*message))
|
||||
{
|
||||
delete message;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::updateSampleRateAndFrequency()
|
||||
{
|
||||
m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate);
|
||||
m_deviceUISet->getSpectrum()->setCenterFrequency(m_centerFrequency);
|
||||
ui->deviceRateText->setText(tr("%1k").arg((float)m_sampleRate / 1000));
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::displaySettings()
|
||||
{
|
||||
blockApplySettings(true);
|
||||
|
||||
ui->centerFrequency->setValue(m_settings.m_centerFrequency / 1000);
|
||||
ui->ppm->setValue(m_settings.m_loPpmCorrection);
|
||||
|
||||
ui->dcOffset->setChecked(m_settings.m_dcBlock);
|
||||
ui->iqImbalance->setChecked(m_settings.m_iqCorrection);
|
||||
ui->biasTee->setChecked(m_settings.m_biasTee);
|
||||
ui->directSampling->setChecked(m_settings.m_directSampling);
|
||||
|
||||
ui->devSampleRate->setValue(m_settings.m_devSampleRate);
|
||||
ui->decim->setCurrentIndex(m_settings.m_log2Decim);
|
||||
|
||||
ui->agc->setChecked(m_settings.m_agc);
|
||||
|
||||
ui->rfBW->setValue(m_settings.m_rfBW / 1000);
|
||||
|
||||
ui->deltaFrequency->setValue(m_settings.m_inputFrequencyOffset);
|
||||
ui->channelGain->setValue(m_settings.m_channelGain);
|
||||
ui->channelGainText->setText(tr("%1dB").arg(m_settings.m_channelGain));
|
||||
ui->channelSampleRate->setValue(m_settings.m_channelSampleRate);
|
||||
ui->deviceRateText->setText(tr("%1k").arg(m_settings.m_channelSampleRate / 1000.0));
|
||||
ui->decimation->setChecked(!m_settings.m_channelDecimation);
|
||||
ui->channelSampleRate->setEnabled(m_settings.m_channelDecimation);
|
||||
ui->sampleBits->setCurrentIndex(m_settings.m_sampleBits/8-1);
|
||||
|
||||
ui->dataPort->setText(tr("%1").arg(m_settings.m_dataPort));
|
||||
ui->dataAddress->setText(m_settings.m_dataAddress);
|
||||
ui->overrideRemoteSettings->setChecked(m_settings.m_overrideRemoteSettings);
|
||||
|
||||
ui->preFill->setValue((int)(m_settings.m_preFill * 10.0));
|
||||
ui->preFillText->setText(QString("%1s").arg(m_settings.m_preFill, 0, 'f', 2));
|
||||
|
||||
displayGains();
|
||||
blockApplySettings(false);
|
||||
}
|
||||
|
||||
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_rtlSDR34kGainRange(
|
||||
"Gain",
|
||||
{
|
||||
-10, 15, 40, 65, 90, 115, 140, 165, 190, 215,
|
||||
240, 290, 340, 420
|
||||
}
|
||||
);
|
||||
const RemoteTCPInputGui::DeviceGains RemoteTCPInputGui::m_rtlSDRe4kGains({RemoteTCPInputGui::m_rtlSDR34kGainRange}, true, false);
|
||||
|
||||
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_rtlSDRR820GainRange(
|
||||
"Gain",
|
||||
{
|
||||
0, 9, 14, 27, 37, 77, 87, 125, 144, 157,
|
||||
166, 197, 207, 229, 254, 280, 297, 328,
|
||||
338, 364, 372, 386, 402, 421, 434, 439,
|
||||
445, 480, 496
|
||||
}
|
||||
);
|
||||
const RemoteTCPInputGui::DeviceGains RemoteTCPInputGui::m_rtlSDRR820Gains({RemoteTCPInputGui::m_rtlSDRR820GainRange}, true, true);
|
||||
|
||||
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_airspyLNAGainRange("LNA", 0, 14, 1, ""); // Not sure what the units are for these
|
||||
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_airspyMixerGainRange("Mixer", 0, 15, 1, "");
|
||||
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_airspyVGAGainRange("VGA", 0, 15, 1, "");
|
||||
const RemoteTCPInputGui::DeviceGains RemoteTCPInputGui::m_airspyGains({m_airspyLNAGainRange, m_airspyMixerGainRange, m_airspyVGAGainRange}, true, true);
|
||||
|
||||
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_airspyHFAttRange("Att", 0, 48, 6);
|
||||
const RemoteTCPInputGui::DeviceGains RemoteTCPInputGui::m_airspyHFGains({m_airspyHFAttRange}, true, false);
|
||||
|
||||
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_bladeRF1LNARange("LNA", 0, 6, 3);
|
||||
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_bladeRF1VGA1Range("VGA1", 5, 30, 1, "");
|
||||
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_bladeRF1VGA2Range("VGA2", 0, 30, 3, "");
|
||||
const RemoteTCPInputGui::DeviceGains RemoteTCPInputGui::m_baldeRF1Gains({m_bladeRF1LNARange, m_bladeRF1VGA1Range, m_bladeRF1VGA2Range}, false, true);
|
||||
|
||||
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_funCubeProPlusRange("Gain", 0, 59, 1);
|
||||
const RemoteTCPInputGui::DeviceGains RemoteTCPInputGui::m_funCubeProPlusGains({m_funCubeProPlusRange}, false, true);
|
||||
|
||||
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_hackRFLNAGainRange("LNA", 0, 40, 8);
|
||||
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_hackRFVGAGainRange("VGA", 0, 62, 2);
|
||||
const RemoteTCPInputGui::DeviceGains RemoteTCPInputGui::m_hackRFGains({m_hackRFLNAGainRange, m_hackRFVGAGainRange}, false, true);
|
||||
|
||||
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_kiwiGainRange("Gain", 0, 120, 1);
|
||||
const RemoteTCPInputGui::DeviceGains RemoteTCPInputGui::m_kiwiGains({m_kiwiGainRange}, true, false);
|
||||
|
||||
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_limeRange("Gain", 0, 70, 1); // Assuming auto setting
|
||||
const RemoteTCPInputGui::DeviceGains RemoteTCPInputGui::m_limeGains({m_limeRange}, true, false);
|
||||
|
||||
// SDRplay LNA gain is device & frequency dependent (See sdrplayv3input.h SDRPlayV3LNA) sp we just fix as 0 for now
|
||||
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_sdrplayV3LNAGainRange("LNA", {0});
|
||||
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_sdrplayV3IFGainRange("IF", -59, 0, 1);
|
||||
const RemoteTCPInputGui::DeviceGains RemoteTCPInputGui::m_sdrplayV3Gains({m_sdrplayV3LNAGainRange, m_sdrplayV3IFGainRange}, true, true);
|
||||
|
||||
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_plutoGainRange("Gain", 1, 77, 1);
|
||||
const RemoteTCPInputGui::DeviceGains RemoteTCPInputGui::m_plutoGains({m_plutoGainRange}, true, false);
|
||||
|
||||
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_usrpGainRange("Gain", 0, 70, 1);
|
||||
const RemoteTCPInputGui::DeviceGains RemoteTCPInputGui::m_usrpGains({m_usrpGainRange}, true, false);
|
||||
|
||||
const RemoteTCPInputGui::DeviceGains::GainRange RemoteTCPInputGui::m_xtrxGainRange("Gain", 0, 77, 1);
|
||||
const RemoteTCPInputGui::DeviceGains RemoteTCPInputGui::m_xtrxGains({m_xtrxGainRange}, true, false);
|
||||
|
||||
const QHash<RemoteTCPProtocol::Device, const RemoteTCPInputGui::DeviceGains *> RemoteTCPInputGui::m_gains =
|
||||
{
|
||||
{RemoteTCPProtocol::RTLSDR_E4000, &m_rtlSDRe4kGains},
|
||||
{RemoteTCPProtocol::RTLSDR_R820T, &m_rtlSDRR820Gains},
|
||||
{RemoteTCPProtocol::AIRSPY, &m_airspyGains},
|
||||
{RemoteTCPProtocol::AIRSPY_HF, &m_airspyHFGains},
|
||||
{RemoteTCPProtocol::BLADE_RF1, &m_baldeRF1Gains},
|
||||
{RemoteTCPProtocol::FCD_PRO_PLUS, &m_funCubeProPlusGains},
|
||||
{RemoteTCPProtocol::HACK_RF, &m_hackRFGains},
|
||||
{RemoteTCPProtocol::KIWI_SDR, &m_kiwiGains},
|
||||
{RemoteTCPProtocol::LIME_SDR, &m_limeGains},
|
||||
{RemoteTCPProtocol::SDRPLAY_V3_RSP1, &m_sdrplayV3Gains},
|
||||
{RemoteTCPProtocol::SDRPLAY_V3_RSP1A, &m_sdrplayV3Gains},
|
||||
{RemoteTCPProtocol::SDRPLAY_V3_RSP2, &m_sdrplayV3Gains},
|
||||
{RemoteTCPProtocol::SDRPLAY_V3_RSPDUO, &m_sdrplayV3Gains},
|
||||
{RemoteTCPProtocol::SDRPLAY_V3_RSPDX, &m_sdrplayV3Gains},
|
||||
{RemoteTCPProtocol::PLUTO_SDR, &m_plutoGains},
|
||||
{RemoteTCPProtocol::USRP, &m_usrpGains},
|
||||
{RemoteTCPProtocol::XTRX, &m_xtrxGains}
|
||||
};
|
||||
|
||||
QString RemoteTCPInputGui::gainText(int stage)
|
||||
{
|
||||
if (m_deviceGains) {
|
||||
return QString("%1.%2%3").arg(m_settings.m_gain[stage] / 10).arg(abs(m_settings.m_gain[stage] % 10)).arg(m_deviceGains->m_gains[stage].m_units);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::displayGains()
|
||||
{
|
||||
QLabel *gainLabels[3] = {ui->gain1Label, ui->gain2Label, ui->gain3Label};
|
||||
QDial *gain[3] = {ui->gain1, ui->gain2, ui->gain3};
|
||||
QLabel *gainTexts[3] = {ui->gain1Text, ui->gain2Text, ui->gain3Text};
|
||||
QWidget *gainLine[2] = {ui->gainLine1, ui->gainLine2};
|
||||
|
||||
m_deviceGains = m_gains.value(m_remoteDevice);
|
||||
if (m_deviceGains)
|
||||
{
|
||||
ui->agc->setVisible(m_deviceGains->m_agc);
|
||||
ui->biasTee->setVisible(m_deviceGains->m_biasTee);
|
||||
ui->directSampling->setVisible(m_remoteDevice <= RemoteTCPProtocol::RTLSDR_R828D);
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
bool visible = i < m_deviceGains->m_gains.size();
|
||||
gainLabels[i]->setVisible(visible);
|
||||
gain[i]->setVisible(visible);
|
||||
gainTexts[i]->setVisible(visible);
|
||||
if (i > 0) {
|
||||
gainLine[i-1]->setVisible(visible);
|
||||
}
|
||||
if (visible)
|
||||
{
|
||||
gainLabels[i]->setText(m_deviceGains->m_gains[i].m_name);
|
||||
if (m_deviceGains->m_gains[i].m_gains.size() > 0)
|
||||
{
|
||||
gain[i]->setMinimum(0);
|
||||
gain[i]->setMaximum(m_deviceGains->m_gains[i].m_gains.size() - 1);
|
||||
gain[i]->setSingleStep(1);
|
||||
gain[i]->setPageStep(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
gain[i]->setMinimum(m_deviceGains->m_gains[i].m_min);
|
||||
gain[i]->setMaximum(m_deviceGains->m_gains[i].m_max);
|
||||
gain[i]->setSingleStep(m_deviceGains->m_gains[i].m_step);
|
||||
gain[i]->setPageStep(m_deviceGains->m_gains[i].m_step);
|
||||
}
|
||||
if (m_deviceGains->m_gains[i].m_gains.size() > 0) {
|
||||
gain[i]->setValue(m_deviceGains->m_gains[i].m_gains.indexOf(m_settings.m_gain[i]));
|
||||
} else {
|
||||
gain[i]->setValue(m_settings.m_gain[i] / 10);
|
||||
}
|
||||
gainTexts[i]->setText(gainText(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::sendSettings()
|
||||
{
|
||||
if (!m_updateTimer.isActive()) {
|
||||
m_updateTimer.start(100);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_startStop_toggled(bool checked)
|
||||
{
|
||||
if (m_doApplySettings)
|
||||
{
|
||||
m_connectionError = false;
|
||||
RemoteTCPInput::MsgStartStop *message = RemoteTCPInput::MsgStartStop::create(checked);
|
||||
m_sampleSource->getInputMessageQueue()->push(message);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_centerFrequency_changed(quint64 value)
|
||||
{
|
||||
m_settings.m_centerFrequency = value * 1000;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_devSampleRate_changed(quint64 value)
|
||||
{
|
||||
m_settings.m_devSampleRate = value;
|
||||
if (!m_settings.m_channelDecimation)
|
||||
{
|
||||
m_settings.m_channelSampleRate = m_settings.m_devSampleRate >> m_settings.m_log2Decim;
|
||||
ui->channelSampleRate->setValue(m_settings.m_channelSampleRate);
|
||||
}
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_ppm_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_loPpmCorrection = value;
|
||||
ui->ppmText->setText(tr("%1").arg(value));
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_dcOffset_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_dcBlock = checked;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_iqImbalance_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_iqCorrection = checked;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_biasTee_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_biasTee = checked;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_directSampling_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_directSampling = checked;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_agc_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_agc = checked;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_decim_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_log2Decim = index;
|
||||
if (!m_settings.m_channelDecimation)
|
||||
{
|
||||
m_settings.m_channelSampleRate = m_settings.m_devSampleRate >> m_settings.m_log2Decim;
|
||||
ui->channelSampleRate->setValue(m_settings.m_channelSampleRate);
|
||||
}
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_gain1_valueChanged(int value)
|
||||
{
|
||||
if (m_deviceGains && (m_deviceGains->m_gains.size() >= 1) && (m_deviceGains->m_gains[0].m_gains.size() > 0)) {
|
||||
m_settings.m_gain[0] = m_deviceGains->m_gains[0].m_gains[value];
|
||||
} else {
|
||||
m_settings.m_gain[0] = value * 10;
|
||||
}
|
||||
ui->gain1Text->setText(gainText(0));
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_gain2_valueChanged(int value)
|
||||
{
|
||||
if (m_deviceGains && (m_deviceGains->m_gains.size() >= 2) && (m_deviceGains->m_gains[1].m_gains.size() > 0)) {
|
||||
m_settings.m_gain[1] = m_deviceGains->m_gains[1].m_gains[value];
|
||||
} else {
|
||||
m_settings.m_gain[1] = value * 10;
|
||||
}
|
||||
ui->gain2Text->setText(gainText(1));
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_gain3_valueChanged(int value)
|
||||
{
|
||||
if (m_deviceGains && (m_deviceGains->m_gains.size() >= 3) && (m_deviceGains->m_gains[2].m_gains.size() > 0)) {
|
||||
m_settings.m_gain[2] = m_deviceGains->m_gains[2].m_gains[value];
|
||||
} else {
|
||||
m_settings.m_gain[2] = value * 10;
|
||||
}
|
||||
ui->gain3Text->setText(gainText(2));
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_rfBW_changed(int value)
|
||||
{
|
||||
m_settings.m_rfBW = value * 1000;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_deltaFrequency_changed(int value)
|
||||
{
|
||||
m_settings.m_inputFrequencyOffset = value;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_channelGain_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_channelGain = value;
|
||||
ui->channelGainText->setText(tr("%1dB").arg(m_settings.m_channelGain));
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_channelSampleRate_changed(quint64 value)
|
||||
{
|
||||
m_settings.m_channelSampleRate = value;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_decimation_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_channelDecimation = !checked;
|
||||
if (!m_settings.m_channelDecimation)
|
||||
{
|
||||
m_settings.m_channelSampleRate = m_settings.m_devSampleRate >> m_settings.m_log2Decim;
|
||||
ui->channelSampleRate->setValue(m_settings.m_channelSampleRate);
|
||||
}
|
||||
ui->channelSampleRate->setEnabled(!checked);
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_sampleBits_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_sampleBits = 8 * (index + 1);
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_dataAddress_editingFinished()
|
||||
{
|
||||
m_settings.m_dataAddress = ui->dataAddress->text();
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_dataPort_editingFinished()
|
||||
{
|
||||
bool ok;
|
||||
quint16 udpPort = ui->dataPort->text().toInt(&ok);
|
||||
|
||||
if ((!ok) || (udpPort < 1024)) {
|
||||
udpPort = 9998;
|
||||
}
|
||||
|
||||
m_settings.m_dataPort = udpPort;
|
||||
ui->dataPort->setText(tr("%1").arg(m_settings.m_dataPort));
|
||||
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_overrideRemoteSettings_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_overrideRemoteSettings = checked;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_preFill_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_preFill = value/10.0f;
|
||||
ui->preFillText->setText(QString("%1s").arg(m_settings.m_preFill, 0, 'f', 2));
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::updateHardware()
|
||||
{
|
||||
if (m_doApplySettings)
|
||||
{
|
||||
qDebug() << "RemoteTCPInputGui::updateHardware";
|
||||
RemoteTCPInput::MsgConfigureRemoteTCPInput* message =
|
||||
RemoteTCPInput::MsgConfigureRemoteTCPInput::create(m_settings, m_forceSettings);
|
||||
m_sampleSource->getInputMessageQueue()->push(message);
|
||||
m_forceSettings = false;
|
||||
m_updateTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::updateStatus()
|
||||
{
|
||||
int state = m_deviceUISet->m_deviceAPI->state();
|
||||
|
||||
if (!m_connectionError && (m_lastEngineState != state))
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
case DeviceAPI::StNotStarted:
|
||||
ui->startStop->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
|
||||
break;
|
||||
case DeviceAPI::StIdle:
|
||||
ui->startStop->setStyleSheet("QToolButton { background-color : blue; }");
|
||||
break;
|
||||
case DeviceAPI::StRunning:
|
||||
ui->startStop->setStyleSheet("QToolButton { background-color : green; }");
|
||||
break;
|
||||
case DeviceAPI::StError:
|
||||
ui->startStop->setStyleSheet("QToolButton { background-color : red; }");
|
||||
QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceAPI->errorMessage());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
m_lastEngineState = state;
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::openDeviceSettingsDialog(const QPoint& p)
|
||||
{
|
||||
if (m_contextMenuType == ContextMenuDeviceSettings)
|
||||
{
|
||||
BasicDeviceSettingsDialog dialog(this);
|
||||
dialog.setUseReverseAPI(m_settings.m_useReverseAPI);
|
||||
dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress);
|
||||
dialog.setReverseAPIPort(m_settings.m_reverseAPIPort);
|
||||
dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex);
|
||||
|
||||
dialog.move(p);
|
||||
dialog.exec();
|
||||
|
||||
m_settings.m_useReverseAPI = dialog.useReverseAPI();
|
||||
m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress();
|
||||
m_settings.m_reverseAPIPort = dialog.getReverseAPIPort();
|
||||
m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex();
|
||||
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
resetContextMenuType();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::makeUIConnections()
|
||||
{
|
||||
QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &RemoteTCPInputGui::on_startStop_toggled);
|
||||
QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &RemoteTCPInputGui::on_centerFrequency_changed);
|
||||
QObject::connect(ui->ppm, &QSlider::valueChanged, this, &RemoteTCPInputGui::on_ppm_valueChanged);
|
||||
QObject::connect(ui->dcOffset, &ButtonSwitch::toggled, this, &RemoteTCPInputGui::on_dcOffset_toggled);
|
||||
QObject::connect(ui->iqImbalance, &ButtonSwitch::toggled, this, &RemoteTCPInputGui::on_iqImbalance_toggled);
|
||||
QObject::connect(ui->biasTee, &ButtonSwitch::toggled, this, &RemoteTCPInputGui::on_biasTee_toggled);
|
||||
QObject::connect(ui->directSampling, &ButtonSwitch::toggled, this, &RemoteTCPInputGui::on_directSampling_toggled);
|
||||
QObject::connect(ui->devSampleRate, &ValueDial::changed, this, &RemoteTCPInputGui::on_devSampleRate_changed);
|
||||
QObject::connect(ui->decim, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RemoteTCPInputGui::on_decim_currentIndexChanged);
|
||||
QObject::connect(ui->gain1, &QDial::valueChanged, this, &RemoteTCPInputGui::on_gain1_valueChanged);
|
||||
QObject::connect(ui->gain2, &QDial::valueChanged, this, &RemoteTCPInputGui::on_gain2_valueChanged);
|
||||
QObject::connect(ui->gain3, &QDial::valueChanged, this, &RemoteTCPInputGui::on_gain3_valueChanged);
|
||||
QObject::connect(ui->agc, &ButtonSwitch::toggled, this, &RemoteTCPInputGui::on_agc_toggled);
|
||||
QObject::connect(ui->rfBW, &ValueDial::changed, this, &RemoteTCPInputGui::on_rfBW_changed);
|
||||
QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &RemoteTCPInputGui::on_deltaFrequency_changed);
|
||||
QObject::connect(ui->channelGain, &QDial::valueChanged, this, &RemoteTCPInputGui::on_channelGain_valueChanged);
|
||||
QObject::connect(ui->channelSampleRate, &ValueDial::changed, this, &RemoteTCPInputGui::on_channelSampleRate_changed);
|
||||
QObject::connect(ui->decimation, &ButtonSwitch::toggled, this, &RemoteTCPInputGui::on_decimation_toggled);
|
||||
QObject::connect(ui->sampleBits, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RemoteTCPInputGui::on_sampleBits_currentIndexChanged);
|
||||
QObject::connect(ui->dataAddress, &QLineEdit::editingFinished, this, &RemoteTCPInputGui::on_dataAddress_editingFinished);
|
||||
QObject::connect(ui->dataPort, &QLineEdit::editingFinished, this, &RemoteTCPInputGui::on_dataPort_editingFinished);
|
||||
QObject::connect(ui->overrideRemoteSettings, &ButtonSwitch::toggled, this, &RemoteTCPInputGui::on_overrideRemoteSettings_toggled);
|
||||
QObject::connect(ui->preFill, &QDial::valueChanged, this, &RemoteTCPInputGui::on_preFill_valueChanged);
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
|
||||
// Copyright (C) 2022 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_REMOTETCPINPUTGUI_H
|
||||
#define INCLUDE_REMOTETCPINPUTGUI_H
|
||||
|
||||
#include <QTimer>
|
||||
#include <QElapsedTimer>
|
||||
#include <QWidget>
|
||||
|
||||
#include "device/devicegui.h"
|
||||
#include "util/messagequeue.h"
|
||||
|
||||
#include "remotetcpinput.h"
|
||||
#include "../../channelrx/remotetcpsink/remotetcpprotocol.h"
|
||||
|
||||
class DeviceUISet;
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
class QJsonObject;
|
||||
|
||||
namespace Ui {
|
||||
class RemoteTCPInputGui;
|
||||
}
|
||||
|
||||
class RemoteTCPInputGui : public DeviceGUI {
|
||||
Q_OBJECT
|
||||
|
||||
struct DeviceGains {
|
||||
struct GainRange {
|
||||
QString m_name;
|
||||
int m_min;
|
||||
int m_max;
|
||||
int m_step; // In dB
|
||||
QVector<int> m_gains; // In 10ths of dB
|
||||
QString m_units; // Units label for display in the GUI
|
||||
|
||||
GainRange(const QString& name, int min, int max, int step, const QString& units = "dB") :
|
||||
m_name(name),
|
||||
m_min(min),
|
||||
m_max(max),
|
||||
m_step(step),
|
||||
m_units(units)
|
||||
{
|
||||
}
|
||||
|
||||
GainRange(const QString& name, QVector<int> gains, const QString& units = "dB") :
|
||||
m_name(name),
|
||||
m_min(0),
|
||||
m_max(0),
|
||||
m_step(0),
|
||||
m_gains(gains),
|
||||
m_units(units)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
DeviceGains()
|
||||
{
|
||||
}
|
||||
|
||||
DeviceGains(QList<GainRange> gains, bool agc, bool biasTee) :
|
||||
m_gains(gains),
|
||||
m_agc(agc),
|
||||
m_biasTee(biasTee)
|
||||
{
|
||||
}
|
||||
|
||||
QList<GainRange> m_gains;
|
||||
bool m_agc;
|
||||
bool m_biasTee;
|
||||
};
|
||||
|
||||
public:
|
||||
explicit RemoteTCPInputGui(DeviceUISet *deviceUISet, QWidget* parent = 0);
|
||||
virtual ~RemoteTCPInputGui();
|
||||
virtual void destroy();
|
||||
|
||||
void resetToDefaults();
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* size);
|
||||
|
||||
private:
|
||||
Ui::RemoteTCPInputGui* ui;
|
||||
|
||||
RemoteTCPInputSettings m_settings; //!< current settings
|
||||
RemoteTCPInput* m_sampleSource;
|
||||
QTimer m_updateTimer;
|
||||
QTimer m_statusTimer;
|
||||
int m_lastEngineState;
|
||||
MessageQueue m_inputMessageQueue;
|
||||
|
||||
int m_sampleRate;
|
||||
quint64 m_centerFrequency;
|
||||
|
||||
bool m_doApplySettings;
|
||||
bool m_forceSettings;
|
||||
|
||||
const DeviceGains *m_deviceGains;
|
||||
RemoteTCPProtocol::Device m_remoteDevice; // Remote device reported when connecting
|
||||
bool m_connectionError;
|
||||
|
||||
static const DeviceGains::GainRange m_rtlSDR34kGainRange;
|
||||
static const DeviceGains m_rtlSDRe4kGains;
|
||||
static const DeviceGains::GainRange m_rtlSDRR820GainRange;
|
||||
static const DeviceGains m_rtlSDRR820Gains;
|
||||
static const DeviceGains::GainRange m_airspyLNAGainRange;
|
||||
static const DeviceGains::GainRange m_airspyMixerGainRange;
|
||||
static const DeviceGains::GainRange m_airspyVGAGainRange;
|
||||
static const DeviceGains m_airspyGains;
|
||||
static const DeviceGains::GainRange m_airspyHFAttRange;
|
||||
static const DeviceGains m_airspyHFGains;
|
||||
static const DeviceGains::GainRange m_bladeRF1LNARange;
|
||||
static const DeviceGains::GainRange m_bladeRF1VGA1Range;
|
||||
static const DeviceGains::GainRange m_bladeRF1VGA2Range;
|
||||
static const DeviceGains m_baldeRF1Gains;
|
||||
static const DeviceGains::GainRange m_funCubeProPlusRange;
|
||||
static const DeviceGains m_funCubeProPlusGains;
|
||||
static const DeviceGains::GainRange m_hackRFLNAGainRange;
|
||||
static const DeviceGains::GainRange m_hackRFVGAGainRange;
|
||||
static const DeviceGains m_hackRFGains;
|
||||
static const DeviceGains::GainRange m_kiwiGainRange;
|
||||
static const DeviceGains m_kiwiGains;
|
||||
static const DeviceGains::GainRange m_limeRange;
|
||||
static const DeviceGains m_limeGains;
|
||||
static const DeviceGains::GainRange m_sdrplayV3LNAGainRange;
|
||||
static const DeviceGains::GainRange m_sdrplayV3IFGainRange;
|
||||
static const DeviceGains m_sdrplayV3Gains;
|
||||
static const DeviceGains::GainRange m_plutoGainRange;
|
||||
static const DeviceGains m_plutoGains;
|
||||
static const DeviceGains::GainRange m_usrpGainRange;
|
||||
static const DeviceGains m_usrpGains;
|
||||
static const DeviceGains::GainRange m_xtrxGainRange;
|
||||
static const DeviceGains m_xtrxGains;
|
||||
static const QHash<RemoteTCPProtocol::Device, const DeviceGains *> m_gains;
|
||||
|
||||
void blockApplySettings(bool block);
|
||||
void displaySettings();
|
||||
QString gainText(int stage);
|
||||
void displayGains();
|
||||
void displayRemoteSettings();
|
||||
void displayRemoteShift();
|
||||
void sendSettings();
|
||||
void updateSampleRateAndFrequency();
|
||||
void applyDecimation();
|
||||
void applyPosition();
|
||||
bool handleMessage(const Message& message);
|
||||
void makeUIConnections();
|
||||
|
||||
private slots:
|
||||
void handleInputMessages();
|
||||
void on_startStop_toggled(bool checked);
|
||||
void on_centerFrequency_changed(quint64 value);
|
||||
void on_ppm_valueChanged(int value);
|
||||
void on_dcOffset_toggled(bool checked);
|
||||
void on_iqImbalance_toggled(bool checked);
|
||||
void on_biasTee_toggled(bool checked);
|
||||
void on_directSampling_toggled(bool checked);
|
||||
void on_devSampleRate_changed(quint64 value);
|
||||
void on_decim_currentIndexChanged(int index);
|
||||
void on_gain1_valueChanged(int value);
|
||||
void on_gain2_valueChanged(int value);
|
||||
void on_gain3_valueChanged(int value);
|
||||
void on_agc_toggled(bool checked);
|
||||
void on_rfBW_changed(int value);
|
||||
void on_deltaFrequency_changed(int value);
|
||||
void on_channelGain_valueChanged(int value);
|
||||
void on_channelSampleRate_changed(quint64 value);
|
||||
void on_decimation_toggled(bool checked);
|
||||
void on_sampleBits_currentIndexChanged(int index);
|
||||
void on_dataAddress_editingFinished();
|
||||
void on_dataPort_editingFinished();
|
||||
void on_overrideRemoteSettings_toggled(bool checked);
|
||||
void on_preFill_valueChanged(int value);
|
||||
void updateHardware();
|
||||
void updateStatus();
|
||||
void openDeviceSettingsDialog(const QPoint& p);
|
||||
};
|
||||
|
||||
#endif // INCLUDE_REMOTETCPINPUTGUI_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,149 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
|
||||
// Copyright (C) 2022 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QtPlugin>
|
||||
|
||||
#include "plugin/pluginapi.h"
|
||||
#include "util/simpleserializer.h"
|
||||
|
||||
#ifdef SERVER_MODE
|
||||
#include "remotetcpinput.h"
|
||||
#else
|
||||
#include "remotetcpinputgui.h"
|
||||
#endif
|
||||
#include "remotetcpinputplugin.h"
|
||||
#include "remotetcpinputwebapiadapter.h"
|
||||
|
||||
const PluginDescriptor RemoteTCPInputPlugin::m_pluginDescriptor = {
|
||||
QStringLiteral("RemoteTCPInput"),
|
||||
QStringLiteral("Remote TCP device input"),
|
||||
QStringLiteral("7.6.0"),
|
||||
QStringLiteral("(c) Jon Beniston, M7RCE"),
|
||||
QStringLiteral("https://github.com/f4exb/sdrangel"),
|
||||
true,
|
||||
QStringLiteral("https://github.com/f4exb/sdrangel")
|
||||
};
|
||||
|
||||
static constexpr const char* const m_hardwareID = "RemoteTCPInput";
|
||||
static constexpr const char* const m_deviceTypeID = REMOTETCPINPUT_DEVICE_TYPE_ID;
|
||||
|
||||
RemoteTCPInputPlugin::RemoteTCPInputPlugin(QObject* parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
const PluginDescriptor& RemoteTCPInputPlugin::getPluginDescriptor() const
|
||||
{
|
||||
return m_pluginDescriptor;
|
||||
}
|
||||
|
||||
void RemoteTCPInputPlugin::initPlugin(PluginAPI* pluginAPI)
|
||||
{
|
||||
pluginAPI->registerSampleSource(m_deviceTypeID, this);
|
||||
}
|
||||
|
||||
void RemoteTCPInputPlugin::enumOriginDevices(QStringList& listedHwIds, OriginDevices& originDevices)
|
||||
{
|
||||
if (listedHwIds.contains(m_hardwareID)) { // check if it was done
|
||||
return;
|
||||
}
|
||||
|
||||
originDevices.append(OriginDevice(
|
||||
"RemoteTCPInput",
|
||||
m_hardwareID,
|
||||
QString(),
|
||||
0,
|
||||
1, // nb Rx
|
||||
0 // nb Tx
|
||||
));
|
||||
|
||||
listedHwIds.append(m_hardwareID);
|
||||
}
|
||||
|
||||
PluginInterface::SamplingDevices RemoteTCPInputPlugin::enumSampleSources(const OriginDevices& originDevices)
|
||||
{
|
||||
SamplingDevices result;
|
||||
|
||||
for (OriginDevices::const_iterator it = originDevices.begin(); it != originDevices.end(); ++it)
|
||||
{
|
||||
if (it->hardwareId == m_hardwareID)
|
||||
{
|
||||
result.append(SamplingDevice(
|
||||
it->displayableName,
|
||||
m_hardwareID,
|
||||
m_deviceTypeID,
|
||||
it->serial,
|
||||
it->sequence,
|
||||
PluginInterface::SamplingDevice::BuiltInDevice,
|
||||
PluginInterface::SamplingDevice::StreamSingleRx,
|
||||
1,
|
||||
0
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef SERVER_MODE
|
||||
DeviceGUI* RemoteTCPInputPlugin::createSampleSourcePluginInstanceGUI(
|
||||
const QString& sourceId,
|
||||
QWidget **widget,
|
||||
DeviceUISet *deviceUISet)
|
||||
{
|
||||
(void) sourceId;
|
||||
(void) widget;
|
||||
(void) deviceUISet;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
DeviceGUI* RemoteTCPInputPlugin::createSampleSourcePluginInstanceGUI(
|
||||
const QString& sourceId,
|
||||
QWidget **widget,
|
||||
DeviceUISet *deviceUISet)
|
||||
{
|
||||
if(sourceId == m_deviceTypeID)
|
||||
{
|
||||
RemoteTCPInputGui* gui = new RemoteTCPInputGui(deviceUISet);
|
||||
*widget = gui;
|
||||
return gui;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
DeviceSampleSource *RemoteTCPInputPlugin::createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI)
|
||||
{
|
||||
if (sourceId == m_deviceTypeID)
|
||||
{
|
||||
RemoteTCPInput* input = new RemoteTCPInput(deviceAPI);
|
||||
return input;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
DeviceWebAPIAdapter *RemoteTCPInputPlugin::createDeviceWebAPIAdapter() const
|
||||
{
|
||||
return new RemoteTCPInputWebAPIAdapter();
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
|
||||
// Copyright (C) 2022 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_REMOTETCPINPUTPLUGIN_H
|
||||
#define INCLUDE_REMOTETCPINPUTPLUGIN_H
|
||||
|
||||
#include <QObject>
|
||||
#include "plugin/plugininterface.h"
|
||||
|
||||
#define REMOTETCPINPUT_DEVICE_TYPE_ID "sdrangel.samplesource.remotetcpinput"
|
||||
|
||||
class PluginAPI;
|
||||
|
||||
class RemoteTCPInputPlugin : public QObject, public PluginInterface {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(PluginInterface)
|
||||
Q_PLUGIN_METADATA(IID REMOTETCPINPUT_DEVICE_TYPE_ID)
|
||||
|
||||
public:
|
||||
explicit RemoteTCPInputPlugin(QObject* parent = NULL);
|
||||
|
||||
const PluginDescriptor& getPluginDescriptor() const;
|
||||
void initPlugin(PluginAPI* pluginAPI);
|
||||
|
||||
virtual void enumOriginDevices(QStringList& listedHwIds, OriginDevices& originDevices);
|
||||
virtual SamplingDevices enumSampleSources(const OriginDevices& originDevices);
|
||||
virtual DeviceGUI* createSampleSourcePluginInstanceGUI(
|
||||
const QString& sourceId,
|
||||
QWidget **widget,
|
||||
DeviceUISet *deviceUISet);
|
||||
virtual DeviceSampleSource* createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI);
|
||||
virtual DeviceWebAPIAdapter* createDeviceWebAPIAdapter() const;
|
||||
|
||||
private:
|
||||
static const PluginDescriptor m_pluginDescriptor;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_REMOTETCPINPUTPLUGIN_H
|
||||
@@ -0,0 +1,148 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2017 Edouard Griffiths, F4EXB //
|
||||
// Copyright (C) 2022 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "util/simpleserializer.h"
|
||||
#include "remotetcpinputsettings.h"
|
||||
|
||||
RemoteTCPInputSettings::RemoteTCPInputSettings()
|
||||
{
|
||||
resetToDefaults();
|
||||
}
|
||||
|
||||
void RemoteTCPInputSettings::resetToDefaults()
|
||||
{
|
||||
m_centerFrequency = 435000000;
|
||||
m_loPpmCorrection = 0;
|
||||
m_dcBlock = false;
|
||||
m_iqCorrection = false;
|
||||
m_biasTee = false;
|
||||
m_directSampling = false;
|
||||
m_devSampleRate = 2000000;
|
||||
m_log2Decim = 1;
|
||||
for (int i = 0; i < m_maxGains; i++) {
|
||||
m_gain[i] = 0;
|
||||
}
|
||||
m_agc = false;
|
||||
m_rfBW = 2500000;
|
||||
m_inputFrequencyOffset = 0;
|
||||
m_channelGain = 0;
|
||||
m_channelSampleRate = m_devSampleRate;
|
||||
m_channelDecimation = false;
|
||||
m_sampleBits = 8;
|
||||
m_dataAddress = "127.0.0.1";
|
||||
m_dataPort = 1234;
|
||||
m_overrideRemoteSettings = true;
|
||||
m_preFill = 1.0f;
|
||||
m_useReverseAPI = false;
|
||||
m_reverseAPIAddress = "127.0.0.1";
|
||||
m_reverseAPIPort = 8888;
|
||||
m_reverseAPIDeviceIndex = 0;
|
||||
}
|
||||
|
||||
QByteArray RemoteTCPInputSettings::serialize() const
|
||||
{
|
||||
SimpleSerializer s(1);
|
||||
|
||||
s.writeS32(1, m_loPpmCorrection);
|
||||
s.writeBool(2, m_dcBlock);
|
||||
s.writeBool(3, m_iqCorrection);
|
||||
s.writeBool(4, m_biasTee);
|
||||
s.writeBool(5, m_directSampling);
|
||||
s.writeS32(6, m_devSampleRate);
|
||||
s.writeS32(7, m_log2Decim);
|
||||
s.writeBool(9, m_agc);
|
||||
s.writeS32(10, m_rfBW);
|
||||
s.writeS32(11, m_inputFrequencyOffset);
|
||||
s.writeS32(12, m_channelGain);
|
||||
s.writeS32(13, m_channelSampleRate);
|
||||
s.writeBool(14, m_channelDecimation);
|
||||
s.writeS32(15, m_sampleBits);
|
||||
s.writeU32(16, m_dataPort);
|
||||
s.writeString(17, m_dataAddress);
|
||||
s.writeBool(18, m_overrideRemoteSettings);
|
||||
s.writeFloat(19, m_preFill);
|
||||
s.writeBool(20, m_useReverseAPI);
|
||||
s.writeString(21, m_reverseAPIAddress);
|
||||
s.writeU32(22, m_reverseAPIPort);
|
||||
s.writeU32(23, m_reverseAPIDeviceIndex);
|
||||
|
||||
for (int i = 0; i < m_maxGains; i++) {
|
||||
s.writeS32(30+i, m_gain[i]);
|
||||
}
|
||||
|
||||
return s.final();
|
||||
}
|
||||
|
||||
bool RemoteTCPInputSettings::deserialize(const QByteArray& data)
|
||||
{
|
||||
SimpleDeserializer d(data);
|
||||
|
||||
if (!d.isValid())
|
||||
{
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (d.getVersion() == 1)
|
||||
{
|
||||
quint32 uintval;
|
||||
|
||||
d.readS32(1, &m_loPpmCorrection, 0);
|
||||
d.readBool(2, &m_dcBlock, false);
|
||||
d.readBool(3, &m_iqCorrection, false);
|
||||
d.readBool(4, &m_biasTee, false);
|
||||
d.readBool(5, &m_directSampling, false);
|
||||
d.readS32(6, &m_devSampleRate, 2000000);
|
||||
d.readS32(7, &m_log2Decim, 1);
|
||||
d.readBool(9, &m_agc, false);
|
||||
d.readS32(10, &m_rfBW, 2500000);
|
||||
d.readS32(11, &m_inputFrequencyOffset, 0);
|
||||
d.readS32(12, &m_channelGain, 0);
|
||||
d.readS32(13, &m_channelSampleRate, 2000000);
|
||||
d.readBool(14, &m_channelDecimation, false);
|
||||
d.readS32(15, &m_sampleBits, 8);
|
||||
d.readU32(16, &uintval, 1234);
|
||||
m_dataPort = uintval % (1<<16);
|
||||
d.readString(17, &m_dataAddress, "127.0.0.1");
|
||||
d.readBool(18, &m_overrideRemoteSettings, false);
|
||||
d.readFloat(19, &m_preFill, 1.0f);
|
||||
d.readBool(20, &m_useReverseAPI, false);
|
||||
d.readString(21, &m_reverseAPIAddress, "127.0.0.1");
|
||||
d.readU32(22, &uintval, 0);
|
||||
|
||||
if ((uintval > 1023) && (uintval < 65535)) {
|
||||
m_reverseAPIPort = uintval;
|
||||
} else {
|
||||
m_reverseAPIPort = 8888;
|
||||
}
|
||||
|
||||
d.readU32(23, &uintval, 0);
|
||||
m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval;
|
||||
|
||||
for (int i = 0; i < m_maxGains; i++) {
|
||||
d.readS32(30+i, &m_gain[i], 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2017 Edouard Griffiths, F4EXB //
|
||||
// Copyright (C) 2022 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef PLUGINS_SAMPLESOURCE_REMOTETCPINPUT_REMOTETCPINPUTSETTINGS_H_
|
||||
#define PLUGINS_SAMPLESOURCE_REMOTETCPINPUT_REMOTETCPINPUTSETTINGS_H_
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
|
||||
struct RemoteTCPInputSettings
|
||||
{
|
||||
static const int m_maxGains = 3;
|
||||
|
||||
uint64_t m_centerFrequency;
|
||||
qint32 m_loPpmCorrection;
|
||||
bool m_dcBlock;
|
||||
bool m_iqCorrection;
|
||||
bool m_biasTee;
|
||||
bool m_directSampling; // RTLSDR only
|
||||
int m_devSampleRate;
|
||||
int m_log2Decim;
|
||||
qint32 m_gain[m_maxGains]; // 10ths of a dB
|
||||
bool m_agc;
|
||||
qint32 m_rfBW;
|
||||
qint32 m_inputFrequencyOffset;
|
||||
qint32 m_channelGain; // In dB
|
||||
qint32 m_channelSampleRate;
|
||||
bool m_channelDecimation; // If false, m_channelSampleRate==m_devSampleRate
|
||||
qint32 m_sampleBits; // Number of bits used to transmit IQ samples (8,16,24,32)
|
||||
QString m_dataAddress;
|
||||
quint16 m_dataPort;
|
||||
bool m_overrideRemoteSettings; // When connected, apply local settings to remote, or apply remote settings to local
|
||||
float m_preFill; // Input buffer prefill in seconds
|
||||
bool m_useReverseAPI;
|
||||
QString m_reverseAPIAddress;
|
||||
uint16_t m_reverseAPIPort;
|
||||
uint16_t m_reverseAPIDeviceIndex;
|
||||
|
||||
RemoteTCPInputSettings();
|
||||
void resetToDefaults();
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
};
|
||||
|
||||
#endif /* PLUGINS_SAMPLESOURCE_REMOTETCPINPUT_REMOTETCPINPUTSETTINGS_H_ */
|
||||
@@ -0,0 +1,782 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
|
||||
// Copyright (C) 2022 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QUdpSocket>
|
||||
#include <QDebug>
|
||||
|
||||
#include "dsp/dspcommands.h"
|
||||
#include "dsp/dspengine.h"
|
||||
#include "device/deviceapi.h"
|
||||
|
||||
#include "remotetcpinputtcphandler.h"
|
||||
#include "remotetcpinput.h"
|
||||
#include "../../channelrx/remotetcpsink/remotetcpprotocol.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(RemoteTCPInputTCPHandler::MsgReportRemoteDevice, Message)
|
||||
MESSAGE_CLASS_DEFINITION(RemoteTCPInputTCPHandler::MsgReportConnection, Message)
|
||||
MESSAGE_CLASS_DEFINITION(RemoteTCPInputTCPHandler::MsgConfigureTcpHandler, Message)
|
||||
|
||||
RemoteTCPInputTCPHandler::RemoteTCPInputTCPHandler(SampleSinkFifo *sampleFifo, DeviceAPI *deviceAPI) :
|
||||
m_deviceAPI(deviceAPI),
|
||||
m_running(false),
|
||||
m_dataSocket(nullptr),
|
||||
m_tcpBuf(nullptr),
|
||||
m_sampleFifo(sampleFifo),
|
||||
m_messageQueueToGUI(0),
|
||||
m_fillBuffer(true),
|
||||
m_timer(this),
|
||||
m_reconnectTimer(this),
|
||||
m_converterBuffer(nullptr),
|
||||
m_converterBufferNbSamples(0),
|
||||
m_mutex(QMutex::Recursive),
|
||||
m_settings()
|
||||
{
|
||||
m_tcpBuf = new char[m_sampleFifo->size()*2*4];
|
||||
m_timer.setInterval(125);
|
||||
connect(&m_reconnectTimer, SIGNAL(timeout()), this, SLOT(reconnect()));
|
||||
m_reconnectTimer.setSingleShot(true);
|
||||
}
|
||||
|
||||
RemoteTCPInputTCPHandler::~RemoteTCPInputTCPHandler()
|
||||
{
|
||||
delete[] m_tcpBuf;
|
||||
if (m_converterBuffer) {
|
||||
delete[] m_converterBuffer;
|
||||
}
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::reset()
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
m_inputMessageQueue.clear();
|
||||
}
|
||||
|
||||
// start() is called from DSPDeviceSourceEngine thread
|
||||
// QTcpSockets need to be created on same thread they are used from, so only create it in started()
|
||||
void RemoteTCPInputTCPHandler::start()
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
qDebug("RemoteTCPInputTCPHandler::start");
|
||||
|
||||
if (m_running) {
|
||||
return;
|
||||
}
|
||||
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
|
||||
connect(thread(), SIGNAL(started()), this, SLOT(started()));
|
||||
connect(thread(), SIGNAL(finished()), this, SLOT(finished()));
|
||||
|
||||
m_running = true;
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::stop()
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
qDebug("RemoteTCPInputTCPHandler::stop");
|
||||
|
||||
disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::started()
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
// Don't connectToHost until we get settings
|
||||
connect(&m_timer, SIGNAL(timeout()), this, SLOT(processData()));
|
||||
m_timer.start();
|
||||
|
||||
disconnect(thread(), SIGNAL(started()), this, SLOT(started()));
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::finished()
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
m_timer.stop();
|
||||
disconnect(&m_timer, SIGNAL(timeout()), this, SLOT(processData()));
|
||||
disconnectFromHost();
|
||||
disconnect(thread(), SIGNAL(finished()), this, SLOT(finished()));
|
||||
m_running = false;
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::connectToHost(const QString& address, quint16 port)
|
||||
{
|
||||
qDebug("RemoteTCPInputTCPHandler::connectToHost: connect to %s:%d", address.toStdString().c_str(), port);
|
||||
m_dataSocket = new QTcpSocket(this);
|
||||
m_fillBuffer = true;
|
||||
m_readMetaData = false;
|
||||
connect(m_dataSocket, SIGNAL(readyRead()), this, SLOT(dataReadyRead()));
|
||||
connect(m_dataSocket, SIGNAL(connected()), this, SLOT(connected()));
|
||||
connect(m_dataSocket, SIGNAL(disconnected()), this, SLOT(disconnected()));
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
||||
connect(m_dataSocket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &RemoteTCPInputTCPHandler::errorOccurred);
|
||||
#else
|
||||
connect(m_dataSocket, &QAbstractSocket::errorOccurred, this, &RemoteTCPInputTCPHandler::errorOccurred);
|
||||
#endif
|
||||
m_dataSocket->connectToHost(address, port);
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::disconnectFromHost()
|
||||
{
|
||||
if (m_dataSocket)
|
||||
{
|
||||
qDebug() << "RemoteTCPInputTCPHandler::disconnectFromHost";
|
||||
disconnect(m_dataSocket, SIGNAL(readyRead()), this, SLOT(dataReadyRead()));
|
||||
disconnect(m_dataSocket, SIGNAL(connected()), this, SLOT(connected()));
|
||||
disconnect(m_dataSocket, SIGNAL(disconnected()), this, SLOT(disconnected()));
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
||||
disconnect(m_dataSocket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &RemoteTCPInputTCPHandler::errorOccurred);
|
||||
#else
|
||||
disconnect(m_dataSocket, &QAbstractSocket::errorOccurred, this, &RemoteTCPInputTCPHandler::errorOccurred);
|
||||
#endif
|
||||
m_dataSocket->disconnectFromHost();
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::cleanup()
|
||||
{
|
||||
if (m_dataSocket)
|
||||
{
|
||||
m_dataSocket->deleteLater();
|
||||
m_dataSocket = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear input buffer when settings change that invalidate the data in it
|
||||
// E.g. sample rate or bit depth
|
||||
void RemoteTCPInputTCPHandler::clearBuffer()
|
||||
{
|
||||
if (m_dataSocket)
|
||||
{
|
||||
m_dataSocket->flush();
|
||||
m_dataSocket->readAll();
|
||||
m_fillBuffer = true;
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::setSampleRate(int sampleRate)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
quint8 request[5];
|
||||
request[0] = RemoteTCPProtocol::setSampleRate;
|
||||
RemoteTCPProtocol::encodeUInt32(&request[1], sampleRate);
|
||||
if (m_dataSocket) {
|
||||
m_dataSocket->write((char*)request, sizeof(request));
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::setCenterFrequency(quint64 frequency)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
quint8 request[5];
|
||||
request[0] = RemoteTCPProtocol::setCenterFrequency;
|
||||
RemoteTCPProtocol::encodeUInt32(&request[1], frequency);
|
||||
if (m_dataSocket) {
|
||||
m_dataSocket->write((char*)request, sizeof(request));
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::setTunerAGC(bool agc)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
quint8 request[5];
|
||||
request[0] = RemoteTCPProtocol::setTunerGainMode;
|
||||
RemoteTCPProtocol::encodeUInt32(&request[1], agc);
|
||||
if (m_dataSocket) {
|
||||
m_dataSocket->write((char*)request, sizeof(request));
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::setTunerGain(int gain)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
quint8 request[5];
|
||||
request[0] = RemoteTCPProtocol::setTunerGain;
|
||||
RemoteTCPProtocol::encodeUInt32(&request[1], gain);
|
||||
if (m_dataSocket) {
|
||||
m_dataSocket->write((char*)request, sizeof(request));
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::setFreqCorrection(int correction)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
quint8 request[5];
|
||||
request[0] = RemoteTCPProtocol::setFrequencyCorrection;
|
||||
RemoteTCPProtocol::encodeUInt32(&request[1], correction);
|
||||
if (m_dataSocket) {
|
||||
m_dataSocket->write((char*)request, sizeof(request));
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::setIFGain(quint16 stage, quint16 gain)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
quint8 request[5];
|
||||
request[0] = RemoteTCPProtocol::setTunerIFGain;
|
||||
RemoteTCPProtocol::encodeUInt32(&request[1], (stage << 16) | gain);
|
||||
if (m_dataSocket) {
|
||||
m_dataSocket->write((char*)request, sizeof(request));
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::setAGC(bool agc)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
quint8 request[5];
|
||||
request[0] = RemoteTCPProtocol::setAGCMode;
|
||||
RemoteTCPProtocol::encodeUInt32(&request[1], agc);
|
||||
if (m_dataSocket) {
|
||||
m_dataSocket->write((char*)request, sizeof(request));
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::setDirectSampling(bool enabled)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
quint8 request[5];
|
||||
request[0] = RemoteTCPProtocol::setDirectSampling;
|
||||
RemoteTCPProtocol::encodeUInt32(&request[1], enabled);
|
||||
if (m_dataSocket) {
|
||||
m_dataSocket->write((char*)request, sizeof(request));
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::setDCOffsetRemoval(bool enabled)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
quint8 request[5];
|
||||
request[0] = RemoteTCPProtocol::setDCOffsetRemoval;
|
||||
RemoteTCPProtocol::encodeUInt32(&request[1], enabled);
|
||||
if (m_dataSocket) {
|
||||
m_dataSocket->write((char*)request, sizeof(request));
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::setIQCorrection(bool enabled)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
quint8 request[5];
|
||||
request[0] = RemoteTCPProtocol::setIQCorrection;
|
||||
RemoteTCPProtocol::encodeUInt32(&request[1], enabled);
|
||||
if (m_dataSocket) {
|
||||
m_dataSocket->write((char*)request, sizeof(request));
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::setBiasTee(bool enabled)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
quint8 request[5];
|
||||
request[0] = RemoteTCPProtocol::setBiasTee;
|
||||
RemoteTCPProtocol::encodeUInt32(&request[1], enabled);
|
||||
if (m_dataSocket) {
|
||||
m_dataSocket->write((char*)request, sizeof(request));
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::setBandwidth(int bandwidth)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
quint8 request[5];
|
||||
request[0] = RemoteTCPProtocol::setTunerBandwidth;
|
||||
RemoteTCPProtocol::encodeUInt32(&request[1], bandwidth);
|
||||
if (m_dataSocket) {
|
||||
m_dataSocket->write((char*)request, sizeof(request));
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::setDecimation(int dec)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
quint8 request[5];
|
||||
request[0] = RemoteTCPProtocol::setDecimation;
|
||||
RemoteTCPProtocol::encodeUInt32(&request[1], dec);
|
||||
if (m_dataSocket) {
|
||||
m_dataSocket->write((char*)request, sizeof(request));
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::setChannelSampleRate(int sampleRate)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
quint8 request[5];
|
||||
request[0] = RemoteTCPProtocol::setChannelSampleRate;
|
||||
RemoteTCPProtocol::encodeUInt32(&request[1], sampleRate);
|
||||
if (m_dataSocket) {
|
||||
m_dataSocket->write((char*)request, sizeof(request));
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::setChannelFreqOffset(int offset)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
quint8 request[5];
|
||||
request[0] = RemoteTCPProtocol::setChannelFreqOffset;
|
||||
RemoteTCPProtocol::encodeUInt32(&request[1], offset);
|
||||
if (m_dataSocket) {
|
||||
m_dataSocket->write((char*)request, sizeof(request));
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::setChannelGain(int gain)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
quint8 request[5];
|
||||
request[0] = RemoteTCPProtocol::setChannelGain;
|
||||
RemoteTCPProtocol::encodeUInt32(&request[1], gain);
|
||||
if (m_dataSocket) {
|
||||
m_dataSocket->write((char*)request, sizeof(request));
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::setSampleBitDepth(int sampleBits)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
quint8 request[5];
|
||||
request[0] = RemoteTCPProtocol::setSampleBitDepth;
|
||||
RemoteTCPProtocol::encodeUInt32(&request[1], sampleBits);
|
||||
if (m_dataSocket) {
|
||||
m_dataSocket->write((char*)request, sizeof(request));
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::applySettings(const RemoteTCPInputSettings& settings, bool force)
|
||||
{
|
||||
qDebug() << "RemoteTCPInputTCPHandler::applySettings: "
|
||||
<< "force: " << force
|
||||
<< "m_dataAddress: " << settings.m_dataAddress
|
||||
<< "m_dataPort: " << settings.m_dataPort
|
||||
<< "m_devSampleRate: " << settings.m_devSampleRate
|
||||
<< "m_channelSampleRate: " << settings.m_channelSampleRate;
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
if ((settings.m_centerFrequency != m_settings.m_centerFrequency) || force) {
|
||||
setCenterFrequency(settings.m_centerFrequency);
|
||||
}
|
||||
if ((settings.m_loPpmCorrection != m_settings.m_loPpmCorrection) || force) {
|
||||
setFreqCorrection(settings.m_loPpmCorrection);
|
||||
}
|
||||
if ((settings.m_dcBlock != m_settings.m_dcBlock) || force) {
|
||||
setDCOffsetRemoval(settings.m_dcBlock);
|
||||
}
|
||||
if ((settings.m_iqCorrection != m_settings.m_iqCorrection) || force) {
|
||||
setIQCorrection(settings.m_iqCorrection);
|
||||
}
|
||||
if ((settings.m_biasTee != m_settings.m_biasTee) || force) {
|
||||
setBiasTee(settings.m_biasTee);
|
||||
}
|
||||
if ((settings.m_directSampling != m_settings.m_directSampling) || force) {
|
||||
setDirectSampling(settings.m_directSampling);
|
||||
}
|
||||
if ((settings.m_log2Decim != m_settings.m_log2Decim) || force) {
|
||||
setDecimation(settings.m_log2Decim);
|
||||
}
|
||||
if ((settings.m_devSampleRate != m_settings.m_devSampleRate) || force) {
|
||||
setSampleRate(settings.m_devSampleRate);
|
||||
}
|
||||
if ((settings.m_agc != m_settings.m_agc) || force) {
|
||||
setAGC(settings.m_agc);
|
||||
}
|
||||
if (force) {
|
||||
setTunerAGC(1); // The SDRangel RTLSDR driver always has tuner gain as manual
|
||||
}
|
||||
if ((settings.m_gain[0] != m_settings.m_gain[0]) || force) {
|
||||
setTunerGain(settings.m_gain[0]);
|
||||
}
|
||||
for (int i = 1; i < 3; i++)
|
||||
{
|
||||
if ((settings.m_gain[i] != m_settings.m_gain[i]) || force) {
|
||||
setIFGain(i, settings.m_gain[i]);
|
||||
}
|
||||
}
|
||||
if ((settings.m_rfBW != m_settings.m_rfBW) || force) {
|
||||
setBandwidth(settings.m_rfBW);
|
||||
}
|
||||
if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) {
|
||||
setChannelFreqOffset(settings.m_inputFrequencyOffset);
|
||||
}
|
||||
if ((settings.m_channelGain != m_settings.m_channelGain) || force) {
|
||||
setChannelGain(settings.m_channelGain);
|
||||
}
|
||||
if ((settings.m_channelSampleRate != m_settings.m_channelSampleRate) || force)
|
||||
{
|
||||
// Resize FIFO to give us 1 second
|
||||
// Can't do this while running
|
||||
if (!m_running && settings.m_channelSampleRate > m_sampleFifo->size())
|
||||
{
|
||||
qDebug() << "RemoteTCPInputTCPHandler::applySettings: Resizing sample FIFO from " << m_sampleFifo->size() << "to" << settings.m_channelSampleRate;
|
||||
m_sampleFifo->setSize(settings.m_channelSampleRate);
|
||||
delete[] m_tcpBuf;
|
||||
m_tcpBuf = new char[m_sampleFifo->size()*2*4];
|
||||
m_fillBuffer = true; // So we reprime FIFO
|
||||
}
|
||||
setChannelSampleRate(settings.m_channelSampleRate);
|
||||
clearBuffer();
|
||||
}
|
||||
if ((settings.m_sampleBits != m_settings.m_sampleBits) || force)
|
||||
{
|
||||
setSampleBitDepth(settings.m_sampleBits);
|
||||
clearBuffer();
|
||||
}
|
||||
|
||||
// Don't use force, as disconnect can cause rtl_tcp to quit
|
||||
if ((settings.m_dataPort != m_settings.m_dataPort) || (settings.m_dataAddress != m_settings.m_dataAddress) || (m_dataSocket == nullptr))
|
||||
{
|
||||
disconnectFromHost();
|
||||
connectToHost(settings.m_dataAddress, settings.m_dataPort);
|
||||
}
|
||||
|
||||
m_settings = settings;
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::connected()
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
qDebug() << "RemoteTCPInputTCPHandler::connected";
|
||||
if (m_settings.m_overrideRemoteSettings)
|
||||
{
|
||||
// Force settings to be sent to remote device
|
||||
applySettings(m_settings, true);
|
||||
}
|
||||
if (m_messageQueueToGUI)
|
||||
{
|
||||
MsgReportConnection *msg = MsgReportConnection::create(true);
|
||||
m_messageQueueToGUI->push(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::reconnect()
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
if (!m_dataSocket) {
|
||||
connectToHost(m_settings.m_dataAddress, m_settings.m_dataPort);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::disconnected()
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
qDebug() << "RemoteTCPInputTCPHandler::disconnected";
|
||||
cleanup();
|
||||
if (m_messageQueueToGUI)
|
||||
{
|
||||
MsgReportConnection *msg = MsgReportConnection::create(false);
|
||||
m_messageQueueToGUI->push(msg);
|
||||
}
|
||||
// Try to reconnect
|
||||
m_reconnectTimer.start(500);
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::errorOccurred(QAbstractSocket::SocketError socketError)
|
||||
{
|
||||
qDebug() << "RemoteTCPInputTCPHandler::errorOccurred: " << socketError;
|
||||
cleanup();
|
||||
if (m_messageQueueToGUI)
|
||||
{
|
||||
MsgReportConnection *msg = MsgReportConnection::create(false);
|
||||
m_messageQueueToGUI->push(msg);
|
||||
}
|
||||
// Try to reconnect
|
||||
m_reconnectTimer.start(500);
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::dataReadyRead()
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
if (!m_readMetaData)
|
||||
{
|
||||
quint8 metaData[RemoteTCPProtocol::m_sdraMetaDataSize];
|
||||
if (m_dataSocket->bytesAvailable() >= sizeof(metaData))
|
||||
{
|
||||
qint64 bytesRead = m_dataSocket->read((char *)&metaData[0], 4);
|
||||
if (bytesRead == 4)
|
||||
{
|
||||
// Read first 4 bytes which indicate which protocol is in use
|
||||
// RTL0 or SDRA
|
||||
char protochars[5];
|
||||
memcpy(protochars, metaData, 4);
|
||||
protochars[4] = '\0';
|
||||
QString protocol(protochars);
|
||||
|
||||
if (protocol == "RTL0")
|
||||
{
|
||||
bytesRead = m_dataSocket->read((char *)&metaData[4], RemoteTCPProtocol::m_rtl0MetaDataSize-4);
|
||||
|
||||
RemoteTCPProtocol::Device tuner = (RemoteTCPProtocol::Device)RemoteTCPProtocol::extractUInt32(&metaData[4]);
|
||||
quint32 gainStages = RemoteTCPProtocol::extractUInt32(&metaData[8]);
|
||||
if (m_messageQueueToGUI) {
|
||||
m_messageQueueToGUI->push(MsgReportRemoteDevice::create(tuner, protocol));
|
||||
}
|
||||
if (m_settings.m_sampleBits != 8)
|
||||
{
|
||||
RemoteTCPInputSettings& settings = m_settings;
|
||||
settings.m_sampleBits = 8;
|
||||
if (m_messageQueueToInput) {
|
||||
m_messageQueueToInput->push(RemoteTCPInput::MsgConfigureRemoteTCPInput::create(settings));
|
||||
}
|
||||
if (m_messageQueueToGUI) {
|
||||
m_messageQueueToGUI->push(RemoteTCPInput::MsgConfigureRemoteTCPInput::create(settings));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (protocol == "SDRA")
|
||||
{
|
||||
bytesRead = m_dataSocket->read((char *)&metaData[4], RemoteTCPProtocol::m_sdraMetaDataSize-4);
|
||||
|
||||
RemoteTCPProtocol::Device device = (RemoteTCPProtocol::Device)RemoteTCPProtocol::extractUInt32(&metaData[4]);
|
||||
if (m_messageQueueToGUI) {
|
||||
m_messageQueueToGUI->push(MsgReportRemoteDevice::create(device, protocol));
|
||||
}
|
||||
if (!m_settings.m_overrideRemoteSettings)
|
||||
{
|
||||
// Update local settings to match remote
|
||||
RemoteTCPInputSettings& settings = m_settings;
|
||||
settings.m_centerFrequency = RemoteTCPProtocol::extractUInt64(&metaData[8]);
|
||||
settings.m_loPpmCorrection = RemoteTCPProtocol::extractUInt32(&metaData[16]);
|
||||
quint32 flags = RemoteTCPProtocol::extractUInt32(&metaData[20]);
|
||||
settings.m_biasTee = flags & 1;
|
||||
settings.m_directSampling = (flags >> 1) & 1;
|
||||
settings.m_agc = (flags >> 2) & 1;
|
||||
settings.m_dcBlock = (flags >> 3) & 1;
|
||||
settings.m_iqCorrection = (flags >> 4) & 1;
|
||||
settings.m_devSampleRate = RemoteTCPProtocol::extractUInt32(&metaData[24]);
|
||||
settings.m_log2Decim = RemoteTCPProtocol::extractUInt32(&metaData[28]);
|
||||
settings.m_gain[0] = RemoteTCPProtocol::extractInt16(&metaData[32]);
|
||||
settings.m_gain[1] = RemoteTCPProtocol::extractInt16(&metaData[34]);
|
||||
settings.m_gain[2] = RemoteTCPProtocol::extractInt16(&metaData[36]);
|
||||
settings.m_rfBW = RemoteTCPProtocol::extractUInt32(&metaData[40]);
|
||||
settings.m_inputFrequencyOffset = RemoteTCPProtocol::extractUInt32(&metaData[44]);
|
||||
settings.m_channelGain = RemoteTCPProtocol::extractUInt32(&metaData[48]);
|
||||
settings.m_channelSampleRate = RemoteTCPProtocol::extractUInt32(&metaData[52]);
|
||||
settings.m_sampleBits = RemoteTCPProtocol::extractUInt32(&metaData[56]);
|
||||
if (settings.m_channelSampleRate != (settings.m_devSampleRate >> settings.m_log2Decim)) {
|
||||
settings.m_channelDecimation = true;
|
||||
}
|
||||
if (m_messageQueueToInput) {
|
||||
m_messageQueueToInput->push(RemoteTCPInput::MsgConfigureRemoteTCPInput::create(settings));
|
||||
}
|
||||
if (m_messageQueueToGUI) {
|
||||
m_messageQueueToGUI->push(RemoteTCPInput::MsgConfigureRemoteTCPInput::create(settings));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "RemoteTCPInputTCPHandler::dataReadyRead: Unknown protocol: " << protocol;
|
||||
}
|
||||
}
|
||||
m_readMetaData = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// QTimer::timeout isn't guarenteed to be called on every timeout, so we need to look at the system clock
|
||||
void RemoteTCPInputTCPHandler::processData()
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
if (m_dataSocket && (m_dataSocket->state() == QAbstractSocket::ConnectedState))
|
||||
{
|
||||
int sampleRate = m_settings.m_channelSampleRate;
|
||||
int bytesPerSample = m_settings.m_sampleBits / 8;
|
||||
int bytesPerSecond = sampleRate * 2 * bytesPerSample;
|
||||
|
||||
if (m_dataSocket->bytesAvailable() < (0.1f * m_settings.m_preFill * bytesPerSecond))
|
||||
{
|
||||
qDebug() << "RemoteTCPInputTCPHandler::processData: Buffering!";
|
||||
m_fillBuffer = true;
|
||||
}
|
||||
|
||||
// Report buffer usage
|
||||
// QTcpSockets buffer size should be unlimited - we pretend here it's twice as big as the point we start reading from it
|
||||
if (m_messageQueueToGUI)
|
||||
{
|
||||
qint64 size = std::max(m_dataSocket->bytesAvailable(), (qint64)(m_settings.m_preFill * bytesPerSecond));
|
||||
RemoteTCPInput::MsgReportTCPBuffer *report = RemoteTCPInput::MsgReportTCPBuffer::create(
|
||||
m_dataSocket->bytesAvailable(), size, m_dataSocket->bytesAvailable() / (float)bytesPerSecond,
|
||||
m_sampleFifo->fill(), m_sampleFifo->size(), m_sampleFifo->fill() / (float)bytesPerSecond
|
||||
);
|
||||
m_messageQueueToGUI->push(report);
|
||||
}
|
||||
|
||||
float factor = 0.0f;
|
||||
// Prime buffer, before we start reading
|
||||
if (m_fillBuffer)
|
||||
{
|
||||
if (m_dataSocket->bytesAvailable() >= m_settings.m_preFill * bytesPerSecond)
|
||||
{
|
||||
qDebug() << "Buffer primed bytesAvailable:" << m_dataSocket->bytesAvailable();
|
||||
m_fillBuffer = false;
|
||||
m_prevDateTime = QDateTime::currentDateTime();
|
||||
factor = 6.0f / 8.0f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QDateTime currentDateTime = QDateTime::currentDateTime();
|
||||
factor = m_prevDateTime.msecsTo(currentDateTime) / 1000.0f;
|
||||
m_prevDateTime = currentDateTime;
|
||||
}
|
||||
|
||||
unsigned int remaining = m_sampleFifo->size() - m_sampleFifo->fill();
|
||||
int requiredSamples = (int)std::min((unsigned int)(factor * sampleRate), remaining);
|
||||
|
||||
if (!m_fillBuffer)
|
||||
{
|
||||
if (m_dataSocket->bytesAvailable() >= requiredSamples*2*bytesPerSample)
|
||||
{
|
||||
m_dataSocket->read(&m_tcpBuf[0], requiredSamples*2*bytesPerSample);
|
||||
convert(requiredSamples);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The following code assumes host is little endian
|
||||
void RemoteTCPInputTCPHandler::convert(int nbSamples)
|
||||
{
|
||||
if (nbSamples > (int) m_converterBufferNbSamples)
|
||||
{
|
||||
if (m_converterBuffer) {
|
||||
delete[] m_converterBuffer;
|
||||
}
|
||||
m_converterBuffer = new int32_t[nbSamples*2];
|
||||
}
|
||||
|
||||
if ((m_settings.m_sampleBits == 32) && (SDR_RX_SAMP_SZ == 24))
|
||||
{
|
||||
m_sampleFifo->write(reinterpret_cast<quint8*>(m_tcpBuf), nbSamples*sizeof(Sample));
|
||||
}
|
||||
else if ((m_settings.m_sampleBits == 8) && (SDR_RX_SAMP_SZ == 16))
|
||||
{
|
||||
qint8 *in = (qint8 *)m_tcpBuf;
|
||||
qint16 *out = (qint16 *)m_converterBuffer;
|
||||
|
||||
for (int is = 0; is < nbSamples*2; is++) {
|
||||
out[is] = (((qint16)in[is]) - 128);
|
||||
}
|
||||
|
||||
m_sampleFifo->write(reinterpret_cast<quint8*>(out), nbSamples*sizeof(Sample));
|
||||
}
|
||||
else if ((m_settings.m_sampleBits == 8) && (SDR_RX_SAMP_SZ == 24))
|
||||
{
|
||||
qint8 *in = (qint8 *)m_tcpBuf;
|
||||
qint32 *out = (qint32 *)m_converterBuffer;
|
||||
|
||||
for (int is = 0; is < nbSamples*2; is++) {
|
||||
out[is] = (((qint32)in[is]) - 128) << 8; // Only shift by 8, rather than 16, to match levels of native driver
|
||||
}
|
||||
|
||||
m_sampleFifo->write(reinterpret_cast<quint8*>(out), nbSamples*sizeof(Sample));
|
||||
}
|
||||
else if ((m_settings.m_sampleBits == 24) && (SDR_RX_SAMP_SZ == 24))
|
||||
{
|
||||
quint8 *in = (quint8 *)m_tcpBuf;
|
||||
qint32 *out = (qint32 *)m_converterBuffer;
|
||||
|
||||
for (int is = 0; is < nbSamples*2; is++) {
|
||||
out[is] = (((in[3*is+2] << 16) | (in[3*is+1] << 8) | in[3*is]) << 8) >> 8;
|
||||
}
|
||||
|
||||
m_sampleFifo->write(reinterpret_cast<quint8*>(out), nbSamples*sizeof(Sample));
|
||||
}
|
||||
else if ((m_settings.m_sampleBits == 24) && (SDR_RX_SAMP_SZ == 16))
|
||||
{
|
||||
quint8 *in = (quint8 *)m_tcpBuf;
|
||||
qint16 *out = (qint16 *)m_converterBuffer;
|
||||
|
||||
for (int is = 0; is < nbSamples*2; is++) {
|
||||
out[is] = (in[3*is+2] << 8) | in[3*is+1];
|
||||
}
|
||||
|
||||
m_sampleFifo->write(reinterpret_cast<quint8*>(out), nbSamples*sizeof(Sample));
|
||||
}
|
||||
else if ((m_settings.m_sampleBits == 16) && (SDR_RX_SAMP_SZ == 24))
|
||||
{
|
||||
qint16 *in = (qint16 *)m_tcpBuf;
|
||||
qint32 *out = (qint32 *)m_converterBuffer;
|
||||
|
||||
for (int is = 0; is < nbSamples*2; is++) {
|
||||
out[is] = in[is] << 8;
|
||||
}
|
||||
|
||||
m_sampleFifo->write(reinterpret_cast<quint8*>(out), nbSamples*sizeof(Sample));
|
||||
}
|
||||
else if ((m_settings.m_sampleBits == 32) && (SDR_RX_SAMP_SZ == 16))
|
||||
{
|
||||
qint32 *in = (qint32 *)m_tcpBuf;
|
||||
qint16 *out = (qint16 *)m_converterBuffer;
|
||||
|
||||
for (int is = 0; is < nbSamples*2; is++) {
|
||||
out[is] = in[is] >> 8;
|
||||
}
|
||||
|
||||
m_sampleFifo->write(reinterpret_cast<quint8*>(out), nbSamples*sizeof(Sample));
|
||||
}
|
||||
else // invalid size
|
||||
{
|
||||
qWarning("RemoteTCPInputTCPHandler::convert: unexpected sample size in stream: %d bits", (int) m_settings.m_sampleBits);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputTCPHandler::handleInputMessages()
|
||||
{
|
||||
Message* message;
|
||||
|
||||
while ((message = m_inputMessageQueue.pop()) != 0)
|
||||
{
|
||||
if (handleMessage(*message)) {
|
||||
delete message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RemoteTCPInputTCPHandler::handleMessage(const Message& cmd)
|
||||
{
|
||||
if (MsgConfigureTcpHandler::match(cmd))
|
||||
{
|
||||
qDebug() << "RemoteTCPInputTCPHandler::handleMessage: MsgConfigureTcpHandler";
|
||||
MsgConfigureTcpHandler& notif = (MsgConfigureTcpHandler&) cmd;
|
||||
applySettings(notif.getSettings(), notif.getForce());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
|
||||
// Copyright (C) 2022 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef PLUGINS_SAMPLESOURCE_REMOTETCPINPUT_REMOTETCPINPUTUDPHANDLER_H_
|
||||
#define PLUGINS_SAMPLESOURCE_REMOTETCPINPUT_REMOTETCPINPUTUDPHANDLER_H_
|
||||
|
||||
#include <QObject>
|
||||
#include <QTcpSocket>
|
||||
#include <QHostAddress>
|
||||
#include <QMutex>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "util/messagequeue.h"
|
||||
#include "remotetcpinputsettings.h"
|
||||
#include "../../channelrx/remotetcpsink/remotetcpprotocol.h"
|
||||
|
||||
class SampleSinkFifo;
|
||||
class MessageQueue;
|
||||
class DeviceAPI;
|
||||
|
||||
class RemoteTCPInputTCPHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
class MsgConfigureTcpHandler : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const RemoteTCPInputSettings& getSettings() const { return m_settings; }
|
||||
bool getForce() const { return m_force; }
|
||||
|
||||
static MsgConfigureTcpHandler* create(const RemoteTCPInputSettings& settings, bool force)
|
||||
{
|
||||
return new MsgConfigureTcpHandler(settings, force);
|
||||
}
|
||||
|
||||
private:
|
||||
RemoteTCPInputSettings m_settings;
|
||||
bool m_force;
|
||||
|
||||
MsgConfigureTcpHandler(const RemoteTCPInputSettings& settings, bool force) :
|
||||
Message(),
|
||||
m_settings(settings),
|
||||
m_force(force)
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgReportRemoteDevice : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
RemoteTCPProtocol::Device getDevice() const { return m_device; }
|
||||
QString getProtocol() const { return m_protocol; }
|
||||
|
||||
static MsgReportRemoteDevice* create(RemoteTCPProtocol::Device device, const QString& protocol)
|
||||
{
|
||||
return new MsgReportRemoteDevice(device, protocol);
|
||||
}
|
||||
|
||||
protected:
|
||||
RemoteTCPProtocol::Device m_device;
|
||||
QString m_protocol;
|
||||
|
||||
MsgReportRemoteDevice(RemoteTCPProtocol::Device device, const QString& protocol) :
|
||||
Message(),
|
||||
m_device(device),
|
||||
m_protocol(protocol)
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgReportConnection : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
bool getConnected() const { return m_connected; }
|
||||
|
||||
static MsgReportConnection* create(bool connected)
|
||||
{
|
||||
return new MsgReportConnection(connected);
|
||||
}
|
||||
|
||||
protected:
|
||||
bool m_connected;
|
||||
|
||||
MsgReportConnection(bool connected) :
|
||||
Message(),
|
||||
m_connected(connected)
|
||||
{ }
|
||||
};
|
||||
|
||||
RemoteTCPInputTCPHandler(SampleSinkFifo* sampleFifo, DeviceAPI *deviceAPI);
|
||||
~RemoteTCPInputTCPHandler();
|
||||
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
|
||||
void setMessageQueueToInput(MessageQueue *queue) { m_messageQueueToInput = queue; }
|
||||
void setMessageQueueToGUI(MessageQueue *queue) { m_messageQueueToGUI = queue; }
|
||||
void reset();
|
||||
void start();
|
||||
void stop();
|
||||
int getBufferGauge() const { return 0; }
|
||||
|
||||
public slots:
|
||||
void dataReadyRead();
|
||||
void connected();
|
||||
void disconnected();
|
||||
void errorOccurred(QAbstractSocket::SocketError socketError);
|
||||
|
||||
private:
|
||||
|
||||
DeviceAPI *m_deviceAPI;
|
||||
bool m_running;
|
||||
QTcpSocket *m_dataSocket;
|
||||
char *m_tcpBuf;
|
||||
SampleSinkFifo *m_sampleFifo;
|
||||
MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
|
||||
MessageQueue *m_messageQueueToInput;
|
||||
MessageQueue *m_messageQueueToGUI;
|
||||
bool m_readMetaData;
|
||||
bool m_fillBuffer;
|
||||
QTimer m_timer;
|
||||
QTimer m_reconnectTimer;
|
||||
QDateTime m_prevDateTime;
|
||||
|
||||
int32_t *m_converterBuffer;
|
||||
uint32_t m_converterBufferNbSamples;
|
||||
|
||||
QMutex m_mutex;
|
||||
RemoteTCPInputSettings m_settings;
|
||||
|
||||
void applyTCPLink(const QString& address, quint16 port);
|
||||
bool handleMessage(const Message& message);
|
||||
void convert(int nbSamples);
|
||||
void connectToHost(const QString& address, quint16 port);
|
||||
void disconnectFromHost();
|
||||
void cleanup();
|
||||
void clearBuffer();
|
||||
void setSampleRate(int sampleRate);
|
||||
void setCenterFrequency(quint64 frequency);
|
||||
void setTunerAGC(bool agc);
|
||||
void setTunerGain(int gain);
|
||||
void setFreqCorrection(int correction);
|
||||
void setIFGain(quint16 stage, quint16 gain);
|
||||
void setAGC(bool agc);
|
||||
void setDirectSampling(bool enabled);
|
||||
void setDCOffsetRemoval(bool enabled);
|
||||
void setIQCorrection(bool enabled);
|
||||
void setBiasTee(bool enabled);
|
||||
void setBandwidth(int bandwidth);
|
||||
void setDecimation(int dec);
|
||||
void setChannelSampleRate(int dec);
|
||||
void setChannelFreqOffset(int offset);
|
||||
void setChannelGain(int gain);
|
||||
void setSampleBitDepth(int sampleBits);
|
||||
void applySettings(const RemoteTCPInputSettings& settings, bool force = false);
|
||||
|
||||
private slots:
|
||||
void started();
|
||||
void finished();
|
||||
void handleInputMessages();
|
||||
void processData();
|
||||
void reconnect();
|
||||
};
|
||||
|
||||
#endif /* PLUGINS_SAMPLESOURCE_REMOTETCPINPUT_REMOTETCPINPUTUDPHANDLER_H_ */
|
||||
@@ -0,0 +1,50 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
|
||||
// Copyright (C) 2022 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "SWGDeviceSettings.h"
|
||||
#include "remotetcpinput.h"
|
||||
#include "remotetcpinputwebapiadapter.h"
|
||||
|
||||
RemoteTCPInputWebAPIAdapter::RemoteTCPInputWebAPIAdapter()
|
||||
{}
|
||||
|
||||
RemoteTCPInputWebAPIAdapter::~RemoteTCPInputWebAPIAdapter()
|
||||
{}
|
||||
|
||||
int RemoteTCPInputWebAPIAdapter::webapiSettingsGet(
|
||||
SWGSDRangel::SWGDeviceSettings& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
response.setRemoteTcpInputSettings(new SWGSDRangel::SWGRemoteTCPInputSettings());
|
||||
response.getRemoteTcpInputSettings()->init();
|
||||
RemoteTCPInput::webapiFormatDeviceSettings(response, m_settings);
|
||||
return 200;
|
||||
}
|
||||
|
||||
int RemoteTCPInputWebAPIAdapter::webapiSettingsPutPatch(
|
||||
bool force,
|
||||
const QStringList& deviceSettingsKeys,
|
||||
SWGSDRangel::SWGDeviceSettings& response, // query + response
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) force; // no action
|
||||
(void) errorMessage;
|
||||
RemoteTCPInput::webapiUpdateDeviceSettings(m_settings, deviceSettingsKeys, response);
|
||||
return 200;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
|
||||
// Copyright (C) 2022 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "device/devicewebapiadapter.h"
|
||||
#include "remotetcpinputsettings.h"
|
||||
|
||||
class RemoteTCPInputWebAPIAdapter : public DeviceWebAPIAdapter
|
||||
{
|
||||
public:
|
||||
RemoteTCPInputWebAPIAdapter();
|
||||
virtual ~RemoteTCPInputWebAPIAdapter();
|
||||
virtual QByteArray serialize() { return m_settings.serialize(); }
|
||||
virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); }
|
||||
|
||||
virtual int webapiSettingsGet(
|
||||
SWGSDRangel::SWGDeviceSettings& response,
|
||||
QString& errorMessage);
|
||||
|
||||
virtual int webapiSettingsPutPatch(
|
||||
bool force,
|
||||
const QStringList& deviceSettingsKeys,
|
||||
SWGSDRangel::SWGDeviceSettings& response, // query + response
|
||||
QString& errorMessage);
|
||||
|
||||
private:
|
||||
RemoteTCPInputSettings m_settings;
|
||||
};
|
||||
@@ -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()
|
||||
|
||||
@@ -914,6 +914,9 @@ void SDRPlayV3Input::webapiUpdateDeviceSettings(
|
||||
if (deviceSettingsKeys.contains("iqOrder")) {
|
||||
settings.m_iqOrder = response.getSdrPlayV3Settings()->getIqOrder() != 0;
|
||||
}
|
||||
if (deviceSettingsKeys.contains("biasTee")) {
|
||||
settings.m_biasTee = response.getSdrPlayV3Settings()->getBiasTee() != 0;
|
||||
}
|
||||
if (deviceSettingsKeys.contains("useReverseAPI")) {
|
||||
settings.m_useReverseAPI = response.getSdrPlayV3Settings()->getUseReverseApi() != 0;
|
||||
}
|
||||
@@ -951,6 +954,7 @@ void SDRPlayV3Input::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings&
|
||||
response.getSdrPlayV3Settings()->setTransverterDeltaFrequency(settings.m_transverterDeltaFrequency);
|
||||
response.getSdrPlayV3Settings()->setTransverterMode(settings.m_transverterMode ? 1 : 0);
|
||||
response.getSdrPlayV3Settings()->setIqOrder(settings.m_iqOrder ? 1 : 0);
|
||||
response.getSdrPlayV3Settings()->setBiasTee(settings.m_biasTee ? 1 : 0);
|
||||
|
||||
response.getSdrPlayV3Settings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
|
||||
|
||||
@@ -1090,6 +1094,9 @@ void SDRPlayV3Input::webapiReverseSendSettings(QList<QString>& deviceSettingsKey
|
||||
if (deviceSettingsKeys.contains("iqOrder") || force) {
|
||||
swgSDRPlayV3Settings->setIqOrder(settings.m_iqOrder ? 1 : 0);
|
||||
}
|
||||
if (deviceSettingsKeys.contains("biasTee") || force) {
|
||||
swgSDRPlayV3Settings->setBiasTee(settings.m_biasTee ? 1 : 0);
|
||||
}
|
||||
|
||||
QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings")
|
||||
.arg(settings.m_reverseAPIAddress)
|
||||
|
||||
Reference in New Issue
Block a user