Files
sdrangel/plugins/samplesource/remotetcpinput/remotetcpinputtcphandler.cpp
T
Robin Getz d4f36753ed remotetcpinput: Avoid using uninitialized converter buffer
Coverity reported that an invalid sample size could leave the
conversion buffer uninitialized before it was passed to calcPower()
and the sample FIFO.

Return early when the sample conversion format is unsupported to
prevent processing invalid data.

Signed-off-by: Robin Getz <rgetz503@gmail.com>
2026-08-02 20:39:50 -04:00

2301 lines
86 KiB
C++

///////////////////////////////////////////////////////////////////////////////////
// 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> //
// //
// 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 <cstring>
#include "device/deviceapi.h"
#include "util/message.h"
#include "maincore.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, ReplayBuffer<FixReal> *replayBuffer) :
m_deviceAPI(deviceAPI),
m_running(false),
m_dataSocket(nullptr),
m_tcpSocket(nullptr),
m_webSocket(nullptr),
m_tcpBuf(nullptr),
m_sampleFifo(sampleFifo),
m_replayBuffer(replayBuffer),
m_messageQueueToInput(nullptr),
m_messageQueueToGUI(nullptr),
m_fillBuffer(true),
m_timer(this),
m_reconnectTimer(this),
m_sdra(false),
m_converterBuffer(nullptr),
m_converterBufferNbSamples(0),
m_settings(),
m_remoteControl(true),
m_iqOnly(false),
m_decoder(nullptr),
m_zOutBuf(m_zBufSize, '\0'),
m_blacklisted(false),
m_magsq(0.0f),
m_magsqSum(0.0f),
m_magsqPeak(0.0f),
m_magsqCount(0)
{
m_sampleFifo->setSize(5000000); // Start with large FIFO, to avoid having to resize
m_tcpBuf = new char[m_sampleFifo->size()*2*4];
m_timer.setInterval(50); // Previously 125, but this results in an obviously slow spectrum refresh rate
connect(&m_reconnectTimer, SIGNAL(timeout()), this, SLOT(reconnect()));
m_reconnectTimer.setSingleShot(true);
// Initialise zlib decompressor
m_zStream.zalloc = nullptr;
m_zStream.zfree = nullptr;
m_zStream.opaque = nullptr;
m_zStream.avail_in = 0;
m_zStream.next_in = nullptr;
if (Z_OK != inflateInit(&m_zStream)) {
qDebug() << "RemoteTCPInputTCPHandler::RemoteTCPInputTCPHandler: inflateInit failed.";
}
}
RemoteTCPInputTCPHandler::~RemoteTCPInputTCPHandler()
{
qDebug() << "RemoteTCPInputTCPHandler::~RemoteTCPInputTCPHandler";
delete[] m_tcpBuf;
if (m_converterBuffer) {
delete[] m_converterBuffer;
}
cleanup();
}
void RemoteTCPInputTCPHandler::reset()
{
QMutexLocker mutexLocker(&m_mutex);
m_inputMessageQueue.clear();
m_blacklisted = false;
}
// 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()));
disconnect(thread(), SIGNAL(started()), this, SLOT(started()));
}
void RemoteTCPInputTCPHandler::finished()
{
qDebug("RemoteTCPInputTCPHandler::finished");
QMutexLocker mutexLocker(&m_mutex);
m_timer.stop();
disconnect(&m_timer, SIGNAL(timeout()), this, SLOT(processData()));
//disconnectFromHost();
cleanup();
disconnect(thread(), SIGNAL(finished()), this, SLOT(finished()));
m_running = false;
}
void RemoteTCPInputTCPHandler::connectToHost(const QString& address, quint16 port, const QString& protocol)
{
qDebug("RemoteTCPInputTCPHandler::connectToHost: connect to %s %s:%d", protocol.toStdString().c_str(), address.toStdString().c_str(), port);
m_fillBuffer = true;
m_readMetaData = false;
if (protocol == "SDRangel wss")
{
#ifndef QT_NO_OPENSSL
m_webSocket = new QWebSocket(QString(), QWebSocketProtocol::VersionLatest, this);
connect(m_webSocket, &QWebSocket::binaryFrameReceived, this, &RemoteTCPInputTCPHandler::dataReadyRead);
connect(m_webSocket, &QWebSocket::connected, this, &RemoteTCPInputTCPHandler::connected);
connect(m_webSocket, &QWebSocket::disconnected, this, &RemoteTCPInputTCPHandler::disconnected);
#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
connect(m_webSocket, &QWebSocket::errorOccurred, this, &RemoteTCPInputTCPHandler::errorOccurred);
#endif
connect(m_webSocket, &QWebSocket::sslErrors, this, &RemoteTCPInputTCPHandler::sslErrors);
m_webSocket->open(QUrl(QString("wss://%1:%2").arg(address).arg(port)));
if (m_webSocket) {
m_dataSocket = new WebSocket(m_webSocket);
}
#else
qWarning() << "RemoteTCPInput unable to use wss protocol as SSL is not supported";
#endif
}
else
{
m_tcpSocket = new QTcpSocket(this);
connect(m_tcpSocket, SIGNAL(readyRead()), this, SLOT(dataReadyRead()));
connect(m_tcpSocket, SIGNAL(connected()), this, SLOT(connected()));
connect(m_tcpSocket, SIGNAL(disconnected()), this, SLOT(disconnected()));
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(m_tcpSocket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &RemoteTCPInputTCPHandler::errorOccurred);
#else
connect(m_tcpSocket, &QAbstractSocket::errorOccurred, this, &RemoteTCPInputTCPHandler::errorOccurred);
#endif
m_tcpSocket->connectToHost(address, port);
if (m_tcpSocket) { // Can be set to nullptr by RemoteTCPInputTCPHandler::errorOccurred while connecting
m_dataSocket = new TCPSocket(m_tcpSocket);
}
}
}
/*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_decoder)
{
FLAC__stream_decoder_delete(m_decoder);
m_decoder = nullptr;
}
#ifndef QT_NO_OPENSSL
if (m_webSocket)
{
qDebug() << "RemoteTCPInputTCPHandler::cleanup: Closing and deleting web socket";
disconnect(m_webSocket, &QWebSocket::binaryFrameReceived, this, &RemoteTCPInputTCPHandler::dataReadyRead);
disconnect(m_webSocket, &QWebSocket::connected, this, &RemoteTCPInputTCPHandler::connected);
disconnect(m_webSocket, &QWebSocket::disconnected, this, &RemoteTCPInputTCPHandler::disconnected);
#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
disconnect(m_webSocket, &QWebSocket::errorOccurred, this, &RemoteTCPInputTCPHandler::errorOccurred);
#endif
}
#endif
if (m_tcpSocket)
{
qDebug() << "RemoteTCPInputTCPHandler::cleanup: Closing and deleting TCP socket";
// Disconnect disconnected, so don't get called recursively
disconnect(m_tcpSocket, SIGNAL(readyRead()), this, SLOT(dataReadyRead()));
disconnect(m_tcpSocket, SIGNAL(connected()), this, SLOT(connected()));
disconnect(m_tcpSocket, SIGNAL(disconnected()), this, SLOT(disconnected()));
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
disconnect(m_tcpSocket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &RemoteTCPInputTCPHandler::errorOccurred);
#else
disconnect(m_tcpSocket, &QAbstractSocket::errorOccurred, this, &RemoteTCPInputTCPHandler::errorOccurred);
#endif
}
if (m_dataSocket)
{
m_dataSocket->close();
m_dataSocket->deleteLater();
m_dataSocket = nullptr;
}
if (m_webSocket)
{
m_webSocket->deleteLater();
m_webSocket = nullptr;
}
if (m_tcpSocket)
{
m_tcpSocket->deleteLater();
m_tcpSocket = 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_readMetaData)
{
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();
if (!m_decoder) { // Can't throw away FLAC header
m_dataSocket->readAll();
m_fillBuffer = true;
}
}
}
}
void RemoteTCPInputTCPHandler::sendCommand(RemoteTCPProtocol::Command cmd, quint32 value)
{
QMutexLocker mutexLocker(&m_mutex);
quint8 request[5];
request[0] = (quint8) cmd;
RemoteTCPProtocol::encodeUInt32(&request[1], value);
if (m_dataSocket)
{
qint64 len = m_dataSocket->write((char*)request, sizeof(request));
if (len != sizeof(request)) {
qDebug() << "RemoteTCPInputTCPHandler::sendCommand: Failed to write all of request:" << len;
}
} else {
qDebug() << "RemoteTCPInputTCPHandler::sendCommand: No socket";
}
}
void RemoteTCPInputTCPHandler::sendCommandFloat(RemoteTCPProtocol::Command cmd, float value)
{
QMutexLocker mutexLocker(&m_mutex);
quint8 request[5];
request[0] = (quint8) cmd;
RemoteTCPProtocol::encodeFloat(&request[1], value);
if (m_dataSocket)
{
qint64 len = m_dataSocket->write((char*)request, sizeof(request));
if (len != sizeof(request)) {
qDebug() << "RemoteTCPInputTCPHandler::sendCommand: Failed to write all of request:" << len;
}
} else {
qDebug() << "RemoteTCPInputTCPHandler::sendCommand: No socket";
}
}
void RemoteTCPInputTCPHandler::setSampleRate(int sampleRate)
{
sendCommand(RemoteTCPProtocol::setSampleRate, sampleRate);
}
void RemoteTCPInputTCPHandler::setCenterFrequency(quint64 frequency)
{
sendCommand(RemoteTCPProtocol::setCenterFrequency, frequency); // FIXME: Can't support >4GHz
}
void RemoteTCPInputTCPHandler::setTunerAGC(bool agc)
{
sendCommand(RemoteTCPProtocol::setTunerGainMode, agc);
}
void RemoteTCPInputTCPHandler::setTunerGain(int gain)
{
sendCommand(RemoteTCPProtocol::setTunerGain, gain);
}
void RemoteTCPInputTCPHandler::setGainByIndex(int index)
{
sendCommand(RemoteTCPProtocol::setGainByIndex, index);
}
void RemoteTCPInputTCPHandler::setFreqCorrection(int correction)
{
sendCommand(RemoteTCPProtocol::setFrequencyCorrection, correction);
}
void RemoteTCPInputTCPHandler::setIFGain(quint16 stage, quint16 gain)
{
sendCommand(RemoteTCPProtocol::setTunerIFGain, (stage << 16) | gain);
}
void RemoteTCPInputTCPHandler::setAGC(bool agc)
{
sendCommand(RemoteTCPProtocol::setAGCMode, agc);
}
void RemoteTCPInputTCPHandler::setDirectSampling(bool enabled)
{
sendCommand(RemoteTCPProtocol::setDirectSampling, enabled ? 3 : 0);
}
void RemoteTCPInputTCPHandler::setDCOffsetRemoval(bool enabled)
{
sendCommand(RemoteTCPProtocol::setDCOffsetRemoval, enabled);
}
void RemoteTCPInputTCPHandler::setIQCorrection(bool enabled)
{
sendCommand(RemoteTCPProtocol::setIQCorrection, enabled);
}
void RemoteTCPInputTCPHandler::setBiasTee(bool enabled)
{
sendCommand(RemoteTCPProtocol::setBiasTee, enabled);
}
void RemoteTCPInputTCPHandler::setBandwidth(int bandwidth)
{
sendCommand(RemoteTCPProtocol::setTunerBandwidth, bandwidth);
}
void RemoteTCPInputTCPHandler::setDecimation(int dec)
{
sendCommand(RemoteTCPProtocol::setDecimation, dec);
}
void RemoteTCPInputTCPHandler::setChannelSampleRate(int sampleRate)
{
sendCommand(RemoteTCPProtocol::setChannelSampleRate, sampleRate);
}
void RemoteTCPInputTCPHandler::setChannelFreqOffset(int offset)
{
sendCommand(RemoteTCPProtocol::setChannelFreqOffset, offset);
}
void RemoteTCPInputTCPHandler::setChannelGain(int gain)
{
sendCommand(RemoteTCPProtocol::setChannelGain, gain);
}
void RemoteTCPInputTCPHandler::setSampleBitDepth(int sampleBits)
{
sendCommand(RemoteTCPProtocol::setSampleBitDepth, sampleBits);
}
void RemoteTCPInputTCPHandler::setSquelchEnabled(bool enabled)
{
sendCommand(RemoteTCPProtocol::setIQSquelchEnabled, (quint32) enabled);
}
void RemoteTCPInputTCPHandler::setSquelch(float squelch)
{
sendCommandFloat(RemoteTCPProtocol::setIQSquelch, squelch);
}
void RemoteTCPInputTCPHandler::setSquelchGate(float squelchGate)
{
sendCommandFloat(RemoteTCPProtocol::setIQSquelchGate, squelchGate);
}
void RemoteTCPInputTCPHandler::sendMessage(const QString& callsign, const QString& text, bool broadcast)
{
QMutexLocker mutexLocker(&m_mutex);
if (m_dataSocket)
{
qint64 len;
char cmd[1+4+1];
QByteArray callsignBytes = callsign.toUtf8();
QByteArray textBytes = text.toUtf8();
QByteArray bytes;
bytes.append(callsignBytes);
bytes.append('\0');
bytes.append(textBytes);
bytes.append('\0');
cmd[0] = (char) RemoteTCPProtocol::sendMessage;
RemoteTCPProtocol::encodeUInt32((quint8*) &cmd[1], bytes.size() + 1);
cmd[5] = (char) broadcast;
len = m_dataSocket->write(&cmd[0], sizeof(cmd));
if (len != sizeof(cmd)) {
qDebug() << "RemoteTCPInputTCPHandler::set: Failed to write all of message header:" << len;
}
len = m_dataSocket->write(bytes.data(), bytes.size());
if (len != bytes.size()) {
qDebug() << "RemoteTCPInputTCPHandler::set: Failed to write all of message:" << len;
}
m_dataSocket->flush();
qDebug() << "sendMessage" << text;
} else {
qDebug() << "RemoteTCPInputTCPHandler::sendMessage: No socket";
}
}
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+4], "SDRangel", 9);
if (m_dataSocket)
{
m_dataSocket->write((char*)request, sizeof(request));
m_dataSocket->flush();
}
}
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));
m_dataSocket->flush();
}
}
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: "
<< "force: " << force
<< settings.getDebugString(settingsKeys, force);
QMutexLocker mutexLocker(&m_mutex);
if (m_spyServer)
{
if (settingsKeys.contains("centerFrequency") || force) {
spyServerSet(SpyServerProtocol::setCenterFrequency, settings.m_centerFrequency);
}
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
}
// Protocol only seems to allow changing decimation
//spyServerSet(SpyServerProtocol::???, settings.m_channelSampleRate);
clearBuffer();
}
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
}
}
else
{
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();
}
if (settingsKeys.contains("squelchEnabled") || force)
{
if (m_sdra) {
setSquelchEnabled(settings.m_squelchEnabled);
}
}
if (settingsKeys.contains("squelch") || force)
{
if (m_sdra) {
setSquelch(settings.m_squelch);
}
}
if (settingsKeys.contains("squelchGate") || force)
{
if (m_sdra) {
setSquelchGate(settings.m_squelchGate);
}
}
}
if (m_dataSocket) {
m_dataSocket->flush(); // Apparently needed for WebAssembly with proxy
}
// Don't use force, as disconnect can cause rtl_tcp to quit
if (settingsKeys.contains("dataAddress")
|| settingsKeys.contains("dataPort")
|| ((m_dataSocket == nullptr) && !m_blacklisted))
{
//disconnectFromHost();
cleanup();
connectToHost(settings.m_dataAddress, settings.m_dataPort, settings.m_protocol);
}
if (force) {
m_settings = settings;
} else {
m_settings.applySettings(settingsKeys, settings);
}
}
static FLAC__StreamDecoderReadStatus flacReadCallback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *clientData)
{
RemoteTCPInputTCPHandler *handler = (RemoteTCPInputTCPHandler *) clientData;
return handler->flacRead(decoder, buffer, bytes);
}
static FLAC__StreamDecoderWriteStatus flacWriteCallback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *clientData)
{
RemoteTCPInputTCPHandler *handler = (RemoteTCPInputTCPHandler *) clientData;
return handler->flacWrite(decoder, frame, buffer);
}
static void flacErrorCallback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *clientData)
{
RemoteTCPInputTCPHandler *handler = (RemoteTCPInputTCPHandler *) clientData;
return handler->flacError(decoder, status);
}
FLAC__StreamDecoderReadStatus RemoteTCPInputTCPHandler::flacRead(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes)
{
(void) decoder;
qsizetype bytesRequested = *bytes;
qsizetype bytesRead = std::min(bytesRequested, (qsizetype) m_compressedData.size());
memcpy(buffer, m_compressedData.constData(), bytesRead);
m_compressedData.remove(0, bytesRead);
if (bytesRead == 0)
{
qDebug() << "RemoteTCPInputTCPHandler::flacRead: Decoder will hang if we can't return data";
abort();
}
*bytes = (size_t) bytesRead;
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
}
FIFO::FIFO(qsizetype elements)
{
m_data.resize(elements);
clear();
}
qsizetype FIFO::write(quint8 *data, qsizetype elements)
{
qsizetype writeCount = std::min(elements, m_data.size() - m_fill);
qsizetype remaining = m_data.size() - m_writePtr;
qsizetype len2 = writeCount - remaining;
//qDebug() << "write" << write << remaining << len2;
if (len2 < 0)
{
std::memcpy(&m_data.data()[m_writePtr], data, writeCount);
m_writePtr += writeCount;
}
else if (len2 == 0)
{
std::memcpy(&m_data.data()[m_writePtr], data, writeCount);
m_writePtr = 0;
}
else
{
std::memcpy(&m_data.data()[m_writePtr], data, remaining);
std::memcpy(&m_data.data()[0], &data[remaining], len2);
m_writePtr = len2;
}
m_fill += writeCount;
return writeCount;
}
qsizetype FIFO::read(quint8 *data, qsizetype elements)
{
qsizetype readCount = std::min(elements, m_fill);
qsizetype remaining = m_data.size() - m_readPtr;
qsizetype len2 = readCount - remaining;
// qDebug() << "read" << read << remaining << len2;
if (len2 < 0)
{
std::memcpy(data, &m_data.data()[m_readPtr], readCount);
m_readPtr += readCount;
}
else if (len2 == 0)
{
std::memcpy(data, &m_data.data()[m_readPtr], readCount);
m_readPtr = 0;
}
else
{
std::memcpy(&data[0], &m_data.data()[m_readPtr], remaining);
std::memcpy(&data[remaining], &m_data.data()[0], len2);
m_readPtr = len2;
}
m_fill -= readCount;
return readCount;
}
qsizetype FIFO::readPtr(quint8 **data, qsizetype elements)
{
*data = (quint8 *) &m_data.data()[m_readPtr];
return std::min(elements, m_data.size() - m_readPtr);
}
void FIFO::read(qsizetype elements)
{
m_readPtr = (m_readPtr + elements) % m_data.size();
m_fill -= elements;
if (m_fill < 0)
{
qDebug() << "FIFO::read: Underrun";
m_fill = 0;
}
}
void FIFO::resize(qsizetype elements)
{
m_data.resize(elements);
m_data.squeeze();
}
void FIFO::clear()
{
m_writePtr = 0;
m_readPtr = 0;
m_fill = 0;
}
void RemoteTCPInputTCPHandler::calcPower(const Sample *iq, int nbSamples)
{
for (int i = 0; i < nbSamples; i++)
{
Real re = iq[i].real();// SDR_RX_SCALED;
Real im = iq[i].imag();// SDR_RX_SCALED;
Real magsq = (re*re + im*im) / (SDR_RX_SCALED*SDR_RX_SCALED);
m_movingAverage(magsq);
m_magsq = m_movingAverage.asDouble();
m_magsqSum += magsq;
m_magsqPeak = std::max<double>(magsq, m_magsqPeak);
m_magsqCount++;
}
}
FLAC__StreamDecoderWriteStatus RemoteTCPInputTCPHandler::flacWrite(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[])
{
(void) decoder;
m_uncompressedFrames++;
int nbSamples = frame->header.blocksize;
if (nbSamples > (int) m_converterBufferNbSamples)
{
if (m_converterBuffer) {
delete[] m_converterBuffer;
}
m_converterBuffer = new int32_t[nbSamples*2];
}
// Convert and interleave samples and output to FIFO
if ((frame->header.bits_per_sample == 8) && (SDR_RX_SAMP_SZ == 24) && (frame->header.channels == 2))
{
qint32 *out = (qint32 *)m_converterBuffer;
const qint32 *inI = buffer[0];
const qint32 *inQ = buffer[1];
for (int i = 0; i < nbSamples; i++)
{
*out++ = *inI++ << 16;
*out++ = *inQ++ << 16;
}
m_uncompressedData.write(reinterpret_cast<quint8*>(m_converterBuffer), nbSamples*sizeof(Sample));
}
else if ((frame->header.bits_per_sample == 16) && (SDR_RX_SAMP_SZ == 24) && (frame->header.channels == 2))
{
qint32 *out = (qint32 *)m_converterBuffer;
const qint32 *inI = buffer[0];
const qint32 *inQ = buffer[1];
for (int i = 0; i < nbSamples; i++)
{
*out++ = *inI++ << 8;
*out++ = *inQ++ << 8;
}
m_uncompressedData.write(reinterpret_cast<quint8*>(m_converterBuffer), nbSamples*sizeof(Sample));
}
else if ((frame->header.bits_per_sample == 24) && (SDR_RX_SAMP_SZ == 24) && (frame->header.channels == 2))
{
qint32 *out = (qint32 *)m_converterBuffer;
const qint32 *inI = buffer[0];
const qint32 *inQ = buffer[1];
for (int i = 0; i < nbSamples; i++)
{
*out++ = *inI++;
*out++ = *inQ++;
}
m_uncompressedData.write(reinterpret_cast<quint8*>(m_converterBuffer), nbSamples*sizeof(Sample));
}
else if ((frame->header.bits_per_sample == 32) && (SDR_RX_SAMP_SZ == 24) && (frame->header.channels == 2))
{
qint32 *out = (qint32 *)m_converterBuffer;
const qint32 *inI = buffer[0];
const qint32 *inQ = buffer[1];
for (int i = 0; i < nbSamples; i++)
{
*out++ = *inI++;
*out++ = *inQ++;
}
m_uncompressedData.write(reinterpret_cast<quint8*>(m_converterBuffer), nbSamples*sizeof(Sample));
}
else if ((frame->header.bits_per_sample == 8) && (SDR_RX_SAMP_SZ == 16) && (frame->header.channels == 2))
{
qint16 *out = (qint16 *)m_converterBuffer;
const qint32 *inI = buffer[0];
const qint32 *inQ = buffer[1];
for (int i = 0; i < nbSamples; i++)
{
*out++ = *inI++ << 8;
*out++ = *inQ++ << 8;
}
m_uncompressedData.write(reinterpret_cast<quint8*>(m_converterBuffer), nbSamples*sizeof(Sample));
}
else if ((frame->header.bits_per_sample == 16) && (SDR_RX_SAMP_SZ == 16) && (frame->header.channels == 2))
{
qint16 *out = (qint16 *)m_converterBuffer;
const qint32 *inI = buffer[0];
const qint32 *inQ = buffer[1];
for (int i = 0; i < nbSamples; i++)
{
*out++ = *inI++;
*out++ = *inQ++;
}
m_uncompressedData.write(reinterpret_cast<quint8*>(m_converterBuffer), nbSamples*sizeof(Sample));
}
else if ((frame->header.bits_per_sample == 24) && (SDR_RX_SAMP_SZ == 16) && (frame->header.channels == 2))
{
qint16 *out = (qint16 *)m_converterBuffer;
const qint32 *inI = buffer[0];
const qint32 *inQ = buffer[1];
for (int i = 0; i < nbSamples; i++)
{
*out++ = *inI++ >> 8;
*out++ = *inQ++ >> 8;
}
m_uncompressedData.write(reinterpret_cast<quint8*>(m_converterBuffer), nbSamples*sizeof(Sample));
}
else if ((frame->header.bits_per_sample == 32) && (SDR_RX_SAMP_SZ == 16) && (frame->header.channels == 2))
{
qint16 *out = (qint16 *)m_converterBuffer;
const qint32 *inI = buffer[0];
const qint32 *inQ = buffer[1];
for (int i = 0; i < nbSamples; i++)
{
*out++ = *inI++ >> 8;
*out++ = *inQ++ >> 8;
}
m_uncompressedData.write(reinterpret_cast<quint8*>(m_converterBuffer), nbSamples*sizeof(Sample));
}
else
{
qDebug() << "RemoteTCPInputTCPHandler::flacWrite: Unsupported format";
}
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
// Convert from zlib uncompressed network format to Samples, to uncompressed data FIFO
void RemoteTCPInputTCPHandler::processDecompressedZlibData(const char *inBuf, int nbSamples)
{
// Ensure conversion buffer is large enough - FIXME: Don't use this buffer - just write in to FIFO
if (nbSamples > (int) m_converterBufferNbSamples)
{
if (m_converterBuffer) {
delete[] m_converterBuffer;
}
m_converterBuffer = new int32_t[nbSamples*2];
}
// Convert from network format to Sample
if ((m_settings.m_sampleBits == 8) && (SDR_RX_SAMP_SZ == 16))
{
const quint8 *in = (const quint8 *) inBuf;
qint16 *out = (qint16 *) m_converterBuffer;
for (int is = 0; is < nbSamples*2; is++) {
out[is] = (((qint16)in[is]) - 128) << 8;
}
}
else if ((m_settings.m_sampleBits == 8) && (SDR_RX_SAMP_SZ == 24))
{
const quint8 *in = (const quint8 *) inBuf;
qint32 *out = (qint32 *) m_converterBuffer;
for (int is = 0; is < nbSamples*2; is++) {
out[is] = (((qint32)in[is]) - 128) << 16;
}
}
else if ((m_settings.m_sampleBits == 16) && (SDR_RX_SAMP_SZ == 16))
{
const qint16 *in = (const qint16 *) inBuf;
qint32 *out = (qint32 *) m_converterBuffer;
for (int is = 0; is < nbSamples*2; is++) {
out[is] = in[is];
}
}
else if ((m_settings.m_sampleBits == 16) && (SDR_RX_SAMP_SZ == 24))
{
const qint16 *in = (const qint16 *) inBuf;
qint32 *out = (qint32 *) m_converterBuffer;
for (int is = 0; is < nbSamples*2; is++) {
out[is] = in[is] << 8;
}
}
else if ((m_settings.m_sampleBits == 24) && (SDR_RX_SAMP_SZ == 24))
{
const quint8 *in = (const quint8 *) inBuf;
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;
}
}
else if ((m_settings.m_sampleBits == 24) && (SDR_RX_SAMP_SZ == 16))
{
const quint8 *in = (const quint8 *) inBuf;
qint16 *out = (qint16 *) m_converterBuffer;
for (int is = 0; is < nbSamples*2; is++) {
out[is] = (in[3*is+2] << 8) | in[3*is+1];
}
}
else if ((m_settings.m_sampleBits == 32) && (SDR_RX_SAMP_SZ == 16))
{
const qint32 *in = (const qint32 *) inBuf;
qint16 *out = (qint16 *) m_converterBuffer;
for (int is = 0; is < nbSamples*2; is++) {
out[is] = in[is] >> 8;
}
}
else if ((m_settings.m_sampleBits == 32) && (SDR_RX_SAMP_SZ == 24))
{
const qint32 *in = (const qint32 *) inBuf;
qint32 *out = (qint32 *) m_converterBuffer;
for (int is = 0; is < nbSamples*2; is++) {
out[is] = in[is];
}
}
else // invalid size
{
qWarning("RemoteTCPInputTCPHandler::convert: unexpected sample size in stream: %d bits", (int) m_settings.m_sampleBits);
}
m_uncompressedData.write(reinterpret_cast<quint8*>(m_converterBuffer), nbSamples*sizeof(Sample));
}
void RemoteTCPInputTCPHandler::flacError(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status)
{
(void) decoder;
qDebug() << "RemoteTCPInputTCPHandler::flacError: Error:" << status;
}
void RemoteTCPInputTCPHandler::connected()
{
QMutexLocker mutexLocker(&m_mutex);
qDebug() << "RemoteTCPInputTCPHandler::connected";
if (m_messageQueueToGUI)
{
MsgReportConnection *msg = MsgReportConnection::create(true);
m_messageQueueToGUI->push(msg);
}
m_spyServer = m_settings.m_protocol == "Spy Server";
m_state = HEADER;
m_sdra = false;
m_remoteControl = true;
m_iqOnly = true;
if (m_spyServer) {
spyServerConnect();
}
// Start calls to processData
m_timer.start();
}
void RemoteTCPInputTCPHandler::reconnect()
{
QMutexLocker mutexLocker(&m_mutex);
if (!m_dataSocket) {
connectToHost(m_settings.m_dataAddress, m_settings.m_dataPort, m_settings.m_protocol);
}
}
void RemoteTCPInputTCPHandler::disconnected()
{
QMutexLocker mutexLocker(&m_mutex);
qDebug() << "RemoteTCPInputTCPHandler::disconnected";
cleanup();
if (m_messageQueueToGUI)
{
MsgReportConnection *msg = MsgReportConnection::create(false);
m_messageQueueToGUI->push(msg);
}
if (!m_blacklisted)
{
// Try to reconnect immediately - it may just be server settings changed
m_reconnectTimer.start(1);
}
else
{
// Stop device so we don't try to reconnect
RemoteTCPInput::MsgStartStop *msg = RemoteTCPInput::MsgStartStop::create(false);
m_messageQueueToInput->push(msg);
}
}
void RemoteTCPInputTCPHandler::errorOccurred(QAbstractSocket::SocketError socketError)
{
QMutexLocker mutexLocker(&m_mutex);
qDebug() << "RemoteTCPInputTCPHandler::errorOccurred: " << socketError;
// For RemoteHostClosedError, disconnected() will be called afterwards, so don't try to reconnect here
// We try to reconnect here, for errors such as ConnectionRefusedError
if (socketError != QAbstractSocket::RemoteHostClosedError)
{
cleanup();
if (m_messageQueueToGUI)
{
MsgReportConnection *msg = MsgReportConnection::create(false);
m_messageQueueToGUI->push(msg);
}
// Try to reconnect
m_reconnectTimer.start(500);
}
}
#ifndef QT_NO_OPENSSL
void RemoteTCPInputTCPHandler::sslErrors(const QList<QSslError> &errors)
{
qDebug() << "RemoteTCPInputTCPHandler::sslErrors: " << errors;
m_webSocket->ignoreSslErrors(); // FIXME: Add a setting whether to do this?
}
#endif
void RemoteTCPInputTCPHandler::dataReadyRead()
{
QMutexLocker mutexLocker(&m_mutex);
if (!m_readMetaData && !m_spyServer) {
processMetaData();
} else if (!m_readMetaData && m_spyServer) {
processSpyServerMetaData();
}
if (m_readMetaData && !m_iqOnly) {
processCommands();
}
}
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)
{
// 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")
{
m_sdra = false;
m_spyServer = false;
bytesRead = m_dataSocket->read((char *)&metaData[4], RemoteTCPProtocol::m_rtl0MetaDataSize-4);
m_device = (RemoteTCPProtocol::Device)RemoteTCPProtocol::extractUInt32(&metaData[4]);
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(MsgReportRemoteDevice::create(m_device, protocol, false, true));
}
if (m_settings.m_sampleBits != 8)
{
RemoteTCPInputSettings& settings = m_settings;
settings.m_sampleBits = 8;
sendSettings(settings, {"sampleBits"});
}
}
else if (protocol == "SDRA")
{
m_sdra = true;
m_spyServer = false;
bytesRead = m_dataSocket->read((char *)&metaData[4], RemoteTCPProtocol::m_sdraMetaDataSize-4);
m_device = (RemoteTCPProtocol::Device)RemoteTCPProtocol::extractUInt32(&metaData[4]);
quint32 protocolRevision = RemoteTCPProtocol::extractUInt32(&metaData[60]);
quint32 flags = RemoteTCPProtocol::extractUInt32(&metaData[20]);
if (protocolRevision >= 1)
{
m_iqOnly = !(bool) ((flags >> 7) & 1);
m_remoteControl = (bool) ((flags >> 6) & 1);
}
else
{
m_iqOnly = true;
m_remoteControl = true;
}
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(MsgReportRemoteDevice::create(m_device, protocol, m_iqOnly, m_remoteControl));
}
if (!m_settings.m_overrideRemoteSettings || !m_remoteControl)
{
// 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");
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 (protocolRevision >= 1)
{
settings.m_squelchEnabled = (flags >> 5) & 1;
settingsKeys.append("squelchEnabled");
settings.m_squelch = RemoteTCPProtocol::extractFloat(&metaData[64]);
settingsKeys.append("squelch");
settings.m_squelchGate = RemoteTCPProtocol::extractFloat(&metaData[68]);
settingsKeys.append("squelchGate");
}
sendSettings(settings, settingsKeys);
}
if (!m_iqOnly)
{
qDebug() << "RemoteTCPInputTCPHandler: Compression enabled";
// Create FLAC decoder for IQ decompression
m_decoder = FLAC__stream_decoder_new();
m_remainingSamples = 0;
m_compressedFrames = 0;
m_uncompressedFrames = 0;
int bytesPerSecond = m_settings.m_channelSampleRate * 2 * sizeof(Sample);
int fifoSize = 2 * m_settings.m_preFill * bytesPerSecond;
m_uncompressedData.resize(fifoSize);
m_uncompressedData.clear();
if (m_decoder)
{
FLAC__StreamDecoderInitStatus initStatus;
initStatus = FLAC__stream_decoder_init_stream(m_decoder, flacReadCallback, nullptr, nullptr, nullptr, nullptr, flacWriteCallback, nullptr, flacErrorCallback, this);
if (initStatus != FLAC__STREAM_DECODER_INIT_STATUS_OK)
{
qDebug() << "RemoteTCPInputTCPHandler: Failed to init FLAC decoder: " << initStatus;
}
}
else
{
qDebug() << "RemoteTCPInputTCPHandler: Failed to allocate FLAC decoder";
}
}
else
{
qDebug() << "RemoteTCPInputTCPHandler: Compression disabled";
}
}
else
{
qDebug() << "RemoteTCPInputTCPHandler::dataReadyRead: Unknown protocol: " << protocol;
m_dataSocket->close();
}
if (m_settings.m_overrideRemoteSettings && m_remoteControl)
{
// 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::processSpyServerMetaData: Failed to read:" << bytesRead << "/" << m_spyServerHeader.m_size;
}
}
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", false, true, 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 < (int) ssDevice->m_minDecimation))
{
m_settings.m_log2Decim = settings.m_log2Decim = ssDevice->m_minDecimation;
settingsKeys.append("log2Decim");
}
sendSettings(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] != (qint32) ssState->m_gain)
{
settings.m_gain[0] = ssState->m_gain;
settingsKeys.append("gain[0]");
}
if (settingsKeys.size() > 0)
{
sendSettings(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() >= (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::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;
processUncompressedData(&m_tcpBuf[0], 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;
}
}
}
}
void RemoteTCPInputTCPHandler::sendSettings(const RemoteTCPInputSettings& settings, const QStringList& settingsKeys)
{
if (m_messageQueueToInput) {
m_messageQueueToInput->push(RemoteTCPInput::MsgConfigureRemoteTCPInput::create(settings, settingsKeys));
}
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(RemoteTCPInput::MsgConfigureRemoteTCPInput::create(settings, settingsKeys));
}
}
void RemoteTCPInputTCPHandler::processCommands()
{
bool done = false;
while (!done)
{
if (m_state == HEADER)
{
if (m_dataSocket->bytesAvailable() >= 5)
{
quint8 buf[5];
qint64 bytesRead = m_dataSocket->read((char *) buf, sizeof(buf));
if (bytesRead == sizeof(buf))
{
m_command = (RemoteTCPProtocol::Command) buf[0];
switch (m_command)
{
case RemoteTCPProtocol::setCenterFrequency:
{
quint32 centerFrequency = (quint32) RemoteTCPProtocol::extractUInt32(&buf[1]);
if (centerFrequency != m_settings.m_centerFrequency)
{
RemoteTCPInputSettings settings = m_settings;
settings.m_centerFrequency = centerFrequency;
sendSettings(settings, {"centerFrequency"});
}
break;
}
case RemoteTCPProtocol::setSampleRate:
{
int devSampleRate = RemoteTCPProtocol::extractInt32(&buf[1]);
if (devSampleRate != m_settings.m_devSampleRate)
{
RemoteTCPInputSettings settings = m_settings;
settings.m_devSampleRate = devSampleRate;
sendSettings(settings, {"devSampleRate"});
}
break;
}
case RemoteTCPProtocol::setTunerGainMode:
{
// Currently fixed as 1
}
case RemoteTCPProtocol::setTunerGain:
{
int gain = RemoteTCPProtocol::extractUInt32(&buf[1]);
if (gain != m_settings.m_gain[0])
{
RemoteTCPInputSettings settings = m_settings;
settings.m_gain[0] = gain;
sendSettings(settings, {"gain[0]"});
}
break;
}
case RemoteTCPProtocol::setFrequencyCorrection:
{
qint32 loPpmCorrection = (qint32) RemoteTCPProtocol::extractUInt32(&buf[1]);
if (loPpmCorrection != m_settings.m_loPpmCorrection)
{
RemoteTCPInputSettings settings = m_settings;
settings.m_loPpmCorrection = loPpmCorrection;
sendSettings(settings, {"loPpmCorrection"});
}
break;
}
case RemoteTCPProtocol::setTunerIFGain:
{
int v = RemoteTCPProtocol::extractUInt32(&buf[1]);
int gain = (int)(qint16)(v & 0xffff);
int stage = (v >> 16) & 0xffff;
if ((stage < RemoteTCPInputSettings::m_maxGains) && (gain != m_settings.m_gain[stage]))
{
RemoteTCPInputSettings settings = m_settings;
settings.m_gain[stage] = gain;
sendSettings(settings, {QString("gain[%1]").arg(stage)});
}
break;
}
case RemoteTCPProtocol::setAGCMode:
{
bool agc = (bool) RemoteTCPProtocol::extractUInt32(&buf[1]);
if (agc != m_settings.m_agc)
{
RemoteTCPInputSettings settings = m_settings;
settings.m_agc = agc;
sendSettings(settings, {"agc"});
}
break;
}
case RemoteTCPProtocol::setDirectSampling:
{
bool directSampling = (bool) RemoteTCPProtocol::extractUInt32(&buf[1]);
if (directSampling != m_settings.m_directSampling)
{
RemoteTCPInputSettings settings = m_settings;
settings.m_directSampling = directSampling;
sendSettings(settings, {"directSampling"});
}
break;
}
case RemoteTCPProtocol::setBiasTee:
{
bool biasTee = (bool) RemoteTCPProtocol::extractUInt32(&buf[1]);
if (biasTee != m_settings.m_biasTee)
{
RemoteTCPInputSettings settings = m_settings;
settings.m_biasTee = biasTee;
sendSettings(settings, {"biasTee"});
}
break;
}
case RemoteTCPProtocol::setTunerBandwidth:
{
int rfBW = RemoteTCPProtocol::extractInt32(&buf[1]);
if (rfBW != m_settings.m_rfBW)
{
RemoteTCPInputSettings settings = m_settings;
settings.m_rfBW = rfBW;
sendSettings(settings, {"rfBW"});
}
break;
}
case RemoteTCPProtocol::setDecimation:
{
int log2Decim = RemoteTCPProtocol::extractInt32(&buf[1]);
if (log2Decim != m_settings.m_log2Decim)
{
RemoteTCPInputSettings settings = m_settings;
settings.m_log2Decim = log2Decim;
sendSettings(settings, {"log2Decim"});
}
break;
}
case RemoteTCPProtocol::setDCOffsetRemoval:
{
bool dcBlock = (bool) RemoteTCPProtocol::extractUInt32(&buf[1]);
if (dcBlock != m_settings.m_dcBlock)
{
RemoteTCPInputSettings settings = m_settings;
settings.m_dcBlock = dcBlock;
sendSettings(settings, {"dcBlock"});
}
break;
}
case RemoteTCPProtocol::setIQCorrection:
{
bool iqCorrection = (bool) RemoteTCPProtocol::extractUInt32(&buf[1]);
if (iqCorrection != m_settings.m_iqCorrection)
{
RemoteTCPInputSettings settings = m_settings;
settings.m_iqCorrection = iqCorrection;
sendSettings(settings, {"iqCorrection"});
}
break;
}
case RemoteTCPProtocol::setChannelSampleRate:
{
qint32 channelSampleRate = RemoteTCPProtocol::extractInt32(&buf[1]);
if (channelSampleRate != m_settings.m_channelSampleRate)
{
RemoteTCPInputSettings settings = m_settings;
settings.m_channelSampleRate = channelSampleRate;
sendSettings(settings, {"channelSampleRate"});
}
break;
}
case RemoteTCPProtocol::setChannelFreqOffset:
{
qint32 inputFrequencyOffset = (qint32) RemoteTCPProtocol::extractUInt32(&buf[1]);
if (inputFrequencyOffset != m_settings.m_inputFrequencyOffset)
{
RemoteTCPInputSettings settings = m_settings;
settings.m_inputFrequencyOffset = inputFrequencyOffset;
sendSettings(settings, {"inputFrequencyOffset"});
}
break;
}
case RemoteTCPProtocol::setChannelGain:
{
qint32 channelGain = RemoteTCPProtocol::extractInt32(&buf[1]);
if (channelGain != m_settings.m_channelGain)
{
RemoteTCPInputSettings settings = m_settings;
settings.m_channelGain = channelGain;
sendSettings(settings, {"channelGain"});
}
break;
}
case RemoteTCPProtocol::setSampleBitDepth:
{
qint32 sampleBits = RemoteTCPProtocol::extractInt32(&buf[1]);
if (sampleBits != m_settings.m_sampleBits)
{
RemoteTCPInputSettings settings = m_settings;
settings.m_sampleBits = sampleBits;
sendSettings(settings, {"sampleBits"});
}
break;
}
case RemoteTCPProtocol::setIQSquelchEnabled:
{
bool squelchEnabled = (bool) RemoteTCPProtocol::extractUInt32(&buf[1]);
if (squelchEnabled != m_settings.m_squelchEnabled)
{
RemoteTCPInputSettings settings = m_settings;
settings.m_squelchEnabled = squelchEnabled;
sendSettings(settings, {"squelchEnabled"});
}
break;
}
case RemoteTCPProtocol::setIQSquelch:
{
float squelch = RemoteTCPProtocol::extractFloat(&buf[1]);
if (squelch != m_settings.m_squelch)
{
RemoteTCPInputSettings settings = m_settings;
settings.m_squelch = squelch;
sendSettings(settings, {"squelch"});
}
break;
}
case RemoteTCPProtocol::setIQSquelchGate:
{
float squelchGate = RemoteTCPProtocol::extractFloat(&buf[1]);
if (squelchGate != m_settings.m_squelchGate)
{
RemoteTCPInputSettings settings = m_settings;
settings.m_squelchGate = squelchGate;
sendSettings(settings, {"squelchGate"});
}
break;
}
default:
m_commandLength = RemoteTCPProtocol::extractUInt32(&buf[1]);
m_state = DATA;
}
}
else
{
qDebug() << "RemoteTCPInputTCPHandler::processCommands: Failed to read:" << bytesRead << "/" << sizeof(buf);
}
}
else
{
done = true;
}
}
if (m_state == DATA)
{
if (m_dataSocket->bytesAvailable() >= m_commandLength)
{
try
{
switch (m_command)
{
case RemoteTCPProtocol::dataIQ:
{
break;
}
case RemoteTCPProtocol::dataIQFLAC:
{
qsizetype s = m_compressedData.size();
m_compressedData.resize(s + m_commandLength);
qint64 bytesRead = m_dataSocket->read(&m_compressedData.data()[s], m_commandLength);
m_compressedFrames++;
if (bytesRead == m_commandLength)
{
// FLAC encoder writes out 4 (fLaC), 38 (STREAMINFO), 51 (?) byte headers, that are transmitted as one command block,
// then each command block will be a complete audio block (first two bytes will be 0xfff8)
// FLAC__stream_decoder_process_single will keep calling the read callback until it's decoded one metadata or audio block
// so we need to make sure there's enough data that it will be able to return
bool decodeDone = false;
while (!decodeDone)
{
//qDebug() << "m_compressedFrames" << m_compressedFrames << "m_uncompressedFrames" << m_uncompressedFrames;
if (m_compressedFrames - 1 > m_uncompressedFrames)
{
if (!FLAC__stream_decoder_process_single(m_decoder))
{
qDebug() << "FLAC decode failed";
decodeDone = true;
}
}
else
{
decodeDone = true;
}
}
}
else
{
qDebug() << "RemoteTCPInputTCPHandler::processCommands: Failed to read:" << bytesRead << "/" << m_commandLength;
}
break;
}
case RemoteTCPProtocol::dataIQzlib:
{
if (m_commandLength > (quint32) m_compressedData.size()) {
m_compressedData.resize(m_commandLength);
}
qint64 bytesRead = m_dataSocket->read(m_compressedData.data(), m_commandLength);
if (bytesRead == m_commandLength)
{
// Decompressing using zlib
m_zStream.next_in = (Bytef *) m_compressedData.data();
m_zStream.avail_in = m_commandLength;
m_zStream.next_out = (Bytef *) m_zOutBuf.data();
m_zStream.avail_out = m_zOutBuf.size();
int ret = inflate(&m_zStream, Z_NO_FLUSH);
if (ret == Z_STREAM_END) {
inflateReset(&m_zStream);
// Convert and write to uncompressed data FIFO
int uncompressedBytes = m_zOutBuf.size() - m_zStream.avail_out;
int nbSamples = uncompressedBytes / 2 / (m_settings.m_sampleBits / 8);
processDecompressedZlibData(m_zOutBuf.data(), nbSamples);
} else if (ret == Z_NEED_DICT) {
qDebug() << "zlib needs dict to inflate";
} else if (ret == Z_DATA_ERROR) {
qDebug() << "zlib data error";
} else if (ret == Z_MEM_ERROR) {
qDebug() << "zlib mem error";
} else {
qDebug() << "Unexpected zlib return value" << ret;
}
}
else
{
qDebug() << "RemoteTCPInputTCPHandler::processCommands: Failed to read:" << bytesRead << "/" << m_commandLength;
}
break;
}
case RemoteTCPProtocol::dataPosition:
{
char pos[4+4+4];
qint64 bytesRead = m_dataSocket->read(pos, m_commandLength);
if (bytesRead == m_commandLength)
{
float latitude = RemoteTCPProtocol::extractFloat((const quint8 *) &pos[0]);
float longitude = RemoteTCPProtocol::extractFloat((const quint8 *) &pos[4]);
float altitude = RemoteTCPProtocol::extractFloat((const quint8 *) &pos[8]);
//qDebug() << "RemoteTCPInputTCPHandler::processCommands: Position " << latitude << longitude << altitude;
if (m_messageQueueToInput) {
m_messageQueueToInput->push(RemoteTCPInput::MsgReportPosition::create(latitude, longitude, altitude));
}
}
else
{
qDebug() << "RemoteTCPInputTCPHandler::processCommands: Failed to read:" << bytesRead << "/" << m_commandLength;
}
break;
}
case RemoteTCPProtocol::dataDirection:
{
char dir[4+4+4];
qint64 bytesRead = m_dataSocket->read(dir, m_commandLength);
if (bytesRead == m_commandLength)
{
float isotropic = RemoteTCPProtocol::extractUInt32((const quint8 *) &dir[0]);
float azimuth = RemoteTCPProtocol::extractFloat((const quint8 *) &dir[4]);
float elevation = RemoteTCPProtocol::extractFloat((const quint8 *) &dir[8]);
//qDebug() << "RemoteTCPInputTCPHandler::processCommands: Direction " << isotropic << azimuth << elevation;
if (m_messageQueueToInput) {
m_messageQueueToInput->push(RemoteTCPInput::MsgReportDirection::create(isotropic, azimuth, elevation));
}
}
else
{
qDebug() << "RemoteTCPInputTCPHandler::processCommands: Failed to read:" << bytesRead << "/" << m_commandLength;
}
break;
}
case RemoteTCPProtocol::sendMessage:
{
char *buf = new char[m_commandLength];
qint64 bytesRead = m_dataSocket->read(buf, m_commandLength);
if (bytesRead == m_commandLength)
{
bool broadcast = (bool) buf[0];
int i;
for (i = 1; i < (int) m_commandLength; i++)
{
if (buf[i] == '\0') {
break;
}
}
QString callsign = QString::fromUtf8(&buf[1]);
QString text = QString::fromUtf8(&buf[i+1]);
qDebug() << "RemoteTCPInputTCPHandler::processCommands: Message " << m_dataSocket->peerAddress() << m_dataSocket->peerPort() << callsign << broadcast << text;
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(RemoteTCPInput::MsgSendMessage::create(callsign, text, broadcast));
}
}
else
{
qDebug() << "RemoteTCPInputTCPHandler::processCommands: Failed to read:" << bytesRead << "/" << m_commandLength;
}
delete[] buf;
break;
}
case RemoteTCPProtocol::sendBlacklistedMessage:
{
qDebug() << "RemoteTCPInputTCPHandler::processCommands: Disconnecting as blacklisted";
if (m_messageQueueToGUI) {
m_messageQueueToGUI->push(RemoteTCPInput::MsgSendMessage::create("", "Disconnecting as IP address is blacklisted", false));
}
m_blacklisted = true;
qDebug() << "set m_blacklisted" << m_blacklisted;
break;
}
default:
{
qDebug() << "RemoteTCPInputTCPHandler::processCommands: Unknown command" << m_command;
char *buf = new char[m_commandLength];
m_dataSocket->read(buf, m_commandLength);
delete[] buf;
break;
}
}
}
catch(std::bad_alloc&)
{
qDebug() << "RemoteTCPInputTCPHandler::processCommands: Failed to allocate memory";
done = true;
}
m_state = HEADER;
}
else
{
done = true;
}
}
}
}
// QTimer::timeout isn't guaranteed 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->isConnected())
{
int sampleRate = m_settings.m_channelSampleRate;
int bytesPerIQPair = m_iqOnly ? (2 * m_settings.m_sampleBits / 8) : (2 * sizeof(Sample));
int bytesPerSecond = sampleRate * bytesPerIQPair;
qint64 bytesAvailable = m_iqOnly ? m_dataSocket->bytesAvailable() : m_uncompressedData.fill();
if ((bytesAvailable < (0.1f * m_settings.m_preFill * bytesPerSecond)) && !m_fillBuffer)
{
qDebug() << "RemoteTCPInputTCPHandler::processData: Buffering - bytesAvailable:" << bytesAvailable;
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(bytesAvailable, (qint64)(m_settings.m_preFill * bytesPerSecond));
RemoteTCPInput::MsgReportTCPBuffer *report = RemoteTCPInput::MsgReportTCPBuffer::create(
bytesAvailable, size, 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 (bytesAvailable >= m_settings.m_preFill * bytesPerSecond)
{
qDebug() << "RemoteTCPInputTCPHandler::processData: Buffer primed - bytesAvailable:" << bytesAvailable;
m_fillBuffer = false;
m_prevDateTime = QDateTime::currentDateTime();
factor = 1.0f / 4.0f; // If this is too high, samples can just be dropped downstream
}
}
else
{
QDateTime currentDateTime = QDateTime::currentDateTime();
factor = m_prevDateTime.msecsTo(currentDateTime) / 1000.0f; // FIXME: Close skew.. Actual sample rate may differ
m_prevDateTime = currentDateTime;
}
unsigned int remaining = m_sampleFifo->size() - m_sampleFifo->fill();
unsigned int maxRequired = (unsigned int) (factor * sampleRate);
int requiredSamples = (int)std::min(maxRequired, remaining);
int overflow = maxRequired - requiredSamples;
if (overflow > 0) {
qDebug() << "Not enough space in FIFO:" << overflow << maxRequired;
}
if (!m_fillBuffer)
{
if (!m_iqOnly)
{
processDecompressedData(requiredSamples);
}
else if (!m_spyServer)
{
if (m_dataSocket->bytesAvailable() >= requiredSamples*bytesPerIQPair)
{
// rtl_tcp stream is just IQ samples
m_dataSocket->read(&m_tcpBuf[0], requiredSamples*bytesPerIQPair);
processUncompressedData(&m_tcpBuf[0], requiredSamples);
}
}
else
{
// SpyServer stream is packetized, into a header and body, with multiple packet types
int requiredBytes = requiredSamples*bytesPerIQPair;
processSpyServerData(requiredBytes, false);
}
}
}
}
// Copy from decompressed FIFO to replay buffer and sample FIFO
void RemoteTCPInputTCPHandler::processDecompressedData(int requiredSamples)
{
qint64 requiredBytes = requiredSamples * sizeof(Sample);
m_replayBuffer->lock();
while ((requiredBytes > 0) && !m_uncompressedData.empty())
{
quint8 *uncompressedPtr;
qsizetype uncompressedBytes = m_uncompressedData.readPtr(&uncompressedPtr, requiredSamples * sizeof(Sample));
qsizetype uncompressedSamples = 2 * uncompressedBytes / sizeof(Sample);
// Save data to replay buffer
bool replayEnabled = m_replayBuffer->getSize() > 0;
if (replayEnabled) {
m_replayBuffer->write((FixReal *) uncompressedPtr, (unsigned int) uncompressedSamples);
}
const FixReal *buf = (FixReal *) uncompressedPtr;
qint32 remaining = uncompressedSamples;
while (remaining > 0)
{
qint32 len;
// Choose between live data or replayed data
if (replayEnabled && m_replayBuffer->useReplay()) {
len = m_replayBuffer->read(remaining, buf);
} else {
len = remaining;
}
remaining -= len;
calcPower(reinterpret_cast<const Sample*>(buf), len / 2);
m_sampleFifo->write(reinterpret_cast<const quint8 *>(buf), len * sizeof(FixReal));
}
m_uncompressedData.read(uncompressedBytes);
requiredBytes -= uncompressedBytes;
}
m_replayBuffer->unlock();
}
// Convert from uncompressed network format to Samples, then copy to replay buffer and sample FIFO
// The following code assumes host is little endian
void RemoteTCPInputTCPHandler::processUncompressedData(const char *inBuf, int nbSamples)
{
// Ensure conversion buffer is large enough
if (nbSamples > (int) m_converterBufferNbSamples)
{
if (m_converterBuffer) {
delete[] m_converterBuffer;
}
m_converterBuffer = new int32_t[nbSamples*2];
}
// Convert from network format to Sample
if ((m_settings.m_sampleBits == 32) && (SDR_RX_SAMP_SZ == 24) && m_spyServer)
{
const float *in = (const float *) inBuf;
qint32 *out = (qint32 *) m_converterBuffer;
for (int is = 0; is < nbSamples*2; is++) {
out[is] = (qint32)(in[is] * SDR_RX_SCALEF);
}
}
else if ((m_settings.m_sampleBits == 32) && (SDR_RX_SAMP_SZ == 16) && m_spyServer)
{
const float *in = (const float *) inBuf;
qint16 *out = (qint16 *) m_converterBuffer;
for (int is = 0; is < nbSamples*2; is++) {
out[is] = (qint16)(in[is] * SDR_RX_SCALEF);
}
}
else if ((m_settings.m_sampleBits == 8) && (SDR_RX_SAMP_SZ == 16))
{
const quint8 *in = (const quint8 *) inBuf;
qint16 *out = (qint16 *) m_converterBuffer;
for (int is = 0; is < nbSamples*2; is++) {
out[is] = (((qint16)in[is]) - 128) << 8;
}
}
else if ((m_settings.m_sampleBits == 8) && (SDR_RX_SAMP_SZ == 24))
{
const quint8 *in = (const quint8 *) inBuf;
qint32 *out = (qint32 *) m_converterBuffer;
for (int is = 0; is < nbSamples*2; is++) {
out[is] = (((qint32)in[is]) - 128) << 16;
}
}
else if ((m_settings.m_sampleBits == 24) && (SDR_RX_SAMP_SZ == 24))
{
const quint8 *in = (const quint8 *) inBuf;
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;
}
}
else if ((m_settings.m_sampleBits == 24) && (SDR_RX_SAMP_SZ == 16))
{
const quint8 *in = (const quint8 *) inBuf;
qint16 *out = (qint16 *) m_converterBuffer;
for (int is = 0; is < nbSamples*2; is++) {
out[is] = (in[3*is+2] << 8) | in[3*is+1];
}
}
else if ((m_settings.m_sampleBits == 16) && (SDR_RX_SAMP_SZ == 24))
{
const qint16 *in = (const qint16 *) inBuf;
qint32 *out = (qint32 *) m_converterBuffer;
for (int is = 0; is < nbSamples*2; is++) {
out[is] = in[is] << 8;
}
}
else if ((m_settings.m_sampleBits == 32) && (SDR_RX_SAMP_SZ == 16))
{
const qint32 *in = (const qint32 *) inBuf;
qint16 *out = (qint16 *) m_converterBuffer;
for (int is = 0; is < nbSamples*2; is++) {
out[is] = in[is] >> 8;
}
}
else if ((m_settings.m_sampleBits == 32) && (SDR_RX_SAMP_SZ == 24))
{
const qint32 *in = (const qint32 *) inBuf;
qint32 *out = (qint32 *) m_converterBuffer;
for (int is = 0; is < nbSamples*2; is++) {
out[is] = in[is];
}
}
else // invalid size
{
qWarning("RemoteTCPInputTCPHandler::convert: unexpected sample size in stream: %d bits", (int) m_settings.m_sampleBits);
return;
}
qint32 len = nbSamples*2;
// Save data to replay buffer
m_replayBuffer->lock();
bool replayEnabled = m_replayBuffer->getSize() > 0;
if (replayEnabled) {
m_replayBuffer->write((const FixReal *) m_converterBuffer, len);
}
const FixReal *buf = (const FixReal *) m_converterBuffer;
qint32 remaining = len;
while (remaining > 0)
{
// Choose between live data or replayed data
if (replayEnabled && m_replayBuffer->useReplay()) {
len = m_replayBuffer->read(remaining, buf);
} else {
len = remaining;
}
remaining -= len;
calcPower(reinterpret_cast<const Sample*>(buf), len / 2);
m_sampleFifo->write(reinterpret_cast<const quint8*>(buf), len * sizeof(FixReal));
}
m_replayBuffer->unlock();
}
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";
const MsgConfigureTcpHandler& notif = (const MsgConfigureTcpHandler&) cmd;
applySettings(notif.getSettings(), notif.getSettingsKeys(), notif.getForce());
return true;
}
else if (RemoteTCPInput::MsgSendMessage::match(cmd))
{
const RemoteTCPInput::MsgSendMessage& msg = (const RemoteTCPInput::MsgSendMessage&) cmd;
sendMessage(MainCore::instance()->getSettings().getStationName(), msg.getText(), msg.getBroadcast());
return true;
}
else
{
return false;
}
}