RemoteTCPInput: Add Spy Server support.

This commit is contained in:
srcejon 2024-02-27 14:03:34 +00:00
parent 9098f26679
commit 32c663c983
11 changed files with 776 additions and 214 deletions

View File

@ -14,6 +14,7 @@ set(remotetcpinput_HEADERS
remotetcpinputsettings.h
remotetcpinputwebapiadapter.h
remotetcpinputplugin.h
spyserver.h
)
include_directories(

View File

@ -2,7 +2,8 @@
<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](../../channelrx/remotetcpsink/readme.md) plugin.
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, Spy Server or SDRangel's [Remote TCP Channel Sink](../../channelrx/remotetcpsink/readme.md) plugin.
<h2>Interface</h2>
@ -92,7 +93,7 @@ When unchecked, the channel sample rate can be set to any value.
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.
When the protocol is RTL0, only 8-bits are supported. SDRA and Spy Server protocol supports 8, 16, 24 and 32-bit samples.
<h3>19: Server IP address</h3>
@ -102,7 +103,11 @@ IP address or hostname of the server that is running SDRangel's Remote TCP Sink
TCP port on the server to connect to.
<h3>21: Connection settings</h3>
<h3>21: Protocol</h3>
Selects protocol to use. Set to SDRangel for rtl_tcp, rsp_tcp or SDRangel's own protocol. Alternative, Spy Server can be selected to connect to Spy Servers.
<h3>23: Connection settings</h3>
Determines which settings are used when connecting.
@ -110,31 +115,32 @@ When checked, settings in the RemoteTCPInput GUI are written to the remote devic
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>
<h3>24: 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 gauge</h3>
<h3>25: Input buffer gauge</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 uninterrupted data.
<h3>24: Output buffer gauge</h3>
<h3>26: Output buffer gauge</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>
<h3>27: Device status</h3>
Shows the type of remote device that has been connected to.
<h3>26: Protocol status</h3>
<h3>28: Protocol status</h3>
Shows the protocol being used by the remote server. This will be RTL0 or SDRA.
Shows the protocol being used by the remote server. This will be RTL0, SDRA or Spy Server.
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.
Spy Server supports decimation and gain, but no other settings.

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022-2023 Jon Beniston, M7RCE <jon@beniston.com> //
// Copyright (C) 2022-2024 Jon Beniston, M7RCE <jon@beniston.com> //
// Copyright (C) 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// This program is free software; you can redistribute it and/or modify //
@ -49,7 +49,9 @@ RemoteTCPInputGui::RemoteTCPInputGui(DeviceUISet *deviceUISet, QWidget* parent)
m_forceSettings(true),
m_deviceGains(nullptr),
m_remoteDevice(RemoteTCPProtocol::RTLSDR_R820T),
m_connectionError(false)
m_connectionError(false),
m_spyServerGainRange("Gain", 0, 41, 1, ""),
m_spyServerGains({m_spyServerGainRange}, false, false)
{
m_deviceUISet = deviceUISet;
setAttribute(Qt::WA_DeleteOnClose, true);
@ -223,23 +225,27 @@ bool RemoteTCPInputGui::handleMessage(const Message& message)
device = devices.value(m_remoteDevice);
}
ui->device->setText(QString("Device: %1").arg(device));
ui->protocol->setText(QString("Protocol: %1").arg(report.getProtocol()));
ui->detectedProtocol->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))
bool spyServer = report.getProtocol() == "Spy Server";
if (spyServer) {
m_spyServerGains.m_gains[0].m_max = report.getMaxGain();
}
if ((sdra || spyServer) && (ui->sampleBits->count() < 4))
{
ui->sampleBits->addItem("16");
ui->sampleBits->addItem("24");
ui->sampleBits->addItem("32");
}
else if (!sdra && (ui->sampleBits->count() != 1))
else if (!(sdra || spyServer) && (ui->sampleBits->count() != 1))
{
while (ui->sampleBits->count() > 1) {
ui->sampleBits->removeItem(ui->sampleBits->count() - 1);
}
}
if (sdra && (ui->decim->count() != 7))
if ((sdra || spyServer) && (ui->decim->count() != 7))
{
ui->decim->addItem("2");
ui->decim->addItem("4");
@ -248,19 +254,24 @@ bool RemoteTCPInputGui::handleMessage(const Message& message)
ui->decim->addItem("32");
ui->decim->addItem("64");
}
else if (!sdra && (ui->decim->count() != 1))
else if (!(sdra || spyServer) && (ui->decim->count() != 1))
{
while (ui->decim->count() > 1) {
ui->decim->removeItem(ui->decim->count() - 1);
}
}
if (!sdra) {
if (!sdra)
{
ui->deltaFrequency->setValue(0);
ui->channelGain->setValue(0);
ui->decimation->setChecked(true);
}
ui->deltaFrequencyLabel->setEnabled(sdra);
ui->deltaFrequency->setEnabled(sdra);
ui->deltaUnits->setEnabled(sdra);
ui->channelGainLabel->setEnabled(sdra);
ui->channelGain->setEnabled(sdra);
ui->channelGainText->setEnabled(sdra);
ui->decimation->setEnabled(sdra);
if (sdra) {
ui->centerFrequency->setValueRange(9, 0, 999999999); // Should add transverter control to protocol in the future
@ -271,7 +282,7 @@ bool RemoteTCPInputGui::handleMessage(const Message& message)
// Set sample rate range
if (m_sampleRateRanges.contains(m_remoteDevice))
{
const SampleRateRange *range = m_sampleRateRanges.value(m_remoteDevice);
const SampleRateRange *range = m_sampleRateRanges.value(m_remoteDevice);
ui->devSampleRate->setValueRange(8, range->m_min, range->m_max);
}
else if (m_sampleRateLists.contains(m_remoteDevice))
@ -284,6 +295,18 @@ bool RemoteTCPInputGui::handleMessage(const Message& message)
{
ui->devSampleRate->setValueRange(8, 0, 99999999);
}
ui->devSampleRateLabel->setEnabled(!spyServer);
ui->devSampleRate->setEnabled(!spyServer);
ui->devSampleRateUnits->setEnabled(!spyServer);
ui->agc->setEnabled(!spyServer);
ui->rfBWLabel->setEnabled(!spyServer);
ui->rfBW->setEnabled(!spyServer);
ui->rfBWUnits->setEnabled(!spyServer);
ui->dcOffset->setEnabled(!spyServer);
ui->iqImbalance->setEnabled(!spyServer);
ui->ppm->setEnabled(!spyServer);
ui->ppmLabel->setEnabled(!spyServer);
ui->ppmText->setEnabled(!spyServer);
displayGains();
return true;
@ -368,7 +391,9 @@ void RemoteTCPInputGui::displaySettings()
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->channelSampleRateLabel->setEnabled(m_settings.m_channelDecimation);
ui->channelSampleRateUnit->setEnabled(m_settings.m_channelDecimation);
ui->sampleBits->setCurrentText(QString::number(m_settings.m_sampleBits));
ui->dataPort->setText(tr("%1").arg(m_settings.m_dataPort));
ui->dataAddress->blockSignals(true);
@ -385,6 +410,10 @@ void RemoteTCPInputGui::displaySettings()
ui->preFill->setValue((int)(m_settings.m_preFill * 10.0));
ui->preFillText->setText(QString("%1s").arg(m_settings.m_preFill, 0, 'f', 2));
int idx = ui->protocol->findText(m_settings.m_protocol);
if (idx > 0) {
ui->protocol->setCurrentIndex(idx);
}
displayGains();
blockApplySettings(false);
@ -526,7 +555,11 @@ void RemoteTCPInputGui::displayGains()
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_settings.m_protocol == "Spy Server") {
m_deviceGains = &m_spyServerGains;
} else {
m_deviceGains = m_gains.value(m_remoteDevice);
}
if (m_deviceGains)
{
ui->agc->setVisible(m_deviceGains->m_agc);
@ -746,12 +779,16 @@ void RemoteTCPInputGui::on_decimation_toggled(bool checked)
ui->channelSampleRate->setValue(m_settings.m_channelSampleRate);
}
ui->channelSampleRate->setEnabled(!checked);
ui->channelSampleRateLabel->setEnabled(!checked);
ui->channelSampleRateUnit->setEnabled(!checked);
sendSettings();
}
void RemoteTCPInputGui::on_sampleBits_currentIndexChanged(int index)
{
m_settings.m_sampleBits = 8 * (index + 1);
(void) index;
m_settings.m_sampleBits = ui->sampleBits->currentText().toInt();
m_settingsKeys.append("sampleBits");
sendSettings();
}
@ -808,6 +845,14 @@ void RemoteTCPInputGui::on_preFill_valueChanged(int value)
sendSettings();
}
void RemoteTCPInputGui::on_protocol_currentIndexChanged(int index)
{
m_settings.m_protocol = ui->protocol->currentText();
m_settingsKeys.append("protocol");
sendSettings();
displayGains();
}
void RemoteTCPInputGui::updateHardware()
{
if (m_doApplySettings)
@ -902,4 +947,5 @@ void RemoteTCPInputGui::makeUIConnections()
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);
QObject::connect(ui->protocol, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RemoteTCPInputGui::on_protocol_currentIndexChanged);
}

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022-2023 Jon Beniston, M7RCE <jon@beniston.com> //
// Copyright (C) 2022-2024 Jon Beniston, M7RCE <jon@beniston.com> //
// Copyright (C) 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// This program is free software; you can redistribute it and/or modify //
@ -122,6 +122,9 @@ private:
RemoteTCPProtocol::Device m_remoteDevice; // Remote device reported when connecting
bool m_connectionError;
DeviceGains::GainRange m_spyServerGainRange;
DeviceGains m_spyServerGains;
static const DeviceGains::GainRange m_rtlSDR34kGainRange;
static const DeviceGains m_rtlSDRe4kGains;
static const DeviceGains::GainRange m_rtlSDRR820GainRange;
@ -208,6 +211,7 @@ private slots:
void on_dataPort_editingFinished();
void on_overrideRemoteSettings_toggled(bool checked);
void on_preFill_valueChanged(int value);
void on_protocol_currentIndexChanged(int index);
void updateHardware();
void updateStatus();
void openDeviceSettingsDialog(const QPoint& p);

View File

@ -742,7 +742,7 @@ Use to ensure full dynamic range of 8-bit data is used.</string>
<item>
<layout class="QHBoxLayout" name="rfBWLayout">
<item>
<widget class="QLabel" name="sampleRateLabel_2">
<widget class="QLabel" name="channelSampleRateLabel">
<property name="text">
<string>Ch SR</string>
</property>
@ -777,7 +777,7 @@ Use to ensure full dynamic range of 8-bit data is used.</string>
</widget>
</item>
<item>
<widget class="QLabel" name="sampleRateUnit">
<widget class="QLabel" name="channelSampleRateUnit">
<property name="text">
<string>S/s</string>
</property>
@ -957,6 +957,29 @@ Use to ensure full dynamic range of 8-bit data is used.</string>
</property>
</spacer>
</item>
<item>
<widget class="QComboBox" name="protocol">
<property name="minimumSize">
<size>
<width>75</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Protocol to use. SDRangel (inc. rtl_tcp) or Spy Server</string>
</property>
<item>
<property name="text">
<string>SDRangel</string>
</property>
</item>
<item>
<property name="text">
<string>Spy Server</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="overrideRemoteSettings">
<property name="toolTip">
@ -1163,7 +1186,7 @@ This should typically be empty. If full, your CPU cannot keep up and data will b
</widget>
</item>
<item>
<widget class="QLabel" name="protocol">
<widget class="QLabel" name="detectedProtocol">
<property name="text">
<string/>
</property>
@ -1174,6 +1197,11 @@ This should typically be empty. If full, your CPU cannot keep up and data will b
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ButtonSwitch</class>
<extends>QToolButton</extends>
<header>gui/buttonswitch.h</header>
</customwidget>
<customwidget>
<class>ValueDialZ</class>
<extends>QWidget</extends>
@ -1186,11 +1214,6 @@ This should typically be empty. If full, your CPU cannot keep up and data will b
<header>gui/valuedial.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ButtonSwitch</class>
<extends>QToolButton</extends>
<header>gui/buttonswitch.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>startStop</tabstop>

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022-2023 Jon Beniston, M7RCE <jon@beniston.com> //
// Copyright (C) 2022-2024 Jon Beniston, M7RCE <jon@beniston.com> //
// Copyright (C) 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// This program is free software; you can redistribute it and/or modify //
@ -48,6 +48,7 @@ void RemoteTCPInputSettings::resetToDefaults()
m_dataPort = 1234;
m_overrideRemoteSettings = true;
m_preFill = 1.0f;
m_protocol = "SDRangel";
m_useReverseAPI = false;
m_reverseAPIAddress = "127.0.0.1";
m_reverseAPIPort = 8888;
@ -81,6 +82,7 @@ QByteArray RemoteTCPInputSettings::serialize() const
s.writeU32(22, m_reverseAPIPort);
s.writeU32(23, m_reverseAPIDeviceIndex);
s.writeList(24, m_addressList);
s.writeString(25, m_protocol);
for (int i = 0; i < m_maxGains; i++) {
s.writeS32(30+i, m_gain[i]);
@ -136,6 +138,7 @@ bool RemoteTCPInputSettings::deserialize(const QByteArray& data)
m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval;
d.readList(24, &m_addressList);
d.readString(25, &m_protocol, "SDRangel");
for (int i = 0; i < m_maxGains; i++) {
d.readS32(30+i, &m_gain[i], 0);
@ -224,6 +227,9 @@ void RemoteTCPInputSettings::applySettings(const QStringList& settingsKeys, cons
if (settingsKeys.contains("addressList")) {
m_addressList = settings.m_addressList;
}
if (settingsKeys.contains("protocol")) {
m_protocol = settings.m_protocol;
}
for (int i = 0; i < m_maxGains; i++)
{
@ -309,6 +315,9 @@ QString RemoteTCPInputSettings::getDebugString(const QStringList& settingsKeys,
if (settingsKeys.contains("addressList") || force) {
ostr << " m_addressList: " << m_addressList.join(",").toStdString();
}
if (settingsKeys.contains("protocol") || force) {
ostr << " m_protocol: " << m_protocol.toStdString();
}
for (int i = 0; i < m_maxGains; i++)
{

View File

@ -2,7 +2,7 @@
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
// written by Christian Daniel //
// Copyright (C) 2015-2020, 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// Copyright (C) 2020, 2022-2023 Jon Beniston, M7RCE <jon@beniston.com> //
// Copyright (C) 2020, 2022-2024 Jon Beniston, M7RCE <jon@beniston.com> //
// //
// 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 //
@ -53,6 +53,7 @@ struct RemoteTCPInputSettings
uint16_t m_reverseAPIPort;
uint16_t m_reverseAPIDeviceIndex;
QStringList m_addressList; // List of dataAddresses that have been used in the past
QString m_protocol; // "SDRangel" or "Spy Server"
RemoteTCPInputSettings();
void resetToDefaults();

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022-2023 Jon Beniston, M7RCE <jon@beniston.com> //
// Copyright (C) 2022-2024 Jon Beniston, M7RCE <jon@beniston.com> //
// Copyright (C) 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// Copyright (C) 2022 Jiří Pinkava <jiri.pinkava@rossum.ai> //
// //
@ -167,9 +167,19 @@ void RemoteTCPInputTCPHandler::clearBuffer()
{
if (m_dataSocket)
{
m_dataSocket->flush();
m_dataSocket->readAll();
m_fillBuffer = true;
if (m_spyServer)
{
// Can't just flush buffer, otherwise we'll lose header sync
// Read and throw away any available data
processSpyServerData(m_dataSocket->bytesAvailable(), true);
m_fillBuffer = true;
}
else
{
m_dataSocket->flush();
m_dataSocket->readAll();
m_fillBuffer = true;
}
}
}
@ -389,6 +399,60 @@ void RemoteTCPInputTCPHandler::setSampleBitDepth(int sampleBits)
}
}
void RemoteTCPInputTCPHandler::spyServerConnect()
{
QMutexLocker mutexLocker(&m_mutex);
quint8 request[8+4+9];
SpyServerProtocol::encodeUInt32(&request[0], 0);
SpyServerProtocol::encodeUInt32(&request[4], 4+9);
SpyServerProtocol::encodeUInt32(&request[8], SpyServerProtocol::ProtocolID);
memcpy(&request[8+5], "SDRangel", 9);
if (m_dataSocket) {
m_dataSocket->write((char*)request, sizeof(request));
}
}
void RemoteTCPInputTCPHandler::spyServerSet(int setting, int value)
{
QMutexLocker mutexLocker(&m_mutex);
quint8 request[8+8];
SpyServerProtocol::encodeUInt32(&request[0], 2);
SpyServerProtocol::encodeUInt32(&request[4], 8);
SpyServerProtocol::encodeUInt32(&request[8], setting);
SpyServerProtocol::encodeUInt32(&request[12], value);
if (m_dataSocket) {
m_dataSocket->write((char*)request, sizeof(request));
}
}
void RemoteTCPInputTCPHandler::spyServerSetIQFormat(int sampleBits)
{
quint32 format;
if (sampleBits == 8) {
format = 1;
} else if (sampleBits == 16) {
format = 2;
} else if (sampleBits == 24) {
format = 3;
} else if (sampleBits == 32) {
format = 4; // This is float
} else {
qDebug() << "RemoteTCPInputTCPHandler::spyServerSetIQFormat: Unsupported value" << sampleBits;
format = 1;
}
spyServerSet(SpyServerProtocol::setIQFormat, format);
}
void RemoteTCPInputTCPHandler::spyServerSetStreamIQ()
{
spyServerSetIQFormat(m_settings.m_sampleBits);
spyServerSet(SpyServerProtocol::setStreamingMode, 1); // Stream IQ only
spyServerSet(SpyServerProtocol::setStreamingEnabled, 1); // Enable streaming
}
void RemoteTCPInputTCPHandler::applySettings(const RemoteTCPInputSettings& settings, const QList<QString>& settingsKeys, bool force)
{
qDebug() << "RemoteTCPInputTCPHandler::applySettings: "
@ -396,86 +460,124 @@ void RemoteTCPInputTCPHandler::applySettings(const RemoteTCPInputSettings& setti
<< settings.getDebugString(settingsKeys, force);
QMutexLocker mutexLocker(&m_mutex);
if (settingsKeys.contains("centerFrequency") || force) {
setCenterFrequency(settings.m_centerFrequency);
}
if (settingsKeys.contains("loPpmCorrection") || force) {
setFreqCorrection(settings.m_loPpmCorrection);
}
if (settingsKeys.contains("dcBlock") || force) {
if (m_sdra) {
setDCOffsetRemoval(settings.m_dcBlock);
}
}
if (settingsKeys.contains("iqCorrection") || force) {
if (m_sdra) {
setIQCorrection(settings.m_iqCorrection);
}
}
if (settingsKeys.contains("biasTee") || force) {
setBiasTee(settings.m_biasTee);
}
if (settingsKeys.contains("directSampling") || force) {
setDirectSampling(settings.m_directSampling);
}
if (settingsKeys.contains("log2Decim") || force) {
if (m_sdra) {
setDecimation(settings.m_log2Decim);
}
}
if (settingsKeys.contains("devSampleRate") || force) {
setSampleRate(settings.m_devSampleRate);
}
if (settingsKeys.contains("agc") || force) {
setAGC(settings.m_agc);
}
if (force) {
setTunerAGC(1); // The SDRangel RTLSDR driver always has tuner gain as manual
}
if (settingsKeys.contains("gain[0]") || force) {
setTunerGain(settings.m_gain[0]);
}
for (int i = 1; i < 3; i++)
if (m_spyServer)
{
if (settingsKeys.contains(QString("gain[%1]").arg(i)) || force) {
setIFGain(i, settings.m_gain[i]);
if (settingsKeys.contains("centerFrequency") || force) {
spyServerSet(SpyServerProtocol::setCenterFrequency, settings.m_centerFrequency);
}
}
if (settingsKeys.contains("rfBW") || force) {
setBandwidth(settings.m_rfBW);
}
if (settingsKeys.contains("inputFrequencyOffset") || force) {
if (m_sdra) {
setChannelFreqOffset(settings.m_inputFrequencyOffset);
}
}
if (settingsKeys.contains("channelGain") || force) {
if (m_sdra) {
setChannelGain(settings.m_channelGain);
}
}
if ((settings.m_channelSampleRate != m_settings.m_channelSampleRate) || force)
{
// Resize FIFO to give us 1 second
if ((settingsKeys.contains("channelSampleRate") || force) && (settings.m_channelSampleRate > (qint32)m_sampleFifo->size()))
if ((settings.m_channelSampleRate != m_settings.m_channelSampleRate) || force)
{
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
// Resize FIFO to give us 1 second
if ((settingsKeys.contains("channelSampleRate") || force) && (settings.m_channelSampleRate > (qint32)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
}
// Protocol only seems to allow changing decimation
//spyServerSet(SpyServerProtocol::???, settings.m_channelSampleRate);
clearBuffer();
}
if (m_sdra) {
setChannelSampleRate(settings.m_channelSampleRate);
if (settingsKeys.contains("sampleBits") || force)
{
spyServerSetIQFormat(settings.m_sampleBits);
clearBuffer();
}
if (settingsKeys.contains("log2Decim") || force)
{
spyServerSet(SpyServerProtocol::setIQDecimation, settings.m_log2Decim);
clearBuffer();
}
if (settingsKeys.contains("gain[0]") || force)
{
spyServerSet(SpyServerProtocol::setGain, settings.m_gain[0] / 10); // Convert 10ths dB to index
}
clearBuffer();
}
if (settingsKeys.contains("sampleBits") || force)
else
{
if (m_sdra) {
setSampleBitDepth(settings.m_sampleBits);
if (settingsKeys.contains("centerFrequency") || force) {
setCenterFrequency(settings.m_centerFrequency);
}
if (settingsKeys.contains("loPpmCorrection") || force) {
setFreqCorrection(settings.m_loPpmCorrection);
}
if (settingsKeys.contains("dcBlock") || force) {
if (m_sdra) {
setDCOffsetRemoval(settings.m_dcBlock);
}
}
if (settingsKeys.contains("iqCorrection") || force) {
if (m_sdra) {
setIQCorrection(settings.m_iqCorrection);
}
}
if (settingsKeys.contains("biasTee") || force) {
setBiasTee(settings.m_biasTee);
}
if (settingsKeys.contains("directSampling") || force) {
setDirectSampling(settings.m_directSampling);
}
if (settingsKeys.contains("log2Decim") || force) {
if (m_sdra) {
setDecimation(settings.m_log2Decim);
}
}
if (settingsKeys.contains("devSampleRate") || force) {
setSampleRate(settings.m_devSampleRate);
}
if (settingsKeys.contains("agc") || force) {
setAGC(settings.m_agc);
}
if (force) {
setTunerAGC(1); // The SDRangel RTLSDR driver always has tuner gain as manual
}
if (settingsKeys.contains("gain[0]") || force) {
setTunerGain(settings.m_gain[0]);
}
for (int i = 1; i < 3; i++)
{
if (settingsKeys.contains(QString("gain[%1]").arg(i)) || force) {
setIFGain(i, settings.m_gain[i]);
}
}
if (settingsKeys.contains("rfBW") || force) {
setBandwidth(settings.m_rfBW);
}
if (settingsKeys.contains("inputFrequencyOffset") || force) {
if (m_sdra) {
setChannelFreqOffset(settings.m_inputFrequencyOffset);
}
}
if (settingsKeys.contains("channelGain") || force) {
if (m_sdra) {
setChannelGain(settings.m_channelGain);
}
}
if ((settings.m_channelSampleRate != m_settings.m_channelSampleRate) || force)
{
// Resize FIFO to give us 1 second
if ((settingsKeys.contains("channelSampleRate") || force) && (settings.m_channelSampleRate > (qint32)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
}
if (m_sdra) {
setChannelSampleRate(settings.m_channelSampleRate);
}
clearBuffer();
}
if (settingsKeys.contains("sampleBits") || force)
{
if (m_sdra) {
setSampleBitDepth(settings.m_sampleBits);
}
clearBuffer();
}
clearBuffer();
}
// Don't use force, as disconnect can cause rtl_tcp to quit
@ -501,6 +603,10 @@ void RemoteTCPInputTCPHandler::connected()
MsgReportConnection *msg = MsgReportConnection::create(true);
m_messageQueueToGUI->push(msg);
}
m_spyServer = m_settings.m_protocol == "Spy Server";
m_state = HEADER;
m_sdra = false;
spyServerConnect();
}
void RemoteTCPInputTCPHandler::reconnect()
@ -542,116 +648,366 @@ void RemoteTCPInputTCPHandler::dataReadyRead()
{
QMutexLocker mutexLocker(&m_mutex);
if (!m_readMetaData)
if (!m_readMetaData && !m_spyServer)
{
quint8 metaData[RemoteTCPProtocol::m_sdraMetaDataSize];
if (m_dataSocket->bytesAvailable() >= (qint64)sizeof(metaData))
processMetaData();
}
else if (!m_readMetaData && m_spyServer)
{
processSpyServerMetaData();
}
}
void RemoteTCPInputTCPHandler::processMetaData()
{
quint8 metaData[RemoteTCPProtocol::m_sdraMetaDataSize];
if (m_dataSocket->bytesAvailable() >= (qint64)sizeof(metaData))
{
qint64 bytesRead = m_dataSocket->read((char *)&metaData[0], 4);
if (bytesRead == 4)
{
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")
{
// 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);
m_sdra = false;
m_spyServer = false;
bytesRead = m_dataSocket->read((char *)&metaData[4], RemoteTCPProtocol::m_rtl0MetaDataSize-4);
if (protocol == "RTL0")
m_device = (RemoteTCPProtocol::Device)RemoteTCPProtocol::extractUInt32(&metaData[4]);
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(MsgReportRemoteDevice::create(m_device, protocol));
}
if (m_settings.m_sampleBits != 8)
{
m_sdra = false;
bytesRead = m_dataSocket->read((char *)&metaData[4], RemoteTCPProtocol::m_rtl0MetaDataSize-4);
RemoteTCPProtocol::Device tuner = (RemoteTCPProtocol::Device)RemoteTCPProtocol::extractUInt32(&metaData[4]);
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(MsgReportRemoteDevice::create(tuner, protocol));
RemoteTCPInputSettings& settings = m_settings;
settings.m_sampleBits = 8;
QList<QString> settingsKeys{"sampleBits"};
if (m_messageQueueToInput) {
m_messageQueueToInput->push(RemoteTCPInput::MsgConfigureRemoteTCPInput::create(settings, settingsKeys));
}
if (m_settings.m_sampleBits != 8)
{
RemoteTCPInputSettings& settings = m_settings;
settings.m_sampleBits = 8;
QList<QString> settingsKeys{"sampleBits"};
if (m_messageQueueToInput) {
m_messageQueueToInput->push(RemoteTCPInput::MsgConfigureRemoteTCPInput::create(settings, settingsKeys));
}
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(RemoteTCPInput::MsgConfigureRemoteTCPInput::create(settings, settingsKeys));
}
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(RemoteTCPInput::MsgConfigureRemoteTCPInput::create(settings, settingsKeys));
}
}
else if (protocol == "SDRA")
{
m_sdra = true;
bytesRead = m_dataSocket->read((char *)&metaData[4], RemoteTCPProtocol::m_sdraMetaDataSize-4);
}
else if (protocol == "SDRA")
{
m_sdra = true;
m_spyServer = false;
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)
m_device = (RemoteTCPProtocol::Device)RemoteTCPProtocol::extractUInt32(&metaData[4]);
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(MsgReportRemoteDevice::create(m_device, protocol));
}
if (!m_settings.m_overrideRemoteSettings)
{
// Update local settings to match remote
RemoteTCPInputSettings& settings = m_settings;
QList<QString> settingsKeys;
settings.m_centerFrequency = RemoteTCPProtocol::extractUInt64(&metaData[8]);
settingsKeys.append("centerFrequency");
settings.m_loPpmCorrection = RemoteTCPProtocol::extractUInt32(&metaData[16]);
settingsKeys.append("loPpmCorrection");
quint32 flags = RemoteTCPProtocol::extractUInt32(&metaData[20]);
settings.m_biasTee = flags & 1;
settingsKeys.append("biasTee");
settings.m_directSampling = (flags >> 1) & 1;
settingsKeys.append("directSampling");
settings.m_agc = (flags >> 2) & 1;
settingsKeys.append("agc");
settings.m_dcBlock = (flags >> 3) & 1;
settingsKeys.append("dcBlock");
settings.m_iqCorrection = (flags >> 4) & 1;
settingsKeys.append("iqCorrection");
settings.m_devSampleRate = RemoteTCPProtocol::extractUInt32(&metaData[24]);
settingsKeys.append("devSampleRate");
settings.m_log2Decim = RemoteTCPProtocol::extractUInt32(&metaData[28]);
settingsKeys.append("log2Decim");
settings.m_gain[0] = RemoteTCPProtocol::extractInt16(&metaData[32]);
settings.m_gain[1] = RemoteTCPProtocol::extractInt16(&metaData[34]);
settings.m_gain[2] = RemoteTCPProtocol::extractInt16(&metaData[36]);
settingsKeys.append("gain[0]");
settingsKeys.append("gain[1]");
settingsKeys.append("gain[2]");
settings.m_rfBW = RemoteTCPProtocol::extractUInt32(&metaData[40]);
settingsKeys.append("rfBW");
settings.m_inputFrequencyOffset = RemoteTCPProtocol::extractUInt32(&metaData[44]);
settingsKeys.append("inputFrequencyOffset");
settings.m_channelGain = RemoteTCPProtocol::extractUInt32(&metaData[48]);
settingsKeys.append("channelGain");
settings.m_channelSampleRate = RemoteTCPProtocol::extractUInt32(&metaData[52]);
settingsKeys.append("channelSampleRate");
settings.m_sampleBits = RemoteTCPProtocol::extractUInt32(&metaData[56]);
settingsKeys.append("sampleBits");
if (settings.m_channelSampleRate != (settings.m_devSampleRate >> settings.m_log2Decim))
{
// Update local settings to match remote
RemoteTCPInputSettings& settings = m_settings;
QList<QString> settingsKeys;
settings.m_centerFrequency = RemoteTCPProtocol::extractUInt64(&metaData[8]);
settingsKeys.append("centerFrequency");
settings.m_loPpmCorrection = RemoteTCPProtocol::extractUInt32(&metaData[16]);
settingsKeys.append("loPpmCorrection");
quint32 flags = RemoteTCPProtocol::extractUInt32(&metaData[20]);
settings.m_biasTee = flags & 1;
settingsKeys.append("biasTee");
settings.m_directSampling = (flags >> 1) & 1;
settingsKeys.append("directSampling");
settings.m_agc = (flags >> 2) & 1;
settingsKeys.append("agc");
settings.m_dcBlock = (flags >> 3) & 1;
settingsKeys.append("dcBlock");
settings.m_iqCorrection = (flags >> 4) & 1;
settingsKeys.append("iqCorrection");
settings.m_devSampleRate = RemoteTCPProtocol::extractUInt32(&metaData[24]);
settingsKeys.append("devSampleRate");
settings.m_log2Decim = RemoteTCPProtocol::extractUInt32(&metaData[28]);
settingsKeys.append("log2Decim");
settings.m_gain[0] = RemoteTCPProtocol::extractInt16(&metaData[32]);
settings.m_gain[1] = RemoteTCPProtocol::extractInt16(&metaData[34]);
settings.m_gain[2] = RemoteTCPProtocol::extractInt16(&metaData[36]);
settingsKeys.append("gain[0]");
settingsKeys.append("gain[1]");
settingsKeys.append("gain[2]");
settings.m_rfBW = RemoteTCPProtocol::extractUInt32(&metaData[40]);
settingsKeys.append("rfBW");
settings.m_inputFrequencyOffset = RemoteTCPProtocol::extractUInt32(&metaData[44]);
settingsKeys.append("inputFrequencyOffset");
settings.m_channelGain = RemoteTCPProtocol::extractUInt32(&metaData[48]);
settingsKeys.append("channelGain");
settings.m_channelSampleRate = RemoteTCPProtocol::extractUInt32(&metaData[52]);
settingsKeys.append("channelSampleRate");
settings.m_sampleBits = RemoteTCPProtocol::extractUInt32(&metaData[56]);
settingsKeys.append("sampleBits");
if (settings.m_channelSampleRate != (settings.m_devSampleRate >> settings.m_log2Decim))
{
settings.m_channelDecimation = true;
settingsKeys.append("channelDecimation");
}
if (m_messageQueueToInput) {
m_messageQueueToInput->push(RemoteTCPInput::MsgConfigureRemoteTCPInput::create(settings, settingsKeys));
}
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(RemoteTCPInput::MsgConfigureRemoteTCPInput::create(settings, settingsKeys));
}
settings.m_channelDecimation = true;
settingsKeys.append("channelDecimation");
}
if (m_messageQueueToInput) {
m_messageQueueToInput->push(RemoteTCPInput::MsgConfigureRemoteTCPInput::create(settings, settingsKeys));
}
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(RemoteTCPInput::MsgConfigureRemoteTCPInput::create(settings, settingsKeys));
}
}
}
else
{
qDebug() << "RemoteTCPInputTCPHandler::dataReadyRead: Unknown protocol: " << protocol;
}
if (m_settings.m_overrideRemoteSettings)
{
// Force settings to be sent to remote device (this needs to be after m_sdra is determined above)
applySettings(m_settings, QList<QString>(), true);
}
}
m_readMetaData = true;
}
}
void RemoteTCPInputTCPHandler::processSpyServerMetaData()
{
bool done = false;
while (!done)
{
if (m_state == HEADER)
{
if (m_dataSocket->bytesAvailable() >= (qint64)sizeof(SpyServerProtocol::Header))
{
qint64 bytesRead = m_dataSocket->read((char *)&m_spyServerHeader, sizeof(SpyServerProtocol::Header));
if (bytesRead == sizeof(SpyServerProtocol::Header)) {
m_state = DATA;
} else {
qDebug() << "RemoteTCPInputTCPHandler::processSpyServerMetaData: Failed to read:" << bytesRead << "/" << sizeof(SpyServerProtocol::Header);
}
}
else
{
done = true;
}
}
else if (m_state == DATA)
{
if (m_dataSocket->bytesAvailable() >= m_spyServerHeader.m_size)
{
qint64 bytesRead = m_dataSocket->read(&m_tcpBuf[0], m_spyServerHeader.m_size);
if (bytesRead == m_spyServerHeader.m_size)
{
if (m_spyServerHeader.m_message == SpyServerProtocol::DeviceMessage)
{
processSpyServerDevice((SpyServerProtocol::Device *) &m_tcpBuf[0]);
m_state = HEADER;
}
else if (m_spyServerHeader.m_message == SpyServerProtocol::StateMessage)
{
// This call can result in applySettings() calling clearBuffer() then processSpyServerData()
processSpyServerState((SpyServerProtocol::State *) &m_tcpBuf[0], true);
spyServerSetStreamIQ();
m_state = HEADER;
m_readMetaData = true;
done = true;
}
else
{
qDebug() << "RemoteTCPInputTCPHandler::processSpyServerMetaData: Unexpected message type" << m_spyServerHeader.m_message;
m_state = HEADER;
}
}
else
{
qDebug() << "RemoteTCPInputTCPHandler::dataReadyRead: Unknown protocol: " << protocol;
}
if (m_settings.m_overrideRemoteSettings)
{
// Force settings to be sent to remote device (this needs to be after m_sdra is determined above)
applySettings(m_settings, QList<QString>(), true);
qDebug() << "RemoteTCPInputTCPHandler::processSpyServerMetaData: Failed to read:" << bytesRead << "/" << m_spyServerHeader.m_size;
}
}
m_readMetaData = true;
else
{
done = true;
}
}
}
}
void RemoteTCPInputTCPHandler::processSpyServerDevice(const SpyServerProtocol::Device* ssDevice)
{
qDebug() << "RemoteTCPInputTCPHandler::processSpyServerDevice:"
<< "device:" << ssDevice->m_device
<< "serial:" << ssDevice->m_serial
<< "sampleRate:" << ssDevice->m_sampleRate
<< "decimationStages:" << ssDevice->m_decimationStages
<< "maxGainIndex:" << ssDevice->m_maxGainIndex
<< "minFrequency:" << ssDevice->m_minFrequency
<< "maxFrequency:" << ssDevice->m_maxFrequency
<< "sampleBits:" << ssDevice->m_sampleBits
<< "minDecimation:" << ssDevice->m_minDecimation;
switch (ssDevice->m_device)
{
case 1:
m_device = RemoteTCPProtocol::AIRSPY;
break;
case 2:
m_device = RemoteTCPProtocol::AIRSPY_HF;
break;
case 3:
m_device = ssDevice->m_maxGainIndex == 14
? RemoteTCPProtocol::RTLSDR_E4000
: RemoteTCPProtocol::RTLSDR_R820T;
break;
default:
m_device = RemoteTCPProtocol::UNKNOWN;
break;
}
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(MsgReportRemoteDevice::create(m_device, "Spy Server", ssDevice->m_maxGainIndex));
}
RemoteTCPInputSettings& settings = m_settings;
QList<QString> settingsKeys{};
// We can't change sample rate, so always have to update local setting to match
m_settings.m_devSampleRate = settings.m_devSampleRate = ssDevice->m_sampleRate;
settingsKeys.append("devSampleRate");
// Make sure decimation setting is at least the minimum
if (!m_settings.m_overrideRemoteSettings || (settings.m_log2Decim < ssDevice->m_minDecimation))
{
m_settings.m_log2Decim = settings.m_log2Decim = ssDevice->m_minDecimation;
settingsKeys.append("log2Decim");
}
if (m_messageQueueToInput) {
m_messageQueueToInput->push(RemoteTCPInput::MsgConfigureRemoteTCPInput::create(settings, settingsKeys));
}
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(RemoteTCPInput::MsgConfigureRemoteTCPInput::create(settings, settingsKeys));
}
}
void RemoteTCPInputTCPHandler::processSpyServerState(const SpyServerProtocol::State* ssState, bool initial)
{
qDebug() << "RemoteTCPInputTCPHandler::processSpyServerState: "
<< "initial:" << initial
<< "controllable:" << ssState->m_controllable
<< "gain:" << ssState->m_gain
<< "deviceCenterFrequency:" << ssState->m_deviceCenterFrequency
<< "iqCenterFrequency:" << ssState->m_iqCenterFrequency;
if (initial && ssState->m_controllable && m_settings.m_overrideRemoteSettings)
{
// Force client settings to be sent to server
applySettings(m_settings, QList<QString>(), true);
}
else
{
// Update client settings with that from server
RemoteTCPInputSettings& settings = m_settings;
QList<QString> settingsKeys;
if (m_settings.m_centerFrequency != ssState->m_iqCenterFrequency)
{
settings.m_centerFrequency = ssState->m_iqCenterFrequency;
settingsKeys.append("centerFrequency");
}
if (m_settings.m_gain[0] != ssState->m_gain)
{
settings.m_gain[0] = ssState->m_gain;
settingsKeys.append("gain[0]");
}
if (settingsKeys.size() > 0)
{
if (m_messageQueueToInput) {
m_messageQueueToInput->push(RemoteTCPInput::MsgConfigureRemoteTCPInput::create(settings, settingsKeys));
}
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(RemoteTCPInput::MsgConfigureRemoteTCPInput::create(settings, settingsKeys));
}
}
}
}
void RemoteTCPInputTCPHandler::processSpyServerData(int requiredBytes, bool clear)
{
if (!m_readMetaData) {
return;
}
bool done = false;
while (!done)
{
if (m_state == HEADER)
{
if (m_dataSocket->bytesAvailable() >= sizeof(SpyServerProtocol::Header))
{
qint64 bytesRead = m_dataSocket->read((char *) &m_spyServerHeader, sizeof(SpyServerProtocol::Header));
if (bytesRead == sizeof(SpyServerProtocol::Header)) {
m_state = DATA;
} else {
qDebug() << "RemoteTCPInputTCPHandler::processSpyServerData: Failed to read:" << bytesRead << "/" << sizeof(SpyServerProtocol::Header);
}
}
else
{
done = true;
}
}
else if (m_state == DATA)
{
int bytes;
if ((m_spyServerHeader.m_message >= SpyServerProtocol::IQ8MMessage) && (m_spyServerHeader.m_message <= SpyServerProtocol::IQ32Message)) {
bytes = std::min(requiredBytes, (int) m_spyServerHeader.m_size);
} else {
bytes = m_spyServerHeader.m_size;
}
if (m_dataSocket->bytesAvailable() >= bytes)
{
qint64 bytesRead = m_dataSocket->read(&m_tcpBuf[0], bytes);
if (bytesRead == bytes)
{
if ((m_spyServerHeader.m_message >= SpyServerProtocol::IQ8MMessage) && (m_spyServerHeader.m_message <= SpyServerProtocol::IQ32Message))
{
if (!clear)
{
const int bytesPerIQPair = 2 * m_settings.m_sampleBits / 8;
convert(bytesRead / bytesPerIQPair);
}
m_spyServerHeader.m_size -= bytesRead;
requiredBytes -= bytesRead;
if (m_spyServerHeader.m_size == 0) {
m_state = HEADER;
}
if (requiredBytes <= 0) {
done = true;
}
}
else if (m_spyServerHeader.m_message == SpyServerProtocol::StateMessage)
{
processSpyServerState((SpyServerProtocol::State *) &m_tcpBuf[0], false);
m_state = HEADER;
}
else
{
qDebug() << "RemoteTCPInputTCPHandler::processSpyServerData: Skipping unsupported message";
m_state = HEADER;
}
}
else
{
qDebug() << "RemoteTCPInputTCPHandler::processSpyServerData: Failed to read:" << bytesRead << "/" << bytes;
}
}
else
{
done = true;
}
}
}
}
@ -663,12 +1019,12 @@ void RemoteTCPInputTCPHandler::processData()
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;
int bytesPerIQPair = 2 * m_settings.m_sampleBits / 8;
int bytesPerSecond = sampleRate * bytesPerIQPair;
if (m_dataSocket->bytesAvailable() < (0.1f * m_settings.m_preFill * bytesPerSecond))
{
qDebug() << "RemoteTCPInputTCPHandler::processData: Buffering!";
qDebug() << "RemoteTCPInputTCPHandler::processData: Buffering - bytesAvailable:" << m_dataSocket->bytesAvailable();
m_fillBuffer = true;
}
@ -690,7 +1046,7 @@ void RemoteTCPInputTCPHandler::processData()
{
if (m_dataSocket->bytesAvailable() >= m_settings.m_preFill * bytesPerSecond)
{
qDebug() << "Buffer primed bytesAvailable:" << m_dataSocket->bytesAvailable();
qDebug() << "RemoteTCPInputTCPHandler::processData: Buffer primed - bytesAvailable:" << m_dataSocket->bytesAvailable();
m_fillBuffer = false;
m_prevDateTime = QDateTime::currentDateTime();
factor = 1.0f / 4.0f; // If this is too high, samples can just be dropped downstream
@ -708,10 +1064,20 @@ void RemoteTCPInputTCPHandler::processData()
if (!m_fillBuffer)
{
if (m_dataSocket->bytesAvailable() >= requiredSamples*2*bytesPerSample)
if (!m_spyServer)
{
m_dataSocket->read(&m_tcpBuf[0], requiredSamples*2*bytesPerSample);
convert(requiredSamples);
// rtl_tcp/SDRA stream is just IQ samples
if (m_dataSocket->bytesAvailable() >= requiredSamples*bytesPerIQPair)
{
m_dataSocket->read(&m_tcpBuf[0], requiredSamples*bytesPerIQPair);
convert(requiredSamples);
}
}
else
{
// SpyServer stream is packetized, into a header and body, with multiple packet types
int requiredBytes = requiredSamples*bytesPerIQPair;
processSpyServerData(requiredBytes, false);
}
}
}

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Jon Beniston, M7RCE <jon@beniston.com> //
// Copyright (C) 2022-2024 Jon Beniston, M7RCE <jon@beniston.com> //
// Copyright (C) 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// Copyright (C) 2022 Jiří Pinkava <jiri.pinkava@rossum.ai> //
// //
@ -29,6 +29,7 @@
#include "util/messagequeue.h"
#include "remotetcpinputsettings.h"
#include "../../channelrx/remotetcpsink/remotetcpprotocol.h"
#include "spyserver.h"
class SampleSinkFifo;
class MessageQueue;
@ -70,20 +71,23 @@ public:
public:
RemoteTCPProtocol::Device getDevice() const { return m_device; }
QString getProtocol() const { return m_protocol; }
int getMaxGain() const { return m_maxGain; }
static MsgReportRemoteDevice* create(RemoteTCPProtocol::Device device, const QString& protocol)
static MsgReportRemoteDevice* create(RemoteTCPProtocol::Device device, const QString& protocol, int maxGain = 0)
{
return new MsgReportRemoteDevice(device, protocol);
return new MsgReportRemoteDevice(device, protocol, maxGain);
}
protected:
RemoteTCPProtocol::Device m_device;
QString m_protocol;
int m_maxGain;
MsgReportRemoteDevice(RemoteTCPProtocol::Device device, const QString& protocol) :
MsgReportRemoteDevice(RemoteTCPProtocol::Device device, const QString& protocol, int maxGain) :
Message(),
m_device(device),
m_protocol(protocol)
m_protocol(protocol),
m_maxGain(maxGain)
{ }
};
@ -139,6 +143,11 @@ private:
QTimer m_reconnectTimer;
QDateTime m_prevDateTime;
bool m_sdra;
bool m_spyServer;
RemoteTCPProtocol::Device m_device;
SpyServerProtocol::Header m_spyServerHeader;
enum {HEADER, DATA} m_state; //!< FSM for reading Spy Server packets
int32_t *m_converterBuffer;
uint32_t m_converterBufferNbSamples;
@ -172,6 +181,15 @@ private:
void setChannelGain(int gain);
void setSampleBitDepth(int sampleBits);
void applySettings(const RemoteTCPInputSettings& settings, const QList<QString>& settingsKeys, bool force = false);
void processMetaData();
void spyServerConnect();
void spyServerSet(int setting, int value);
void spyServerSetIQFormat(int sampleBits);
void spyServerSetStreamIQ();
void processSpyServerMetaData();
void processSpyServerDevice(const SpyServerProtocol::Device* ssDevice);
void processSpyServerState(const SpyServerProtocol::State* ssState, bool initial);
void processSpyServerData(int requiredBytes, bool clear);
private slots:
void started();

View File

@ -0,0 +1,85 @@
#ifndef SPY_SERVER_H
#define SPY_SERVER_H
#include <QtCore>
class SpyServerProtocol {
public:
static constexpr int ProtocolID = (2<<24) | 1700;
enum Command {
setStreamingMode = 0,
setStreamingEnabled = 1,
setGain = 2,
setIQFormat = 100,
setCenterFrequency = 101,
setIQDecimation = 102,
};
enum Message {
DeviceMessage = 0,
StateMessage = 1,
IQ8MMessage = 100,
IQ16Message = 101,
IQ24Message = 102,
IQ32Message = 103
};
struct Header {
quint32 m_id;
quint32 m_message;
quint32 m_unused1;
quint32 m_unused2;
quint32 m_size;
};
struct Device {
quint32 m_device;
quint32 m_serial;
quint32 m_sampleRate;
quint32 m_unused1;
quint32 m_decimationStages; // 8 for Airspy HF, 11 for Airspy, 9 for E4000/R828D/R820
quint32 m_unused2;
quint32 m_maxGainIndex; // 8 for Airspy HF, 21 for Airspy, 14 for E4000, 29 for R828D/R820
quint32 m_minFrequency;
quint32 m_maxFrequency;
quint32 m_sampleBits;
quint32 m_minDecimation; // Set when maximum_bandwidth is set in spyserver.config
quint32 m_unused3;
};
struct State {
quint32 m_controllable;
quint32 m_gain;
quint32 m_deviceCenterFrequency;
quint32 m_iqCenterFrequency;
quint32 m_unused1;
quint32 m_unused2;
quint32 m_unused3;
quint32 m_unused4;
quint32 m_unused5;
};
static void encodeUInt32(quint8 *p, quint32 data)
{
p[3] = (data >> 24) & 0xff;
p[2] = (data >> 16) & 0xff;
p[1] = (data >> 8) & 0xff;
p[0] = data & 0xff;
}
static quint32 extractUInt32(quint8 *p)
{
quint32 data;
data = (p[0] & 0xff)
| ((p[1] & 0xff) << 8)
| ((p[2] & 0xff) << 16)
| ((p[3] & 0xff) << 24);
return data;
}
};
#endif /* SPY_SERVER_H */

View File

@ -43,6 +43,9 @@ RemoteTCPInputSettings:
type: integer
preFill:
type: integer
protocol:
description: (SDRangel or Spy Server)
type: string
useReverseAPI:
description: Synchronize with reverse API (1 for yes, 0 for no)
type: integer