mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-25 17:28:50 -05:00
Compare commits
No commits in common. "ef4b54a7b1e8b91c02b2042fbcea88001fb59a61" and "bb1f833d02726eafc75590b7b2957f36d9cbaf78" have entirely different histories.
ef4b54a7b1
...
bb1f833d02
@ -85,6 +85,7 @@ void RemoteSinkSender::sendDataBlock(RemoteDataBlock *dataBlock)
|
|||||||
|
|
||||||
uint16_t frameIndex = dataBlock->m_txControlBlock.m_frameIndex;
|
uint16_t frameIndex = dataBlock->m_txControlBlock.m_frameIndex;
|
||||||
int nbBlocksFEC = dataBlock->m_txControlBlock.m_nbBlocksFEC;
|
int nbBlocksFEC = dataBlock->m_txControlBlock.m_nbBlocksFEC;
|
||||||
|
int txDelay = dataBlock->m_txControlBlock.m_txDelay;
|
||||||
m_address.setAddress(dataBlock->m_txControlBlock.m_dataAddress);
|
m_address.setAddress(dataBlock->m_txControlBlock.m_dataAddress);
|
||||||
uint16_t dataPort = dataBlock->m_txControlBlock.m_dataPort;
|
uint16_t dataPort = dataBlock->m_txControlBlock.m_dataPort;
|
||||||
RemoteSuperBlock *txBlockx = dataBlock->m_superBlocks;
|
RemoteSuperBlock *txBlockx = dataBlock->m_superBlocks;
|
||||||
@ -93,8 +94,11 @@ void RemoteSinkSender::sendDataBlock(RemoteDataBlock *dataBlock)
|
|||||||
{
|
{
|
||||||
if (m_socket)
|
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);
|
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
|
// Transmit all blocks
|
||||||
if (m_socket)
|
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);
|
m_socket->writeDatagram((const char*)&txBlockx[i], (qint64 ) RemoteUdpSize, m_address, dataPort);
|
||||||
|
std::this_thread::sleep_for(std::chrono::microseconds(txDelay));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ RemoteSinkSink::RemoteSinkSink() :
|
|||||||
m_frequencyOffset(0),
|
m_frequencyOffset(0),
|
||||||
m_basebandSampleRate(48000),
|
m_basebandSampleRate(48000),
|
||||||
m_nbBlocksFEC(0),
|
m_nbBlocksFEC(0),
|
||||||
|
m_txDelay(35),
|
||||||
m_dataAddress("127.0.0.1"),
|
m_dataAddress("127.0.0.1"),
|
||||||
m_dataPort(9090)
|
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_processed = false;
|
||||||
m_dataBlock->m_txControlBlock.m_complete = true;
|
m_dataBlock->m_txControlBlock.m_complete = true;
|
||||||
m_dataBlock->m_txControlBlock.m_nbBlocksFEC = m_nbBlocksFEC;
|
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_dataAddress = m_dataAddress;
|
||||||
m_dataBlock->m_txControlBlock.m_dataPort = m_dataPort;
|
m_dataBlock->m_txControlBlock.m_dataPort = m_dataPort;
|
||||||
|
|
||||||
|
@ -61,6 +61,7 @@ private:
|
|||||||
int64_t m_frequencyOffset;
|
int64_t m_frequencyOffset;
|
||||||
uint32_t m_basebandSampleRate;
|
uint32_t m_basebandSampleRate;
|
||||||
int m_nbBlocksFEC;
|
int m_nbBlocksFEC;
|
||||||
|
int m_txDelay;
|
||||||
QString m_dataAddress;
|
QString m_dataAddress;
|
||||||
uint16_t m_dataPort;
|
uint16_t m_dataPort;
|
||||||
|
|
||||||
|
@ -330,9 +330,12 @@ void RemoteSourceGUI::on_dataPort_returnPressed()
|
|||||||
bool dataOk;
|
bool dataOk;
|
||||||
int dataPort = ui->dataPort->text().toInt(&dataOk);
|
int dataPort = ui->dataPort->text().toInt(&dataOk);
|
||||||
|
|
||||||
if ((!dataOk) || (dataPort < 1024) || (dataPort > 65535)) {
|
if((!dataOk) || (dataPort < 1024) || (dataPort > 65535))
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
m_settings.m_dataPort = dataPort;
|
m_settings.m_dataPort = dataPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,7 +350,8 @@ void RemoteSourceGUI::on_dataApplyButton_clicked(bool checked)
|
|||||||
bool dataOk;
|
bool dataOk;
|
||||||
int udpDataPort = ui->dataPort->text().toInt(&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;
|
m_settings.m_dataPort = udpDataPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@
|
|||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="dataApplyButton">
|
<widget class="QPushButton" name="pushButton">
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>30</width>
|
<width>30</width>
|
||||||
|
@ -35,9 +35,7 @@ RemoteSourceSource::RemoteSourceSource() :
|
|||||||
}
|
}
|
||||||
|
|
||||||
RemoteSourceSource::~RemoteSourceSource()
|
RemoteSourceSource::~RemoteSourceSource()
|
||||||
{
|
{}
|
||||||
stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoteSourceSource::pull(SampleVector::iterator begin, unsigned int nbSamples)
|
void RemoteSourceSource::pull(SampleVector::iterator begin, unsigned int nbSamples)
|
||||||
{
|
{
|
||||||
@ -107,7 +105,7 @@ void RemoteSourceSource::stop()
|
|||||||
{
|
{
|
||||||
stopWorker();
|
stopWorker();
|
||||||
m_sourceWorker->deleteLater();
|
m_sourceWorker->deleteLater();
|
||||||
m_sourceWorker = nullptr;
|
m_sourceWorker = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_running = false;
|
m_running = false;
|
||||||
@ -278,4 +276,4 @@ void RemoteSourceSource::applyChannelSettings(int channelSampleRate, bool force)
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_channelSampleRate = channelSampleRate;
|
m_channelSampleRate = channelSampleRate;
|
||||||
}
|
}
|
@ -19,9 +19,9 @@
|
|||||||
#include <channel/remotedataqueue.h>
|
#include <channel/remotedataqueue.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include <QThread>
|
#include <QUdpSocket>
|
||||||
|
|
||||||
#include "cm256cc/cm256.h"
|
#include "cm256cc/cm256.h"
|
||||||
|
|
||||||
#include "remotesourceworker.h"
|
#include "remotesourceworker.h"
|
||||||
|
|
||||||
MESSAGE_CLASS_DEFINITION(RemoteSourceWorker::MsgDataBind, Message)
|
MESSAGE_CLASS_DEFINITION(RemoteSourceWorker::MsgDataBind, Message)
|
||||||
@ -31,18 +31,10 @@ RemoteSourceWorker::RemoteSourceWorker(RemoteDataQueue *dataQueue, QObject* pare
|
|||||||
m_running(false),
|
m_running(false),
|
||||||
m_dataQueue(dataQueue),
|
m_dataQueue(dataQueue),
|
||||||
m_address(QHostAddress::LocalHost),
|
m_address(QHostAddress::LocalHost),
|
||||||
m_socket(this),
|
m_socket(nullptr)
|
||||||
m_mutex(QMutex::Recursive),
|
|
||||||
m_sampleRate(0)
|
|
||||||
{
|
{
|
||||||
std::fill(m_dataBlocks, m_dataBlocks+4, (RemoteDataBlock *) 0);
|
std::fill(m_dataBlocks, m_dataBlocks+4, (RemoteDataBlock *) 0);
|
||||||
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection);
|
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()
|
RemoteSourceWorker::~RemoteSourceWorker()
|
||||||
@ -56,43 +48,19 @@ void RemoteSourceWorker::dataBind(const QString& address, uint16_t port)
|
|||||||
m_inputMessageQueue.push(msg);
|
m_inputMessageQueue.push(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RemoteSourceWorker::startWork()
|
void RemoteSourceWorker::startWork()
|
||||||
{
|
{
|
||||||
qDebug("RemoteSourceWorker::startWork");
|
qDebug("RemoteSourceWorker::startWork");
|
||||||
QMutexLocker mutexLocker(&m_mutex);
|
m_socket = new QUdpSocket(this);
|
||||||
m_socket.setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, getDataSocketBufferSize(m_sampleRate));
|
m_running = false;
|
||||||
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()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteSourceWorker::stopWork()
|
void RemoteSourceWorker::stopWork()
|
||||||
{
|
{
|
||||||
qDebug("RemoteSourceWorker::stopWork");
|
qDebug("RemoteSourceWorker::stopWork");
|
||||||
QMutexLocker mutexLocker(&m_mutex);
|
delete m_socket;
|
||||||
disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
|
m_socket = nullptr;
|
||||||
}
|
|
||||||
|
|
||||||
void RemoteSourceWorker::finished()
|
|
||||||
{
|
|
||||||
// Close any existing connection
|
|
||||||
if (m_socket.isOpen()) {
|
|
||||||
m_socket.close();
|
|
||||||
}
|
|
||||||
m_running = false;
|
m_running = false;
|
||||||
disconnect(thread(), SIGNAL(finished()), this, SLOT(finished()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoteSourceWorker::errorOccurred(QAbstractSocket::SocketError socketError)
|
|
||||||
{
|
|
||||||
qWarning() << "RemoteSourceWorker::errorOccurred: " << socketError;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteSourceWorker::handleInputMessages()
|
void RemoteSourceWorker::handleInputMessages()
|
||||||
@ -103,12 +71,15 @@ void RemoteSourceWorker::handleInputMessages()
|
|||||||
{
|
{
|
||||||
if (MsgDataBind::match(*message))
|
if (MsgDataBind::match(*message))
|
||||||
{
|
{
|
||||||
QMutexLocker mutexLocker(&m_mutex);
|
|
||||||
MsgDataBind* notif = (MsgDataBind*) message;
|
MsgDataBind* notif = (MsgDataBind*) message;
|
||||||
qDebug("RemoteSourceWorker::handleInputMessages: MsgDataBind: %s:%d", qPrintable(notif->getAddress().toString()), notif->getPort());
|
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());
|
if (m_socket)
|
||||||
connect(&m_socket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
|
{
|
||||||
|
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;
|
RemoteSuperBlock superBlock;
|
||||||
qint64 size;
|
qint64 size;
|
||||||
|
|
||||||
while (m_socket.hasPendingDatagrams())
|
while (m_socket->hasPendingDatagrams())
|
||||||
{
|
{
|
||||||
QHostAddress sender;
|
QHostAddress sender;
|
||||||
quint16 senderPort = 0;
|
quint16 senderPort = 0;
|
||||||
//qint64 pendingDataSize = m_socket->pendingDatagramSize();
|
//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))
|
if (size == sizeof(RemoteSuperBlock))
|
||||||
{
|
{
|
||||||
unsigned int dataBlockIndex = superBlock.m_header.m_frameIndex % m_nbDataBlocks;
|
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
|
// create the first block for this index
|
||||||
if (m_dataBlocks[dataBlockIndex] == 0) {
|
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;
|
|
||||||
}
|
|
||||||
|
@ -20,13 +20,13 @@
|
|||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QHostAddress>
|
#include <QHostAddress>
|
||||||
#include <QUdpSocket>
|
|
||||||
|
|
||||||
#include "util/message.h"
|
#include "util/message.h"
|
||||||
#include "util/messagequeue.h"
|
#include "util/messagequeue.h"
|
||||||
|
|
||||||
class RemoteDataQueue;
|
class RemoteDataQueue;
|
||||||
class RemoteDataBlock;
|
class RemoteDataBlock;
|
||||||
|
class QUdpSocket;
|
||||||
|
|
||||||
class RemoteSourceWorker : public QObject {
|
class RemoteSourceWorker : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -57,7 +57,7 @@ public:
|
|||||||
RemoteSourceWorker(RemoteDataQueue *dataQueue, QObject* parent = 0);
|
RemoteSourceWorker(RemoteDataQueue *dataQueue, QObject* parent = 0);
|
||||||
~RemoteSourceWorker();
|
~RemoteSourceWorker();
|
||||||
|
|
||||||
bool startWork();
|
void startWork();
|
||||||
void stopWork();
|
void stopWork();
|
||||||
void dataBind(const QString& address, uint16_t port);
|
void dataBind(const QString& address, uint16_t port);
|
||||||
|
|
||||||
@ -68,19 +68,12 @@ private:
|
|||||||
RemoteDataQueue *m_dataQueue;
|
RemoteDataQueue *m_dataQueue;
|
||||||
|
|
||||||
QHostAddress m_address;
|
QHostAddress m_address;
|
||||||
QUdpSocket m_socket;
|
QUdpSocket *m_socket;
|
||||||
QMutex m_mutex;
|
|
||||||
|
|
||||||
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
|
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:
|
private slots:
|
||||||
void started();
|
|
||||||
void finished();
|
|
||||||
void errorOccurred(QAbstractSocket::SocketError socketError);
|
|
||||||
void handleInputMessages();
|
void handleInputMessages();
|
||||||
void readPendingDatagrams();
|
void readPendingDatagrams();
|
||||||
};
|
};
|
||||||
|
@ -100,6 +100,8 @@ bool RemoteOutput::start()
|
|||||||
m_lastQueueLength = -2; // set first value out of bounds
|
m_lastQueueLength = -2; // set first value out of bounds
|
||||||
m_chunkSizeCorrection = 0;
|
m_chunkSizeCorrection = 0;
|
||||||
|
|
||||||
|
m_remoteOutputWorker->setTxDelay(m_settings.m_txDelay);
|
||||||
|
|
||||||
mutexLocker.unlock();
|
mutexLocker.unlock();
|
||||||
//applySettings(m_generalSettings, m_settings, true);
|
//applySettings(m_generalSettings, m_settings, true);
|
||||||
qDebug("RemoteOutput::start: started");
|
qDebug("RemoteOutput::start: started");
|
||||||
@ -258,6 +260,7 @@ void RemoteOutput::applySettings(const RemoteOutputSettings& settings, bool forc
|
|||||||
{
|
{
|
||||||
QMutexLocker mutexLocker(&m_mutex);
|
QMutexLocker mutexLocker(&m_mutex);
|
||||||
bool forwardChange = false;
|
bool forwardChange = false;
|
||||||
|
bool changeTxDelay = false;
|
||||||
QList<QString> reverseAPIKeys;
|
QList<QString> reverseAPIKeys;
|
||||||
|
|
||||||
if ((m_settings.m_dataAddress != settings.m_dataAddress) || force) {
|
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
|
m_tickMultiplier = m_tickMultiplier < 20 ? 20 : m_tickMultiplier; // not below half a second
|
||||||
|
|
||||||
forwardChange = true;
|
forwardChange = true;
|
||||||
|
changeTxDelay = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (force || (m_settings.m_nbFECBlocks != settings.m_nbFECBlocks))
|
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) {
|
if (m_remoteOutputWorker != 0) {
|
||||||
m_remoteOutputWorker->setNbBlocksFEC(settings.m_nbFECBlocks);
|
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();
|
mutexLocker.unlock();
|
||||||
|
|
||||||
qDebug() << "RemoteOutput::applySettings:"
|
qDebug() << "RemoteOutput::applySettings:"
|
||||||
<< " m_sampleRate: " << settings.m_sampleRate
|
<< " m_sampleRate: " << settings.m_sampleRate
|
||||||
|
<< " m_txDelay: " << settings.m_txDelay
|
||||||
<< " m_nbFECBlocks: " << settings.m_nbFECBlocks
|
<< " m_nbFECBlocks: " << settings.m_nbFECBlocks
|
||||||
<< " m_apiAddress: " << settings.m_apiAddress
|
<< " m_apiAddress: " << settings.m_apiAddress
|
||||||
<< " m_apiPort: " << settings.m_apiPort
|
<< " m_apiPort: " << settings.m_apiPort
|
||||||
@ -401,6 +421,9 @@ void RemoteOutput::webapiUpdateDeviceSettings(
|
|||||||
if (deviceSettingsKeys.contains("sampleRate")) {
|
if (deviceSettingsKeys.contains("sampleRate")) {
|
||||||
settings.m_sampleRate = response.getRemoteOutputSettings()->getSampleRate();
|
settings.m_sampleRate = response.getRemoteOutputSettings()->getSampleRate();
|
||||||
}
|
}
|
||||||
|
if (deviceSettingsKeys.contains("txDelay")) {
|
||||||
|
settings.m_txDelay = response.getRemoteOutputSettings()->getTxDelay();
|
||||||
|
}
|
||||||
if (deviceSettingsKeys.contains("nbFECBlocks")) {
|
if (deviceSettingsKeys.contains("nbFECBlocks")) {
|
||||||
settings.m_nbFECBlocks = response.getRemoteOutputSettings()->getNbFecBlocks();
|
settings.m_nbFECBlocks = response.getRemoteOutputSettings()->getNbFecBlocks();
|
||||||
}
|
}
|
||||||
@ -451,6 +474,7 @@ void RemoteOutput::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& re
|
|||||||
{
|
{
|
||||||
response.getRemoteOutputSettings()->setCenterFrequency(settings.m_centerFrequency);
|
response.getRemoteOutputSettings()->setCenterFrequency(settings.m_centerFrequency);
|
||||||
response.getRemoteOutputSettings()->setSampleRate(settings.m_sampleRate);
|
response.getRemoteOutputSettings()->setSampleRate(settings.m_sampleRate);
|
||||||
|
response.getRemoteOutputSettings()->setTxDelay(settings.m_txDelay);
|
||||||
response.getRemoteOutputSettings()->setNbFecBlocks(settings.m_nbFECBlocks);
|
response.getRemoteOutputSettings()->setNbFecBlocks(settings.m_nbFECBlocks);
|
||||||
response.getRemoteOutputSettings()->setApiAddress(new QString(settings.m_apiAddress));
|
response.getRemoteOutputSettings()->setApiAddress(new QString(settings.m_apiAddress));
|
||||||
response.getRemoteOutputSettings()->setApiPort(settings.m_apiPort);
|
response.getRemoteOutputSettings()->setApiPort(settings.m_apiPort);
|
||||||
@ -635,6 +659,9 @@ void RemoteOutput::webapiReverseSendSettings(QList<QString>& deviceSettingsKeys,
|
|||||||
if (deviceSettingsKeys.contains("sampleRate") || force) {
|
if (deviceSettingsKeys.contains("sampleRate") || force) {
|
||||||
swgRemoteOutputSettings->setSampleRate(settings.m_sampleRate);
|
swgRemoteOutputSettings->setSampleRate(settings.m_sampleRate);
|
||||||
}
|
}
|
||||||
|
if (deviceSettingsKeys.contains("txDelay") || force) {
|
||||||
|
swgRemoteOutputSettings->setTxDelay(settings.m_txDelay);
|
||||||
|
}
|
||||||
if (deviceSettingsKeys.contains("nbFECBlocks") || force) {
|
if (deviceSettingsKeys.contains("nbFECBlocks") || force) {
|
||||||
swgRemoteOutputSettings->setNbFecBlocks(settings.m_nbFECBlocks);
|
swgRemoteOutputSettings->setNbFecBlocks(settings.m_nbFECBlocks);
|
||||||
}
|
}
|
||||||
|
@ -211,11 +211,20 @@ void RemoteOutputSinkGui::updateSampleRate()
|
|||||||
ui->deviceRateText->setText(tr("%1k").arg((float)(m_sampleRate) / 1000));
|
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()
|
void RemoteOutputSinkGui::displaySettings()
|
||||||
{
|
{
|
||||||
blockApplySettings(true);
|
blockApplySettings(true);
|
||||||
ui->centerFrequency->setValue(m_deviceCenterFrequency / 1000);
|
ui->centerFrequency->setValue(m_deviceCenterFrequency / 1000);
|
||||||
ui->sampleRate->setValue(m_settings.m_sampleRate);
|
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);
|
ui->nbFECBlocks->setValue(m_settings.m_nbFECBlocks);
|
||||||
|
|
||||||
QString s0 = QString::number(128 + m_settings.m_nbFECBlocks, 'f', 0);
|
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)
|
void RemoteOutputSinkGui::on_sampleRate_changed(quint64 value)
|
||||||
{
|
{
|
||||||
m_settings.m_sampleRate = 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();
|
sendSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,6 +308,7 @@ void RemoteOutputSinkGui::on_nbFECBlocks_valueChanged(int value)
|
|||||||
QString s = QString::number(nbOriginalBlocks + nbFECBlocks, 'f', 0);
|
QString s = QString::number(nbOriginalBlocks + nbFECBlocks, 'f', 0);
|
||||||
QString s1 = QString::number(nbFECBlocks, 'f', 0);
|
QString s1 = QString::number(nbFECBlocks, 'f', 0);
|
||||||
ui->nominalNbBlocksText->setText(tr("%1/%2").arg(s).arg(s1));
|
ui->nominalNbBlocksText->setText(tr("%1/%2").arg(s).arg(s1));
|
||||||
|
updateTxDelayTooltip();
|
||||||
sendSettings();
|
sendSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,6 +124,7 @@ private:
|
|||||||
void sendControl(bool force = false);
|
void sendControl(bool force = false);
|
||||||
void sendSettings();
|
void sendSettings();
|
||||||
void updateSampleRate();
|
void updateSampleRate();
|
||||||
|
void updateTxDelayTooltip();
|
||||||
void displayEventCounts();
|
void displayEventCounts();
|
||||||
void displayEventStatus(int recoverableCount, int unrecoverableCount);
|
void displayEventStatus(int recoverableCount, int unrecoverableCount);
|
||||||
void displayEventTimer();
|
void displayEventTimer();
|
||||||
@ -133,6 +134,7 @@ private:
|
|||||||
private slots:
|
private slots:
|
||||||
void handleInputMessages();
|
void handleInputMessages();
|
||||||
void on_sampleRate_changed(quint64 value);
|
void on_sampleRate_changed(quint64 value);
|
||||||
|
void on_txDelay_valueChanged(int value);
|
||||||
void on_nbFECBlocks_valueChanged(int value);
|
void on_nbFECBlocks_valueChanged(int value);
|
||||||
void on_deviceIndex_returnPressed();
|
void on_deviceIndex_returnPressed();
|
||||||
void on_channelIndex_returnPressed();
|
void on_channelIndex_returnPressed();
|
||||||
|
@ -249,6 +249,60 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</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>
|
<item>
|
||||||
<spacer name="horizontalSpacer">
|
<spacer name="horizontalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
///////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
#include <chrono>
|
||||||
#include <boost/crc.hpp>
|
#include <boost/crc.hpp>
|
||||||
#include <boost/cstdint.hpp>
|
#include <boost/cstdint.hpp>
|
||||||
|
|
||||||
@ -91,6 +93,7 @@ void RemoteOutputSender::sendDataBlock(RemoteDataBlock *dataBlock)
|
|||||||
|
|
||||||
uint16_t frameIndex = dataBlock->m_txControlBlock.m_frameIndex;
|
uint16_t frameIndex = dataBlock->m_txControlBlock.m_frameIndex;
|
||||||
int nbBlocksFEC = dataBlock->m_txControlBlock.m_nbBlocksFEC;
|
int nbBlocksFEC = dataBlock->m_txControlBlock.m_nbBlocksFEC;
|
||||||
|
int txDelay = dataBlock->m_txControlBlock.m_txDelay;
|
||||||
m_remoteHostAddress.setAddress(dataBlock->m_txControlBlock.m_dataAddress);
|
m_remoteHostAddress.setAddress(dataBlock->m_txControlBlock.m_dataAddress);
|
||||||
uint16_t dataPort = dataBlock->m_txControlBlock.m_dataPort;
|
uint16_t dataPort = dataBlock->m_txControlBlock.m_dataPort;
|
||||||
RemoteSuperBlock *txBlockx = dataBlock->m_superBlocks;
|
RemoteSuperBlock *txBlockx = dataBlock->m_superBlocks;
|
||||||
@ -99,8 +102,11 @@ void RemoteOutputSender::sendDataBlock(RemoteDataBlock *dataBlock)
|
|||||||
{
|
{
|
||||||
if (m_udpSocket)
|
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);
|
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
|
// Transmit all blocks
|
||||||
if (m_udpSocket)
|
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);
|
m_udpSocket->writeDatagram((const char*)&txBlockx[i], (qint64 ) RemoteUdpSize, m_remoteHostAddress, dataPort);
|
||||||
|
std::this_thread::sleep_for(std::chrono::microseconds(txDelay));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ void RemoteOutputSettings::resetToDefaults()
|
|||||||
{
|
{
|
||||||
m_centerFrequency = 435000*1000;
|
m_centerFrequency = 435000*1000;
|
||||||
m_sampleRate = 48000;
|
m_sampleRate = 48000;
|
||||||
|
m_txDelay = 0.35;
|
||||||
m_nbFECBlocks = 0;
|
m_nbFECBlocks = 0;
|
||||||
m_apiAddress = "127.0.0.1";
|
m_apiAddress = "127.0.0.1";
|
||||||
m_apiPort = 9091;
|
m_apiPort = 9091;
|
||||||
@ -46,6 +47,7 @@ QByteArray RemoteOutputSettings::serialize() const
|
|||||||
|
|
||||||
s.writeU64(1, m_centerFrequency);
|
s.writeU64(1, m_centerFrequency);
|
||||||
s.writeU32(2, m_sampleRate);
|
s.writeU32(2, m_sampleRate);
|
||||||
|
s.writeFloat(3, m_txDelay);
|
||||||
s.writeU32(4, m_nbFECBlocks);
|
s.writeU32(4, m_nbFECBlocks);
|
||||||
s.writeString(5, m_apiAddress);
|
s.writeString(5, m_apiAddress);
|
||||||
s.writeU32(6, m_apiPort);
|
s.writeU32(6, m_apiPort);
|
||||||
@ -77,6 +79,7 @@ bool RemoteOutputSettings::deserialize(const QByteArray& data)
|
|||||||
|
|
||||||
d.readU64(1, &m_centerFrequency, 435000*1000);
|
d.readU64(1, &m_centerFrequency, 435000*1000);
|
||||||
d.readU32(2, &m_sampleRate, 48000);
|
d.readU32(2, &m_sampleRate, 48000);
|
||||||
|
d.readFloat(3, &m_txDelay, 0.5);
|
||||||
d.readU32(4, &m_nbFECBlocks, 0);
|
d.readU32(4, &m_nbFECBlocks, 0);
|
||||||
d.readString(5, &m_apiAddress, "127.0.0.1");
|
d.readString(5, &m_apiAddress, "127.0.0.1");
|
||||||
d.readU32(6, &uintval, 9090);
|
d.readU32(6, &uintval, 9090);
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
struct RemoteOutputSettings {
|
struct RemoteOutputSettings {
|
||||||
quint64 m_centerFrequency;
|
quint64 m_centerFrequency;
|
||||||
quint32 m_sampleRate;
|
quint32 m_sampleRate;
|
||||||
|
float m_txDelay;
|
||||||
quint32 m_nbFECBlocks;
|
quint32 m_nbFECBlocks;
|
||||||
QString m_apiAddress;
|
QString m_apiAddress;
|
||||||
quint16 m_apiPort;
|
quint16 m_apiPort;
|
||||||
|
@ -49,6 +49,7 @@ public:
|
|||||||
|
|
||||||
void setSamplerate(int samplerate);
|
void setSamplerate(int samplerate);
|
||||||
void setNbBlocksFEC(uint32_t nbBlocksFEC) { m_udpSinkFEC.setNbBlocksFEC(nbBlocksFEC); };
|
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); }
|
void setDataAddress(const QString& address, uint16_t port) { m_udpSinkFEC.setRemoteAddress(address, port); }
|
||||||
|
|
||||||
bool isRunning() const { return m_running; }
|
bool isRunning() const { return m_running; }
|
||||||
|
@ -32,6 +32,7 @@ UDPSinkFEC::UDPSinkFEC() :
|
|||||||
m_nbSamples(0),
|
m_nbSamples(0),
|
||||||
m_nbBlocksFEC(0),
|
m_nbBlocksFEC(0),
|
||||||
m_txDelayRatio(0.0),
|
m_txDelayRatio(0.0),
|
||||||
|
m_txDelay(0),
|
||||||
m_dataBlock(nullptr),
|
m_dataBlock(nullptr),
|
||||||
m_txBlockIndex(0),
|
m_txBlockIndex(0),
|
||||||
m_txBlocksIndex(0),
|
m_txBlocksIndex(0),
|
||||||
@ -70,16 +71,35 @@ void UDPSinkFEC::stopSender()
|
|||||||
m_senderThread->wait();
|
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)
|
void UDPSinkFEC::setNbBlocksFEC(uint32_t nbBlocksFEC)
|
||||||
{
|
{
|
||||||
qDebug() << "UDPSinkFEC::setNbBlocksFEC: nbBlocksFEC: " << nbBlocksFEC;
|
qDebug() << "UDPSinkFEC::setNbBlocksFEC: nbBlocksFEC: " << nbBlocksFEC;
|
||||||
m_nbBlocksFEC = nbBlocksFEC;
|
m_nbBlocksFEC = nbBlocksFEC;
|
||||||
|
setTxDelay(m_txDelayRatio);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDPSinkFEC::setSampleRate(uint32_t sampleRate)
|
void UDPSinkFEC::setSampleRate(uint32_t sampleRate)
|
||||||
{
|
{
|
||||||
qDebug() << "UDPSinkFEC::setSampleRate: sampleRate: " << sampleRate;
|
qDebug() << "UDPSinkFEC::setSampleRate: sampleRate: " << sampleRate;
|
||||||
m_sampleRate = sampleRate;
|
m_sampleRate = sampleRate;
|
||||||
|
setTxDelay(m_txDelayRatio);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDPSinkFEC::setRemoteAddress(const QString& address, uint16_t port)
|
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_processed = false;
|
||||||
m_dataBlock->m_txControlBlock.m_complete = true;
|
m_dataBlock->m_txControlBlock.m_complete = true;
|
||||||
m_dataBlock->m_txControlBlock.m_nbBlocksFEC = m_nbBlocksFEC;
|
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_dataAddress = m_remoteAddress;
|
||||||
m_dataBlock->m_txControlBlock.m_dataPort = m_remotePort;
|
m_dataBlock->m_txControlBlock.m_dataPort = m_remotePort;
|
||||||
|
|
||||||
|
@ -67,6 +67,7 @@ public:
|
|||||||
void setSampleRate(uint32_t sampleRate);
|
void setSampleRate(uint32_t sampleRate);
|
||||||
|
|
||||||
void setNbBlocksFEC(uint32_t nbBlocksFEC);
|
void setNbBlocksFEC(uint32_t nbBlocksFEC);
|
||||||
|
void setTxDelay(float txDelayRatio);
|
||||||
void setRemoteAddress(const QString& address, uint16_t port);
|
void setRemoteAddress(const QString& address, uint16_t port);
|
||||||
|
|
||||||
/** Return true if the stream is OK, return false if there is an error. */
|
/** 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
|
RemoteMetaDataFEC m_currentMetaFEC; //!< Meta data for current frame
|
||||||
uint32_t m_nbBlocksFEC; //!< Variable number of FEC blocks
|
uint32_t m_nbBlocksFEC; //!< Variable number of FEC blocks
|
||||||
float m_txDelayRatio; //!< Delay in ratio of nominal frame period
|
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;
|
RemoteDataBlock *m_dataBlock;
|
||||||
RemoteSuperBlock m_superBlock; //!< current super block being built
|
RemoteSuperBlock m_superBlock; //!< current super block being built
|
||||||
int m_txBlockIndex; //!< Current index in blocks to transmit in the Tx row
|
int m_txBlockIndex; //!< Current index in blocks to transmit in the Tx row
|
||||||
|
@ -68,7 +68,8 @@ LocalInputGui::LocalInputGui(DeviceUISet *deviceUISet, QWidget* parent) :
|
|||||||
m_countUnrecoverable(0),
|
m_countUnrecoverable(0),
|
||||||
m_countRecovered(0),
|
m_countRecovered(0),
|
||||||
m_doApplySettings(true),
|
m_doApplySettings(true),
|
||||||
m_forceSettings(true)
|
m_forceSettings(true),
|
||||||
|
m_txDelay(0.0)
|
||||||
{
|
{
|
||||||
m_paletteGreenText.setColor(QPalette::WindowText, Qt::green);
|
m_paletteGreenText.setColor(QPalette::WindowText, Qt::green);
|
||||||
m_paletteWhiteText.setColor(QPalette::WindowText, Qt::white);
|
m_paletteWhiteText.setColor(QPalette::WindowText, Qt::white);
|
||||||
|
@ -92,6 +92,7 @@ private:
|
|||||||
|
|
||||||
bool m_doApplySettings;
|
bool m_doApplySettings;
|
||||||
bool m_forceSettings;
|
bool m_forceSettings;
|
||||||
|
double m_txDelay;
|
||||||
|
|
||||||
QPalette m_paletteGreenText;
|
QPalette m_paletteGreenText;
|
||||||
QPalette m_paletteWhiteText;
|
QPalette m_paletteWhiteText;
|
||||||
|
@ -68,7 +68,8 @@ RemoteInputGui::RemoteInputGui(DeviceUISet *deviceUISet, QWidget* parent) :
|
|||||||
m_countUnrecoverable(0),
|
m_countUnrecoverable(0),
|
||||||
m_countRecovered(0),
|
m_countRecovered(0),
|
||||||
m_doApplySettings(true),
|
m_doApplySettings(true),
|
||||||
m_forceSettings(true)
|
m_forceSettings(true),
|
||||||
|
m_txDelay(0.0)
|
||||||
{
|
{
|
||||||
m_paletteGreenText.setColor(QPalette::WindowText, Qt::green);
|
m_paletteGreenText.setColor(QPalette::WindowText, Qt::green);
|
||||||
m_paletteWhiteText.setColor(QPalette::WindowText, Qt::white);
|
m_paletteWhiteText.setColor(QPalette::WindowText, Qt::white);
|
||||||
|
@ -94,6 +94,7 @@ private:
|
|||||||
|
|
||||||
bool m_doApplySettings;
|
bool m_doApplySettings;
|
||||||
bool m_forceSettings;
|
bool m_forceSettings;
|
||||||
|
double m_txDelay;
|
||||||
|
|
||||||
QPalette m_paletteGreenText;
|
QPalette m_paletteGreenText;
|
||||||
QPalette m_paletteWhiteText;
|
QPalette m_paletteWhiteText;
|
||||||
|
@ -177,6 +177,7 @@ Example of a JSON file (delay is an example you normally do not need it):
|
|||||||
"reverseAPIPort": 8888,
|
"reverseAPIPort": 8888,
|
||||||
"rgbColor": -7601148,
|
"rgbColor": -7601148,
|
||||||
"title": "Channel 0",
|
"title": "Channel 0",
|
||||||
|
"txDelay": 0,
|
||||||
"useReverseAPI": 0
|
"useReverseAPI": 0
|
||||||
},
|
},
|
||||||
"channelType": "RemoteSink",
|
"channelType": "RemoteSink",
|
||||||
|
@ -126,15 +126,16 @@ struct RemoteTxControlBlock
|
|||||||
bool m_processed;
|
bool m_processed;
|
||||||
uint16_t m_frameIndex;
|
uint16_t m_frameIndex;
|
||||||
int m_nbBlocksFEC;
|
int m_nbBlocksFEC;
|
||||||
|
int m_txDelay;
|
||||||
QString m_dataAddress;
|
QString m_dataAddress;
|
||||||
uint16_t m_dataPort;
|
uint16_t m_dataPort;
|
||||||
|
|
||||||
RemoteTxControlBlock()
|
RemoteTxControlBlock() {
|
||||||
{
|
|
||||||
m_complete = false;
|
m_complete = false;
|
||||||
m_processed = false;
|
m_processed = false;
|
||||||
m_frameIndex = 0;
|
m_frameIndex = 0;
|
||||||
m_nbBlocksFEC = 0;
|
m_nbBlocksFEC = 0;
|
||||||
|
m_txDelay = 100;
|
||||||
m_dataAddress = "127.0.0.1";
|
m_dataAddress = "127.0.0.1";
|
||||||
m_dataPort = 9090;
|
m_dataPort = 9090;
|
||||||
}
|
}
|
||||||
|
@ -9841,6 +9841,11 @@ margin-bottom: 20px;
|
|||||||
"sampleRate" : {
|
"sampleRate" : {
|
||||||
"type" : "integer"
|
"type" : "integer"
|
||||||
},
|
},
|
||||||
|
"txDelay" : {
|
||||||
|
"type" : "number",
|
||||||
|
"format" : "float",
|
||||||
|
"description" : "minimum delay in ms between two consecutive packets sending"
|
||||||
|
},
|
||||||
"nbFECBlocks" : {
|
"nbFECBlocks" : {
|
||||||
"type" : "integer"
|
"type" : "integer"
|
||||||
},
|
},
|
||||||
@ -51596,7 +51601,7 @@ except ApiException as e:
|
|||||||
</div>
|
</div>
|
||||||
<div id="generator">
|
<div id="generator">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
Generated 2021-12-07T06:38:29.681+01:00
|
Generated 2021-12-04T20:19:18.225+01:00
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,6 +6,10 @@ RemoteOutputSettings:
|
|||||||
format: int64
|
format: int64
|
||||||
sampleRate:
|
sampleRate:
|
||||||
type: integer
|
type: integer
|
||||||
|
txDelay:
|
||||||
|
description: minimum delay in ms between two consecutive packets sending
|
||||||
|
type: number
|
||||||
|
format: float
|
||||||
nbFECBlocks:
|
nbFECBlocks:
|
||||||
type: integer
|
type: integer
|
||||||
apiAddress:
|
apiAddress:
|
||||||
|
@ -6,6 +6,10 @@ RemoteOutputSettings:
|
|||||||
format: int64
|
format: int64
|
||||||
sampleRate:
|
sampleRate:
|
||||||
type: integer
|
type: integer
|
||||||
|
txDelay:
|
||||||
|
description: minimum delay in ms between two consecutive packets sending
|
||||||
|
type: number
|
||||||
|
format: float
|
||||||
nbFECBlocks:
|
nbFECBlocks:
|
||||||
type: integer
|
type: integer
|
||||||
apiAddress:
|
apiAddress:
|
||||||
|
@ -9841,6 +9841,11 @@ margin-bottom: 20px;
|
|||||||
"sampleRate" : {
|
"sampleRate" : {
|
||||||
"type" : "integer"
|
"type" : "integer"
|
||||||
},
|
},
|
||||||
|
"txDelay" : {
|
||||||
|
"type" : "number",
|
||||||
|
"format" : "float",
|
||||||
|
"description" : "minimum delay in ms between two consecutive packets sending"
|
||||||
|
},
|
||||||
"nbFECBlocks" : {
|
"nbFECBlocks" : {
|
||||||
"type" : "integer"
|
"type" : "integer"
|
||||||
},
|
},
|
||||||
@ -51596,7 +51601,7 @@ except ApiException as e:
|
|||||||
</div>
|
</div>
|
||||||
<div id="generator">
|
<div id="generator">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
Generated 2021-12-07T06:38:29.681+01:00
|
Generated 2021-12-04T20:19:18.225+01:00
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -32,6 +32,8 @@ SWGRemoteOutputSettings::SWGRemoteOutputSettings() {
|
|||||||
m_center_frequency_isSet = false;
|
m_center_frequency_isSet = false;
|
||||||
sample_rate = 0;
|
sample_rate = 0;
|
||||||
m_sample_rate_isSet = false;
|
m_sample_rate_isSet = false;
|
||||||
|
tx_delay = 0.0f;
|
||||||
|
m_tx_delay_isSet = false;
|
||||||
nb_fec_blocks = 0;
|
nb_fec_blocks = 0;
|
||||||
m_nb_fec_blocks_isSet = false;
|
m_nb_fec_blocks_isSet = false;
|
||||||
api_address = nullptr;
|
api_address = nullptr;
|
||||||
@ -66,6 +68,8 @@ SWGRemoteOutputSettings::init() {
|
|||||||
m_center_frequency_isSet = false;
|
m_center_frequency_isSet = false;
|
||||||
sample_rate = 0;
|
sample_rate = 0;
|
||||||
m_sample_rate_isSet = false;
|
m_sample_rate_isSet = false;
|
||||||
|
tx_delay = 0.0f;
|
||||||
|
m_tx_delay_isSet = false;
|
||||||
nb_fec_blocks = 0;
|
nb_fec_blocks = 0;
|
||||||
m_nb_fec_blocks_isSet = false;
|
m_nb_fec_blocks_isSet = false;
|
||||||
api_address = new QString("");
|
api_address = new QString("");
|
||||||
@ -95,6 +99,7 @@ SWGRemoteOutputSettings::cleanup() {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if(api_address != nullptr) {
|
if(api_address != nullptr) {
|
||||||
delete api_address;
|
delete api_address;
|
||||||
}
|
}
|
||||||
@ -128,6 +133,8 @@ SWGRemoteOutputSettings::fromJsonObject(QJsonObject &pJson) {
|
|||||||
|
|
||||||
::SWGSDRangel::setValue(&sample_rate, pJson["sampleRate"], "qint32", "");
|
::SWGSDRangel::setValue(&sample_rate, pJson["sampleRate"], "qint32", "");
|
||||||
|
|
||||||
|
::SWGSDRangel::setValue(&tx_delay, pJson["txDelay"], "float", "");
|
||||||
|
|
||||||
::SWGSDRangel::setValue(&nb_fec_blocks, pJson["nbFECBlocks"], "qint32", "");
|
::SWGSDRangel::setValue(&nb_fec_blocks, pJson["nbFECBlocks"], "qint32", "");
|
||||||
|
|
||||||
::SWGSDRangel::setValue(&api_address, pJson["apiAddress"], "QString", "QString");
|
::SWGSDRangel::setValue(&api_address, pJson["apiAddress"], "QString", "QString");
|
||||||
@ -172,6 +179,9 @@ SWGRemoteOutputSettings::asJsonObject() {
|
|||||||
if(m_sample_rate_isSet){
|
if(m_sample_rate_isSet){
|
||||||
obj->insert("sampleRate", QJsonValue(sample_rate));
|
obj->insert("sampleRate", QJsonValue(sample_rate));
|
||||||
}
|
}
|
||||||
|
if(m_tx_delay_isSet){
|
||||||
|
obj->insert("txDelay", QJsonValue(tx_delay));
|
||||||
|
}
|
||||||
if(m_nb_fec_blocks_isSet){
|
if(m_nb_fec_blocks_isSet){
|
||||||
obj->insert("nbFECBlocks", QJsonValue(nb_fec_blocks));
|
obj->insert("nbFECBlocks", QJsonValue(nb_fec_blocks));
|
||||||
}
|
}
|
||||||
@ -229,6 +239,16 @@ SWGRemoteOutputSettings::setSampleRate(qint32 sample_rate) {
|
|||||||
this->m_sample_rate_isSet = true;
|
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
|
qint32
|
||||||
SWGRemoteOutputSettings::getNbFecBlocks() {
|
SWGRemoteOutputSettings::getNbFecBlocks() {
|
||||||
return nb_fec_blocks;
|
return nb_fec_blocks;
|
||||||
@ -350,6 +370,9 @@ SWGRemoteOutputSettings::isSet(){
|
|||||||
if(m_sample_rate_isSet){
|
if(m_sample_rate_isSet){
|
||||||
isObjectUpdated = true; break;
|
isObjectUpdated = true; break;
|
||||||
}
|
}
|
||||||
|
if(m_tx_delay_isSet){
|
||||||
|
isObjectUpdated = true; break;
|
||||||
|
}
|
||||||
if(m_nb_fec_blocks_isSet){
|
if(m_nb_fec_blocks_isSet){
|
||||||
isObjectUpdated = true; break;
|
isObjectUpdated = true; break;
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,9 @@ public:
|
|||||||
qint32 getSampleRate();
|
qint32 getSampleRate();
|
||||||
void setSampleRate(qint32 sample_rate);
|
void setSampleRate(qint32 sample_rate);
|
||||||
|
|
||||||
|
float getTxDelay();
|
||||||
|
void setTxDelay(float tx_delay);
|
||||||
|
|
||||||
qint32 getNbFecBlocks();
|
qint32 getNbFecBlocks();
|
||||||
void setNbFecBlocks(qint32 nb_fec_blocks);
|
void setNbFecBlocks(qint32 nb_fec_blocks);
|
||||||
|
|
||||||
@ -91,6 +94,9 @@ private:
|
|||||||
qint32 sample_rate;
|
qint32 sample_rate;
|
||||||
bool m_sample_rate_isSet;
|
bool m_sample_rate_isSet;
|
||||||
|
|
||||||
|
float tx_delay;
|
||||||
|
bool m_tx_delay_isSet;
|
||||||
|
|
||||||
qint32 nb_fec_blocks;
|
qint32 nb_fec_blocks;
|
||||||
bool m_nb_fec_blocks_isSet;
|
bool m_nb_fec_blocks_isSet;
|
||||||
|
|
||||||
|
@ -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-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-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-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()
|
(options, args) = parser.parse_args()
|
||||||
|
|
||||||
@ -325,6 +326,8 @@ def setupChannel(deviceset_url, options):
|
|||||||
settings["RemoteSinkSettings"]["dataPort"] = options.remote_port
|
settings["RemoteSinkSettings"]["dataPort"] = options.remote_port
|
||||||
if options.remote_fec:
|
if options.remote_fec:
|
||||||
settings["RemoteSinkSettings"]["nbFECBlocks"] = 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")
|
r = callAPI(deviceset_url + "/channel/%d/settings" % i, "PATCH", None, settings, "Change demod")
|
||||||
if r is None:
|
if r is None:
|
||||||
|
@ -63,6 +63,7 @@
|
|||||||
"reverseAPIPort": 8888,
|
"reverseAPIPort": 8888,
|
||||||
"rgbColor": -7601148,
|
"rgbColor": -7601148,
|
||||||
"title": "Channel 0",
|
"title": "Channel 0",
|
||||||
|
"txDelay": 0,
|
||||||
"useReverseAPI": 0
|
"useReverseAPI": 0
|
||||||
},
|
},
|
||||||
"channelType": "RemoteSink",
|
"channelType": "RemoteSink",
|
||||||
@ -97,6 +98,7 @@
|
|||||||
"reverseAPIPort": 8888,
|
"reverseAPIPort": 8888,
|
||||||
"rgbColor": -7601148,
|
"rgbColor": -7601148,
|
||||||
"title": "Remote sink",
|
"title": "Remote sink",
|
||||||
|
"txDelay": 0,
|
||||||
"useReverseAPI": 0
|
"useReverseAPI": 0
|
||||||
},
|
},
|
||||||
"channelType": "RemoteSink",
|
"channelType": "RemoteSink",
|
||||||
@ -131,6 +133,7 @@
|
|||||||
"reverseAPIPort": 8888,
|
"reverseAPIPort": 8888,
|
||||||
"rgbColor": -7601148,
|
"rgbColor": -7601148,
|
||||||
"title": "Remote sink",
|
"title": "Remote sink",
|
||||||
|
"txDelay": 0,
|
||||||
"useReverseAPI": 0
|
"useReverseAPI": 0
|
||||||
},
|
},
|
||||||
"channelType": "RemoteSink",
|
"channelType": "RemoteSink",
|
||||||
@ -145,4 +148,4 @@
|
|||||||
null,
|
null,
|
||||||
"Start device on deviceset R0"
|
"Start device on deviceset R0"
|
||||||
]
|
]
|
||||||
]
|
]
|
Loading…
Reference in New Issue
Block a user