mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-11-03 21:20:31 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			1763 lines
		
	
	
		
			70 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1763 lines
		
	
	
		
			70 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
///////////////////////////////////////////////////////////////////////////////////
 | 
						|
// Copyright (C) 2022-2024 Jon Beniston, M7RCE <jon@beniston.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 <QMutexLocker>
 | 
						|
#include <QThread>
 | 
						|
#include <QSslConfiguration>
 | 
						|
#include <QSslCertificate>
 | 
						|
#include <QSslKey>
 | 
						|
 | 
						|
#include "channel/channelwebapiutils.h"
 | 
						|
#include "device/deviceapi.h"
 | 
						|
#include "maincore.h"
 | 
						|
 | 
						|
#include "remotetcpsinksink.h"
 | 
						|
#include "remotetcpsink.h"
 | 
						|
 | 
						|
static FLAC__StreamEncoderWriteStatus flacWriteCallback(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, uint32_t samples, uint32_t currentFrame, void *clientData)
 | 
						|
{
 | 
						|
    RemoteTCPSinkSink *sink = (RemoteTCPSinkSink*) clientData;
 | 
						|
 | 
						|
    return sink->flacWrite(encoder, buffer, bytes, samples, currentFrame);
 | 
						|
}
 | 
						|
 | 
						|
RemoteTCPSinkSink::RemoteTCPSinkSink() :
 | 
						|
    m_running(false),
 | 
						|
    m_messageQueueToGUI(nullptr),
 | 
						|
    m_messageQueueToChannel(nullptr),
 | 
						|
    m_channelFrequencyOffset(0),
 | 
						|
    m_channelSampleRate(48000),
 | 
						|
    m_linearGain(1.0f),
 | 
						|
    m_server(nullptr),
 | 
						|
    m_webSocketServer(nullptr),
 | 
						|
    m_encoder(nullptr),
 | 
						|
    m_zStreamInitialised(false),
 | 
						|
    m_zInBuf(m_zBufSize, '\0'),
 | 
						|
    m_zOutBuf(m_zBufSize, '\0'),
 | 
						|
    m_zInBufCount(0),
 | 
						|
    m_bytesUncompressed(0),
 | 
						|
    m_bytesCompressed(0),
 | 
						|
    m_bytesTransmitted(0),
 | 
						|
    m_squelchLevel(-150.0f),
 | 
						|
    m_squelchCount(0),
 | 
						|
    m_squelchOpen(false),
 | 
						|
    m_squelchDelayLine(48000/2),
 | 
						|
    m_magsq(0.0f),
 | 
						|
    m_magsqSum(0.0f),
 | 
						|
    m_magsqPeak(0.0f),
 | 
						|
    m_magsqCount(0),
 | 
						|
    m_centerFrequency(0.0),
 | 
						|
    m_ppmCorrection(0),
 | 
						|
    m_biasTeeEnabled(false),
 | 
						|
    m_directSampling(false),
 | 
						|
    m_agc(false),
 | 
						|
    m_dcOffsetRemoval(false),
 | 
						|
    m_iqCorrection(false),
 | 
						|
    m_devSampleRate(0),
 | 
						|
    m_log2Decim(0),
 | 
						|
    m_rfBW(0),
 | 
						|
    m_gain(),
 | 
						|
    m_timer(this),
 | 
						|
    m_azimuth(std::numeric_limits<float>::quiet_NaN()),
 | 
						|
    m_elevation(std::numeric_limits<float>::quiet_NaN())
 | 
						|
{
 | 
						|
    qDebug("RemoteTCPSinkSink::RemoteTCPSinkSink");
 | 
						|
    applySettings(m_settings, QStringList(), true);
 | 
						|
    applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true);
 | 
						|
 | 
						|
    // Get updated when position changes
 | 
						|
    connect(&MainCore::instance()->getSettings(), &MainSettings::preferenceChanged, this, &RemoteTCPSinkSink::preferenceChanged);
 | 
						|
 | 
						|
    m_timer.setSingleShot(false);
 | 
						|
    m_timer.setInterval(500);
 | 
						|
    connect(&m_timer, &QTimer::timeout, this, &RemoteTCPSinkSink::checkDeviceSettings);
 | 
						|
}
 | 
						|
 | 
						|
RemoteTCPSinkSink::~RemoteTCPSinkSink()
 | 
						|
{
 | 
						|
    qDebug("RemoteTCPSinkSink::~RemoteTCPSinkSink");
 | 
						|
    disconnect(&MainCore::instance()->getSettings(), &MainSettings::preferenceChanged, this, &RemoteTCPSinkSink::preferenceChanged);
 | 
						|
    stop();
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::start()
 | 
						|
{
 | 
						|
    qDebug("RemoteTCPSinkSink::start");
 | 
						|
 | 
						|
    if (m_running) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    connect(thread(), SIGNAL(started()), this, SLOT(started()));
 | 
						|
    connect(thread(), SIGNAL(finished()), this, SLOT(finished()));
 | 
						|
 | 
						|
    m_running = true;
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::stop()
 | 
						|
{
 | 
						|
    qDebug("RemoteTCPSinkSink::stop");
 | 
						|
 | 
						|
    m_running = false;
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::started()
 | 
						|
{
 | 
						|
    QMutexLocker mutexLocker(&m_mutex);
 | 
						|
    startServer();
 | 
						|
    disconnect(thread(), SIGNAL(started()), this, SLOT(started()));
 | 
						|
    m_timer.start();
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::finished()
 | 
						|
{
 | 
						|
    QMutexLocker mutexLocker(&m_mutex);
 | 
						|
    stopServer();
 | 
						|
    disconnect(thread(), SIGNAL(finished()), this, SLOT(finished()));
 | 
						|
    m_timer.stop();
 | 
						|
    m_running = false;
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::init()
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
 | 
						|
{
 | 
						|
    QMutexLocker mutexLocker(&m_mutex);
 | 
						|
 | 
						|
    if (m_clients.size() > 0)
 | 
						|
    {
 | 
						|
        Complex ci;
 | 
						|
        int bytes = 0;
 | 
						|
 | 
						|
        for (SampleVector::const_iterator it = begin; it != end; ++it)
 | 
						|
        {
 | 
						|
            Complex c(it->real(), it->imag());
 | 
						|
            c *= m_nco.nextIQ();
 | 
						|
 | 
						|
            if (m_interpolatorDistance < 1.0f) // interpolate
 | 
						|
            {
 | 
						|
                while (!m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci))
 | 
						|
                {
 | 
						|
                    processOneSample(ci);
 | 
						|
                    bytes += 2 * m_settings.m_sampleBits / 8;
 | 
						|
                    m_interpolatorDistanceRemain += m_interpolatorDistance;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else // decimate
 | 
						|
            {
 | 
						|
                if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci))
 | 
						|
                {
 | 
						|
                    processOneSample(ci);
 | 
						|
                    bytes += 2 * m_settings.m_sampleBits / 8;
 | 
						|
                    m_interpolatorDistanceRemain += m_interpolatorDistance;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        for (const auto client : m_clients) {
 | 
						|
            client->flush();
 | 
						|
        }
 | 
						|
 | 
						|
        QDateTime currentDateTime = QDateTime::currentDateTime();
 | 
						|
        if (m_bwDateTime.isValid())
 | 
						|
        {
 | 
						|
            qint64 msecs = m_bwDateTime.msecsTo(currentDateTime) ;
 | 
						|
            if (msecs >= 1000)
 | 
						|
            {
 | 
						|
                float secs = msecs / 1000.0f;
 | 
						|
                float bw = (8 * m_bwBytes) / secs;
 | 
						|
                float networkBW = (8 * m_bytesTransmitted) / secs;
 | 
						|
                if (m_messageQueueToGUI) {
 | 
						|
                    m_messageQueueToGUI->push(RemoteTCPSink::MsgReportBW::create(bw, networkBW, m_bytesUncompressed, m_bytesCompressed, m_bytesTransmitted));
 | 
						|
                }
 | 
						|
                m_bwDateTime = currentDateTime;
 | 
						|
                m_bwBytes = bytes;
 | 
						|
                m_bytesUncompressed = 0;
 | 
						|
                m_bytesCompressed = 0;
 | 
						|
                m_bytesTransmitted = 0;
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                m_bwBytes += bytes;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            m_bwDateTime = currentDateTime;
 | 
						|
            m_bwBytes = bytes;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static qint32 clamp8(qint32 x)
 | 
						|
{
 | 
						|
    x = std::max(x, -128);
 | 
						|
    x = std::min(x, 127);
 | 
						|
    return x;
 | 
						|
}
 | 
						|
 | 
						|
static qint32 clamp16(qint32 x)
 | 
						|
{
 | 
						|
    x = std::max(x, -32768);
 | 
						|
    x = std::min(x, 32767);
 | 
						|
    return x;
 | 
						|
}
 | 
						|
 | 
						|
static qint32 clamp24(qint32 x)
 | 
						|
{
 | 
						|
    x = std::max(x, -8388608);
 | 
						|
    x = std::min(x, 8388607);
 | 
						|
    return x;
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::processOneSample(Complex &ci)
 | 
						|
{
 | 
						|
    // Apply gain
 | 
						|
    ci = ci * m_linearGain;
 | 
						|
 | 
						|
    // Calculate channel power
 | 
						|
    Real re = ci.real();
 | 
						|
    Real im = ci.imag();
 | 
						|
    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++;
 | 
						|
 | 
						|
    // Squelch
 | 
						|
    if (m_settings.m_squelchEnabled)
 | 
						|
    {
 | 
						|
        // Convert gate time from seconds to samples
 | 
						|
        int squelchGate = m_settings.m_squelchGate * m_channelSampleRate;
 | 
						|
 | 
						|
        m_squelchDelayLine.write(ci);
 | 
						|
 | 
						|
        if (m_magsq < m_squelchLevel)
 | 
						|
        {
 | 
						|
            if (m_squelchCount > 0) {
 | 
						|
                m_squelchCount--;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            m_squelchCount = squelchGate;
 | 
						|
        }
 | 
						|
        m_squelchOpen = m_squelchCount > 0;
 | 
						|
 | 
						|
        if (m_squelchOpen) {
 | 
						|
            ci = m_squelchDelayLine.readBack(squelchGate);
 | 
						|
        } else {
 | 
						|
            ci = 0.0;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (!m_settings.m_iqOnly && (m_settings.m_compression == RemoteTCPSinkSettings::FLAC) && (m_settings.m_protocol != RemoteTCPSinkSettings::RTL0))
 | 
						|
    {
 | 
						|
        // Compress using FLAC
 | 
						|
        FLAC__int32 iqBuf[2];
 | 
						|
 | 
						|
        if (m_settings.m_sampleBits == 8)
 | 
						|
        {
 | 
						|
            iqBuf[0] = (qint32) (ci.real() / 65536.0f);
 | 
						|
            iqBuf[1] = (qint32) (ci.imag() / 65536.0f);
 | 
						|
            iqBuf[0] = clamp8(iqBuf[0]);
 | 
						|
            iqBuf[1] = clamp8(iqBuf[1]);
 | 
						|
        }
 | 
						|
        else if (m_settings.m_sampleBits == 16)
 | 
						|
        {
 | 
						|
            iqBuf[0] = (qint32) (ci.real() / 256.0f);
 | 
						|
            iqBuf[1] = (qint32) (ci.imag() / 256.0f);
 | 
						|
            iqBuf[0] = clamp16(iqBuf[0]);
 | 
						|
            iqBuf[1] = clamp16(iqBuf[1]);
 | 
						|
        }
 | 
						|
        else if (m_settings.m_sampleBits == 24)
 | 
						|
        {
 | 
						|
            iqBuf[0] = (qint32) ci.real();
 | 
						|
            iqBuf[1] = (qint32) ci.imag();
 | 
						|
            iqBuf[0] = clamp24(iqBuf[0]);
 | 
						|
            iqBuf[1] = clamp24(iqBuf[1]);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            iqBuf[0] = (qint32) ci.real();
 | 
						|
            iqBuf[1] = (qint32) ci.imag();
 | 
						|
        }
 | 
						|
        int bytes = 2 * m_settings.m_sampleBits / 8;
 | 
						|
        m_bytesUncompressed += bytes;
 | 
						|
 | 
						|
        if (m_encoder && !FLAC__stream_encoder_process_interleaved(m_encoder, iqBuf, 1)) { // Number of samples in one channel
 | 
						|
            qDebug() << "RemoteTCPSinkSink::processOneSample: FLAC failed to encode:" << FLAC__stream_encoder_get_state(m_encoder);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        quint8 iqBuf[4*2];
 | 
						|
 | 
						|
        if (m_settings.m_sampleBits == 8)
 | 
						|
        {
 | 
						|
            // Transmit data as per rtl_tcp - Interleaved unsigned 8-bit IQ
 | 
						|
            iqBuf[0] = clamp8((qint32) (ci.real() / 65536.0f)) + 128;
 | 
						|
            iqBuf[1] = clamp8((qint32) (ci.imag() / 65536.0f)) + 128;
 | 
						|
        }
 | 
						|
        else if (m_settings.m_sampleBits == 16)
 | 
						|
        {
 | 
						|
            // Interleaved little-endian signed 16-bit IQ
 | 
						|
            qint32 i, q;
 | 
						|
            i = clamp16((qint32) (ci.real() / 256.0f));
 | 
						|
            q = clamp16((qint32) (ci.imag() / 256.0f));
 | 
						|
            iqBuf[1] = (i >> 8) & 0xff;
 | 
						|
            iqBuf[0] = i & 0xff;
 | 
						|
            iqBuf[3] = (q >> 8) & 0xff;
 | 
						|
            iqBuf[2] = q & 0xff;
 | 
						|
        }
 | 
						|
        else if (m_settings.m_sampleBits == 24)
 | 
						|
        {
 | 
						|
            // Interleaved little-endian signed 24-bit IQ
 | 
						|
            qint32 i, q;
 | 
						|
            i = clamp24((qint32) ci.real());
 | 
						|
            q = clamp24((qint32) ci.imag());
 | 
						|
            iqBuf[2] = (i >> 16) & 0xff;
 | 
						|
            iqBuf[1] = (i >> 8) & 0xff;
 | 
						|
            iqBuf[0] = i & 0xff;
 | 
						|
            iqBuf[5] = (q >> 16) & 0xff;
 | 
						|
            iqBuf[4] = (q >> 8) & 0xff;
 | 
						|
            iqBuf[3] = q & 0xff;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            // Interleaved little-endian signed 32-bit IQ
 | 
						|
            qint32 i, q;
 | 
						|
            i = (qint32) ci.real();
 | 
						|
            q = (qint32) ci.imag();
 | 
						|
            iqBuf[3] = (i >> 24) & 0xff;
 | 
						|
            iqBuf[2] = (i >> 16) & 0xff;
 | 
						|
            iqBuf[1] = (i >> 8) & 0xff;
 | 
						|
            iqBuf[0] = i & 0xff;
 | 
						|
            iqBuf[7] = (q >> 24) & 0xff;
 | 
						|
            iqBuf[6] = (q >> 16) & 0xff;
 | 
						|
            iqBuf[5] = (q >> 8) & 0xff;
 | 
						|
            iqBuf[4] = q & 0xff;
 | 
						|
        }
 | 
						|
 | 
						|
        int bytes = 2 * m_settings.m_sampleBits / 8;
 | 
						|
        m_bytesUncompressed += bytes;
 | 
						|
 | 
						|
        if (!m_settings.m_iqOnly && (m_settings.m_compression == RemoteTCPSinkSettings::ZLIB) && (m_settings.m_protocol != RemoteTCPSinkSettings::RTL0))
 | 
						|
        {
 | 
						|
            if (m_zStreamInitialised)
 | 
						|
            {
 | 
						|
                // Store in block buffer
 | 
						|
                memcpy(&m_zInBuf.data()[m_zInBufCount], iqBuf, bytes);
 | 
						|
                m_zInBufCount += bytes;
 | 
						|
 | 
						|
                if (m_zInBufCount >= m_settings.m_blockSize)
 | 
						|
                {
 | 
						|
                    // Compress using zlib
 | 
						|
                    m_zStream.next_in = (Bytef *) m_zInBuf.data();
 | 
						|
                    m_zStream.avail_in = m_zInBufCount;
 | 
						|
                    m_zStream.next_out = (Bytef *) m_zOutBuf.data();
 | 
						|
                    m_zStream.avail_out = m_zOutBuf.size();
 | 
						|
                    int ret = deflate(&m_zStream, Z_FINISH);
 | 
						|
 | 
						|
                    if (ret == Z_STREAM_END) {
 | 
						|
                        deflateReset(&m_zStream);
 | 
						|
                    } else if (ret != Z_OK) {
 | 
						|
                        qDebug() << "RemoteTCPSinkSink::processOneSample: Failed to deflate" << ret;
 | 
						|
                    }
 | 
						|
                    if (m_zStream.avail_in != 0) {
 | 
						|
                        qDebug() << "RemoteTCPSinkSink::processOneSample: Data still in input buffer";
 | 
						|
                    }
 | 
						|
                    int compressedBytes = m_zOutBuf.size() - m_zStream.avail_out;
 | 
						|
 | 
						|
                    //qDebug() << "zlib ret" << ret << "m_settings.m_blockSize" << m_settings.m_blockSize << "m_zInBufCount" << m_zInBufCount << "compressedBytes" << compressedBytes << "avail_in" << m_zStream.avail_in << "avail_out" << m_zStream.avail_out << " % " << round(100.0 * compressedBytes / (float) m_zInBufCount );
 | 
						|
 | 
						|
                    m_zInBufCount = 0;
 | 
						|
 | 
						|
                    // Send to clients
 | 
						|
 | 
						|
                    int clients = std::min((int) m_clients.size(), m_settings.m_maxClients);
 | 
						|
                    char header[1+4];
 | 
						|
 | 
						|
                    header[0] = (char) RemoteTCPProtocol::dataIQzlib;
 | 
						|
                    RemoteTCPProtocol::encodeUInt32((quint8 *) &header[1], compressedBytes);
 | 
						|
 | 
						|
                    for (int i = 0; i < clients; i++)
 | 
						|
                    {
 | 
						|
                        m_clients[i]->write(header, sizeof(header));
 | 
						|
                        m_bytesTransmitted += sizeof(header);
 | 
						|
                        m_clients[i]->write((const char *)m_zOutBuf.data(), compressedBytes);
 | 
						|
                        m_bytesTransmitted += compressedBytes;
 | 
						|
                    }
 | 
						|
                    m_bytesCompressed += sizeof(header) + compressedBytes;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            // Send uncompressed
 | 
						|
            int clients = std::min((int) m_clients.size(), m_settings.m_maxClients);
 | 
						|
 | 
						|
            for (int i = 0; i < clients; i++)
 | 
						|
            {
 | 
						|
                m_clients[i]->write((const char *)iqBuf, bytes);
 | 
						|
                m_bytesTransmitted += bytes;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force)
 | 
						|
{
 | 
						|
    qDebug() << "RemoteTCPSinkSink::applyChannelSettings:"
 | 
						|
        << " channelSampleRate: " << channelSampleRate
 | 
						|
        << " channelFrequencyOffset: " << channelFrequencyOffset;
 | 
						|
 | 
						|
    if ((m_channelFrequencyOffset != channelFrequencyOffset) ||
 | 
						|
        (m_channelSampleRate != channelSampleRate) || force)
 | 
						|
    {
 | 
						|
        m_nco.setFreq(-channelFrequencyOffset, channelSampleRate);
 | 
						|
    }
 | 
						|
 | 
						|
    if ((m_channelSampleRate != channelSampleRate) || force)
 | 
						|
    {
 | 
						|
        m_interpolator.create(16, channelSampleRate, m_settings.m_channelSampleRate / 2.0);
 | 
						|
        m_interpolatorDistance = (Real) channelSampleRate / (Real) m_settings.m_channelSampleRate;
 | 
						|
        m_interpolatorDistanceRemain = m_interpolatorDistance;
 | 
						|
    }
 | 
						|
 | 
						|
    m_channelSampleRate = channelSampleRate;
 | 
						|
    m_channelFrequencyOffset = channelFrequencyOffset;
 | 
						|
 | 
						|
    m_squelchDelayLine.resize(m_settings.m_squelchGate * m_channelSampleRate + 1);
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::applySettings(const RemoteTCPSinkSettings& settings, const QStringList& settingsKeys, bool force, bool restartRequired)
 | 
						|
{
 | 
						|
    bool initFLAC = false;
 | 
						|
    bool initZLib = false;
 | 
						|
 | 
						|
    QMutexLocker mutexLocker(&m_mutex);
 | 
						|
 | 
						|
    qDebug() << "RemoteTCPSinkSink::applySettings:" << settings.getDebugString(settingsKeys, force) << " force: " << force;
 | 
						|
 | 
						|
    if (settingsKeys.contains("gain") || force)
 | 
						|
    {
 | 
						|
        m_linearGain = powf(10.0f, settings.m_gain/20.0f);
 | 
						|
    }
 | 
						|
 | 
						|
    if (settingsKeys.contains("channelSampleRate") || force)
 | 
						|
    {
 | 
						|
        m_interpolator.create(16, m_channelSampleRate, settings.m_channelSampleRate / 2.0);
 | 
						|
        m_interpolatorDistance = (Real) m_channelSampleRate / (Real) settings.m_channelSampleRate;
 | 
						|
        m_interpolatorDistanceRemain = m_interpolatorDistance;
 | 
						|
    }
 | 
						|
 | 
						|
    // Update time limit for connected clients
 | 
						|
    if (settingsKeys.contains("timeLimit") && (m_settings.m_timeLimit != settings.m_timeLimit))
 | 
						|
    {
 | 
						|
        if (settings.m_timeLimit > 0)
 | 
						|
        {
 | 
						|
            // Set new timelimit
 | 
						|
            for (int i = 0; i < m_timers.size(); i++) {
 | 
						|
                m_timers[i]->setInterval(settings.m_timeLimit * 60 * 1000);
 | 
						|
            }
 | 
						|
            // Start timers if they weren't previously started
 | 
						|
            if (m_settings.m_timeLimit == 0)
 | 
						|
            {
 | 
						|
                for (int i = 0; i < std::min((int) m_timers.size(), m_settings.m_maxClients); i++) {
 | 
						|
                    m_timers[i]->start();
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            // Stop any existing timers
 | 
						|
            for (int i = 0; i < m_timers.size(); i++) {
 | 
						|
                m_timers[i]->stop();
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if ((settingsKeys.contains("compressionLevel") && (settings.m_compressionLevel != m_settings.m_compressionLevel))
 | 
						|
        || (settingsKeys.contains("compression") && (settings.m_compression != m_settings.m_compression))
 | 
						|
        || (settingsKeys.contains("sampleBits")  && (settings.m_sampleBits != m_settings.m_sampleBits))
 | 
						|
        || (settingsKeys.contains("blockSize")  && (settings.m_blockSize != m_settings.m_blockSize))
 | 
						|
        || (settingsKeys.contains("channelSampleRate")  && (settings.m_channelSampleRate != m_settings.m_channelSampleRate))
 | 
						|
        || force)
 | 
						|
    {
 | 
						|
        initFLAC = true;
 | 
						|
    }
 | 
						|
 | 
						|
    if ((settingsKeys.contains("compressionLevel") && (settings.m_compressionLevel != m_settings.m_compressionLevel))
 | 
						|
        || (settingsKeys.contains("compression") && (settings.m_compression != m_settings.m_compression))
 | 
						|
        || force)
 | 
						|
    {
 | 
						|
        initZLib = true;
 | 
						|
    }
 | 
						|
 | 
						|
    if (settingsKeys.contains("squelch") || force)
 | 
						|
    {
 | 
						|
        m_squelchLevel = std::pow(10.0, settings.m_squelch / 10.0);
 | 
						|
        m_movingAverage.reset();
 | 
						|
        m_squelchCount = 0;
 | 
						|
    }
 | 
						|
 | 
						|
    if (settingsKeys.contains("squelchGate") || force) {
 | 
						|
        m_squelchDelayLine.resize(settings.m_squelchGate * m_channelSampleRate + 1);
 | 
						|
    }
 | 
						|
 | 
						|
    // Do clients need to reconnect to get these updated settings?
 | 
						|
    // settingsKeys will be empty if force is set
 | 
						|
    bool restart = (settingsKeys.contains("dataAddress") && (m_settings.m_dataAddress != settings.m_dataAddress))
 | 
						|
                || (settingsKeys.contains("dataPort") && (m_settings.m_dataPort != settings.m_dataPort))
 | 
						|
                || (settingsKeys.contains("certificate") && (m_settings.m_certificate != settings.m_certificate))
 | 
						|
                || (settingsKeys.contains("key") && (m_settings.m_key!= settings.m_key))
 | 
						|
                || (settingsKeys.contains("sampleBits") && (m_settings.m_sampleBits != settings.m_sampleBits))
 | 
						|
                || (settingsKeys.contains("protocol") && (m_settings.m_protocol != settings.m_protocol))
 | 
						|
                || (settingsKeys.contains("compression") && (m_settings.m_compression != settings.m_compression))
 | 
						|
                || (settingsKeys.contains("remoteControl") && (m_settings.m_remoteControl != settings.m_remoteControl))
 | 
						|
                || initFLAC
 | 
						|
                || restartRequired
 | 
						|
                   ;
 | 
						|
 | 
						|
    if (!restart && (m_settings.m_protocol != RemoteTCPSinkSettings::RTL0) && !m_settings.m_iqOnly)
 | 
						|
    {
 | 
						|
        // Forward settings to clients if they've changed
 | 
						|
        if ((settingsKeys.contains("channelSampleRate") || force) && (settings.m_channelSampleRate != m_settings.m_channelSampleRate)) {
 | 
						|
            sendCommand(RemoteTCPProtocol::setChannelSampleRate, settings.m_channelSampleRate);
 | 
						|
        }
 | 
						|
        if ((settingsKeys.contains("inputFrequencyOffset") || force) && (settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset)) {
 | 
						|
            sendCommand(RemoteTCPProtocol::setChannelFreqOffset, settings.m_inputFrequencyOffset);
 | 
						|
        }
 | 
						|
        if ((settingsKeys.contains("gain") || force) && (settings.m_gain != m_settings.m_gain)) {
 | 
						|
            sendCommand(RemoteTCPProtocol::setChannelGain, settings.m_gain);
 | 
						|
        }
 | 
						|
        if ((settingsKeys.contains("sampleBits") || force) && (settings.m_sampleBits != m_settings.m_sampleBits)) {
 | 
						|
            sendCommand(RemoteTCPProtocol::setSampleBitDepth, settings.m_sampleBits);
 | 
						|
        }
 | 
						|
        if ((settingsKeys.contains("squelchEnabled") || force) && (settings.m_squelchEnabled != m_settings.m_squelchEnabled)) {
 | 
						|
            sendCommand(RemoteTCPProtocol::setIQSquelchEnabled, (quint32) settings.m_squelchEnabled);
 | 
						|
        }
 | 
						|
        if ((settingsKeys.contains("squelch") || force) && (settings.m_squelch != m_settings.m_squelch))  {
 | 
						|
            sendCommandFloat(RemoteTCPProtocol::setIQSquelch, settings.m_squelch);
 | 
						|
        }
 | 
						|
        if ((settingsKeys.contains("squelchGate") || force) && (settings.m_squelchGate != m_settings.m_squelchGate))  {
 | 
						|
            sendCommandFloat(RemoteTCPProtocol::setIQSquelchGate, settings.m_squelchGate);
 | 
						|
        }
 | 
						|
        // m_remoteControl rather than restart?
 | 
						|
    }
 | 
						|
 | 
						|
    if (force) {
 | 
						|
        m_settings = settings;
 | 
						|
    } else {
 | 
						|
        m_settings.applySettings(settingsKeys, settings);
 | 
						|
    }
 | 
						|
 | 
						|
    if (m_running && (restart || force)) {
 | 
						|
        startServer();
 | 
						|
    }
 | 
						|
 | 
						|
    if (initFLAC && (m_settings.m_compression == RemoteTCPSinkSettings::FLAC))
 | 
						|
    {
 | 
						|
        if (m_encoder)
 | 
						|
        {
 | 
						|
            // Delete existing decoder
 | 
						|
            FLAC__stream_encoder_finish(m_encoder);
 | 
						|
            FLAC__stream_encoder_delete(m_encoder);
 | 
						|
            m_encoder = nullptr;
 | 
						|
            m_flacHeader.clear();
 | 
						|
        }
 | 
						|
 | 
						|
        // Create FLAC encoder
 | 
						|
        FLAC__StreamEncoderInitStatus init_status;
 | 
						|
        m_encoder = FLAC__stream_encoder_new();
 | 
						|
        if (m_encoder)
 | 
						|
        {
 | 
						|
            const int maxSampleRate = 176400; // Spec says max is 655350, but doesn't seem to work
 | 
						|
            FLAC__bool ok = true;
 | 
						|
 | 
						|
            ok &= FLAC__stream_encoder_set_verify(m_encoder, false);
 | 
						|
	        ok &= FLAC__stream_encoder_set_compression_level(m_encoder, m_settings.m_compressionLevel);
 | 
						|
	        ok &= FLAC__stream_encoder_set_channels(m_encoder, 2);
 | 
						|
	        ok &= FLAC__stream_encoder_set_bits_per_sample(m_encoder, m_settings.m_sampleBits);
 | 
						|
            // We'll get FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE if we use the real sample rate
 | 
						|
            if (m_settings.m_channelSampleRate < maxSampleRate) {
 | 
						|
	            ok &= FLAC__stream_encoder_set_sample_rate(m_encoder, m_settings.m_channelSampleRate);
 | 
						|
            } else {
 | 
						|
	            ok &= FLAC__stream_encoder_set_sample_rate(m_encoder, maxSampleRate);
 | 
						|
            }
 | 
						|
	        ok &= FLAC__stream_encoder_set_total_samples_estimate(m_encoder, 0);
 | 
						|
            //ok &= FLAC__stream_encoder_set_do_mid_side_stereo(m_encoder, false);
 | 
						|
            // FLAC__MAX_BLOCK_SIZE is 65536
 | 
						|
            // However, FLAC__format_blocksize_is_subset says anything over 16384 is not streamable
 | 
						|
            // Also, if sampleRate <= 48000, then max block size is 4608
 | 
						|
            if (FLAC__format_blocksize_is_subset(m_settings.m_blockSize, m_settings.m_channelSampleRate)) {
 | 
						|
                ok &= FLAC__stream_encoder_set_blocksize(m_encoder, m_settings.m_blockSize);
 | 
						|
            } else {
 | 
						|
                ok &= FLAC__stream_encoder_set_blocksize(m_encoder, 4096);
 | 
						|
            }
 | 
						|
            if (ok)
 | 
						|
            {
 | 
						|
                init_status = FLAC__stream_encoder_init_stream(m_encoder, flacWriteCallback, nullptr, nullptr, nullptr, this);
 | 
						|
                if (init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK)
 | 
						|
                {
 | 
						|
			        qDebug() << "RemoteTCPSinkSink::applySettings: Error initializing FLAC encoder:" << FLAC__StreamEncoderInitStatusString[init_status];
 | 
						|
                    FLAC__stream_encoder_delete(m_encoder);
 | 
						|
                    m_encoder = nullptr;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                qDebug() << "RemoteTCPSinkSink::applySettings: Failed to configure FLAC encoder";
 | 
						|
                FLAC__stream_encoder_delete(m_encoder);
 | 
						|
                m_encoder = nullptr;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            qDebug() << "RemoteTCPSinkSink::applySettings: Failed to allocate FLAC encoder";
 | 
						|
        }
 | 
						|
 | 
						|
        m_bytesUncompressed = 0;
 | 
						|
        m_bytesCompressed = 0;
 | 
						|
    }
 | 
						|
 | 
						|
    if (initZLib && (m_settings.m_compression == RemoteTCPSinkSettings::ZLIB))
 | 
						|
    {
 | 
						|
        // Intialise zlib compression
 | 
						|
        m_zStream.zalloc = nullptr;
 | 
						|
        m_zStream.zfree = nullptr;
 | 
						|
        m_zStream.opaque = nullptr;
 | 
						|
        m_zStream.data_type = Z_BINARY;
 | 
						|
        int windowBits = log2(m_settings.m_blockSize);
 | 
						|
 | 
						|
        if (Z_OK == deflateInit2(&m_zStream, m_settings.m_compressionLevel, Z_DEFLATED, windowBits, 9, Z_DEFAULT_STRATEGY))
 | 
						|
        {
 | 
						|
            m_zStreamInitialised = true;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            qDebug() << "RemoteTCPSinkSink::applySettings: deflateInit failed";
 | 
						|
            m_zStreamInitialised = false;
 | 
						|
        }
 | 
						|
 | 
						|
        m_bytesUncompressed = 0;
 | 
						|
        m_bytesCompressed = 0;
 | 
						|
    }
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::startServer()
 | 
						|
{
 | 
						|
    stopServer();
 | 
						|
 | 
						|
    if (m_settings.m_protocol == RemoteTCPSinkSettings::SDRA_WSS)
 | 
						|
    {
 | 
						|
#ifndef QT_NO_OPENSSL
 | 
						|
        QSslConfiguration sslConfiguration;
 | 
						|
        qDebug() << "RemoteTCPSinkSink::startServer: SSL config: " << m_settings.m_certificate << m_settings.m_key;
 | 
						|
        if (m_settings.m_certificate.isEmpty())
 | 
						|
        {
 | 
						|
            QString msg = "RemoteTCPSink requires an SSL certificate in order to use wss protocol";
 | 
						|
            qWarning() << msg;
 | 
						|
            if (m_messageQueueToGUI) {
 | 
						|
                m_messageQueueToGUI->push(RemoteTCPSink::MsgError::create(msg));
 | 
						|
            }
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (m_settings.m_certificate.isEmpty())
 | 
						|
        {
 | 
						|
            QString msg = "RemoteTCPSink requires an SSL key in order to use wss protocol";
 | 
						|
            qWarning() << msg;
 | 
						|
            if (m_messageQueueToGUI) {
 | 
						|
                m_messageQueueToGUI->push(RemoteTCPSink::MsgError::create(msg));
 | 
						|
            }
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        QFile certFile(m_settings.m_certificate);
 | 
						|
        if (!certFile.open(QIODevice::ReadOnly))
 | 
						|
        {
 | 
						|
            QString msg = QString("RemoteTCPSink failed to open certificate %1: %2").arg(m_settings.m_certificate).arg(certFile.errorString());
 | 
						|
            qWarning() << msg;
 | 
						|
            if (m_messageQueueToGUI) {
 | 
						|
                m_messageQueueToGUI->push(RemoteTCPSink::MsgError::create(msg));
 | 
						|
            }
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        QFile keyFile(m_settings.m_key);
 | 
						|
        if (!keyFile.open(QIODevice::ReadOnly))
 | 
						|
        {
 | 
						|
            QString msg = QString("RemoteTCPSink failed to open key %1: %2").arg(m_settings.m_key).arg(keyFile.errorString());
 | 
						|
            qWarning() << msg;
 | 
						|
            if (m_messageQueueToGUI) {
 | 
						|
                m_messageQueueToGUI->push(RemoteTCPSink::MsgError::create(msg));
 | 
						|
            }
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        QSslCertificate certificate(&certFile, QSsl::Pem);
 | 
						|
        QSslKey sslKey(&keyFile, QSsl::Rsa, QSsl::Pem);
 | 
						|
        certFile.close();
 | 
						|
        keyFile.close();
 | 
						|
        sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone);
 | 
						|
        sslConfiguration.setLocalCertificate(certificate);
 | 
						|
        sslConfiguration.setPrivateKey(sslKey);
 | 
						|
 | 
						|
        m_webSocketServer = new QWebSocketServer(QStringLiteral("Remote TCP Sink"),
 | 
						|
                                                 QWebSocketServer::SecureMode,
 | 
						|
                                                 this);
 | 
						|
        m_webSocketServer->setSslConfiguration(sslConfiguration);
 | 
						|
 | 
						|
        QHostAddress address(m_settings.m_dataAddress);
 | 
						|
 | 
						|
#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
 | 
						|
        m_webSocketServer->setSupportedSubprotocols({"binary"}); // Chrome wont connect without this - "Sent non-empty 'Sec-WebSocket-Protocol' header but no response was received"
 | 
						|
#endif
 | 
						|
        if (!m_webSocketServer->listen(address, m_settings.m_dataPort))
 | 
						|
        {
 | 
						|
            QString msg = QString("RemoteTCPSink failed to listen on %1 port %2: %3").arg(m_settings.m_dataAddress).arg(m_settings.m_dataPort).arg(m_webSocketServer->errorString());
 | 
						|
            qWarning() << msg;
 | 
						|
            if (m_messageQueueToGUI) {
 | 
						|
                m_messageQueueToGUI->push(RemoteTCPSink::MsgError::create(msg));
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            qInfo() << "RemoteTCPSink listening on" << m_settings.m_dataAddress << "port" << m_settings.m_dataPort;
 | 
						|
            connect(m_webSocketServer, &QWebSocketServer::newConnection, this, &RemoteTCPSinkSink::acceptWebConnection);
 | 
						|
            connect(m_webSocketServer, &QWebSocketServer::sslErrors, this, &RemoteTCPSinkSink::onSslErrors);
 | 
						|
        }
 | 
						|
#else
 | 
						|
        QString msg = "RemoteTCPSink unable to use wss protocol as SSL is not supported";
 | 
						|
        qWarning() << msg;
 | 
						|
        if (m_messageQueueToGUI) {
 | 
						|
            m_messageQueueToGUI->push(RemoteTCPSink::MsgError::create(msg));
 | 
						|
        }
 | 
						|
#endif
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        m_server = new QTcpServer(this);
 | 
						|
        if (!m_server->listen(QHostAddress(m_settings.m_dataAddress), m_settings.m_dataPort))
 | 
						|
        {
 | 
						|
            QString msg = QString("RemoteTCPSink failed to listen on %1 port %2: %3").arg(m_settings.m_dataAddress).arg(m_settings.m_dataPort).arg(m_webSocketServer->errorString());
 | 
						|
            qWarning() << msg;
 | 
						|
            if (m_messageQueueToGUI) {
 | 
						|
                m_messageQueueToGUI->push(RemoteTCPSink::MsgError::create(msg));
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            qInfo() << "RemoteTCPSink listening on" << m_settings.m_dataAddress << "port" << m_settings.m_dataPort;
 | 
						|
            connect(m_server, &QTcpServer::newConnection, this, &RemoteTCPSinkSink::acceptTCPConnection);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::stopServer()
 | 
						|
{
 | 
						|
    // Close connections to any existing clients
 | 
						|
    while (m_clients.size() > 0) {
 | 
						|
        m_clients[0]->close(); // This results in disconnected() being called, where we delete and remove from m_clients
 | 
						|
    }
 | 
						|
 | 
						|
    // Close server sockets
 | 
						|
    if (m_server)
 | 
						|
    {
 | 
						|
        qDebug() << "RemoteTCPSinkSink::stopServer: Closing old socket server";
 | 
						|
        m_server->close();
 | 
						|
        m_server->deleteLater();
 | 
						|
        m_server = nullptr;
 | 
						|
    }
 | 
						|
    if (m_webSocketServer)
 | 
						|
    {
 | 
						|
        qDebug() << "RemoteTCPSinkSink::stopServer: Closing old web socket server";
 | 
						|
        m_webSocketServer->close();
 | 
						|
        m_webSocketServer->deleteLater();
 | 
						|
        m_webSocketServer = nullptr;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
RemoteTCPProtocol::Device RemoteTCPSinkSink::getDevice()
 | 
						|
{
 | 
						|
    DeviceAPI *deviceAPI = MainCore::instance()->getDevice(m_deviceIndex);
 | 
						|
    if (deviceAPI)
 | 
						|
    {
 | 
						|
        QString id = deviceAPI->getHardwareId();
 | 
						|
        QHash<QString, RemoteTCPProtocol::Device> map = {
 | 
						|
            {"Airspy", RemoteTCPProtocol::AIRSPY},
 | 
						|
            {"AirspyHF", RemoteTCPProtocol::AIRSPY_HF},
 | 
						|
            {"AudioInput", RemoteTCPProtocol::AUDIO_INPUT},
 | 
						|
            {"BladeRF1", RemoteTCPProtocol::BLADE_RF1},
 | 
						|
            {"BladeRF2", RemoteTCPProtocol::BLADE_RF2},
 | 
						|
            {"FCDPro", RemoteTCPProtocol::FCD_PRO},
 | 
						|
            {"FCDProPlus", RemoteTCPProtocol::FCD_PRO_PLUS},
 | 
						|
            {"FileInput", RemoteTCPProtocol::FILE_INPUT},
 | 
						|
            {"HackRF", RemoteTCPProtocol::HACK_RF},
 | 
						|
            {"KiwiSDR", RemoteTCPProtocol::KIWI_SDR},
 | 
						|
            {"LimeSDR", RemoteTCPProtocol::LIME_SDR},
 | 
						|
            {"LocalInput", RemoteTCPProtocol::LOCAL_INPUT},
 | 
						|
            {"Perseus", RemoteTCPProtocol::PERSEUS},
 | 
						|
            {"PlutoSDR", RemoteTCPProtocol::PLUTO_SDR},
 | 
						|
            {"RemoteInput", RemoteTCPProtocol::REMOTE_INPUT},
 | 
						|
            {"RemoteTCPInput", RemoteTCPProtocol::REMOTE_TCP_INPUT},
 | 
						|
            {"SDRplay1", RemoteTCPProtocol::SDRPLAY_1},
 | 
						|
            {"SigMFFileInput", RemoteTCPProtocol::SIGMF_FILE_INPUT},
 | 
						|
            {"SoapySDR", RemoteTCPProtocol::SOAPY_SDR},
 | 
						|
            {"TestSource", RemoteTCPProtocol::TEST_SOURCE},
 | 
						|
            {"USRP", RemoteTCPProtocol::USRP},
 | 
						|
            {"XTRX", RemoteTCPProtocol::XTRX},
 | 
						|
        };
 | 
						|
        if (map.contains(id))
 | 
						|
        {
 | 
						|
            return map.value(id);
 | 
						|
        }
 | 
						|
        else if (id == "SDRplayV3")
 | 
						|
        {
 | 
						|
            QString deviceType;
 | 
						|
            if (ChannelWebAPIUtils::getDeviceReportValue(m_deviceIndex, "deviceType", deviceType))
 | 
						|
            {
 | 
						|
                QHash<QString, RemoteTCPProtocol::Device> sdrplayMap = {
 | 
						|
                    {"RSP1", RemoteTCPProtocol::SDRPLAY_V3_RSP1},
 | 
						|
                    {"RSP1A", RemoteTCPProtocol::SDRPLAY_V3_RSP1A},
 | 
						|
                    {"RSP1B", RemoteTCPProtocol::SDRPLAY_V3_RSP1B},
 | 
						|
                    {"RSP2", RemoteTCPProtocol::SDRPLAY_V3_RSP2},
 | 
						|
                    {"RSPduo", RemoteTCPProtocol::SDRPLAY_V3_RSPDUO},
 | 
						|
                    {"RSPdx", RemoteTCPProtocol::SDRPLAY_V3_RSPDX},
 | 
						|
                };
 | 
						|
                if (sdrplayMap.contains(deviceType)) {
 | 
						|
                    return sdrplayMap.value(deviceType);
 | 
						|
                }
 | 
						|
                qDebug() << "RemoteTCPSinkSink::getDevice: Unknown SDRplayV3 deviceType: " << deviceType;
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                qDebug() << "RemoteTCPSinkSink::getDevice: Failed to get deviceType for SDRplayV3";
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else if (id == "RTLSDR")
 | 
						|
        {
 | 
						|
            QString tunerType;
 | 
						|
            if (ChannelWebAPIUtils::getDeviceReportValue(m_deviceIndex, "tunerType", tunerType))
 | 
						|
            {
 | 
						|
                QHash<QString, RemoteTCPProtocol::Device> rtlsdrMap = {
 | 
						|
                    {"E4000", RemoteTCPProtocol::RTLSDR_E4000},
 | 
						|
                    {"FC0012", RemoteTCPProtocol::RTLSDR_FC0012},
 | 
						|
                    {"FC0013", RemoteTCPProtocol::RTLSDR_FC0013},
 | 
						|
                    {"FC2580", RemoteTCPProtocol::RTLSDR_FC2580},
 | 
						|
                    {"R820T", RemoteTCPProtocol::RTLSDR_R820T},
 | 
						|
                    {"R828D", RemoteTCPProtocol::RTLSDR_R828D},
 | 
						|
                };
 | 
						|
                if (rtlsdrMap.contains(tunerType)) {
 | 
						|
                    return rtlsdrMap.value(tunerType);
 | 
						|
                }
 | 
						|
                qDebug() << "RemoteTCPSinkSink::getDevice: Unknown RTLSDR tunerType: " << tunerType;
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                qDebug() << "RemoteTCPSinkSink::getDevice: Failed to get tunerType for RTLSDR";
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return RemoteTCPProtocol::UNKNOWN;
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::acceptWebConnection()
 | 
						|
{
 | 
						|
     QMutexLocker mutexLocker(&m_mutex);
 | 
						|
     QWebSocket *client = m_webSocketServer->nextPendingConnection();
 | 
						|
 | 
						|
    connect(client, &QWebSocket::binaryMessageReceived, this, &RemoteTCPSinkSink::processCommand);
 | 
						|
    connect(client, &QWebSocket::disconnected, this, &RemoteTCPSinkSink::disconnected);
 | 
						|
    qDebug() << "RemoteTCPSinkSink::acceptWebConnection: client connected";
 | 
						|
 | 
						|
    // https://bugreports.qt.io/browse/QTBUG-125874
 | 
						|
    QTimer::singleShot(200, this, [this, client] () {
 | 
						|
        QMutexLocker mutexLocker(&m_mutex);
 | 
						|
        m_clients.append(new WebSocket(client));
 | 
						|
        acceptConnection(m_clients.last());
 | 
						|
    });
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::acceptTCPConnection()
 | 
						|
{
 | 
						|
    QMutexLocker mutexLocker(&m_mutex);
 | 
						|
    QTcpSocket *client = m_server->nextPendingConnection();
 | 
						|
 | 
						|
    connect(client, &QIODevice::readyRead, this, &RemoteTCPSinkSink::processCommand);
 | 
						|
    connect(client, &QTcpSocket::disconnected, this, &RemoteTCPSinkSink::disconnected);
 | 
						|
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
 | 
						|
    connect(client, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &RemoteTCPSinkSink::errorOccurred);
 | 
						|
#else
 | 
						|
    connect(client, &QAbstractSocket::errorOccurred, this, &RemoteTCPSinkSink::errorOccurred);
 | 
						|
#endif
 | 
						|
    qDebug() << "RemoteTCPSinkSink::acceptTCPConnection: client connected";
 | 
						|
 | 
						|
    QTimer::singleShot(200, this, [this, client] () {
 | 
						|
        QMutexLocker mutexLocker(&m_mutex);
 | 
						|
        m_clients.append(new TCPSocket(client));
 | 
						|
        acceptConnection(m_clients.last());
 | 
						|
    });
 | 
						|
}
 | 
						|
 | 
						|
FLAC__StreamEncoderWriteStatus RemoteTCPSinkSink::flacWrite(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, uint32_t samples, uint32_t currentFrame)
 | 
						|
{
 | 
						|
    (void) encoder;
 | 
						|
 | 
						|
    char header[1+4];
 | 
						|
 | 
						|
    // Save FLAC header for clients that connect later
 | 
						|
    if ((currentFrame == 0) && (samples == 0))
 | 
						|
    {
 | 
						|
        m_flacHeader.append((const char *) buffer, bytes);
 | 
						|
 | 
						|
        // Write complete header to all clients
 | 
						|
        if (m_flacHeader.size() == m_flacHeaderSize)
 | 
						|
        {
 | 
						|
            header[0] = (char) RemoteTCPProtocol::dataIQFLAC;
 | 
						|
            RemoteTCPProtocol::encodeUInt32((quint8 *) &header[1], m_flacHeader.size());
 | 
						|
 | 
						|
            for (auto client : m_clients)
 | 
						|
            {
 | 
						|
                client->write(header, sizeof(header));
 | 
						|
                client->write(m_flacHeader.constData(), m_flacHeader.size());
 | 
						|
                m_bytesTransmitted += sizeof(header) + m_flacHeader.size();
 | 
						|
                client->flush();
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        // Send compressed IQ data to max number of clients
 | 
						|
        header[0] = (char) RemoteTCPProtocol::dataIQFLAC;
 | 
						|
        RemoteTCPProtocol::encodeUInt32((quint8 *) &header[1], bytes);
 | 
						|
        int clients = std::min((int) m_clients.size(), m_settings.m_maxClients);
 | 
						|
        for (int i = 0; i < clients; i++)
 | 
						|
        {
 | 
						|
            Socket *client = m_clients[i];
 | 
						|
            client->write(header, sizeof(header));
 | 
						|
            client->write((const char *) buffer, bytes);
 | 
						|
            m_bytesTransmitted += sizeof(header) + bytes;
 | 
						|
            client->flush();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    m_bytesCompressed += sizeof(header) + bytes;
 | 
						|
 | 
						|
    return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::acceptConnection(Socket *client)
 | 
						|
{
 | 
						|
    if (m_settings.m_protocol == RemoteTCPSinkSettings::RTL0)
 | 
						|
    {
 | 
						|
        quint8 metaData[RemoteTCPProtocol::m_rtl0MetaDataSize] = {'R', 'T', 'L', '0'};
 | 
						|
        RemoteTCPProtocol::encodeUInt32(&metaData[4], getDevice()); // Tuner ID
 | 
						|
        RemoteTCPProtocol::encodeUInt32(&metaData[8], 1); // Gain stages
 | 
						|
        client->write((const char *)metaData, sizeof(metaData));
 | 
						|
        m_bytesTransmitted += sizeof(metaData);
 | 
						|
        client->flush();
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        quint8 metaData[RemoteTCPProtocol::m_sdraMetaDataSize] = {'S', 'D', 'R', 'A'};
 | 
						|
        RemoteTCPProtocol::encodeUInt32(&metaData[4], getDevice());
 | 
						|
 | 
						|
        // Send device/channel settings, so they can be displayed in the remote GUI
 | 
						|
        ChannelWebAPIUtils::getCenterFrequency(m_deviceIndex, m_centerFrequency);
 | 
						|
        ChannelWebAPIUtils::getLOPpmCorrection(m_deviceIndex, m_ppmCorrection);
 | 
						|
        ChannelWebAPIUtils::getDevSampleRate(m_deviceIndex, m_devSampleRate);
 | 
						|
        ChannelWebAPIUtils::getSoftDecim(m_deviceIndex, m_log2Decim);
 | 
						|
        for (int i = 0; i < 4; i++) {
 | 
						|
            ChannelWebAPIUtils::getGain(m_deviceIndex, i, m_gain[i]);
 | 
						|
        }
 | 
						|
        ChannelWebAPIUtils::getRFBandwidth(m_deviceIndex, m_rfBW);
 | 
						|
        ChannelWebAPIUtils::getBiasTee(m_deviceIndex, m_biasTeeEnabled);
 | 
						|
        ChannelWebAPIUtils::getDeviceSetting(m_deviceIndex, "noModMode", m_directSampling);
 | 
						|
        ChannelWebAPIUtils::getAGC(m_deviceIndex, m_agc);
 | 
						|
        ChannelWebAPIUtils::getDCOffsetRemoval(m_deviceIndex, m_dcOffsetRemoval);
 | 
						|
        ChannelWebAPIUtils::getIQCorrection(m_deviceIndex, m_iqCorrection);
 | 
						|
 | 
						|
        quint32 flags =   ((!m_settings.m_iqOnly) << 7)
 | 
						|
                        | (m_settings.m_remoteControl << 6)
 | 
						|
                        | (m_settings.m_squelchEnabled << 5)
 | 
						|
                        | (m_iqCorrection << 4)
 | 
						|
                        | (m_dcOffsetRemoval << 3)
 | 
						|
                        | (m_agc << 2)
 | 
						|
                        | (m_directSampling << 1)
 | 
						|
                        | m_biasTeeEnabled;
 | 
						|
 | 
						|
        RemoteTCPProtocol::encodeUInt64(&metaData[8], (quint64)m_centerFrequency);
 | 
						|
        RemoteTCPProtocol::encodeUInt32(&metaData[16], m_ppmCorrection);
 | 
						|
        RemoteTCPProtocol::encodeUInt32(&metaData[20], flags);
 | 
						|
        RemoteTCPProtocol::encodeUInt32(&metaData[24], m_devSampleRate);
 | 
						|
        RemoteTCPProtocol::encodeUInt32(&metaData[28], m_log2Decim);
 | 
						|
        RemoteTCPProtocol::encodeInt16(&metaData[32], m_gain[0]);
 | 
						|
        RemoteTCPProtocol::encodeInt16(&metaData[34], m_gain[1]);
 | 
						|
        RemoteTCPProtocol::encodeInt16(&metaData[36], m_gain[2]);
 | 
						|
        RemoteTCPProtocol::encodeInt16(&metaData[38], m_gain[3]);
 | 
						|
        RemoteTCPProtocol::encodeUInt32(&metaData[40], m_rfBW);
 | 
						|
        RemoteTCPProtocol::encodeInt32(&metaData[44], m_settings.m_inputFrequencyOffset);
 | 
						|
        RemoteTCPProtocol::encodeUInt32(&metaData[48], m_settings.m_gain);
 | 
						|
        RemoteTCPProtocol::encodeUInt32(&metaData[52], m_settings.m_channelSampleRate);
 | 
						|
        RemoteTCPProtocol::encodeUInt32(&metaData[56], m_settings.m_sampleBits);
 | 
						|
        RemoteTCPProtocol::encodeUInt32(&metaData[60], 1); // Protocol revision. 0=64 byte meta data, 1=128 byte meta data
 | 
						|
        RemoteTCPProtocol::encodeFloat(&metaData[64], m_settings.m_squelch);
 | 
						|
        RemoteTCPProtocol::encodeFloat(&metaData[68], m_settings.m_squelchGate);
 | 
						|
        // Send API port? Not accessible via MainCore
 | 
						|
 | 
						|
        client->write((const char *)metaData, sizeof(metaData));
 | 
						|
        m_bytesTransmitted += sizeof(metaData);
 | 
						|
        client->flush();
 | 
						|
 | 
						|
        // Inform client if they are in a queue
 | 
						|
        if (!m_settings.m_iqOnly && (m_clients.size() > m_settings.m_maxClients) && (m_settings.m_protocol != RemoteTCPSinkSettings::RTL0)) {
 | 
						|
            sendQueuePosition(client, m_clients.size() - m_settings.m_maxClients);
 | 
						|
        }
 | 
						|
 | 
						|
        // Send existing FLAC header to new client
 | 
						|
        if (!m_settings.m_iqOnly && (m_settings.m_compression == RemoteTCPSinkSettings::FLAC) && (m_flacHeader.size() == m_flacHeaderSize) && (m_settings.m_protocol != RemoteTCPSinkSettings::RTL0))
 | 
						|
        {
 | 
						|
            char header[1+4];
 | 
						|
 | 
						|
            header[0] = (char) RemoteTCPProtocol::dataIQFLAC;
 | 
						|
            RemoteTCPProtocol::encodeUInt32((quint8 *) &header[1], m_flacHeader.size());
 | 
						|
            client->write(header, sizeof(header));
 | 
						|
            client->write(m_flacHeader.constData(), m_flacHeader.size());
 | 
						|
            m_bytesTransmitted += sizeof(header) + m_flacHeader.size();
 | 
						|
            client->flush();
 | 
						|
        }
 | 
						|
 | 
						|
        // Send position / direction of antenna
 | 
						|
        sendPosition();
 | 
						|
        if (m_settings.m_isotropic) {
 | 
						|
            sendDirection(true, std::numeric_limits<float>::quiet_NaN(), std::numeric_limits<float>::quiet_NaN());
 | 
						|
        } else if (m_settings.m_rotator == "None") {
 | 
						|
            sendDirection(false, m_settings.m_azimuth, m_settings.m_elevation);
 | 
						|
        } else {
 | 
						|
            sendRotatorDirection(true);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // Create timer to disconnect client after timelimit reached
 | 
						|
    QTimer *timer = new QTimer();
 | 
						|
    timer->setSingleShot(true);
 | 
						|
    timer->callOnTimeout(this, [this, client] () {
 | 
						|
        qDebug() << "Disconnecting" << client->peerAddress() << "as time limit reached";
 | 
						|
        if (m_settings.m_compression) {
 | 
						|
            sendTimeLimit(client);
 | 
						|
        }
 | 
						|
        client->close();
 | 
						|
    });
 | 
						|
    if (m_settings.m_timeLimit > 0)
 | 
						|
    {
 | 
						|
        timer->setInterval(m_settings.m_timeLimit * 60 * 1000);
 | 
						|
        // Only start timer if we will receive data immediately
 | 
						|
        if (m_clients.size() <= m_settings.m_maxClients) {
 | 
						|
            timer->start();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    m_timers.append(timer);
 | 
						|
 | 
						|
    // Close connection if blacklisted
 | 
						|
    for (const auto& ip : m_settings.m_ipBlacklist)
 | 
						|
    {
 | 
						|
        QHostAddress address(ip);
 | 
						|
        if (address == client->peerAddress())
 | 
						|
        {
 | 
						|
            qDebug() << "Disconnecting" << client->peerAddress() << "as blacklisted";
 | 
						|
            if (m_settings.m_compression) {
 | 
						|
                sendBlacklisted(client);
 | 
						|
            }
 | 
						|
            client->close();
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    m_messageQueueToChannel->push(RemoteTCPSink::MsgReportConnection::create(m_clients.size(), client->peerAddress(), client->peerPort()));
 | 
						|
    if (m_messageQueueToGUI) {
 | 
						|
        m_messageQueueToGUI->push(RemoteTCPSink::MsgReportConnection::create(m_clients.size(), client->peerAddress(), client->peerPort()));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::disconnected()
 | 
						|
{
 | 
						|
    QMutexLocker mutexLocker(&m_mutex);
 | 
						|
    qDebug() << "RemoteTCPSinkSink::disconnected";
 | 
						|
    QObject *object = sender();
 | 
						|
    QMutableListIterator<Socket *> itr(m_clients);
 | 
						|
 | 
						|
    // Remove from list of clients
 | 
						|
    int idx = 0;
 | 
						|
    while (itr.hasNext())
 | 
						|
    {
 | 
						|
        Socket *socket = itr.next();
 | 
						|
        if (socket->socket() == object)
 | 
						|
        {
 | 
						|
            itr.remove();
 | 
						|
            delete m_timers.takeAt(idx);
 | 
						|
            m_messageQueueToChannel->push(RemoteTCPSink::MsgReportDisconnect::create(m_clients.size(), socket->peerAddress(), socket->peerPort()));
 | 
						|
            if (m_messageQueueToGUI) {
 | 
						|
                m_messageQueueToGUI->push(RemoteTCPSink::MsgReportDisconnect::create(m_clients.size(), socket->peerAddress(), socket->peerPort()));
 | 
						|
            }
 | 
						|
            socket->deleteLater();
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            idx++;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // Start timer for next waiting client
 | 
						|
    if ((idx < m_settings.m_maxClients) && (m_settings.m_timeLimit > 0))
 | 
						|
    {
 | 
						|
        int newActiveIdx = m_settings.m_maxClients - 1;
 | 
						|
        if (newActiveIdx < m_clients.size()) {
 | 
						|
            m_timers[newActiveIdx]->start();
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // Update other clients waiting with current queue position
 | 
						|
    for (int i = m_settings.m_maxClients; i < m_clients.size(); i++) {
 | 
						|
        sendQueuePosition(m_clients[i], i - m_settings.m_maxClients + 1);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
#ifndef QT_NO_OPENSSL
 | 
						|
void RemoteTCPSinkSink::onSslErrors(const QList<QSslError> &errors)
 | 
						|
{
 | 
						|
    qWarning() << "RemoteTCPSinkSink::onSslErrors: " << errors;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
void RemoteTCPSinkSink::errorOccurred(QAbstractSocket::SocketError socketError)
 | 
						|
{
 | 
						|
    qDebug() << "RemoteTCPSinkSink::errorOccurred: " << socketError;
 | 
						|
    /*if (m_msgQueueToFeature)
 | 
						|
    {
 | 
						|
        RemoteTCPSinkSink::MsgReportWorker *msg = RemoteTCPSinkSink::MsgReportWorker::create(m_socket.errorString() + " " + socketError);
 | 
						|
        m_msgQueueToFeature->push(msg);
 | 
						|
    }*/
 | 
						|
}
 | 
						|
 | 
						|
Socket *RemoteTCPSinkSink::getSocket(QObject *object) const
 | 
						|
{
 | 
						|
    for (const auto client : m_clients)
 | 
						|
    {
 | 
						|
        if (client->socket() == object) {
 | 
						|
            return client;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::processCommand()
 | 
						|
{
 | 
						|
    QMutexLocker mutexLocker(&m_mutex);
 | 
						|
    Socket *client = getSocket(sender());
 | 
						|
    RemoteTCPSinkSettings settings = m_settings;
 | 
						|
    quint8 cmd[5];
 | 
						|
 | 
						|
    while (client && (client->bytesAvailable() >= (qint64)sizeof(cmd)))
 | 
						|
    {
 | 
						|
        int len = client->read((char *)cmd, sizeof(cmd));
 | 
						|
 | 
						|
        if (len == sizeof(cmd))
 | 
						|
        {
 | 
						|
            if (cmd[0] == RemoteTCPProtocol::sendMessage)
 | 
						|
            {
 | 
						|
                quint32 msgLen = RemoteTCPProtocol::extractUInt32(&cmd[1]);
 | 
						|
                try
 | 
						|
                {
 | 
						|
                    char *buf = new char[msgLen];
 | 
						|
                    len = client->read((char *)buf, msgLen);
 | 
						|
                    if (len == (int) msgLen)
 | 
						|
                    {
 | 
						|
                        bool broadcast = (bool) buf[0];
 | 
						|
                        int i;
 | 
						|
                        for (i = 1; i < (int) msgLen; i++)
 | 
						|
                        {
 | 
						|
                            if (buf[i] == '\0') {
 | 
						|
                                break;
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                        QString callsign = QString::fromUtf8(&buf[1]);
 | 
						|
                        QString text = QString::fromUtf8(&buf[i+1]);
 | 
						|
 | 
						|
                        if (m_messageQueueToGUI) {
 | 
						|
                            m_messageQueueToGUI->push(RemoteTCPSink::MsgSendMessage::create(client->peerAddress(), client->peerPort(), callsign, text, broadcast));
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        qDebug() << "RemoteTCPSinkSink::processCommand: sendMessage: Failed to read" << msgLen;
 | 
						|
                    }
 | 
						|
                    delete[] buf;
 | 
						|
                }
 | 
						|
                catch(std::bad_alloc&)
 | 
						|
                {
 | 
						|
                    qDebug() << "RemoteTCPSinkSink::processCommand: sendMessage - Failed to allocate" << msgLen;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else if (!m_settings.m_remoteControl)
 | 
						|
            {
 | 
						|
                qDebug() << "RemoteTCPSinkSink::processCommand: Ignoring command from client as remote control disabled";
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                switch (cmd[0])
 | 
						|
                {
 | 
						|
                case RemoteTCPProtocol::setCenterFrequency:
 | 
						|
                {
 | 
						|
                    quint64 centerFrequency = RemoteTCPProtocol::extractUInt32(&cmd[1]);
 | 
						|
                    qDebug() << "RemoteTCPSinkSink::processCommand: set center frequency " << centerFrequency;
 | 
						|
                    ChannelWebAPIUtils::setCenterFrequency(m_deviceIndex, (double)centerFrequency);
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
                case RemoteTCPProtocol::setSampleRate:
 | 
						|
                {
 | 
						|
                    int sampleRate = RemoteTCPProtocol::extractUInt32(&cmd[1]);
 | 
						|
                    qDebug() << "RemoteTCPSinkSink::processCommand: set sample rate " << sampleRate;
 | 
						|
                    ChannelWebAPIUtils::setDevSampleRate(m_deviceIndex, sampleRate);
 | 
						|
                    if (m_settings.m_protocol == RemoteTCPSinkSettings::RTL0)
 | 
						|
                    {
 | 
						|
                        // Match channel sample rate with device sample rate for RTL0 protocol
 | 
						|
                        ChannelWebAPIUtils::setSoftDecim(m_deviceIndex, 0);
 | 
						|
                        settings.m_channelSampleRate = sampleRate;
 | 
						|
                        if (m_messageQueueToGUI) {
 | 
						|
                            m_messageQueueToGUI->push(RemoteTCPSink::MsgConfigureRemoteTCPSink::create(settings, {"channelSampleRate"}, false));
 | 
						|
                        }
 | 
						|
                        if (m_messageQueueToChannel) {
 | 
						|
                            m_messageQueueToChannel->push(RemoteTCPSink::MsgConfigureRemoteTCPSink::create(settings, {"channelSampleRate"}, false));
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
                case RemoteTCPProtocol::setTunerGainMode:
 | 
						|
                    // SDRangel's rtlsdr sample source always has this fixed as 1, so nothing to do
 | 
						|
                    break;
 | 
						|
                case RemoteTCPProtocol::setTunerGain: // gain is gain in 10th of a dB
 | 
						|
                {
 | 
						|
                    int gain = RemoteTCPProtocol::extractUInt32(&cmd[1]);
 | 
						|
                    qDebug() << "RemoteTCPSinkSink::processCommand: set gain " << gain;
 | 
						|
                    ChannelWebAPIUtils::setGain(m_deviceIndex, 0, gain);
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
                case RemoteTCPProtocol::setFrequencyCorrection:
 | 
						|
                {
 | 
						|
                    int ppm = RemoteTCPProtocol::extractUInt32(&cmd[1]);
 | 
						|
                    qDebug() << "RemoteTCPSinkSink::processCommand: set LO ppm correction " << ppm;
 | 
						|
                    ChannelWebAPIUtils::setLOPpmCorrection(m_deviceIndex, ppm);
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
                case RemoteTCPProtocol::setTunerIFGain:
 | 
						|
                {
 | 
						|
                    int v = RemoteTCPProtocol::extractUInt32(&cmd[1]);
 | 
						|
                    int gain = (int)(qint16)(v & 0xffff);
 | 
						|
                    int stage = (v >> 16) & 0xffff;
 | 
						|
                    ChannelWebAPIUtils::setGain(m_deviceIndex, stage, gain);
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
                case RemoteTCPProtocol::setAGCMode:
 | 
						|
                {
 | 
						|
                    int agc = RemoteTCPProtocol::extractUInt32(&cmd[1]);
 | 
						|
                    qDebug() << "RemoteTCPSinkSink::processCommand: set AGC " << agc;
 | 
						|
                    ChannelWebAPIUtils::setAGC(m_deviceIndex, agc);
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
                case RemoteTCPProtocol::setDirectSampling:
 | 
						|
                {
 | 
						|
                    int ds = RemoteTCPProtocol::extractUInt32(&cmd[1]);
 | 
						|
                    qDebug() << "RemoteTCPSinkSink::processCommand: set direct sampling " << ds;
 | 
						|
                    ChannelWebAPIUtils::patchDeviceSetting(m_deviceIndex, "noModMode", ds);  // RTLSDR only
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
                case RemoteTCPProtocol::setBiasTee:
 | 
						|
                {
 | 
						|
                    int biasTee = RemoteTCPProtocol::extractUInt32(&cmd[1]);
 | 
						|
                    qDebug() << "RemoteTCPSinkSink::processCommand: set bias tee " << biasTee;
 | 
						|
                    ChannelWebAPIUtils::setBiasTee(m_deviceIndex, biasTee);
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
                case RemoteTCPProtocol::setTunerBandwidth:
 | 
						|
                {
 | 
						|
                    int rfBW = RemoteTCPProtocol::extractUInt32(&cmd[1]);
 | 
						|
                    qDebug() << "RemoteTCPSinkSink::processCommand: set tuner bandwidth " << rfBW;
 | 
						|
                    ChannelWebAPIUtils::setRFBandwidth(m_deviceIndex, rfBW);
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
                case RemoteTCPProtocol::setDecimation:
 | 
						|
                {
 | 
						|
                    int dec = RemoteTCPProtocol::extractUInt32(&cmd[1]);
 | 
						|
                    qDebug() << "RemoteTCPSinkSink::processCommand: set decimation " << dec;
 | 
						|
                    ChannelWebAPIUtils::setSoftDecim(m_deviceIndex, dec);
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
                case RemoteTCPProtocol::setDCOffsetRemoval:
 | 
						|
                {
 | 
						|
                    int dc = RemoteTCPProtocol::extractUInt32(&cmd[1]);
 | 
						|
                    qDebug() << "RemoteTCPSinkSink::processCommand: set DC offset removal " << dc;
 | 
						|
                    ChannelWebAPIUtils::setDCOffsetRemoval(m_deviceIndex, dc);
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
                case RemoteTCPProtocol::setIQCorrection:
 | 
						|
                {
 | 
						|
                    int iq = RemoteTCPProtocol::extractUInt32(&cmd[1]);
 | 
						|
                    qDebug() << "RemoteTCPSinkSink::processCommand: set IQ correction " << iq;
 | 
						|
                    ChannelWebAPIUtils::setIQCorrection(m_deviceIndex, iq);
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
                case RemoteTCPProtocol::setChannelSampleRate:
 | 
						|
                {
 | 
						|
                    int channelSampleRate = RemoteTCPProtocol::extractUInt32(&cmd[1]);
 | 
						|
                    qDebug() << "RemoteTCPSinkSink::processCommand: set channel sample rate " << channelSampleRate;
 | 
						|
                    bool restartRequired;
 | 
						|
                    if (channelSampleRate <= m_settings.m_maxSampleRate)
 | 
						|
                    {
 | 
						|
                        settings.m_channelSampleRate = channelSampleRate;
 | 
						|
                        restartRequired = false;
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        settings.m_channelSampleRate = m_settings.m_maxSampleRate;
 | 
						|
                        restartRequired = true; // Need to restart so client gets max sample rate
 | 
						|
                    }
 | 
						|
                    if (m_messageQueueToGUI) {
 | 
						|
                        m_messageQueueToGUI->push(RemoteTCPSink::MsgConfigureRemoteTCPSink::create(settings, {"channelSampleRate"}, false, restartRequired));
 | 
						|
                    }
 | 
						|
                    if (m_messageQueueToChannel) {
 | 
						|
                        m_messageQueueToChannel->push(RemoteTCPSink::MsgConfigureRemoteTCPSink::create(settings, {"channelSampleRate"}, false, restartRequired));
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
                case RemoteTCPProtocol::setChannelFreqOffset:
 | 
						|
                {
 | 
						|
                    int offset = (int)RemoteTCPProtocol::extractUInt32(&cmd[1]);
 | 
						|
                    qDebug() << "RemoteTCPSinkSink::processCommand: set channel input frequency offset " << offset;
 | 
						|
                    settings.m_inputFrequencyOffset = offset;
 | 
						|
                    if (m_messageQueueToGUI) {
 | 
						|
                        m_messageQueueToGUI->push(RemoteTCPSink::MsgConfigureRemoteTCPSink::create(settings, {"inputFrequencyOffset"}, false));
 | 
						|
                    }
 | 
						|
                    if (m_messageQueueToChannel) {
 | 
						|
                        m_messageQueueToChannel->push(RemoteTCPSink::MsgConfigureRemoteTCPSink::create(settings, {"inputFrequencyOffset"}, false));
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
                case RemoteTCPProtocol::setChannelGain:
 | 
						|
                {
 | 
						|
                    int gain = (int)RemoteTCPProtocol::extractUInt32(&cmd[1]);
 | 
						|
                    qDebug() << "RemoteTCPSinkSink::processCommand: set channel gain " << gain;
 | 
						|
                    settings.m_gain = gain;
 | 
						|
                    if (m_messageQueueToGUI) {
 | 
						|
                        m_messageQueueToGUI->push(RemoteTCPSink::MsgConfigureRemoteTCPSink::create(settings, {"gain"}, false));
 | 
						|
                    }
 | 
						|
                    if (m_messageQueueToChannel) {
 | 
						|
                        m_messageQueueToChannel->push(RemoteTCPSink::MsgConfigureRemoteTCPSink::create(settings, {"gain"}, false));
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
                case RemoteTCPProtocol::setSampleBitDepth:
 | 
						|
                {
 | 
						|
                    int bits = RemoteTCPProtocol::extractUInt32(&cmd[1]);
 | 
						|
                    qDebug() << "RemoteTCPSinkSink::processCommand: set sample bit depth " << bits;
 | 
						|
                    settings.m_sampleBits = bits;
 | 
						|
                    if (m_messageQueueToGUI) {
 | 
						|
                        m_messageQueueToGUI->push(RemoteTCPSink::MsgConfigureRemoteTCPSink::create(settings, {"sampleBits"}, false));
 | 
						|
                    }
 | 
						|
                    if (m_messageQueueToChannel) {
 | 
						|
                        m_messageQueueToChannel->push(RemoteTCPSink::MsgConfigureRemoteTCPSink::create(settings, {"sampleBits"}, false));
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
                case RemoteTCPProtocol::setIQSquelchEnabled:
 | 
						|
                {
 | 
						|
                    bool enabled = (bool) RemoteTCPProtocol::extractUInt32(&cmd[1]);
 | 
						|
                    qDebug() << "RemoteTCPSinkSink::processCommand: set IQ squelch enabled " << enabled;
 | 
						|
                    settings.m_squelchEnabled = enabled;
 | 
						|
                    if (m_messageQueueToGUI) {
 | 
						|
                        m_messageQueueToGUI->push(RemoteTCPSink::MsgConfigureRemoteTCPSink::create(settings, {"squelchEnabled"}, false));
 | 
						|
                    }
 | 
						|
                    if (m_messageQueueToChannel) {
 | 
						|
                        m_messageQueueToChannel->push(RemoteTCPSink::MsgConfigureRemoteTCPSink::create(settings, {"squelchEnabled"}, false));
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
                case RemoteTCPProtocol::setIQSquelch:
 | 
						|
                {
 | 
						|
                    float squelch = RemoteTCPProtocol::extractFloat(&cmd[1]);
 | 
						|
                    qDebug() << "RemoteTCPSinkSink::processCommand: set IQ squelch " << squelch;
 | 
						|
                    settings.m_squelch = squelch;
 | 
						|
                    if (m_messageQueueToGUI) {
 | 
						|
                        m_messageQueueToGUI->push(RemoteTCPSink::MsgConfigureRemoteTCPSink::create(settings, {"squelch"}, false));
 | 
						|
                    }
 | 
						|
                    if (m_messageQueueToChannel) {
 | 
						|
                        m_messageQueueToChannel->push(RemoteTCPSink::MsgConfigureRemoteTCPSink::create(settings, {"squelch"}, false));
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
                case RemoteTCPProtocol::setIQSquelchGate:
 | 
						|
                {
 | 
						|
                    float squelchGate = RemoteTCPProtocol::extractFloat(&cmd[1]);
 | 
						|
                    qDebug() << "RemoteTCPSinkSink::processCommand: set IQ squelch gate " << squelchGate;
 | 
						|
                    settings.m_squelchGate = squelchGate;
 | 
						|
                    if (m_messageQueueToGUI) {
 | 
						|
                        m_messageQueueToGUI->push(RemoteTCPSink::MsgConfigureRemoteTCPSink::create(settings, {"squelchGate"}, false));
 | 
						|
                    }
 | 
						|
                    if (m_messageQueueToChannel) {
 | 
						|
                        m_messageQueueToChannel->push(RemoteTCPSink::MsgConfigureRemoteTCPSink::create(settings, {"squelchGate"}, false));
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
                default:
 | 
						|
                    qDebug() << "RemoteTCPSinkSink::processCommand: unknown command " << cmd[0];
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            qDebug() << "RemoteTCPSinkSink::processCommand: read only " << len << " bytes - Expecting " << sizeof(cmd);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::sendCommand(RemoteTCPProtocol::Command cmdId, quint32 value)
 | 
						|
{
 | 
						|
    QMutexLocker mutexLocker(&m_mutex);
 | 
						|
    quint8 cmd[5];
 | 
						|
 | 
						|
    cmd[0] = (quint8) cmdId;
 | 
						|
    RemoteTCPProtocol::encodeUInt32(&cmd[1], value);
 | 
						|
 | 
						|
    for (const auto client : m_clients)
 | 
						|
    {
 | 
						|
        qint64 len = client->write((char *) cmd, sizeof(cmd));
 | 
						|
        if (len != sizeof(cmd)) {
 | 
						|
            qDebug() << "RemoteTCPSinkSink::sendCommand: Failed to write all of message:" << len;
 | 
						|
        }
 | 
						|
        m_bytesTransmitted += sizeof(cmd);
 | 
						|
        client->flush();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::sendCommandFloat(RemoteTCPProtocol::Command cmdId, float value)
 | 
						|
{
 | 
						|
    QMutexLocker mutexLocker(&m_mutex);
 | 
						|
    quint8 cmd[5];
 | 
						|
 | 
						|
    cmd[0] = (quint8) cmdId;
 | 
						|
    RemoteTCPProtocol::encodeFloat(&cmd[1], value);
 | 
						|
 | 
						|
    for (const auto client : m_clients)
 | 
						|
    {
 | 
						|
        qint64 len = client->write((char *) cmd, sizeof(cmd));
 | 
						|
        if (len != sizeof(cmd)) {
 | 
						|
            qDebug() << "RemoteTCPSinkSink::sendCommand: Failed to write all of message:" << len;
 | 
						|
        }
 | 
						|
        m_bytesTransmitted += sizeof(cmd);
 | 
						|
        client->flush();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::sendMessage(QHostAddress address, quint16 port, const QString& callsign, const QString& text, bool broadcast)
 | 
						|
{
 | 
						|
    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;
 | 
						|
 | 
						|
    for (const auto client : m_clients)
 | 
						|
    {
 | 
						|
        bool addressMatch = (address == client->peerAddress()) && (port == client->peerPort());
 | 
						|
        if ((broadcast && !addressMatch) || (!broadcast && addressMatch))
 | 
						|
        {
 | 
						|
            len = client->write(cmd, sizeof(cmd));
 | 
						|
            if (len != sizeof(cmd)) {
 | 
						|
                 qDebug() << "RemoteTCPSinkSink::sendMessage: Failed to write all of message header:" << len;
 | 
						|
            }
 | 
						|
            len = client->write(bytes.data(), bytes.size());
 | 
						|
            if (len != bytes.size()) {
 | 
						|
                 qDebug() << "RemoteTCPSinkSink::sendMessage: Failed to write all of message:" << len;
 | 
						|
            }
 | 
						|
            m_bytesTransmitted += sizeof(cmd) + bytes.size();
 | 
						|
            client->flush();
 | 
						|
            qDebug() << "RemoteTCPSinkSink::sendMessage:" << client->peerAddress() << client->peerPort() << text;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::sendQueuePosition(Socket *client, int position)
 | 
						|
{
 | 
						|
    QString callsign = MainCore::instance()->getSettings().getStationName();
 | 
						|
 | 
						|
    sendMessage(client->peerAddress(), client->peerPort(), callsign, QString("Server busy. You are number %1 in the queue.").arg(position), false);
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::sendBlacklisted(Socket *client)
 | 
						|
{
 | 
						|
    char cmd[1+4];
 | 
						|
 | 
						|
    cmd[0] = (char) RemoteTCPProtocol::sendBlacklistedMessage;
 | 
						|
    RemoteTCPProtocol::encodeUInt32((quint8*) &cmd[1], 0);
 | 
						|
 | 
						|
    qint64 len = client->write(cmd, sizeof(cmd));
 | 
						|
    if (len != sizeof(cmd)) {
 | 
						|
        qDebug() << "RemoteTCPSinkSink::sendBlacklisted: Failed to write all of message:" << len;
 | 
						|
    }
 | 
						|
    m_bytesTransmitted += sizeof(cmd);
 | 
						|
    client->flush();
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::sendTimeLimit(Socket *client)
 | 
						|
{
 | 
						|
    QString callsign = MainCore::instance()->getSettings().getStationName();
 | 
						|
 | 
						|
    sendMessage(client->peerAddress(), client->peerPort(), callsign, "Time limit reached.", false);
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::sendPosition(float latitude, float longitude, float altitude)
 | 
						|
{
 | 
						|
    char msg[1+4+4+4+4];
 | 
						|
    msg[0] = (char) RemoteTCPProtocol::dataPosition;
 | 
						|
    RemoteTCPProtocol::encodeUInt32((quint8 *) &msg[1], 4+4+4);
 | 
						|
    RemoteTCPProtocol::encodeFloat((quint8 *) &msg[1+4], latitude);
 | 
						|
    RemoteTCPProtocol::encodeFloat((quint8 *) &msg[1+4+4], longitude);
 | 
						|
    RemoteTCPProtocol::encodeFloat((quint8 *) &msg[1+4+4+4], altitude);
 | 
						|
 | 
						|
    int clients = std::min((int) m_clients.size(), m_settings.m_maxClients);
 | 
						|
    for (int i = 0; i < clients; i++)
 | 
						|
    {
 | 
						|
        Socket *client = m_clients[i];
 | 
						|
        client->write(msg, sizeof(msg));
 | 
						|
        m_bytesTransmitted += sizeof(msg);
 | 
						|
        client->flush();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::sendDirection(bool isotropic, float azimuth, float elevation)
 | 
						|
{
 | 
						|
    char msg[1+4+4+4+4];
 | 
						|
    msg[0] = (char) RemoteTCPProtocol::dataDirection;
 | 
						|
    RemoteTCPProtocol::encodeUInt32((quint8 *) &msg[1], 4+4+4);
 | 
						|
    RemoteTCPProtocol::encodeUInt32((quint8 *) &msg[1+4], isotropic);
 | 
						|
    RemoteTCPProtocol::encodeFloat((quint8 *) &msg[1+4+4], azimuth);
 | 
						|
    RemoteTCPProtocol::encodeFloat((quint8 *) &msg[1+4+4+4], elevation);
 | 
						|
 | 
						|
    int clients = std::min((int) m_clients.size(), m_settings.m_maxClients);
 | 
						|
    for (int i = 0; i < clients; i++)
 | 
						|
    {
 | 
						|
        Socket *client = m_clients[i];
 | 
						|
        client->write(msg, sizeof(msg));
 | 
						|
        m_bytesTransmitted += sizeof(msg);
 | 
						|
        client->flush();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::sendPosition()
 | 
						|
{
 | 
						|
    float latitude = MainCore::instance()->getSettings().getLatitude();
 | 
						|
    float longitude = MainCore::instance()->getSettings().getLongitude();
 | 
						|
    float altitude = MainCore::instance()->getSettings().getAltitude();
 | 
						|
 | 
						|
    // Use device postion in preference to My Position
 | 
						|
    ChannelWebAPIUtils::getDevicePosition(m_deviceIndex, latitude, longitude, altitude);
 | 
						|
 | 
						|
    sendPosition(latitude, longitude, altitude);
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::sendRotatorDirection(bool force)
 | 
						|
{
 | 
						|
    unsigned int rotatorFeatureSetIndex;
 | 
						|
    unsigned int rotatorFeatureIndex;
 | 
						|
 | 
						|
    if (MainCore::getFeatureIndexFromId(m_settings.m_rotator, rotatorFeatureSetIndex, rotatorFeatureIndex))
 | 
						|
    {
 | 
						|
        double azimuth;
 | 
						|
        double elevation;
 | 
						|
 | 
						|
        if (ChannelWebAPIUtils::getFeatureReportValue(rotatorFeatureSetIndex, rotatorFeatureIndex, "currentAzimuth", azimuth)
 | 
						|
            && ChannelWebAPIUtils::getFeatureReportValue(rotatorFeatureSetIndex, rotatorFeatureIndex, "currentElevation", elevation))
 | 
						|
        {
 | 
						|
            if (force || ((azimuth != m_azimuth) || (elevation != m_elevation)))
 | 
						|
            {
 | 
						|
                sendDirection(false, (float) azimuth, (float) elevation);
 | 
						|
                m_azimuth = azimuth;
 | 
						|
                m_elevation = elevation;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RemoteTCPSinkSink::preferenceChanged(int elementType)
 | 
						|
{
 | 
						|
    Preferences::ElementType pref = (Preferences::ElementType)elementType;
 | 
						|
 | 
						|
    if ((pref == Preferences::Latitude) || (pref == Preferences::Longitude) || (pref == Preferences::Altitude)) {
 | 
						|
        sendPosition();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Poll for changes to device settings - FIXME: Need a signal from DeviceAPI - reverseAPI?
 | 
						|
void RemoteTCPSinkSink::checkDeviceSettings()
 | 
						|
{
 | 
						|
    if ((m_settings.m_protocol != RemoteTCPSinkSettings::RTL0) && !m_settings.m_iqOnly)
 | 
						|
    {
 | 
						|
        // Forward device settings to clients if they've changed
 | 
						|
 | 
						|
        double centerFrequency;
 | 
						|
        if (ChannelWebAPIUtils::getCenterFrequency(m_deviceIndex, centerFrequency))
 | 
						|
        {
 | 
						|
            if (centerFrequency != m_centerFrequency)
 | 
						|
            {
 | 
						|
                m_centerFrequency = centerFrequency;
 | 
						|
                sendCommand(RemoteTCPProtocol::setCenterFrequency, m_centerFrequency);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        int ppmCorrection;
 | 
						|
        if (ChannelWebAPIUtils::getLOPpmCorrection(m_deviceIndex, ppmCorrection))
 | 
						|
        {
 | 
						|
            if (ppmCorrection != m_ppmCorrection)
 | 
						|
            {
 | 
						|
                m_ppmCorrection = ppmCorrection;
 | 
						|
                sendCommand(RemoteTCPProtocol::setFrequencyCorrection, m_ppmCorrection);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        int biasTeeEnabled;
 | 
						|
        if (ChannelWebAPIUtils::getBiasTee(m_deviceIndex, biasTeeEnabled))
 | 
						|
        {
 | 
						|
            if (biasTeeEnabled != m_biasTeeEnabled)
 | 
						|
            {
 | 
						|
                m_biasTeeEnabled = biasTeeEnabled;
 | 
						|
                sendCommand(RemoteTCPProtocol::setBiasTee, m_biasTeeEnabled);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        int directSampling;
 | 
						|
        if (ChannelWebAPIUtils::getDeviceSetting(m_deviceIndex, "noModMode", directSampling))
 | 
						|
        {
 | 
						|
            if (directSampling != m_directSampling)
 | 
						|
            {
 | 
						|
                m_directSampling = directSampling;
 | 
						|
                sendCommand(RemoteTCPProtocol::setDirectSampling, m_directSampling);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        int agc;
 | 
						|
        if (ChannelWebAPIUtils::getAGC(m_deviceIndex, agc))
 | 
						|
        {
 | 
						|
            if (agc != m_agc)
 | 
						|
            {
 | 
						|
                m_agc = agc;
 | 
						|
                sendCommand(RemoteTCPProtocol::setAGCMode, m_agc);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        int dcOffsetRemoval;
 | 
						|
        if (ChannelWebAPIUtils::getDCOffsetRemoval(m_deviceIndex, dcOffsetRemoval))
 | 
						|
        {
 | 
						|
            if (dcOffsetRemoval != m_dcOffsetRemoval)
 | 
						|
            {
 | 
						|
                m_dcOffsetRemoval = dcOffsetRemoval;
 | 
						|
                sendCommand(RemoteTCPProtocol::setDCOffsetRemoval, m_dcOffsetRemoval);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        int iqCorrection;
 | 
						|
        if (ChannelWebAPIUtils::getIQCorrection(m_deviceIndex, iqCorrection))
 | 
						|
        {
 | 
						|
            if (iqCorrection != m_iqCorrection)
 | 
						|
            {
 | 
						|
                m_iqCorrection = iqCorrection;
 | 
						|
                sendCommand(RemoteTCPProtocol::setIQCorrection, m_iqCorrection);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        qint32 devSampleRate;
 | 
						|
        if (ChannelWebAPIUtils::getDevSampleRate(m_deviceIndex, devSampleRate))
 | 
						|
        {
 | 
						|
            if (devSampleRate != m_devSampleRate)
 | 
						|
            {
 | 
						|
                m_devSampleRate = devSampleRate;
 | 
						|
                sendCommand(RemoteTCPProtocol::setSampleRate, m_devSampleRate);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        qint32 log2Decim;
 | 
						|
        if (ChannelWebAPIUtils::getSoftDecim(m_deviceIndex, log2Decim))
 | 
						|
        {
 | 
						|
            if (log2Decim != m_log2Decim)
 | 
						|
            {
 | 
						|
                m_log2Decim = log2Decim;
 | 
						|
                sendCommand(RemoteTCPProtocol::setDecimation, m_log2Decim);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        qint32 rfBW;
 | 
						|
        if (ChannelWebAPIUtils::getRFBandwidth(m_deviceIndex, rfBW))
 | 
						|
        {
 | 
						|
            if (rfBW != m_rfBW)
 | 
						|
            {
 | 
						|
                m_rfBW = rfBW;
 | 
						|
                sendCommand(RemoteTCPProtocol::setTunerBandwidth, m_rfBW);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        for (int i = 0; i < 4; i++)
 | 
						|
        {
 | 
						|
            qint32 gain;
 | 
						|
            if (ChannelWebAPIUtils::getGain(m_deviceIndex, i, gain))
 | 
						|
            {
 | 
						|
                if (gain != m_gain[i])
 | 
						|
                {
 | 
						|
                    m_gain[i] = gain;
 | 
						|
                    if (i == 0)
 | 
						|
                    {
 | 
						|
                        sendCommand(RemoteTCPProtocol::setTunerGain, gain);
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        int v = (gain & 0xffff) | (i << 16);
 | 
						|
                        sendCommand(RemoteTCPProtocol::setTunerIFGain, v);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        if (!m_settings.m_isotropic && !m_settings.m_rotator.isEmpty() && (m_settings.m_rotator != "None")) {
 | 
						|
            sendRotatorDirection(false);
 | 
						|
        }
 | 
						|
 | 
						|
    }
 | 
						|
}
 |