1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-21 23:55:13 -05:00

Compare commits

..

No commits in common. "ef4b54a7b1e8b91c02b2042fbcea88001fb59a61" and "bb1f833d02726eafc75590b7b2957f36d9cbaf78" have entirely different histories.

32 changed files with 251 additions and 100 deletions

View File

@ -85,6 +85,7 @@ void RemoteSinkSender::sendDataBlock(RemoteDataBlock *dataBlock)
uint16_t frameIndex = dataBlock->m_txControlBlock.m_frameIndex;
int nbBlocksFEC = dataBlock->m_txControlBlock.m_nbBlocksFEC;
int txDelay = dataBlock->m_txControlBlock.m_txDelay;
m_address.setAddress(dataBlock->m_txControlBlock.m_dataAddress);
uint16_t dataPort = dataBlock->m_txControlBlock.m_dataPort;
RemoteSuperBlock *txBlockx = dataBlock->m_superBlocks;
@ -93,8 +94,11 @@ void RemoteSinkSender::sendDataBlock(RemoteDataBlock *dataBlock)
{
if (m_socket)
{
for (int i = 0; i < RemoteNbOrginalBlocks; i++) { // send block via UDP
for (int i = 0; i < RemoteNbOrginalBlocks; i++)
{
// send block via UDP
m_socket->writeDatagram((const char*)&txBlockx[i], (qint64 ) RemoteUdpSize, m_address, dataPort);
std::this_thread::sleep_for(std::chrono::microseconds(txDelay));
}
}
}
@ -135,8 +139,11 @@ void RemoteSinkSender::sendDataBlock(RemoteDataBlock *dataBlock)
// Transmit all blocks
if (m_socket)
{
for (int i = 0; i < cm256Params.OriginalCount + cm256Params.RecoveryCount; i++) { // send block via UDP
for (int i = 0; i < cm256Params.OriginalCount + cm256Params.RecoveryCount; i++)
{
// send block via UDP
m_socket->writeDatagram((const char*)&txBlockx[i], (qint64 ) RemoteUdpSize, m_address, dataPort);
std::this_thread::sleep_for(std::chrono::microseconds(txDelay));
}
}
}

View File

@ -36,6 +36,7 @@ RemoteSinkSink::RemoteSinkSink() :
m_frequencyOffset(0),
m_basebandSampleRate(48000),
m_nbBlocksFEC(0),
m_txDelay(35),
m_dataAddress("127.0.0.1"),
m_dataPort(9090)
{
@ -164,6 +165,7 @@ void RemoteSinkSink::feed(const SampleVector::const_iterator& begin, const Sampl
m_dataBlock->m_txControlBlock.m_processed = false;
m_dataBlock->m_txControlBlock.m_complete = true;
m_dataBlock->m_txControlBlock.m_nbBlocksFEC = m_nbBlocksFEC;
m_dataBlock->m_txControlBlock.m_txDelay = m_txDelay;
m_dataBlock->m_txControlBlock.m_dataAddress = m_dataAddress;
m_dataBlock->m_txControlBlock.m_dataPort = m_dataPort;

View File

@ -61,6 +61,7 @@ private:
int64_t m_frequencyOffset;
uint32_t m_basebandSampleRate;
int m_nbBlocksFEC;
int m_txDelay;
QString m_dataAddress;
uint16_t m_dataPort;

View File

@ -330,9 +330,12 @@ void RemoteSourceGUI::on_dataPort_returnPressed()
bool dataOk;
int dataPort = ui->dataPort->text().toInt(&dataOk);
if ((!dataOk) || (dataPort < 1024) || (dataPort > 65535)) {
if((!dataOk) || (dataPort < 1024) || (dataPort > 65535))
{
return;
} else {
}
else
{
m_settings.m_dataPort = dataPort;
}
@ -347,7 +350,8 @@ void RemoteSourceGUI::on_dataApplyButton_clicked(bool checked)
bool dataOk;
int udpDataPort = ui->dataPort->text().toInt(&dataOk);
if ((dataOk) && (udpDataPort >= 1024) && (udpDataPort < 65535)) {
if((dataOk) && (udpDataPort >= 1024) && (udpDataPort < 65535))
{
m_settings.m_dataPort = udpDataPort;
}

View File

@ -139,7 +139,7 @@
</spacer>
</item>
<item>
<widget class="QPushButton" name="dataApplyButton">
<widget class="QPushButton" name="pushButton">
<property name="maximumSize">
<size>
<width>30</width>

View File

@ -35,9 +35,7 @@ RemoteSourceSource::RemoteSourceSource() :
}
RemoteSourceSource::~RemoteSourceSource()
{
stop();
}
{}
void RemoteSourceSource::pull(SampleVector::iterator begin, unsigned int nbSamples)
{
@ -107,7 +105,7 @@ void RemoteSourceSource::stop()
{
stopWorker();
m_sourceWorker->deleteLater();
m_sourceWorker = nullptr;
m_sourceWorker = 0;
}
m_running = false;
@ -278,4 +276,4 @@ void RemoteSourceSource::applyChannelSettings(int channelSampleRate, bool force)
}
m_channelSampleRate = channelSampleRate;
}
}

View File

@ -19,9 +19,9 @@
#include <channel/remotedataqueue.h>
#include <algorithm>
#include <QThread>
#include <QUdpSocket>
#include "cm256cc/cm256.h"
#include "remotesourceworker.h"
MESSAGE_CLASS_DEFINITION(RemoteSourceWorker::MsgDataBind, Message)
@ -31,18 +31,10 @@ RemoteSourceWorker::RemoteSourceWorker(RemoteDataQueue *dataQueue, QObject* pare
m_running(false),
m_dataQueue(dataQueue),
m_address(QHostAddress::LocalHost),
m_socket(this),
m_mutex(QMutex::Recursive),
m_sampleRate(0)
m_socket(nullptr)
{
std::fill(m_dataBlocks, m_dataBlocks+4, (RemoteDataBlock *) 0);
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection);
connect(&m_socket, SIGNAL(readyRead()),this, SLOT(recv()));
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(&m_socket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &APRSWorker::errorOccurred);
#else
connect(&m_socket, &QAbstractSocket::errorOccurred, this, &RemoteSourceWorker::errorOccurred);
#endif
}
RemoteSourceWorker::~RemoteSourceWorker()
@ -56,43 +48,19 @@ void RemoteSourceWorker::dataBind(const QString& address, uint16_t port)
m_inputMessageQueue.push(msg);
}
bool RemoteSourceWorker::startWork()
void RemoteSourceWorker::startWork()
{
qDebug("RemoteSourceWorker::startWork");
QMutexLocker mutexLocker(&m_mutex);
m_socket.setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, getDataSocketBufferSize(m_sampleRate));
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
connect(thread(), SIGNAL(started()), this, SLOT(started()));
connect(thread(), SIGNAL(finished()), this, SLOT(finished()));
m_running = true;
return m_running;
}
void RemoteSourceWorker::started()
{
disconnect(thread(), SIGNAL(started()), this, SLOT(started()));
m_socket = new QUdpSocket(this);
m_running = false;
}
void RemoteSourceWorker::stopWork()
{
qDebug("RemoteSourceWorker::stopWork");
QMutexLocker mutexLocker(&m_mutex);
disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
}
void RemoteSourceWorker::finished()
{
// Close any existing connection
if (m_socket.isOpen()) {
m_socket.close();
}
delete m_socket;
m_socket = nullptr;
m_running = false;
disconnect(thread(), SIGNAL(finished()), this, SLOT(finished()));
}
void RemoteSourceWorker::errorOccurred(QAbstractSocket::SocketError socketError)
{
qWarning() << "RemoteSourceWorker::errorOccurred: " << socketError;
}
void RemoteSourceWorker::handleInputMessages()
@ -103,12 +71,15 @@ void RemoteSourceWorker::handleInputMessages()
{
if (MsgDataBind::match(*message))
{
QMutexLocker mutexLocker(&m_mutex);
MsgDataBind* notif = (MsgDataBind*) message;
qDebug("RemoteSourceWorker::handleInputMessages: MsgDataBind: %s:%d", qPrintable(notif->getAddress().toString()), notif->getPort());
disconnect(&m_socket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
m_socket.bind(notif->getAddress(), notif->getPort());
connect(&m_socket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
if (m_socket)
{
disconnect(m_socket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
m_socket->bind(notif->getAddress(), notif->getPort());
connect(m_socket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
}
}
}
}
@ -118,30 +89,16 @@ void RemoteSourceWorker::readPendingDatagrams()
RemoteSuperBlock superBlock;
qint64 size;
while (m_socket.hasPendingDatagrams())
while (m_socket->hasPendingDatagrams())
{
QHostAddress sender;
quint16 senderPort = 0;
//qint64 pendingDataSize = m_socket->pendingDatagramSize();
size = m_socket.readDatagram((char *) &superBlock, (long long int) sizeof(RemoteSuperBlock), &sender, &senderPort);
size = m_socket->readDatagram((char *) &superBlock, (long long int) sizeof(RemoteSuperBlock), &sender, &senderPort);
if (size == sizeof(RemoteSuperBlock))
{
unsigned int dataBlockIndex = superBlock.m_header.m_frameIndex % m_nbDataBlocks;
int blockIndex = superBlock.m_header.m_blockIndex;
if (blockIndex == 0) // first block with meta data
{
const RemoteMetaDataFEC *metaData = (const RemoteMetaDataFEC *) &superBlock.m_protectedBlock;
uint32_t sampleRate = metaData->m_sampleRate;
if (m_sampleRate != sampleRate)
{
qDebug("RemoteSourceWorker::readPendingDatagrams: sampleRate: %u", sampleRate);
m_socket.setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, getDataSocketBufferSize(sampleRate));
m_sampleRate = sampleRate;
}
}
// create the first block for this index
if (m_dataBlocks[dataBlockIndex] == 0) {
@ -188,12 +145,3 @@ void RemoteSourceWorker::readPendingDatagrams()
}
}
int RemoteSourceWorker::getDataSocketBufferSize(uint32_t inSampleRate)
{
// set a floor value at 24 kS/s
uint32_t samplerate = inSampleRate < 24000 ? 24000 : inSampleRate;
// 250 ms (1/4s) at current sample rate
int bufferSize = (samplerate * 2 * (SDR_RX_SAMP_SZ == 16 ? 2 : 4)) / 4;
qDebug("RemoteSourceWorker::getDataSocketBufferSize: %d bytes", bufferSize);
return bufferSize;
}

View File

@ -20,13 +20,13 @@
#include <QObject>
#include <QHostAddress>
#include <QUdpSocket>
#include "util/message.h"
#include "util/messagequeue.h"
class RemoteDataQueue;
class RemoteDataBlock;
class QUdpSocket;
class RemoteSourceWorker : public QObject {
Q_OBJECT
@ -57,7 +57,7 @@ public:
RemoteSourceWorker(RemoteDataQueue *dataQueue, QObject* parent = 0);
~RemoteSourceWorker();
bool startWork();
void startWork();
void stopWork();
void dataBind(const QString& address, uint16_t port);
@ -68,19 +68,12 @@ private:
RemoteDataQueue *m_dataQueue;
QHostAddress m_address;
QUdpSocket m_socket;
QMutex m_mutex;
QUdpSocket *m_socket;
static const uint32_t m_nbDataBlocks = 4; //!< number of data blocks in the ring buffer
static const uint32_t m_nbDataBlocks = 4; //!< number of data blocks in the ring buffer
RemoteDataBlock *m_dataBlocks[m_nbDataBlocks]; //!< ring buffer of data blocks indexed by frame affinity
uint32_t m_sampleRate; //!< current sample rate from meta data
static int getDataSocketBufferSize(uint32_t inSampleRate);
private slots:
void started();
void finished();
void errorOccurred(QAbstractSocket::SocketError socketError);
void handleInputMessages();
void readPendingDatagrams();
};

View File

@ -100,6 +100,8 @@ bool RemoteOutput::start()
m_lastQueueLength = -2; // set first value out of bounds
m_chunkSizeCorrection = 0;
m_remoteOutputWorker->setTxDelay(m_settings.m_txDelay);
mutexLocker.unlock();
//applySettings(m_generalSettings, m_settings, true);
qDebug("RemoteOutput::start: started");
@ -258,6 +260,7 @@ void RemoteOutput::applySettings(const RemoteOutputSettings& settings, bool forc
{
QMutexLocker mutexLocker(&m_mutex);
bool forwardChange = false;
bool changeTxDelay = false;
QList<QString> reverseAPIKeys;
if ((m_settings.m_dataAddress != settings.m_dataAddress) || force) {
@ -292,6 +295,7 @@ void RemoteOutput::applySettings(const RemoteOutputSettings& settings, bool forc
m_tickMultiplier = m_tickMultiplier < 20 ? 20 : m_tickMultiplier; // not below half a second
forwardChange = true;
changeTxDelay = true;
}
if (force || (m_settings.m_nbFECBlocks != settings.m_nbFECBlocks))
@ -301,12 +305,28 @@ void RemoteOutput::applySettings(const RemoteOutputSettings& settings, bool forc
if (m_remoteOutputWorker != 0) {
m_remoteOutputWorker->setNbBlocksFEC(settings.m_nbFECBlocks);
}
changeTxDelay = true;
}
if (force || (m_settings.m_txDelay != settings.m_txDelay))
{
reverseAPIKeys.append("txDelay");
changeTxDelay = true;
}
if (changeTxDelay)
{
if (m_remoteOutputWorker != 0) {
m_remoteOutputWorker->setTxDelay(settings.m_txDelay);
}
}
mutexLocker.unlock();
qDebug() << "RemoteOutput::applySettings:"
<< " m_sampleRate: " << settings.m_sampleRate
<< " m_txDelay: " << settings.m_txDelay
<< " m_nbFECBlocks: " << settings.m_nbFECBlocks
<< " m_apiAddress: " << settings.m_apiAddress
<< " m_apiPort: " << settings.m_apiPort
@ -401,6 +421,9 @@ void RemoteOutput::webapiUpdateDeviceSettings(
if (deviceSettingsKeys.contains("sampleRate")) {
settings.m_sampleRate = response.getRemoteOutputSettings()->getSampleRate();
}
if (deviceSettingsKeys.contains("txDelay")) {
settings.m_txDelay = response.getRemoteOutputSettings()->getTxDelay();
}
if (deviceSettingsKeys.contains("nbFECBlocks")) {
settings.m_nbFECBlocks = response.getRemoteOutputSettings()->getNbFecBlocks();
}
@ -451,6 +474,7 @@ void RemoteOutput::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& re
{
response.getRemoteOutputSettings()->setCenterFrequency(settings.m_centerFrequency);
response.getRemoteOutputSettings()->setSampleRate(settings.m_sampleRate);
response.getRemoteOutputSettings()->setTxDelay(settings.m_txDelay);
response.getRemoteOutputSettings()->setNbFecBlocks(settings.m_nbFECBlocks);
response.getRemoteOutputSettings()->setApiAddress(new QString(settings.m_apiAddress));
response.getRemoteOutputSettings()->setApiPort(settings.m_apiPort);
@ -635,6 +659,9 @@ void RemoteOutput::webapiReverseSendSettings(QList<QString>& deviceSettingsKeys,
if (deviceSettingsKeys.contains("sampleRate") || force) {
swgRemoteOutputSettings->setSampleRate(settings.m_sampleRate);
}
if (deviceSettingsKeys.contains("txDelay") || force) {
swgRemoteOutputSettings->setTxDelay(settings.m_txDelay);
}
if (deviceSettingsKeys.contains("nbFECBlocks") || force) {
swgRemoteOutputSettings->setNbFecBlocks(settings.m_nbFECBlocks);
}

View File

@ -211,11 +211,20 @@ void RemoteOutputSinkGui::updateSampleRate()
ui->deviceRateText->setText(tr("%1k").arg((float)(m_sampleRate) / 1000));
}
void RemoteOutputSinkGui::updateTxDelayTooltip()
{
int samplesPerBlock = RemoteNbBytesPerBlock / (SDR_RX_SAMP_SZ <= 16 ? 4 : 8);
double delay = ((127*samplesPerBlock*m_settings.m_txDelay) / m_settings.m_sampleRate)/(128 + m_settings.m_nbFECBlocks);
ui->txDelayText->setToolTip(tr("%1 us").arg(QString::number(delay*1e6, 'f', 0)));
}
void RemoteOutputSinkGui::displaySettings()
{
blockApplySettings(true);
ui->centerFrequency->setValue(m_deviceCenterFrequency / 1000);
ui->sampleRate->setValue(m_settings.m_sampleRate);
ui->txDelay->setValue(m_settings.m_txDelay*100);
ui->txDelayText->setText(tr("%1").arg(m_settings.m_txDelay*100));
ui->nbFECBlocks->setValue(m_settings.m_nbFECBlocks);
QString s0 = QString::number(128 + m_settings.m_nbFECBlocks, 'f', 0);
@ -279,6 +288,15 @@ void RemoteOutputSinkGui::updateStatus()
void RemoteOutputSinkGui::on_sampleRate_changed(quint64 value)
{
m_settings.m_sampleRate = value;
updateTxDelayTooltip();
sendSettings();
}
void RemoteOutputSinkGui::on_txDelay_valueChanged(int value)
{
m_settings.m_txDelay = value / 100.0;
ui->txDelayText->setText(tr("%1").arg(value));
updateTxDelayTooltip();
sendSettings();
}
@ -290,6 +308,7 @@ void RemoteOutputSinkGui::on_nbFECBlocks_valueChanged(int value)
QString s = QString::number(nbOriginalBlocks + nbFECBlocks, 'f', 0);
QString s1 = QString::number(nbFECBlocks, 'f', 0);
ui->nominalNbBlocksText->setText(tr("%1/%2").arg(s).arg(s1));
updateTxDelayTooltip();
sendSettings();
}

View File

@ -124,6 +124,7 @@ private:
void sendControl(bool force = false);
void sendSettings();
void updateSampleRate();
void updateTxDelayTooltip();
void displayEventCounts();
void displayEventStatus(int recoverableCount, int unrecoverableCount);
void displayEventTimer();
@ -133,6 +134,7 @@ private:
private slots:
void handleInputMessages();
void on_sampleRate_changed(quint64 value);
void on_txDelay_valueChanged(int value);
void on_nbFECBlocks_valueChanged(int value);
void on_deviceIndex_returnPressed();
void on_channelIndex_returnPressed();

View File

@ -249,6 +249,60 @@
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="txDelayLabel">
<property name="text">
<string>UDly</string>
</property>
</widget>
</item>
<item>
<widget class="QDial" name="txDelay">
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>Delay between consecutive UDP packets in percentage of nominal UDP packet process time</string>
</property>
<property name="minimum">
<number>10</number>
</property>
<property name="maximum">
<number>90</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>50</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="txDelayText">
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>20</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>90</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">

View File

@ -22,6 +22,8 @@
///////////////////////////////////////////////////////////////////////////////////
#include <thread>
#include <chrono>
#include <boost/crc.hpp>
#include <boost/cstdint.hpp>
@ -91,6 +93,7 @@ void RemoteOutputSender::sendDataBlock(RemoteDataBlock *dataBlock)
uint16_t frameIndex = dataBlock->m_txControlBlock.m_frameIndex;
int nbBlocksFEC = dataBlock->m_txControlBlock.m_nbBlocksFEC;
int txDelay = dataBlock->m_txControlBlock.m_txDelay;
m_remoteHostAddress.setAddress(dataBlock->m_txControlBlock.m_dataAddress);
uint16_t dataPort = dataBlock->m_txControlBlock.m_dataPort;
RemoteSuperBlock *txBlockx = dataBlock->m_superBlocks;
@ -99,8 +102,11 @@ void RemoteOutputSender::sendDataBlock(RemoteDataBlock *dataBlock)
{
if (m_udpSocket)
{
for (int i = 0; i < RemoteNbOrginalBlocks; i++) { // send block via UDP
for (int i = 0; i < RemoteNbOrginalBlocks; i++)
{
// send block via UDP
m_udpSocket->writeDatagram((const char*)&txBlockx[i], (qint64 ) RemoteUdpSize, m_remoteHostAddress, dataPort);
std::this_thread::sleep_for(std::chrono::microseconds(txDelay));
}
}
}
@ -146,8 +152,11 @@ void RemoteOutputSender::sendDataBlock(RemoteDataBlock *dataBlock)
// Transmit all blocks
if (m_udpSocket)
{
for (int i = 0; i < cm256Params.OriginalCount + cm256Params.RecoveryCount; i++) { // send block via UDP
for (int i = 0; i < cm256Params.OriginalCount + cm256Params.RecoveryCount; i++)
{
// send block via UDP
m_udpSocket->writeDatagram((const char*)&txBlockx[i], (qint64 ) RemoteUdpSize, m_remoteHostAddress, dataPort);
std::this_thread::sleep_for(std::chrono::microseconds(txDelay));
}
}
}

View File

@ -27,6 +27,7 @@ void RemoteOutputSettings::resetToDefaults()
{
m_centerFrequency = 435000*1000;
m_sampleRate = 48000;
m_txDelay = 0.35;
m_nbFECBlocks = 0;
m_apiAddress = "127.0.0.1";
m_apiPort = 9091;
@ -46,6 +47,7 @@ QByteArray RemoteOutputSettings::serialize() const
s.writeU64(1, m_centerFrequency);
s.writeU32(2, m_sampleRate);
s.writeFloat(3, m_txDelay);
s.writeU32(4, m_nbFECBlocks);
s.writeString(5, m_apiAddress);
s.writeU32(6, m_apiPort);
@ -77,6 +79,7 @@ bool RemoteOutputSettings::deserialize(const QByteArray& data)
d.readU64(1, &m_centerFrequency, 435000*1000);
d.readU32(2, &m_sampleRate, 48000);
d.readFloat(3, &m_txDelay, 0.5);
d.readU32(4, &m_nbFECBlocks, 0);
d.readString(5, &m_apiAddress, "127.0.0.1");
d.readU32(6, &uintval, 9090);

View File

@ -24,6 +24,7 @@
struct RemoteOutputSettings {
quint64 m_centerFrequency;
quint32 m_sampleRate;
float m_txDelay;
quint32 m_nbFECBlocks;
QString m_apiAddress;
quint16 m_apiPort;

View File

@ -49,6 +49,7 @@ public:
void setSamplerate(int samplerate);
void setNbBlocksFEC(uint32_t nbBlocksFEC) { m_udpSinkFEC.setNbBlocksFEC(nbBlocksFEC); };
void setTxDelay(float txDelay) { m_udpSinkFEC.setTxDelay(txDelay); };
void setDataAddress(const QString& address, uint16_t port) { m_udpSinkFEC.setRemoteAddress(address, port); }
bool isRunning() const { return m_running; }

View File

@ -32,6 +32,7 @@ UDPSinkFEC::UDPSinkFEC() :
m_nbSamples(0),
m_nbBlocksFEC(0),
m_txDelayRatio(0.0),
m_txDelay(0),
m_dataBlock(nullptr),
m_txBlockIndex(0),
m_txBlocksIndex(0),
@ -70,16 +71,35 @@ void UDPSinkFEC::stopSender()
m_senderThread->wait();
}
void UDPSinkFEC::setTxDelay(float txDelayRatio)
{
// delay is calculated from the fraction of the nominal UDP block process time
// frame size: 127 * (126 or 63 samples depending on I or Q sample bytes of 2 or 4 bytes respectively)
// divided by sample rate gives the frame process time
// divided by the number of actual blocks including FEC blocks gives the block (i.e. UDP block) process time
m_txDelayRatio = txDelayRatio;
int samplesPerBlock = RemoteNbBytesPerBlock / sizeof(Sample);
double delay = m_sampleRate == 0 ? 1.0 : (127*samplesPerBlock*txDelayRatio) / m_sampleRate;
delay /= 128 + m_nbBlocksFEC;
m_txDelay = delay * 1e6;
qDebug() << "UDPSinkFEC::setTxDelay:"
<< "txDelay:" << txDelayRatio
<< "m_txDelay:" << m_txDelay << " us"
<< "m_sampleRate:" << m_sampleRate;
}
void UDPSinkFEC::setNbBlocksFEC(uint32_t nbBlocksFEC)
{
qDebug() << "UDPSinkFEC::setNbBlocksFEC: nbBlocksFEC: " << nbBlocksFEC;
m_nbBlocksFEC = nbBlocksFEC;
setTxDelay(m_txDelayRatio);
}
void UDPSinkFEC::setSampleRate(uint32_t sampleRate)
{
qDebug() << "UDPSinkFEC::setSampleRate: sampleRate: " << sampleRate;
m_sampleRate = sampleRate;
setTxDelay(m_txDelayRatio);
}
void UDPSinkFEC::setRemoteAddress(const QString& address, uint16_t port)
@ -180,6 +200,7 @@ void UDPSinkFEC::write(const SampleVector::iterator& begin, uint32_t sampleChunk
m_dataBlock->m_txControlBlock.m_processed = false;
m_dataBlock->m_txControlBlock.m_complete = true;
m_dataBlock->m_txControlBlock.m_nbBlocksFEC = m_nbBlocksFEC;
m_dataBlock->m_txControlBlock.m_txDelay = m_txDelay;
m_dataBlock->m_txControlBlock.m_dataAddress = m_remoteAddress;
m_dataBlock->m_txControlBlock.m_dataPort = m_remotePort;

View File

@ -67,6 +67,7 @@ public:
void setSampleRate(uint32_t sampleRate);
void setNbBlocksFEC(uint32_t nbBlocksFEC);
void setTxDelay(float txDelayRatio);
void setRemoteAddress(const QString& address, uint16_t port);
/** Return true if the stream is OK, return false if there is an error. */
@ -86,6 +87,7 @@ private:
RemoteMetaDataFEC m_currentMetaFEC; //!< Meta data for current frame
uint32_t m_nbBlocksFEC; //!< Variable number of FEC blocks
float m_txDelayRatio; //!< Delay in ratio of nominal frame period
uint32_t m_txDelay; //!< Delay in microseconds (usleep) between each sending of an UDP datagram
RemoteDataBlock *m_dataBlock;
RemoteSuperBlock m_superBlock; //!< current super block being built
int m_txBlockIndex; //!< Current index in blocks to transmit in the Tx row

View File

@ -68,7 +68,8 @@ LocalInputGui::LocalInputGui(DeviceUISet *deviceUISet, QWidget* parent) :
m_countUnrecoverable(0),
m_countRecovered(0),
m_doApplySettings(true),
m_forceSettings(true)
m_forceSettings(true),
m_txDelay(0.0)
{
m_paletteGreenText.setColor(QPalette::WindowText, Qt::green);
m_paletteWhiteText.setColor(QPalette::WindowText, Qt::white);

View File

@ -92,6 +92,7 @@ private:
bool m_doApplySettings;
bool m_forceSettings;
double m_txDelay;
QPalette m_paletteGreenText;
QPalette m_paletteWhiteText;

View File

@ -68,7 +68,8 @@ RemoteInputGui::RemoteInputGui(DeviceUISet *deviceUISet, QWidget* parent) :
m_countUnrecoverable(0),
m_countRecovered(0),
m_doApplySettings(true),
m_forceSettings(true)
m_forceSettings(true),
m_txDelay(0.0)
{
m_paletteGreenText.setColor(QPalette::WindowText, Qt::green);
m_paletteWhiteText.setColor(QPalette::WindowText, Qt::white);

View File

@ -94,6 +94,7 @@ private:
bool m_doApplySettings;
bool m_forceSettings;
double m_txDelay;
QPalette m_paletteGreenText;
QPalette m_paletteWhiteText;

View File

@ -177,6 +177,7 @@ Example of a JSON file (delay is an example you normally do not need it):
"reverseAPIPort": 8888,
"rgbColor": -7601148,
"title": "Channel 0",
"txDelay": 0,
"useReverseAPI": 0
},
"channelType": "RemoteSink",

View File

@ -126,15 +126,16 @@ struct RemoteTxControlBlock
bool m_processed;
uint16_t m_frameIndex;
int m_nbBlocksFEC;
int m_txDelay;
QString m_dataAddress;
uint16_t m_dataPort;
RemoteTxControlBlock()
{
RemoteTxControlBlock() {
m_complete = false;
m_processed = false;
m_frameIndex = 0;
m_nbBlocksFEC = 0;
m_txDelay = 100;
m_dataAddress = "127.0.0.1";
m_dataPort = 9090;
}

View File

@ -9841,6 +9841,11 @@ margin-bottom: 20px;
"sampleRate" : {
"type" : "integer"
},
"txDelay" : {
"type" : "number",
"format" : "float",
"description" : "minimum delay in ms between two consecutive packets sending"
},
"nbFECBlocks" : {
"type" : "integer"
},
@ -51596,7 +51601,7 @@ except ApiException as e:
</div>
<div id="generator">
<div class="content">
Generated 2021-12-07T06:38:29.681+01:00
Generated 2021-12-04T20:19:18.225+01:00
</div>
</div>
</div>

View File

@ -6,6 +6,10 @@ RemoteOutputSettings:
format: int64
sampleRate:
type: integer
txDelay:
description: minimum delay in ms between two consecutive packets sending
type: number
format: float
nbFECBlocks:
type: integer
apiAddress:

View File

@ -6,6 +6,10 @@ RemoteOutputSettings:
format: int64
sampleRate:
type: integer
txDelay:
description: minimum delay in ms between two consecutive packets sending
type: number
format: float
nbFECBlocks:
type: integer
apiAddress:

View File

@ -9841,6 +9841,11 @@ margin-bottom: 20px;
"sampleRate" : {
"type" : "integer"
},
"txDelay" : {
"type" : "number",
"format" : "float",
"description" : "minimum delay in ms between two consecutive packets sending"
},
"nbFECBlocks" : {
"type" : "integer"
},
@ -51596,7 +51601,7 @@ except ApiException as e:
</div>
<div id="generator">
<div class="content">
Generated 2021-12-07T06:38:29.681+01:00
Generated 2021-12-04T20:19:18.225+01:00
</div>
</div>
</div>

View File

@ -32,6 +32,8 @@ SWGRemoteOutputSettings::SWGRemoteOutputSettings() {
m_center_frequency_isSet = false;
sample_rate = 0;
m_sample_rate_isSet = false;
tx_delay = 0.0f;
m_tx_delay_isSet = false;
nb_fec_blocks = 0;
m_nb_fec_blocks_isSet = false;
api_address = nullptr;
@ -66,6 +68,8 @@ SWGRemoteOutputSettings::init() {
m_center_frequency_isSet = false;
sample_rate = 0;
m_sample_rate_isSet = false;
tx_delay = 0.0f;
m_tx_delay_isSet = false;
nb_fec_blocks = 0;
m_nb_fec_blocks_isSet = false;
api_address = new QString("");
@ -95,6 +99,7 @@ SWGRemoteOutputSettings::cleanup() {
if(api_address != nullptr) {
delete api_address;
}
@ -128,6 +133,8 @@ SWGRemoteOutputSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&sample_rate, pJson["sampleRate"], "qint32", "");
::SWGSDRangel::setValue(&tx_delay, pJson["txDelay"], "float", "");
::SWGSDRangel::setValue(&nb_fec_blocks, pJson["nbFECBlocks"], "qint32", "");
::SWGSDRangel::setValue(&api_address, pJson["apiAddress"], "QString", "QString");
@ -172,6 +179,9 @@ SWGRemoteOutputSettings::asJsonObject() {
if(m_sample_rate_isSet){
obj->insert("sampleRate", QJsonValue(sample_rate));
}
if(m_tx_delay_isSet){
obj->insert("txDelay", QJsonValue(tx_delay));
}
if(m_nb_fec_blocks_isSet){
obj->insert("nbFECBlocks", QJsonValue(nb_fec_blocks));
}
@ -229,6 +239,16 @@ SWGRemoteOutputSettings::setSampleRate(qint32 sample_rate) {
this->m_sample_rate_isSet = true;
}
float
SWGRemoteOutputSettings::getTxDelay() {
return tx_delay;
}
void
SWGRemoteOutputSettings::setTxDelay(float tx_delay) {
this->tx_delay = tx_delay;
this->m_tx_delay_isSet = true;
}
qint32
SWGRemoteOutputSettings::getNbFecBlocks() {
return nb_fec_blocks;
@ -350,6 +370,9 @@ SWGRemoteOutputSettings::isSet(){
if(m_sample_rate_isSet){
isObjectUpdated = true; break;
}
if(m_tx_delay_isSet){
isObjectUpdated = true; break;
}
if(m_nb_fec_blocks_isSet){
isObjectUpdated = true; break;
}

View File

@ -48,6 +48,9 @@ public:
qint32 getSampleRate();
void setSampleRate(qint32 sample_rate);
float getTxDelay();
void setTxDelay(float tx_delay);
qint32 getNbFecBlocks();
void setNbFecBlocks(qint32 nb_fec_blocks);
@ -91,6 +94,9 @@ private:
qint32 sample_rate;
bool m_sample_rate_isSet;
float tx_delay;
bool m_tx_delay_isSet;
qint32 nb_fec_blocks;
bool m_nb_fec_blocks_isSet;

View File

@ -53,6 +53,7 @@ def getInputOptions():
parser.add_option("--rmt-address", dest="remote_address", help="RemoteSink: destination data address", metavar="IP_ADDRESS", type="string")
parser.add_option("--rmt-port", dest="remote_port", help="RemoteSink: destination data port", metavar="PORT", type="int")
parser.add_option("--rmt-fec", dest="remote_fec", help="RemoteSink: number of FEC blocks per frame", metavar="NUMBER", type="int")
parser.add_option("--rmt-txdelay", dest="remote_tx_delay", help="RemoteSink: inter block UDP Tx delay percentage", metavar="PERCENT", type="int")
(options, args) = parser.parse_args()
@ -325,6 +326,8 @@ def setupChannel(deviceset_url, options):
settings["RemoteSinkSettings"]["dataPort"] = options.remote_port
if options.remote_fec:
settings["RemoteSinkSettings"]["nbFECBlocks"] = options.remote_fec
if options.remote_tx_delay:
settings["RemoteSinkSettings"]["txDelay"] = options.remote_tx_delay
r = callAPI(deviceset_url + "/channel/%d/settings" % i, "PATCH", None, settings, "Change demod")
if r is None:

View File

@ -63,6 +63,7 @@
"reverseAPIPort": 8888,
"rgbColor": -7601148,
"title": "Channel 0",
"txDelay": 0,
"useReverseAPI": 0
},
"channelType": "RemoteSink",
@ -97,6 +98,7 @@
"reverseAPIPort": 8888,
"rgbColor": -7601148,
"title": "Remote sink",
"txDelay": 0,
"useReverseAPI": 0
},
"channelType": "RemoteSink",
@ -131,6 +133,7 @@
"reverseAPIPort": 8888,
"rgbColor": -7601148,
"title": "Remote sink",
"txDelay": 0,
"useReverseAPI": 0
},
"channelType": "RemoteSink",
@ -145,4 +148,4 @@
null,
"Start device on deviceset R0"
]
]
]