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.
ef4b54a7b1
...
bb1f833d02
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -139,7 +139,7 @@
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="dataApplyButton">
|
||||
<widget class="QPushButton" name="pushButton">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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">
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -24,6 +24,7 @@
|
||||
struct RemoteOutputSettings {
|
||||
quint64 m_centerFrequency;
|
||||
quint32 m_sampleRate;
|
||||
float m_txDelay;
|
||||
quint32 m_nbFECBlocks;
|
||||
QString m_apiAddress;
|
||||
quint16 m_apiPort;
|
||||
|
@ -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; }
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -92,6 +92,7 @@ private:
|
||||
|
||||
bool m_doApplySettings;
|
||||
bool m_forceSettings;
|
||||
double m_txDelay;
|
||||
|
||||
QPalette m_paletteGreenText;
|
||||
QPalette m_paletteWhiteText;
|
||||
|
@ -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);
|
||||
|
@ -94,6 +94,7 @@ private:
|
||||
|
||||
bool m_doApplySettings;
|
||||
bool m_forceSettings;
|
||||
double m_txDelay;
|
||||
|
||||
QPalette m_paletteGreenText;
|
||||
QPalette m_paletteWhiteText;
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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"
|
||||
]
|
||||
]
|
||||
]
|
Loading…
Reference in New Issue
Block a user