1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-25 01:18:38 -05:00

Compare commits

...

6 Commits

53 changed files with 1167 additions and 840 deletions

View File

@ -113,9 +113,6 @@ public:
static const char* const m_channelIdURI;
static const char* const m_channelId;
signals:
void dataBlockAvailable(RemoteDataBlock *dataBlock);
private:
DeviceAPI *m_deviceAPI;
QThread *m_thread;

View File

@ -47,7 +47,7 @@ void RemoteSinkFifo::reset()
m_writeHead = 0;
}
RemoteDataBlock *RemoteSinkFifo::getDataBlock()
RemoteDataFrame *RemoteSinkFifo::getDataFrame()
{
QMutexLocker mutexLocker(&m_mutex);
m_servedHead = m_writeHead;
@ -62,18 +62,18 @@ RemoteDataBlock *RemoteSinkFifo::getDataBlock()
return &m_data[m_servedHead];
}
unsigned int RemoteSinkFifo::readDataBlock(RemoteDataBlock **dataBlock)
unsigned int RemoteSinkFifo::readDataFrame(RemoteDataFrame **dataFrame)
{
QMutexLocker mutexLocker(&m_mutex);
if (calculateRemainder() == 0)
{
*dataBlock = nullptr;
*dataFrame = nullptr;
return 0;
}
else
{
*dataBlock = &m_data[m_readHead];
*dataFrame = &m_data[m_readHead];
m_readHead = m_readHead < m_size - 1 ? m_readHead + 1 : 0;
return calculateRemainder();
}
@ -92,4 +92,4 @@ unsigned int RemoteSinkFifo::calculateRemainder()
} else {
return m_size - (m_readHead - m_servedHead);
}
}
}

View File

@ -34,15 +34,15 @@ public:
void resize(unsigned int size);
void reset();
RemoteDataBlock *getDataBlock();
unsigned int readDataBlock(RemoteDataBlock **dataBlock);
RemoteDataFrame *getDataFrame();
unsigned int readDataFrame(RemoteDataFrame **dataFrame);
unsigned int getRemainder();
signals:
void dataBlockServed();
private:
std::vector<RemoteDataBlock> m_data;
std::vector<RemoteDataFrame> m_data;
int m_size;
int m_readHead; //!< index of last data block processed
int m_servedHead; //!< index of last data block served

View File

@ -57,37 +57,37 @@ RemoteSinkSender::~RemoteSinkSender()
m_socket->deleteLater();
}
RemoteDataBlock *RemoteSinkSender::getDataBlock()
RemoteDataFrame *RemoteSinkSender::getDataFrame()
{
return m_fifo.getDataBlock();
return m_fifo.getDataFrame();
}
void RemoteSinkSender::handleData()
{
RemoteDataBlock *dataBlock;
RemoteDataFrame *dataFrame;
unsigned int remainder = m_fifo.getRemainder();
while (remainder != 0)
{
remainder = m_fifo.readDataBlock(&dataBlock);
remainder = m_fifo.readDataFrame(&dataFrame);
if (dataBlock) {
sendDataBlock(dataBlock);
if (dataFrame) {
sendDataFrame(dataFrame);
}
}
}
void RemoteSinkSender::sendDataBlock(RemoteDataBlock *dataBlock)
void RemoteSinkSender::sendDataFrame(RemoteDataFrame *dataFrame)
{
CM256::cm256_encoder_params cm256Params; //!< Main interface with CM256 encoder
CM256::cm256_block descriptorBlocks[256]; //!< Pointers to data for CM256 encoder
RemoteProtectedBlock fecBlocks[256]; //!< FEC data
uint16_t frameIndex = dataBlock->m_txControlBlock.m_frameIndex;
int nbBlocksFEC = dataBlock->m_txControlBlock.m_nbBlocksFEC;
m_address.setAddress(dataBlock->m_txControlBlock.m_dataAddress);
uint16_t dataPort = dataBlock->m_txControlBlock.m_dataPort;
RemoteSuperBlock *txBlockx = dataBlock->m_superBlocks;
uint16_t frameIndex = dataFrame->m_txControlBlock.m_frameIndex;
int nbBlocksFEC = dataFrame->m_txControlBlock.m_nbBlocksFEC;
m_address.setAddress(dataFrame->m_txControlBlock.m_dataAddress);
uint16_t dataPort = dataFrame->m_txControlBlock.m_dataPort;
RemoteSuperBlock *txBlockx = dataFrame->m_superBlocks;
if ((nbBlocksFEC == 0) || !m_cm256p) // Do not FEC encode
{
@ -141,5 +141,5 @@ void RemoteSinkSender::sendDataBlock(RemoteDataBlock *dataBlock)
}
}
dataBlock->m_txControlBlock.m_processed = true;
dataFrame->m_txControlBlock.m_processed = true;
}

View File

@ -36,7 +36,7 @@
#include "remotesinkfifo.h"
class RemoteDataBlock;
class RemoteDataFrame;
class CM256;
class QUdpSocket;
@ -47,7 +47,7 @@ public:
RemoteSinkSender();
~RemoteSinkSender();
RemoteDataBlock *getDataBlock();
RemoteDataFrame *getDataFrame();
private:
RemoteSinkFifo m_fifo;
@ -57,7 +57,7 @@ private:
QHostAddress m_address;
QUdpSocket *m_socket;
void sendDataBlock(RemoteDataBlock *dataBlock);
void sendDataFrame(RemoteDataFrame *dataFrame);
private slots:
void handleData();

View File

@ -31,7 +31,7 @@ RemoteSinkSink::RemoteSinkSink() :
m_txBlockIndex(0),
m_frameCount(0),
m_sampleIndex(0),
m_dataBlock(nullptr),
m_dataFrame(nullptr),
m_deviceCenterFrequency(0),
m_frequencyOffset(0),
m_basebandSampleRate(48000),
@ -99,14 +99,14 @@ void RemoteSinkSink::feed(const SampleVector::const_iterator& begin, const Sampl
metaData.m_tv_sec = nowus / 1000000UL; // tv.tv_sec;
metaData.m_tv_usec = nowus % 1000000UL; // tv.tv_usec;
if (!m_dataBlock) { // on the very first cycle there is no data block allocated
m_dataBlock = m_remoteSinkSender->getDataBlock(); // ask a new block to sender
if (!m_dataFrame) { // on the very first cycle there is no data block allocated
m_dataFrame = m_remoteSinkSender->getDataFrame(); // ask a new block to sender
}
boost::crc_32_type crc32;
crc32.process_bytes(&metaData, sizeof(RemoteMetaDataFEC)-4);
metaData.m_crc32 = crc32.checksum();
RemoteSuperBlock& superBlock = m_dataBlock->m_superBlocks[0]; // first block
RemoteSuperBlock& superBlock = m_dataFrame->m_superBlocks[0]; // first block
superBlock.init();
superBlock.m_header.m_frameIndex = m_frameCount;
superBlock.m_header.m_blockIndex = m_txBlockIndex;
@ -156,18 +156,18 @@ void RemoteSinkSink::feed(const SampleVector::const_iterator& begin, const Sampl
m_superBlock.m_header.m_blockIndex = m_txBlockIndex;
m_superBlock.m_header.m_sampleBytes = (SDR_RX_SAMP_SZ <= 16 ? 2 : 4);
m_superBlock.m_header.m_sampleBits = SDR_RX_SAMP_SZ;
m_dataBlock->m_superBlocks[m_txBlockIndex] = m_superBlock;
m_dataFrame->m_superBlocks[m_txBlockIndex] = m_superBlock;
if (m_txBlockIndex == RemoteNbOrginalBlocks - 1) // frame complete
{
m_dataBlock->m_txControlBlock.m_frameIndex = m_frameCount;
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_dataAddress = m_dataAddress;
m_dataBlock->m_txControlBlock.m_dataPort = m_dataPort;
m_dataFrame->m_txControlBlock.m_frameIndex = m_frameCount;
m_dataFrame->m_txControlBlock.m_processed = false;
m_dataFrame->m_txControlBlock.m_complete = true;
m_dataFrame->m_txControlBlock.m_nbBlocksFEC = m_nbBlocksFEC;
m_dataFrame->m_txControlBlock.m_dataAddress = m_dataAddress;
m_dataFrame->m_txControlBlock.m_dataPort = m_dataPort;
m_dataBlock = m_remoteSinkSender->getDataBlock(); // ask a new block to sender
m_dataFrame = m_remoteSinkSender->getDataFrame(); // ask a new block to sender
m_txBlockIndex = 0;
m_frameCount++;

View File

@ -55,7 +55,7 @@ private:
int m_sampleIndex; //!< Current sample index in protected block data
RemoteSuperBlock m_superBlock;
RemoteMetaDataFEC m_currentMetaFEC;
RemoteDataBlock *m_dataBlock;
RemoteDataFrame *m_dataFrame;
uint64_t m_deviceCenterFrequency;
int64_t m_frequencyOffset;

View File

@ -28,6 +28,8 @@
#include "SWGRemoteSourceReport.h"
#include "dsp/devicesamplesink.h"
#include "dsp/hbfilterchainconverter.h"
#include "dsp/dspcommands.h"
#include "device/deviceapi.h"
#include "feature/feature.h"
#include "settings/serializable.h"
@ -40,13 +42,17 @@
MESSAGE_CLASS_DEFINITION(RemoteSource::MsgConfigureRemoteSource, Message)
MESSAGE_CLASS_DEFINITION(RemoteSource::MsgQueryStreamData, Message)
MESSAGE_CLASS_DEFINITION(RemoteSource::MsgReportStreamData, Message)
MESSAGE_CLASS_DEFINITION(RemoteSource::MsgBasebandSampleRateNotification, Message)
const char* const RemoteSource::m_channelIdURI = "sdrangel.channeltx.remotesource";
const char* const RemoteSource::m_channelId ="RemoteSource";
RemoteSource::RemoteSource(DeviceAPI *deviceAPI) :
ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSource),
m_deviceAPI(deviceAPI)
m_deviceAPI(deviceAPI),
m_centerFrequency(0),
m_frequencyOffset(0),
m_basebandSampleRate(48000)
{
setObjectName(m_channelId);
@ -96,7 +102,27 @@ void RemoteSource::pull(SampleVector::iterator& begin, unsigned int nbSamples)
bool RemoteSource::handleMessage(const Message& cmd)
{
if (MsgConfigureRemoteSource::match(cmd))
if (DSPSignalNotification::match(cmd))
{
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
qDebug() << "RemoteSource::handleMessage: DSPSignalNotification:"
<< " inputSampleRate: " << notif.getSampleRate()
<< " centerFrequency: " << notif.getCenterFrequency();
m_basebandSampleRate = notif.getSampleRate();
calculateFrequencyOffset(m_settings.m_log2Interp, m_settings.m_filterChainHash); // This is when device sample rate changes
m_centerFrequency = notif.getCenterFrequency();
if (m_guiMessageQueue)
{
MsgBasebandSampleRateNotification *msg = MsgBasebandSampleRateNotification::create(notif.getSampleRate());
m_guiMessageQueue->push(msg);
}
return true;
}
else if (MsgConfigureRemoteSource::match(cmd))
{
MsgConfigureRemoteSource& cfg = (MsgConfigureRemoteSource&) cmd;
qDebug() << "MsgConfigureRemoteSource::handleMessage: MsgConfigureRemoteSource";
@ -158,27 +184,39 @@ bool RemoteSource::deserialize(const QByteArray& data)
void RemoteSource::applySettings(const RemoteSourceSettings& settings, bool force)
{
qDebug() << "RemoteSource::applySettings:"
<< " m_dataAddress: " << settings.m_dataAddress
<< " m_dataPort: " << settings.m_dataPort
<< " m_rgbColor: " << settings.m_rgbColor
<< " m_title: " << settings.m_title
<< " m_useReverseAPI: " << settings.m_useReverseAPI
<< " m_reverseAPIAddress: " << settings.m_reverseAPIAddress
<< " m_reverseAPIChannelIndex: " << settings.m_reverseAPIChannelIndex
<< " m_reverseAPIDeviceIndex: " << settings.m_reverseAPIDeviceIndex
<< " m_reverseAPIPort: " << settings.m_reverseAPIPort
<< " force: " << force;
<< "m_log2Interp:" << settings.m_log2Interp
<< "m_filterChainHash:" << settings.m_filterChainHash
<< "m_dataAddress:" << settings.m_dataAddress
<< "m_dataPort:" << settings.m_dataPort
<< "m_rgbColor:" << settings.m_rgbColor
<< "m_title:" << settings.m_title
<< "m_useReverseAPI:" << settings.m_useReverseAPI
<< "m_reverseAPIAddress:" << settings.m_reverseAPIAddress
<< "m_reverseAPIChannelIndex:" << settings.m_reverseAPIChannelIndex
<< "m_reverseAPIDeviceIndex:" << settings.m_reverseAPIDeviceIndex
<< "m_reverseAPIPort:" << settings.m_reverseAPIPort
<< "force:" << force;
QList<QString> reverseAPIKeys;
if ((m_settings.m_log2Interp != settings.m_log2Interp) || force) {
reverseAPIKeys.append("log2Interp");
}
if ((m_settings.m_filterChainHash != settings.m_filterChainHash) || force) {
reverseAPIKeys.append("filterChainHash");
}
if ((m_settings.m_dataAddress != settings.m_dataAddress) || force) {
reverseAPIKeys.append("dataAddress");
}
if ((m_settings.m_dataPort != settings.m_dataPort) || force) {
reverseAPIKeys.append("dataPort");
}
if ((m_settings.m_log2Interp != settings.m_log2Interp)
|| (m_settings.m_filterChainHash != settings.m_filterChainHash) || force) {
calculateFrequencyOffset(settings.m_log2Interp, settings.m_filterChainHash);
}
if (m_settings.m_streamIndex != settings.m_streamIndex)
{
if (m_deviceAPI->getSampleMIMO()) // change of stream is possible for MIMO devices only
@ -214,6 +252,23 @@ void RemoteSource::applySettings(const RemoteSourceSettings& settings, bool forc
m_settings = settings;
}
void RemoteSource::validateFilterChainHash(RemoteSourceSettings& settings)
{
unsigned int s = 1;
for (unsigned int i = 0; i < settings.m_log2Interp; i++) {
s *= 3;
}
settings.m_filterChainHash = settings.m_filterChainHash >= s ? s-1 : settings.m_filterChainHash;
}
void RemoteSource::calculateFrequencyOffset(uint32_t log2Interp, uint32_t filterChainHash)
{
double shiftFactor = HBFilterChainConverter::getShiftFactor(log2Interp, filterChainHash);
m_frequencyOffset = m_basebandSampleRate * shiftFactor;
}
int RemoteSource::webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
@ -274,6 +329,12 @@ void RemoteSource::webapiUpdateChannelSettings(
if (channelSettingsKeys.contains("title")) {
settings.m_title = *response.getRemoteSourceSettings()->getTitle();
}
if (channelSettingsKeys.contains("log2Interp")) {
settings.m_log2Interp = response.getRemoteSourceSettings()->getLog2Interp();
}
if (channelSettingsKeys.contains("filterChainHash")) {
settings.m_filterChainHash = response.getRemoteSourceSettings()->getFilterChainHash();
}
if (channelSettingsKeys.contains("streamIndex")) {
settings.m_streamIndex = response.getRemoteSourceSettings()->getStreamIndex();
}
@ -325,6 +386,8 @@ void RemoteSource::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings&
response.getRemoteSourceSettings()->setTitle(new QString(settings.m_title));
}
response.getRemoteSourceSettings()->setLog2Interp(settings.m_log2Interp);
response.getRemoteSourceSettings()->setFilterChainHash(settings.m_filterChainHash);
response.getRemoteSourceSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
if (response.getRemoteSourceSettings()->getReverseApiAddress()) {
@ -367,9 +430,10 @@ void RemoteSource::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& resp
response.getRemoteSourceReport()->setUncorrectableErrorsCount(m_basebandSource->getNbUncorrectableErrors());
response.getRemoteSourceReport()->setNbOriginalBlocks(currentMeta.m_nbOriginalBlocks);
response.getRemoteSourceReport()->setNbFecBlocks(currentMeta.m_nbFECBlocks);
response.getRemoteSourceReport()->setCenterFreq(currentMeta.m_centerFrequency);
response.getRemoteSourceReport()->setSampleRate(currentMeta.m_sampleRate);
response.getRemoteSourceReport()->setDeviceCenterFreq(m_deviceAPI->getSampleSink()->getCenterFrequency()/1000);
response.getRemoteSourceReport()->setCenterFreq(m_frequencyOffset);
double channelSampleRate = ((double) m_basebandSampleRate) / (1<<m_settings.m_log2Interp);
response.getRemoteSourceReport()->setSampleRate(channelSampleRate);
response.getRemoteSourceReport()->setDeviceCenterFreq(m_deviceAPI->getSampleSink()->getCenterFrequency());
response.getRemoteSourceReport()->setDeviceSampleRate(m_deviceAPI->getSampleSink()->getSampleRate());
}
@ -445,6 +509,12 @@ void RemoteSource::webapiFormatChannelSettings(
if (channelSettingsKeys.contains("rgbColor") || force) {
swgRemoteSourceSettings->setRgbColor(settings.m_rgbColor);
}
if (channelSettingsKeys.contains("log2Interp") || force) {
swgRemoteSourceSettings->setLog2Interp(settings.m_log2Interp);
}
if (channelSettingsKeys.contains("filterChainHash") || force) {
swgRemoteSourceSettings->setFilterChainHash(settings.m_filterChainHash);
}
if (channelSettingsKeys.contains("title") || force) {
swgRemoteSourceSettings->setTitle(new QString(settings.m_title));
}

View File

@ -154,6 +154,27 @@ public:
{ }
};
class MsgBasebandSampleRateNotification : public Message {
MESSAGE_CLASS_DECLARATION
public:
static MsgBasebandSampleRateNotification* create(int sampleRate) {
return new MsgBasebandSampleRateNotification(sampleRate);
}
int getBasebandSampleRate() const { return m_sampleRate; }
private:
MsgBasebandSampleRateNotification(int sampleRate) :
Message(),
m_sampleRate(sampleRate)
{ }
int m_sampleRate;
};
RemoteSource(DeviceAPI *deviceAPI);
virtual ~RemoteSource();
@ -218,7 +239,13 @@ private:
QNetworkAccessManager *m_networkManager;
QNetworkRequest m_networkRequest;
uint64_t m_centerFrequency;
int64_t m_frequencyOffset;
uint32_t m_basebandSampleRate;
void applySettings(const RemoteSourceSettings& settings, bool force = false);
static void validateFilterChainHash(RemoteSourceSettings& settings);
void calculateFrequencyOffset(uint32_t log2Interp, uint32_t filterChainHash);
void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response);
void webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const RemoteSourceSettings& settings, bool force);
void sendChannelSettings(

View File

@ -164,15 +164,23 @@ bool RemoteSourceBaseband::handleMessage(const Message& cmd)
void RemoteSourceBaseband::applySettings(const RemoteSourceSettings& settings, bool force)
{
qDebug() << "RemoteSourceBaseband::applySettings:"
<< " m_dataAddress: " << settings.m_dataAddress
<< " m_dataPort: " << settings.m_dataPort
<< " force: " << force;
<< "m_log2Interp:" << settings.m_log2Interp
<< "m_filterChainHash:" << settings.m_filterChainHash
<< "m_dataAddress:" << settings.m_dataAddress
<< "m_dataPort:" << settings.m_dataPort
<< "force:" << force;
if ((settings.m_dataAddress != m_settings.m_dataAddress)
|| (settings.m_dataPort != m_settings.m_dataPort) || force) {
m_source.dataBind(settings.m_dataAddress, settings.m_dataPort);
}
if ((m_settings.m_filterChainHash != settings.m_filterChainHash)
|| (m_settings.m_log2Interp != settings.m_log2Interp) || force)
{
m_channelizer->setInterpolation(settings.m_log2Interp, settings.m_filterChainHash);
}
m_settings = settings;
}
@ -184,4 +192,4 @@ int RemoteSourceBaseband::getChannelSampleRate() const
void RemoteSourceBaseband::newRemoteSampleRate(unsigned int sampleRate)
{
m_channelizer->setChannelization(sampleRate, 0); // Adjust channelizer to match remote sample rate
}
}

View File

@ -19,6 +19,7 @@
#include "device/deviceapi.h"
#include "device/deviceuiset.h"
#include "dsp/hbfilterchainconverter.h"
#include "gui/basicchannelsettingsdialog.h"
#include "gui/devicestreamselectiondialog.h"
#include "mainwindow.h"
@ -65,7 +66,14 @@ bool RemoteSourceGUI::deserialize(const QByteArray& data)
bool RemoteSourceGUI::handleMessage(const Message& message)
{
if (RemoteSource::MsgConfigureRemoteSource::match(message))
if (RemoteSource::MsgBasebandSampleRateNotification::match(message))
{
RemoteSource::MsgBasebandSampleRateNotification& notif = (RemoteSource::MsgBasebandSampleRateNotification&) message;
m_basebandSampleRate = notif.getBasebandSampleRate();
displayRateAndShift();
return true;
}
else if (RemoteSource::MsgConfigureRemoteSource::match(message))
{
const RemoteSource::MsgConfigureRemoteSource& cfg = (RemoteSource::MsgConfigureRemoteSource&) message;
m_settings = cfg.getSettings();
@ -149,6 +157,8 @@ RemoteSourceGUI::RemoteSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet,
m_pluginAPI(pluginAPI),
m_deviceUISet(deviceUISet),
m_remoteSampleRate(48000),
m_basebandSampleRate(48000),
m_shiftFrequencyFactor(0.0),
m_countUnrecoverable(0),
m_countRecovered(0),
m_lastCountUnrecoverable(0),
@ -189,6 +199,8 @@ RemoteSourceGUI::RemoteSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet,
m_time.start();
displaySettings();
displayPosition();
displayRateAndShift();
applySettings(true);
}
@ -233,6 +245,25 @@ void RemoteSourceGUI::displaySettings()
blockApplySettings(false);
}
void RemoteSourceGUI::displayRateAndShift()
{
int shift = m_shiftFrequencyFactor * m_basebandSampleRate;
double channelSampleRate = ((double) m_basebandSampleRate) / (1<<m_settings.m_log2Interp);
QLocale loc;
ui->offsetFrequencyText->setText(tr("%1 Hz").arg(loc.toString(shift)));
ui->channelRateText->setText(tr("%1k").arg(QString::number(channelSampleRate / 1000.0, 'g', 5)));
m_channelMarker.setCenterFrequency(shift);
m_channelMarker.setBandwidth(channelSampleRate);
}
void RemoteSourceGUI::displayPosition()
{
ui->filterChainIndex->setText(tr("%1").arg(m_settings.m_filterChainHash));
QString s;
HBFilterChainConverter::convertToString(m_settings.m_log2Interp, m_settings.m_filterChainHash, s);
ui->filterChainText->setText(s);
}
void RemoteSourceGUI::displayStreamIndex()
{
if (m_deviceUISet->m_deviceMIMOEngine) {
@ -319,6 +350,18 @@ void RemoteSourceGUI::onMenuDialogCalled(const QPoint &p)
resetContextMenuType();
}
void RemoteSourceGUI::on_interpolationFactor_currentIndexChanged(int index)
{
m_settings.m_log2Interp = index;
applyInterpolation();
}
void RemoteSourceGUI::on_position_valueChanged(int value)
{
m_settings.m_filterChainHash = value;
applyPosition();
}
void RemoteSourceGUI::on_dataAddress_returnPressed()
{
m_settings.m_dataAddress = ui->dataAddress->text();
@ -364,6 +407,31 @@ void RemoteSourceGUI::on_eventCountsReset_clicked(bool checked)
displayEventTimer();
}
void RemoteSourceGUI::applyInterpolation()
{
uint32_t maxHash = 1;
for (uint32_t i = 0; i < m_settings.m_log2Interp; i++) {
maxHash *= 3;
}
ui->position->setMaximum(maxHash-1);
ui->position->setValue(m_settings.m_filterChainHash);
m_settings.m_filterChainHash = ui->position->value();
applyPosition();
}
void RemoteSourceGUI::applyPosition()
{
ui->filterChainIndex->setText(tr("%1").arg(m_settings.m_filterChainHash));
QString s;
m_shiftFrequencyFactor = HBFilterChainConverter::convertToString(m_settings.m_log2Interp, m_settings.m_filterChainHash, s);
ui->filterChainText->setText(s);
displayRateAndShift();
applySettings();
}
void RemoteSourceGUI::displayEventCounts()
{
QString nstr = QString("%1").arg(m_countUnrecoverable, 3, 10, QChar('0'));

View File

@ -57,7 +57,9 @@ private:
ChannelMarker m_channelMarker;
RemoteSourceSettings m_settings;
int m_remoteSampleRate;
int m_basebandSampleRate;
bool m_doApplySettings;
double m_shiftFrequencyFactor; //!< Channel frequency shift factor
RemoteSource* m_remoteSrc;
MessageQueue m_inputMessageQueue;
@ -78,6 +80,8 @@ private:
void blockApplySettings(bool block);
void applySettings(bool force = false);
void displaySettings();
void displayRateAndShift();
void displayPosition();
void displayStreamIndex();
bool handleMessage(const Message& message);
@ -88,8 +92,13 @@ private:
void displayEventStatus(int recoverableCount, int unrecoverableCount);
void displayEventTimer();
void applyInterpolation();
void applyPosition();
private slots:
void handleSourceMessages();
void on_interpolationFactor_currentIndexChanged(int index);
void on_position_valueChanged(int value);
void on_dataAddress_returnPressed();
void on_dataPort_returnPressed();
void on_dataApplyButton_clicked(bool checked);

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>320</width>
<height>140</height>
<height>221</height>
</rect>
</property>
<property name="sizePolicy">
@ -43,7 +43,7 @@
<x>10</x>
<y>10</y>
<width>301</width>
<height>121</height>
<height>191</height>
</rect>
</property>
<property name="windowTitle">
@ -156,6 +156,188 @@
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="decimationLayer">
<property name="spacing">
<number>3</number>
</property>
<item>
<layout class="QHBoxLayout" name="decimationStageLayer">
<item>
<widget class="QLabel" name="interpolationLabel">
<property name="text">
<string>Int</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="interpolationFactor">
<property name="maximumSize">
<size>
<width>55</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Decimation factor</string>
</property>
<item>
<property name="text">
<string>1</string>
</property>
</item>
<item>
<property name="text">
<string>2</string>
</property>
</item>
<item>
<property name="text">
<string>4</string>
</property>
</item>
<item>
<property name="text">
<string>8</string>
</property>
</item>
<item>
<property name="text">
<string>16</string>
</property>
</item>
<item>
<property name="text">
<string>32</string>
</property>
</item>
<item>
<property name="text">
<string>64</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="channelRateText">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Effective channel rate (kS/s)</string>
</property>
<property name="text">
<string>0000k</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="filterChainText">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Filter chain stages left to right (L: low, C: center, H: high) </string>
</property>
<property name="text">
<string>LLLLLL</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="offsetFrequencyText">
<property name="minimumSize">
<size>
<width>85</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Offset frequency with thousands separator (Hz)</string>
</property>
<property name="text">
<string>-9,999,999 Hz</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="decimationShiftLayer">
<property name="rightMargin">
<number>10</number>
</property>
<item>
<widget class="QLabel" name="positionLabel">
<property name="text">
<string>Pos</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="position">
<property name="toolTip">
<string>Center frequency position</string>
</property>
<property name="maximum">
<number>2</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="filterChainIndex">
<property name="minimumSize">
<size>
<width>24</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Filter chain hash code</string>
</property>
<property name="text">
<string>000</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="nominalValuesLayout">
<item>

View File

@ -34,6 +34,8 @@ void RemoteSourceSettings::resetToDefaults()
m_dataPort = 9090;
m_rgbColor = QColor(140, 4, 4).rgb();
m_title = "Remote source";
m_log2Interp = 0;
m_filterChainHash = 0;
m_channelMarker = nullptr;
m_streamIndex = 0;
m_useReverseAPI = false;
@ -57,6 +59,8 @@ QByteArray RemoteSourceSettings::serialize() const
s.writeU32(9, m_reverseAPIChannelIndex);
s.writeS32(10, m_streamIndex);
s.writeBlob(11, m_rollupState);
s.writeU32(12, m_log2Interp);
s.writeU32(13, m_filterChainHash);
return s.final();
}
@ -103,6 +107,8 @@ bool RemoteSourceSettings::deserialize(const QByteArray& data)
m_reverseAPIChannelIndex = tmp > 99 ? 99 : tmp;
d.readS32(10, &m_streamIndex, 0);
d.readBlob(11, &m_rollupState);
d.readU32(13, &m_filterChainHash, 0);
d.readS32(14, &m_streamIndex, 0);
return true;
}

View File

@ -29,6 +29,8 @@ struct RemoteSourceSettings
uint16_t m_dataPort; //!< Listening data port
quint32 m_rgbColor;
QString m_title;
uint32_t m_log2Interp;
uint32_t m_filterChainHash;
int m_streamIndex;
bool m_useReverseAPI;
QString m_reverseAPIAddress;

View File

@ -31,6 +31,7 @@ RemoteSourceSource::RemoteSourceSource() :
connect(&m_dataQueue, SIGNAL(dataBlockEnqueued()), this, SLOT(handleData()), Qt::QueuedConnection);
m_cm256p = m_cm256.isInitialized() ? &m_cm256 : 0;
m_currentMeta.init();
m_dataReadQueue.setSize(20);
applyChannelSettings(m_channelSampleRate, true);
}
@ -53,35 +54,6 @@ void RemoteSourceSource::pull(SampleVector::iterator begin, unsigned int nbSampl
void RemoteSourceSource::pullOne(Sample& sample)
{
m_dataReadQueue.readSample(sample, true); // true is scale for Tx
Complex ci;
if (m_interpolatorDistance > 1.0f) // decimate
{
getSample();
while (!m_interpolator.decimate(&m_interpolatorDistanceRemain, m_modSample, &ci)) {
getSample();
}
}
else
{
if (m_interpolator.interpolate(&m_interpolatorDistanceRemain, m_modSample, &ci)) {
getSample();
}
}
m_interpolatorDistanceRemain += m_interpolatorDistance;
sample.m_real = (FixReal) ci.real();
sample.m_imag = (FixReal) ci.imag();
}
void RemoteSourceSource::getSample()
{
Sample s;
m_dataReadQueue.readSample(s, true); // true is scale for Tx
m_modSample.real(s.m_real);
m_modSample.imag(s.m_imag);
}
void RemoteSourceSource::start()
@ -128,19 +100,18 @@ void RemoteSourceSource::stopWorker()
void RemoteSourceSource::handleData()
{
RemoteDataBlock* dataBlock;
RemoteDataFrame* dataFrame;
while (m_running && ((dataBlock = m_dataQueue.pop()) != 0)) {
handleDataBlock(dataBlock);
while (m_running && ((dataFrame = m_dataQueue.pop()) != nullptr)) {
handleDataFrame(dataFrame);
}
}
void RemoteSourceSource::handleDataBlock(RemoteDataBlock* dataBlock)
void RemoteSourceSource::handleDataFrame(RemoteDataFrame* dataFrame)
{
(void) dataBlock;
if (dataBlock->m_rxControlBlock.m_blockCount < RemoteNbOrginalBlocks)
if (dataFrame->m_rxControlBlock.m_blockCount < RemoteNbOrginalBlocks)
{
qWarning("RemoteSourceSource::handleDataBlock: incomplete data block: not processing");
qWarning("RemoteSourceSource::handleDataFrame: incomplete data frame: not processing");
}
else
{
@ -148,69 +119,69 @@ void RemoteSourceSource::handleDataBlock(RemoteDataBlock* dataBlock)
for (int blockIndex = 0; blockIndex < 256; blockIndex++)
{
if ((blockIndex == 0) && (dataBlock->m_rxControlBlock.m_metaRetrieved))
if ((blockIndex == 0) && (dataFrame->m_rxControlBlock.m_metaRetrieved))
{
m_cm256DescriptorBlocks[blockCount].Index = 0;
m_cm256DescriptorBlocks[blockCount].Block = (void *) &(dataBlock->m_superBlocks[0].m_protectedBlock);
m_cm256DescriptorBlocks[blockCount].Block = (void *) &(dataFrame->m_superBlocks[0].m_protectedBlock);
blockCount++;
}
else if (dataBlock->m_superBlocks[blockIndex].m_header.m_blockIndex != 0)
else if (dataFrame->m_superBlocks[blockIndex].m_header.m_blockIndex != 0)
{
m_cm256DescriptorBlocks[blockCount].Index = dataBlock->m_superBlocks[blockIndex].m_header.m_blockIndex;
m_cm256DescriptorBlocks[blockCount].Block = (void *) &(dataBlock->m_superBlocks[blockIndex].m_protectedBlock);
m_cm256DescriptorBlocks[blockCount].Index = dataFrame->m_superBlocks[blockIndex].m_header.m_blockIndex;
m_cm256DescriptorBlocks[blockCount].Block = (void *) &(dataFrame->m_superBlocks[blockIndex].m_protectedBlock);
blockCount++;
}
}
//qDebug("RemoteSourceSource::handleDataBlock: frame: %u blocks: %d", dataBlock.m_rxControlBlock.m_frameIndex, blockCount);
//qDebug("RemoteSourceSource::handleDataFrame: frame: %u blocks: %d", dataFrame.m_rxControlBlock.m_frameIndex, blockCount);
// Need to use the CM256 recovery
if (m_cm256p &&(dataBlock->m_rxControlBlock.m_originalCount < RemoteNbOrginalBlocks))
if (m_cm256p &&(dataFrame->m_rxControlBlock.m_originalCount < RemoteNbOrginalBlocks))
{
qDebug("RemoteSourceSource::handleDataBlock: %d recovery blocks", dataBlock->m_rxControlBlock.m_recoveryCount);
qDebug("RemoteSourceSource::handleDataFrame: %d recovery blocks", dataFrame->m_rxControlBlock.m_recoveryCount);
CM256::cm256_encoder_params paramsCM256;
paramsCM256.BlockBytes = sizeof(RemoteProtectedBlock); // never changes
paramsCM256.OriginalCount = RemoteNbOrginalBlocks; // never changes
if (m_currentMeta.m_tv_sec == 0) {
paramsCM256.RecoveryCount = dataBlock->m_rxControlBlock.m_recoveryCount;
paramsCM256.RecoveryCount = dataFrame->m_rxControlBlock.m_recoveryCount;
} else {
paramsCM256.RecoveryCount = m_currentMeta.m_nbFECBlocks;
}
// update counters
if (dataBlock->m_rxControlBlock.m_originalCount < RemoteNbOrginalBlocks - paramsCM256.RecoveryCount) {
m_nbUncorrectableErrors += RemoteNbOrginalBlocks - paramsCM256.RecoveryCount - dataBlock->m_rxControlBlock.m_originalCount;
if (dataFrame->m_rxControlBlock.m_originalCount < RemoteNbOrginalBlocks - paramsCM256.RecoveryCount) {
m_nbUncorrectableErrors += RemoteNbOrginalBlocks - paramsCM256.RecoveryCount - dataFrame->m_rxControlBlock.m_originalCount;
} else {
m_nbCorrectableErrors += dataBlock->m_rxControlBlock.m_recoveryCount;
m_nbCorrectableErrors += dataFrame->m_rxControlBlock.m_recoveryCount;
}
if (m_cm256.cm256_decode(paramsCM256, m_cm256DescriptorBlocks)) // CM256 decode
{
qWarning() << "RemoteSourceSource::handleDataBlock: decode CM256 error:"
<< " m_originalCount: " << dataBlock->m_rxControlBlock.m_originalCount
<< " m_recoveryCount: " << dataBlock->m_rxControlBlock.m_recoveryCount;
qWarning() << "RemoteSourceSource::handleDataFrame: decode CM256 error:"
<< " m_originalCount: " << dataFrame->m_rxControlBlock.m_originalCount
<< " m_recoveryCount: " << dataFrame->m_rxControlBlock.m_recoveryCount;
}
else
{
for (int ir = 0; ir < dataBlock->m_rxControlBlock.m_recoveryCount; ir++) // restore missing blocks
for (int ir = 0; ir < dataFrame->m_rxControlBlock.m_recoveryCount; ir++) // restore missing blocks
{
int recoveryIndex = RemoteNbOrginalBlocks - dataBlock->m_rxControlBlock.m_recoveryCount + ir;
int recoveryIndex = RemoteNbOrginalBlocks - dataFrame->m_rxControlBlock.m_recoveryCount + ir;
int blockIndex = m_cm256DescriptorBlocks[recoveryIndex].Index;
RemoteProtectedBlock *recoveredBlock =
(RemoteProtectedBlock *) m_cm256DescriptorBlocks[recoveryIndex].Block;
memcpy((void *) &(dataBlock->m_superBlocks[blockIndex].m_protectedBlock), recoveredBlock, sizeof(RemoteProtectedBlock));
if ((blockIndex == 0) && !dataBlock->m_rxControlBlock.m_metaRetrieved) {
dataBlock->m_rxControlBlock.m_metaRetrieved = true;
memcpy((void *) &(dataFrame->m_superBlocks[blockIndex].m_protectedBlock), recoveredBlock, sizeof(RemoteProtectedBlock));
if ((blockIndex == 0) && !dataFrame->m_rxControlBlock.m_metaRetrieved) {
dataFrame->m_rxControlBlock.m_metaRetrieved = true;
}
}
}
}
// Validate block zero and retrieve its data
if (dataBlock->m_rxControlBlock.m_metaRetrieved)
if (dataFrame->m_rxControlBlock.m_metaRetrieved)
{
RemoteMetaDataFEC *metaData = (RemoteMetaDataFEC *) &(dataBlock->m_superBlocks[0].m_protectedBlock);
RemoteMetaDataFEC *metaData = (RemoteMetaDataFEC *) &(dataFrame->m_superBlocks[0].m_protectedBlock);
boost::crc_32_type crc32;
crc32.process_bytes(metaData, sizeof(RemoteMetaDataFEC)-4);
@ -218,7 +189,7 @@ void RemoteSourceSource::handleDataBlock(RemoteDataBlock* dataBlock)
{
if (!(m_currentMeta == *metaData))
{
printMeta("RemoteSourceSource::handleDataBlock", metaData);
printMeta("RemoteSourceSource::handleDataFrame", metaData);
if (m_currentMeta.m_sampleRate != metaData->m_sampleRate) {
emit newRemoteSampleRate(metaData->m_sampleRate);
@ -230,11 +201,11 @@ void RemoteSourceSource::handleDataBlock(RemoteDataBlock* dataBlock)
}
else
{
qWarning() << "RemoteSource::handleDataBlock: recovered meta: invalid CRC32";
qWarning() << "RemoteSource::handleDataFrame: recovered meta: invalid CRC32";
}
}
m_dataReadQueue.push(dataBlock); // Push into R/W buffer
m_dataReadQueue.push(dataFrame); // Push into R/W buffer
}
}
@ -266,16 +237,6 @@ void RemoteSourceSource::applyChannelSettings(int channelSampleRate, bool force)
{
qDebug() << "RemoteSourceSource::applyChannelSettings:"
<< " channelSampleRate: " << channelSampleRate
<< " m_currentMeta.m_sampleRate: " << m_currentMeta.m_sampleRate
<< " force: " << force;
if ((channelSampleRate != m_channelSampleRate) || force)
{
m_interpolatorDistanceRemain = 0;
m_interpolatorConsumed = false;
m_interpolatorDistance = (Real) m_currentMeta.m_sampleRate / (Real) channelSampleRate;
m_interpolator.create(48, m_currentMeta.m_sampleRate, m_currentMeta.m_sampleRate / 2.2, 3.0);
}
m_channelSampleRate = channelSampleRate;
}

View File

@ -24,7 +24,6 @@
#include "cm256cc/cm256.h"
#include "dsp/channelsamplesource.h"
#include "dsp/interpolator.h"
#include "channel/remotedatablock.h"
#include "channel/remotedataqueue.h"
#include "channel/remotedatareadqueue.h"
@ -72,17 +71,11 @@ private:
uint32_t m_nbUncorrectableErrors; //!< count of uncorrectable errors in number of blocks
int m_channelSampleRate;
Interpolator m_interpolator;
Real m_interpolatorDistance;
Real m_interpolatorDistanceRemain;
bool m_interpolatorConsumed;
Complex m_modSample;
void startWorker();
void stopWorker();
void handleDataBlock(RemoteDataBlock *dataBlock);
void handleDataFrame(RemoteDataFrame *dataFrame);
void printMeta(const QString& header, RemoteMetaDataFEC *metaData);
void getSample();
private slots:
void handleData();

View File

@ -33,9 +33,12 @@ RemoteSourceWorker::RemoteSourceWorker(RemoteDataQueue *dataQueue, QObject* pare
m_address(QHostAddress::LocalHost),
m_socket(this),
m_mutex(QMutex::Recursive),
m_sampleRate(0)
m_sampleRate(0),
m_udpReadBytes(0)
{
std::fill(m_dataBlocks, m_dataBlocks+4, (RemoteDataBlock *) 0);
m_udpBuf = new char[RemoteUdpSize];
std::fill(m_dataFrames, m_dataFrames+4, (RemoteDataFrame *) 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)
@ -99,93 +102,94 @@ void RemoteSourceWorker::handleInputMessages()
{
Message* message;
while ((message = m_inputMessageQueue.pop()) != 0)
while ((message = m_inputMessageQueue.pop()) != nullptr)
{
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()));
disconnect(&m_socket, SIGNAL(readyRead()), this, SLOT(dataReadyRead()));
m_socket.abort();
m_socket.bind(notif->getAddress(), notif->getPort());
connect(&m_socket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
connect(&m_socket, SIGNAL(readyRead()), this, SLOT(dataReadyRead()));
}
}
}
void RemoteSourceWorker::readPendingDatagrams()
void RemoteSourceWorker::dataReadyRead()
{
RemoteSuperBlock superBlock;
qint64 size;
m_udpReadBytes = 0;
while (m_socket.hasPendingDatagrams())
{
while (m_socket.hasPendingDatagrams())
{
qint64 pendingDataSize = m_socket.pendingDatagramSize();
QHostAddress sender;
quint16 senderPort = 0;
//qint64 pendingDataSize = m_socket->pendingDatagramSize();
size = m_socket.readDatagram((char *) &superBlock, (long long int) sizeof(RemoteSuperBlock), &sender, &senderPort);
m_udpReadBytes += m_socket.readDatagram(&m_udpBuf[m_udpReadBytes], pendingDataSize, &sender, nullptr);
if (size == sizeof(RemoteSuperBlock))
if (m_udpReadBytes == RemoteUdpSize)
{
unsigned int dataBlockIndex = superBlock.m_header.m_frameIndex % m_nbDataBlocks;
int blockIndex = superBlock.m_header.m_blockIndex;
processData();
m_udpReadBytes = 0;
}
}
}
if (blockIndex == 0) // first block with meta data
{
const RemoteMetaDataFEC *metaData = (const RemoteMetaDataFEC *) &superBlock.m_protectedBlock;
uint32_t sampleRate = metaData->m_sampleRate;
void RemoteSourceWorker::processData()
{
RemoteSuperBlock *superBlock = (RemoteSuperBlock *) m_udpBuf;
unsigned int dataBlockIndex = superBlock->m_header.m_frameIndex % m_nbDataFrames;
int blockIndex = superBlock->m_header.m_blockIndex;
if (m_sampleRate != sampleRate)
{
qDebug("RemoteSourceWorker::readPendingDatagrams: sampleRate: %u", sampleRate);
m_socket.setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, getDataSocketBufferSize(sampleRate));
m_sampleRate = sampleRate;
}
}
if (blockIndex == 0) // first block with meta data
{
const RemoteMetaDataFEC *metaData = (const RemoteMetaDataFEC *) &superBlock->m_protectedBlock;
uint32_t sampleRate = metaData->m_sampleRate;
// create the first block for this index
if (m_dataBlocks[dataBlockIndex] == 0) {
m_dataBlocks[dataBlockIndex] = new RemoteDataBlock();
}
if (m_dataBlocks[dataBlockIndex]->m_rxControlBlock.m_frameIndex < 0)
{
// initialize virgin block with the frame index
m_dataBlocks[dataBlockIndex]->m_rxControlBlock.m_frameIndex = superBlock.m_header.m_frameIndex;
}
else
{
// if the frame index is not the same for the same slot it means we are starting a new frame
uint32_t frameIndex = m_dataBlocks[dataBlockIndex]->m_rxControlBlock.m_frameIndex;
if (superBlock.m_header.m_frameIndex != frameIndex)
{
//qDebug("RemoteSourceWorker::readPendingDatagrams: push frame %u", frameIndex);
m_dataQueue->push(m_dataBlocks[dataBlockIndex]);
m_dataBlocks[dataBlockIndex] = new RemoteDataBlock();
m_dataBlocks[dataBlockIndex]->m_rxControlBlock.m_frameIndex = superBlock.m_header.m_frameIndex;
}
}
m_dataBlocks[dataBlockIndex]->m_superBlocks[superBlock.m_header.m_blockIndex] = superBlock;
if (superBlock.m_header.m_blockIndex == 0) {
m_dataBlocks[dataBlockIndex]->m_rxControlBlock.m_metaRetrieved = true;
}
if (superBlock.m_header.m_blockIndex < RemoteNbOrginalBlocks) {
m_dataBlocks[dataBlockIndex]->m_rxControlBlock.m_originalCount++;
} else {
m_dataBlocks[dataBlockIndex]->m_rxControlBlock.m_recoveryCount++;
}
m_dataBlocks[dataBlockIndex]->m_rxControlBlock.m_blockCount++;
}
else
if (m_sampleRate != sampleRate)
{
qWarning("RemoteSourceWorker::readPendingDatagrams: wrong super block size not processing");
qDebug("RemoteSourceWorker::processData: sampleRate: %u", sampleRate);
m_socket.setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, getDataSocketBufferSize(sampleRate));
m_sampleRate = sampleRate;
}
}
// create the first block for this index
if (m_dataFrames[dataBlockIndex] == nullptr) {
m_dataFrames[dataBlockIndex] = new RemoteDataFrame();
}
if (m_dataFrames[dataBlockIndex]->m_rxControlBlock.m_frameIndex < 0)
{
// initialize virgin block with the frame index
m_dataFrames[dataBlockIndex]->m_rxControlBlock.m_frameIndex = superBlock->m_header.m_frameIndex;
}
else
{
// if the frame index is not the same for the same slot it means we are starting a new frame
uint32_t frameIndex = m_dataFrames[dataBlockIndex]->m_rxControlBlock.m_frameIndex;
if (superBlock->m_header.m_frameIndex != frameIndex)
{
m_dataQueue->push(m_dataFrames[dataBlockIndex]);
m_dataFrames[dataBlockIndex] = new RemoteDataFrame();
m_dataFrames[dataBlockIndex]->m_rxControlBlock.m_frameIndex = superBlock->m_header.m_frameIndex;
}
}
m_dataFrames[dataBlockIndex]->m_superBlocks[superBlock->m_header.m_blockIndex] = *superBlock;
if (superBlock->m_header.m_blockIndex == 0) {
m_dataFrames[dataBlockIndex]->m_rxControlBlock.m_metaRetrieved = true;
}
if (superBlock->m_header.m_blockIndex < RemoteNbOrginalBlocks) {
m_dataFrames[dataBlockIndex]->m_rxControlBlock.m_originalCount++;
} else {
m_dataFrames[dataBlockIndex]->m_rxControlBlock.m_recoveryCount++;
}
m_dataFrames[dataBlockIndex]->m_rxControlBlock.m_blockCount++;
}
int RemoteSourceWorker::getDataSocketBufferSize(uint32_t inSampleRate)

View File

@ -26,7 +26,7 @@
#include "util/messagequeue.h"
class RemoteDataQueue;
class RemoteDataBlock;
class RemoteDataFrame;
class RemoteSourceWorker : public QObject {
Q_OBJECT
@ -71,18 +71,22 @@ private:
QUdpSocket m_socket;
QMutex m_mutex;
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
static const uint32_t m_nbDataFrames = 4; //!< number of data frames in the ring buffer
RemoteDataFrame *m_dataFrames[m_nbDataFrames]; //!< ring buffer of data frames indexed by frame affinity
uint32_t m_sampleRate; //!< current sample rate from meta data
qint64 m_udpReadBytes;
char *m_udpBuf;
static int getDataSocketBufferSize(uint32_t inSampleRate);
void processData();
private slots:
void started();
void finished();
void errorOccurred(QAbstractSocket::SocketError socketError);
void handleInputMessages();
void readPendingDatagrams();
void dataReadyRead();
};

View File

@ -41,32 +41,30 @@ MESSAGE_CLASS_DEFINITION(RemoteOutput::MsgConfigureRemoteOutput, Message)
MESSAGE_CLASS_DEFINITION(RemoteOutput::MsgConfigureRemoteOutputWork, Message)
MESSAGE_CLASS_DEFINITION(RemoteOutput::MsgStartStop, Message)
MESSAGE_CLASS_DEFINITION(RemoteOutput::MsgConfigureRemoteOutputChunkCorrection, Message)
const uint32_t RemoteOutput::NbSamplesForRateCorrection = 5000000;
MESSAGE_CLASS_DEFINITION(RemoteOutput::MsgConfigureRemoteOutputSampleRate, Message)
MESSAGE_CLASS_DEFINITION(RemoteOutput::MsgReportRemoteData, Message)
MESSAGE_CLASS_DEFINITION(RemoteOutput::MsgReportRemoteFixedData, Message)
MESSAGE_CLASS_DEFINITION(RemoteOutput::MsgRequestFixedData, Message)
RemoteOutput::RemoteOutput(DeviceAPI *deviceAPI) :
m_deviceAPI(deviceAPI),
m_settings(),
m_centerFrequency(0),
m_centerFrequency(435000000),
m_sampleRate(48000),
m_remoteOutputWorker(nullptr),
m_deviceDescription("RemoteOutput"),
m_startingTimeStamp(0),
m_masterTimer(deviceAPI->getMasterTimer()),
m_tickCount(0),
m_tickMultiplier(20),
m_lastRemoteSampleCount(0),
m_lastSampleCount(0),
m_lastRemoteTimestampRateCorrection(0),
m_lastTimestampRateCorrection(0),
m_lastQueueLength(-2),
m_nbRemoteSamplesSinceRateCorrection(0),
m_nbSamplesSinceRateCorrection(0),
m_chunkSizeCorrection(0)
m_greaterTickCount(0),
m_tickMultiplier(1)
{
m_deviceAPI->setNbSinkStreams(1);
m_networkManager = new QNetworkAccessManager();
connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
connect(&m_masterTimer, SIGNAL(timeout()), this, SLOT(tick()));
applyCenterFrequency();
applySampleRate();
}
RemoteOutput::~RemoteOutput()
@ -89,19 +87,14 @@ bool RemoteOutput::start()
m_remoteOutputWorker = new RemoteOutputWorker(&m_sampleSourceFifo);
m_remoteOutputWorker->moveToThread(&m_remoteOutputWorkerThread);
m_remoteOutputWorker->setDataAddress(m_settings.m_dataAddress, m_settings.m_dataPort);
m_remoteOutputWorker->setSamplerate(m_settings.m_sampleRate);
m_remoteOutputWorker->setSamplerate(m_sampleRate);
m_remoteOutputWorker->setNbBlocksFEC(m_settings.m_nbFECBlocks);
m_remoteOutputWorker->connectTimer(m_masterTimer);
startWorker();
// restart auto rate correction
m_lastRemoteTimestampRateCorrection = 0;
m_lastTimestampRateCorrection = 0;
m_lastQueueLength = -2; // set first value out of bounds
m_chunkSizeCorrection = 0;
mutexLocker.unlock();
//applySettings(m_generalSettings, m_settings, true);
applySampleRate();
qDebug("RemoteOutput::start: started");
return true;
@ -172,7 +165,7 @@ const QString& RemoteOutput::getDeviceDescription() const
int RemoteOutput::getSampleRate() const
{
return m_settings.m_sampleRate;
return m_sampleRate;
}
quint64 RemoteOutput::getCenterFrequency() const
@ -200,14 +193,11 @@ bool RemoteOutput::handleMessage(const Message& message)
MsgConfigureRemoteOutputWork& conf = (MsgConfigureRemoteOutputWork&) message;
bool working = conf.isWorking();
if (m_remoteOutputWorker != 0)
if (m_remoteOutputWorker != nullptr)
{
if (working)
{
if (working) {
m_remoteOutputWorker->startWork();
}
else
{
} else {
m_remoteOutputWorker->stopWork();
}
}
@ -221,8 +211,7 @@ bool RemoteOutput::handleMessage(const Message& message)
if (cmd.getStartStop())
{
if (m_deviceAPI->initDeviceEngine())
{
if (m_deviceAPI->initDeviceEngine()) {
m_deviceAPI->startDeviceEngine();
}
}
@ -241,13 +230,25 @@ bool RemoteOutput::handleMessage(const Message& message)
{
MsgConfigureRemoteOutputChunkCorrection& conf = (MsgConfigureRemoteOutputChunkCorrection&) message;
if (m_remoteOutputWorker != 0)
{
if (m_remoteOutputWorker != nullptr) {
m_remoteOutputWorker->setChunkCorrection(conf.getChunkCorrection());
}
return true;
}
else if (MsgRequestFixedData::match(message))
{
QString reportURL;
reportURL = QString("http://%1:%2/sdrangel")
.arg(m_settings.m_apiAddress)
.arg(m_settings.m_apiPort);
m_networkRequest.setUrl(QUrl(reportURL));
m_networkManager->get(m_networkRequest);
return true;
}
else
{
return false;
@ -257,7 +258,6 @@ bool RemoteOutput::handleMessage(const Message& message)
void RemoteOutput::applySettings(const RemoteOutputSettings& settings, bool force)
{
QMutexLocker mutexLocker(&m_mutex);
bool forwardChange = false;
QList<QString> reverseAPIKeys;
if ((m_settings.m_dataAddress != settings.m_dataAddress) || force) {
@ -280,20 +280,6 @@ void RemoteOutput::applySettings(const RemoteOutputSettings& settings, bool forc
}
}
if (force || (m_settings.m_sampleRate != settings.m_sampleRate))
{
reverseAPIKeys.append("sampleRate");
if (m_remoteOutputWorker != 0) {
m_remoteOutputWorker->setSamplerate(settings.m_sampleRate);
}
m_tickMultiplier = (21*NbSamplesForRateCorrection) / (2*settings.m_sampleRate); // two times per sample filling period plus small extension
m_tickMultiplier = m_tickMultiplier < 20 ? 20 : m_tickMultiplier; // not below half a second
forwardChange = true;
}
if (force || (m_settings.m_nbFECBlocks != settings.m_nbFECBlocks))
{
reverseAPIKeys.append("nbFECBlocks");
@ -306,19 +292,12 @@ void RemoteOutput::applySettings(const RemoteOutputSettings& settings, bool forc
mutexLocker.unlock();
qDebug() << "RemoteOutput::applySettings:"
<< " m_sampleRate: " << settings.m_sampleRate
<< " m_nbFECBlocks: " << settings.m_nbFECBlocks
<< " m_apiAddress: " << settings.m_apiAddress
<< " m_apiPort: " << settings.m_apiPort
<< " m_dataAddress: " << settings.m_dataAddress
<< " m_dataPort: " << settings.m_dataPort;
if (forwardChange)
{
DSPSignalNotification *notif = new DSPSignalNotification(settings.m_sampleRate, m_centerFrequency);
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
}
if (settings.m_useReverseAPI)
{
bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) ||
@ -331,6 +310,26 @@ void RemoteOutput::applySettings(const RemoteOutputSettings& settings, bool forc
m_settings = settings;
}
void RemoteOutput::applyCenterFrequency()
{
DSPSignalNotification *notif = new DSPSignalNotification(m_sampleRate, m_centerFrequency);
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
}
void RemoteOutput::applySampleRate()
{
if (m_remoteOutputWorker) {
m_remoteOutputWorker->setSamplerate(m_sampleRate);
}
m_tickMultiplier = 480000 / m_sampleRate;
m_tickMultiplier = m_tickMultiplier < 1 ? 1 : m_tickMultiplier > 10 ? 10 : m_tickMultiplier;
m_greaterTickCount = 0;
DSPSignalNotification *notif = new DSPSignalNotification(m_sampleRate, m_centerFrequency);
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
}
int RemoteOutput::webapiRunGet(
SWGSDRangel::SWGDeviceState& response,
QString& errorMessage)
@ -398,9 +397,6 @@ void RemoteOutput::webapiUpdateDeviceSettings(
const QStringList& deviceSettingsKeys,
SWGSDRangel::SWGDeviceSettings& response)
{
if (deviceSettingsKeys.contains("sampleRate")) {
settings.m_sampleRate = response.getRemoteOutputSettings()->getSampleRate();
}
if (deviceSettingsKeys.contains("nbFECBlocks")) {
settings.m_nbFECBlocks = response.getRemoteOutputSettings()->getNbFecBlocks();
}
@ -449,8 +445,6 @@ int RemoteOutput::webapiReportGet(
void RemoteOutput::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const RemoteOutputSettings& settings)
{
response.getRemoteOutputSettings()->setCenterFrequency(settings.m_centerFrequency);
response.getRemoteOutputSettings()->setSampleRate(settings.m_sampleRate);
response.getRemoteOutputSettings()->setNbFecBlocks(settings.m_nbFECBlocks);
response.getRemoteOutputSettings()->setApiAddress(new QString(settings.m_apiAddress));
response.getRemoteOutputSettings()->setApiPort(settings.m_apiPort);
@ -472,14 +466,15 @@ void RemoteOutput::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& re
void RemoteOutput::webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response)
{
uint64_t ts_usecs;
response.getRemoteOutputReport()->setCenterFrequency(m_centerFrequency);
response.getRemoteOutputReport()->setSampleRate(m_sampleRate);
response.getRemoteOutputReport()->setBufferRwBalance(m_sampleSourceFifo.getRWBalance());
response.getRemoteOutputReport()->setSampleCount(m_remoteOutputWorker ? (int) m_remoteOutputWorker->getSamplesCount(ts_usecs) : 0);
response.getRemoteOutputReport()->setSampleCount(m_remoteOutputWorker ? (int) m_remoteOutputWorker->getSamplesCount() : 0);
}
void RemoteOutput::tick()
{
if (++m_tickCount == m_tickMultiplier)
if (++m_tickCount == 20) // Every second
{
QString reportURL;
@ -536,88 +531,105 @@ void RemoteOutput::analyzeApiReply(const QJsonObject& jsonObject, const QString&
{
if (jsonObject.contains("RemoteSourceReport"))
{
MsgReportRemoteData::RemoteData msgRemoteData;
QJsonObject report = jsonObject["RemoteSourceReport"].toObject();
m_settings.m_centerFrequency = report["deviceCenterFreq"].toInt();
m_centerFrequency = m_settings.m_centerFrequency * 1000;
uint64_t centerFrequency = report["deviceCenterFreq"].toInt() + report["centerFreq"].toInt();
if (centerFrequency != m_centerFrequency)
{
m_centerFrequency = centerFrequency;
applyCenterFrequency();
}
int remoteRate = report["sampleRate"].toInt();
if (remoteRate != m_sampleRate)
{
m_sampleRate = remoteRate;
applySampleRate();
}
msgRemoteData.m_centerFrequency = m_centerFrequency;
msgRemoteData.m_sampleRate = m_sampleRate;
int queueSize = report["queueSize"].toInt();
queueSize = queueSize == 0 ? 20 : queueSize;
msgRemoteData.m_queueSize = queueSize;
int queueLength = report["queueLength"].toInt();
msgRemoteData.m_queueLength = queueLength;
uint64_t remoteTimestampUs = report["tvSec"].toInt()*1000000ULL + report["tvUSec"].toInt();
msgRemoteData.m_timestampUs = remoteTimestampUs;
int intRemoteSampleCount = report["samplesCount"].toInt();
uint32_t remoteSampleCount = intRemoteSampleCount < 0 ? 0 : intRemoteSampleCount;
msgRemoteData.m_sampleCount = remoteSampleCount;
int unrecoverableCount = report["uncorrectableErrorsCount"].toInt();
msgRemoteData.m_unrecoverableCount = unrecoverableCount;
int recoverableCount = report["correctableErrorsCount"].toInt();
msgRemoteData.m_recoverableCount = recoverableCount;
if (m_guiMessageQueue)
{
MsgReportRemoteData *msg = MsgReportRemoteData::create(msgRemoteData);
m_guiMessageQueue->push(msg);
}
if (!m_remoteOutputWorker) {
return;
}
int queueSize = report["queueSize"].toInt();
queueSize = queueSize == 0 ? 10 : queueSize;
int queueLength = report["queueLength"].toInt();
int queueLengthPercent = (queueLength*100)/queueSize;
uint64_t remoteTimestampUs = report["tvSec"].toInt()*1000000ULL + report["tvUSec"].toInt();
uint32_t remoteSampleCountDelta, remoteSampleCount;
remoteSampleCount = report["samplesCount"].toInt();
if (remoteSampleCount < m_lastRemoteSampleCount) {
remoteSampleCountDelta = (0xFFFFFFFFU - m_lastRemoteSampleCount) + remoteSampleCount + 1;
} else {
remoteSampleCountDelta = remoteSampleCount - m_lastRemoteSampleCount;
}
uint32_t sampleCountDelta, sampleCount;
uint64_t timestampUs;
sampleCount = m_remoteOutputWorker->getSamplesCount(timestampUs);
if (sampleCount < m_lastSampleCount) {
sampleCountDelta = (0xFFFFFFFFU - m_lastSampleCount) + sampleCount + 1;
} else {
sampleCountDelta = sampleCount - m_lastSampleCount;
}
// on initial state wait for queue stabilization
if ((m_lastRemoteTimestampRateCorrection == 0) && (queueLength >= m_lastQueueLength-1) && (queueLength <= m_lastQueueLength+1))
if (++m_greaterTickCount == m_tickMultiplier)
{
m_lastRemoteTimestampRateCorrection = remoteTimestampUs;
m_lastTimestampRateCorrection = timestampUs;
m_nbRemoteSamplesSinceRateCorrection = 0;
m_nbSamplesSinceRateCorrection = 0;
queueLengthCompensation(m_sampleRate, queueLength, queueSize);
m_greaterTickCount = 0;
}
else
{
m_nbRemoteSamplesSinceRateCorrection += remoteSampleCountDelta;
m_nbSamplesSinceRateCorrection += sampleCountDelta;
qDebug("RemoteOutput::analyzeApiReply: queueLengthPercent: %d m_nbSamplesSinceRateCorrection: %u",
queueLengthPercent,
m_nbRemoteSamplesSinceRateCorrection);
if (m_nbRemoteSamplesSinceRateCorrection > NbSamplesForRateCorrection)
{
sampleRateCorrection(remoteTimestampUs - m_lastRemoteTimestampRateCorrection,
timestampUs - m_lastTimestampRateCorrection,
m_nbRemoteSamplesSinceRateCorrection,
m_nbSamplesSinceRateCorrection);
m_lastRemoteTimestampRateCorrection = remoteTimestampUs;
m_lastTimestampRateCorrection = timestampUs;
m_nbRemoteSamplesSinceRateCorrection = 0;
m_nbSamplesSinceRateCorrection = 0;
}
}
m_lastRemoteSampleCount = remoteSampleCount;
m_lastSampleCount = sampleCount;
m_lastQueueLength = queueLength;
}
else if (jsonObject.contains("remoteOutputSettings"))
{
qDebug("RemoteOutput::analyzeApiReply: reply:\n%s", answer.toStdString().c_str());
}
else if (jsonObject.contains("version"))
{
MsgReportRemoteFixedData::RemoteData msgRemoteFixedData;
msgRemoteFixedData.m_version = jsonObject["version"].toString();
if (jsonObject.contains("qtVersion")) {
msgRemoteFixedData.m_qtVersion = jsonObject["qtVersion"].toString();
}
if (jsonObject.contains("architecture")) {
msgRemoteFixedData.m_architecture = jsonObject["architecture"].toString();
}
if (jsonObject.contains("os")) {
msgRemoteFixedData.m_os = jsonObject["os"].toString();
}
if (jsonObject.contains("dspRxBits") && jsonObject.contains("dspTxBits"))
{
msgRemoteFixedData.m_rxBits = jsonObject["dspRxBits"].toInt();
msgRemoteFixedData.m_txBits = jsonObject["dspTxBits"].toInt();
}
if (m_guiMessageQueue)
{
MsgReportRemoteFixedData *msg = MsgReportRemoteFixedData::create(msgRemoteFixedData);
m_guiMessageQueue->push(msg);
}
}
}
void RemoteOutput::sampleRateCorrection(double remoteTimeDeltaUs, double timeDeltaUs, uint32_t remoteSampleCount, uint32_t sampleCount)
void RemoteOutput::queueLengthCompensation(
int nominalSR,
int queueLength,
int queueSize
)
{
double deltaSR = (remoteSampleCount/remoteTimeDeltaUs) - (sampleCount/timeDeltaUs);
double chunkCorr = 50000 * deltaSR; // for 50ms chunk intervals (50000us)
m_chunkSizeCorrection += roundf(chunkCorr);
qDebug("RemoteOutput::sampleRateCorrection: %d (%f) samples", m_chunkSizeCorrection, chunkCorr);
MsgConfigureRemoteOutputChunkCorrection* message = MsgConfigureRemoteOutputChunkCorrection::create(m_chunkSizeCorrection);
int deltaQueueBlocks = (queueSize/2) - queueLength;
int blockMultiplier = nominalSR / 4000;
blockMultiplier = blockMultiplier < 12 ? 12 : blockMultiplier;
int corr = deltaQueueBlocks * blockMultiplier;
qDebug("RemoteOutput::queueLengthCompensation: deltaQueueBlocks: %d corr: %d", deltaQueueBlocks, corr);
MsgConfigureRemoteOutputChunkCorrection* message = MsgConfigureRemoteOutputChunkCorrection::create(corr);
getInputMessageQueue()->push(message);
}
@ -632,9 +644,6 @@ void RemoteOutput::webapiReverseSendSettings(QList<QString>& deviceSettingsKeys,
// transfer data that has been modified. When force is on transfer all data except reverse API data
if (deviceSettingsKeys.contains("sampleRate") || force) {
swgRemoteOutputSettings->setSampleRate(settings.m_sampleRate);
}
if (deviceSettingsKeys.contains("nbFECBlocks") || force) {
swgRemoteOutputSettings->setNbFecBlocks(settings.m_nbFECBlocks);
}

View File

@ -123,6 +123,100 @@ public:
{ }
};
class MsgConfigureRemoteOutputSampleRate : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getSampleRate() const { return m_sampleRate; }
static MsgConfigureRemoteOutputSampleRate* create(int sampleRate) {
return new MsgConfigureRemoteOutputSampleRate(sampleRate);
}
private:
int m_sampleRate;
MsgConfigureRemoteOutputSampleRate(int sampleRate) :
Message(),
m_sampleRate(sampleRate)
{ }
};
class MsgReportRemoteData : public Message {
MESSAGE_CLASS_DECLARATION
public:
struct RemoteData
{
quint64 m_centerFrequency; //!< Center frequency where the stream is placed
int m_sampleRate; //!< Sample rate requested by remote
int m_queueSize; //!< Remote data read queue size
int m_queueLength; //!< Remote data queue length
int m_unrecoverableCount; //!< Number of unrecoverable errors from FEC
int m_recoverableCount; //!< Number of recoverable errors from FEC
uint64_t m_timestampUs; //!< Unix time stamp of request in microseconds
uint32_t m_sampleCount; //!< Number of samples processed in the remote at request time
};
const RemoteData& getData() const { return m_remoteData; }
static MsgReportRemoteData* create(const RemoteData& remoteData) {
return new MsgReportRemoteData(remoteData);
}
private:
RemoteData m_remoteData;
MsgReportRemoteData(const RemoteData& remoteData) :
Message(),
m_remoteData(remoteData)
{}
};
class MsgReportRemoteFixedData : public Message {
MESSAGE_CLASS_DECLARATION
public:
struct RemoteData
{
QString m_version; //!< Remote SDRangel version
QString m_qtVersion; //!< Remote Qt version used to build SDRangel
QString m_architecture; //!< Remote CPU architecture
QString m_os; //!< Remote O/S
int m_rxBits; //!< Number of bits used for I or Q sample on Rx side
int m_txBits; //!< Number of bits used for I or Q sample on Tx side
};
const RemoteData& getData() const { return m_remoteData; }
static MsgReportRemoteFixedData* create(const RemoteData& remoteData) {
return new MsgReportRemoteFixedData(remoteData);
}
private:
RemoteData m_remoteData;
MsgReportRemoteFixedData(const RemoteData& remoteData) :
Message(),
m_remoteData(remoteData)
{}
};
class MsgRequestFixedData : public Message {
MESSAGE_CLASS_DECLARATION
public:
static MsgRequestFixedData* create() {
return new MsgRequestFixedData();
}
private:
MsgRequestFixedData() :
Message()
{}
};
RemoteOutput(DeviceAPI *deviceAPI);
virtual ~RemoteOutput();
virtual void destroy();
@ -181,34 +275,32 @@ private:
QMutex m_mutex;
RemoteOutputSettings m_settings;
uint64_t m_centerFrequency;
int m_sampleRate;
RemoteOutputWorker* m_remoteOutputWorker;
QThread m_remoteOutputWorkerThread;
QString m_deviceDescription;
std::time_t m_startingTimeStamp;
const QTimer& m_masterTimer;
uint32_t m_tickCount;
uint32_t m_tickMultiplier;
uint32_t m_tickCount; // for 50 ms timer
uint32_t m_greaterTickCount; // for 1 s derived timer
uint32_t m_tickMultiplier; // for greater tick count
QNetworkAccessManager *m_networkManager;
QNetworkRequest m_networkRequest;
uint32_t m_lastRemoteSampleCount;
uint32_t m_lastSampleCount;
uint64_t m_lastRemoteTimestampRateCorrection;
uint64_t m_lastTimestampRateCorrection;
int m_lastQueueLength;
uint32_t m_nbRemoteSamplesSinceRateCorrection;
uint32_t m_nbSamplesSinceRateCorrection;
int m_chunkSizeCorrection;
static const uint32_t NbSamplesForRateCorrection;
void startWorker();
void stopWorker();
void applySettings(const RemoteOutputSettings& settings, bool force = false);
void applyCenterFrequency();
void applySampleRate();
void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response);
void analyzeApiReply(const QJsonObject& jsonObject, const QString& answer);
void sampleRateCorrection(double remoteTimeDeltaUs, double timeDeltaUs, uint32_t remoteSampleCount, uint32_t sampleCount);
void queueLengthCompensation(
int nominalSR,
int queueLength,
int queueSize
);
void webapiReverseSendSettings(QList<QString>& deviceSettingsKeys, const RemoteOutputSettings& settings, bool force);
void webapiReverseSendStartStop(bool start);

View File

@ -47,7 +47,7 @@ void RemoteOutputFifo::reset()
m_writeHead = 0;
}
RemoteDataBlock *RemoteOutputFifo::getDataBlock()
RemoteDataFrame *RemoteOutputFifo::getDataFrame()
{
QMutexLocker mutexLocker(&m_mutex);
m_servedHead = m_writeHead;
@ -62,18 +62,18 @@ RemoteDataBlock *RemoteOutputFifo::getDataBlock()
return &m_data[m_servedHead];
}
unsigned int RemoteOutputFifo::readDataBlock(RemoteDataBlock **dataBlock)
unsigned int RemoteOutputFifo::readDataFrame(RemoteDataFrame **dataFrame)
{
QMutexLocker mutexLocker(&m_mutex);
if (calculateRemainder() == 0)
{
*dataBlock = nullptr;
*dataFrame = nullptr;
return 0;
}
else
{
*dataBlock = &m_data[m_readHead];
*dataFrame = &m_data[m_readHead];
m_readHead = m_readHead < m_size - 1 ? m_readHead + 1 : 0;
return calculateRemainder();
}
@ -92,4 +92,4 @@ unsigned int RemoteOutputFifo::calculateRemainder()
} else {
return m_size - (m_readHead - m_servedHead);
}
}
}

View File

@ -34,15 +34,15 @@ public:
void resize(unsigned int size);
void reset();
RemoteDataBlock *getDataBlock();
unsigned int readDataBlock(RemoteDataBlock **dataBlock);
RemoteDataFrame *getDataFrame();
unsigned int readDataFrame(RemoteDataFrame **dataFrame);
unsigned int getRemainder();
signals:
void dataBlockServed();
private:
std::vector<RemoteDataBlock> m_data;
std::vector<RemoteDataFrame> m_data;
int m_size;
int m_readHead; //!< index of last data block processed
int m_servedHead; //!< index of last data block served

View File

@ -20,9 +20,6 @@
#include <QDateTime>
#include <QString>
#include <QMessageBox>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QJsonParseError>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
@ -51,21 +48,21 @@ RemoteOutputSinkGui::RemoteOutputSinkGui(DeviceUISet *deviceUISet, QWidget* pare
ui(new Ui::RemoteOutputGui),
m_deviceUISet(deviceUISet),
m_settings(),
m_deviceSampleSink(0),
m_remoteOutput(0),
m_deviceCenterFrequency(0),
m_samplesCount(0),
m_tickCount(0),
m_nbSinceLastFlowCheck(0),
m_lastEngineState(DeviceAPI::StNotStarted),
m_doApplySettings(true),
m_forceSettings(true)
m_forceSettings(true),
m_remoteAPIConnected(false)
{
m_countUnrecoverable = 0;
m_countRecovered = 0;
m_lastCountUnrecoverable = 0;
m_lastCountRecovered = 0;
m_lastSampleCount = 0;
m_resetCounts = true;
m_paletteGreenText.setColor(QPalette::WindowText, Qt::green);
m_paletteRedText.setColor(QPalette::WindowText, Qt::red);
@ -73,26 +70,15 @@ RemoteOutputSinkGui::RemoteOutputSinkGui(DeviceUISet *deviceUISet, QWidget* pare
ui->setupUi(this);
ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
ui->centerFrequency->setValueRange(7, 0, pow(10,7));
ui->sampleRate->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow));
ui->sampleRate->setValueRange(7, 32000U, 9000000U);
ui->apiAddressLabel->setStyleSheet("QLabel { background:rgb(79,79,79); }");
connect(&(m_deviceUISet->m_deviceAPI->getMasterTimer()), SIGNAL(timeout()), this, SLOT(tick()));
connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware()));
connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
m_statusTimer.start(500);
m_deviceSampleSink = (RemoteOutput*) m_deviceUISet->m_deviceAPI->getSampleSink();
m_remoteOutput = (RemoteOutput*) m_deviceUISet->m_deviceAPI->getSampleSink();
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection);
m_networkManager = new QNetworkAccessManager();
connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency);
m_time.start();
@ -108,8 +94,6 @@ RemoteOutputSinkGui::RemoteOutputSinkGui(DeviceUISet *deviceUISet, QWidget* pare
RemoteOutputSinkGui::~RemoteOutputSinkGui()
{
disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
delete m_networkManager;
delete ui;
}
@ -174,6 +158,18 @@ bool RemoteOutputSinkGui::handleMessage(const Message& message)
ui->startStop->setChecked(notif.getStartStop());
blockApplySettings(false);
return true;
}
else if (RemoteOutput::MsgReportRemoteData::match(message))
{
const RemoteOutput::MsgReportRemoteData& report = (const RemoteOutput::MsgReportRemoteData&) message;
displayRemoteData(report.getData());
return true;
}
else if (RemoteOutput::MsgReportRemoteFixedData::match(message))
{
const RemoteOutput::MsgReportRemoteFixedData& report = (const RemoteOutput::MsgReportRemoteFixedData&) message;
displayRemoteFixedData(report.getData());
return true;
}
else
{
@ -214,8 +210,7 @@ void RemoteOutputSinkGui::updateSampleRate()
void RemoteOutputSinkGui::displaySettings()
{
blockApplySettings(true);
ui->centerFrequency->setValue(m_deviceCenterFrequency / 1000);
ui->sampleRate->setValue(m_settings.m_sampleRate);
ui->centerFrequency->setText(QString("%L1").arg(m_deviceCenterFrequency));
ui->nbFECBlocks->setValue(m_settings.m_nbFECBlocks);
QString s0 = QString::number(128 + m_settings.m_nbFECBlocks, 'f', 0);
@ -242,7 +237,7 @@ void RemoteOutputSinkGui::updateHardware()
{
qDebug() << "RemoteOutputSinkGui::updateHardware";
RemoteOutput::MsgConfigureRemoteOutput* message = RemoteOutput::MsgConfigureRemoteOutput::create(m_settings, m_forceSettings);
m_deviceSampleSink->getInputMessageQueue()->push(message);
m_remoteOutput->getInputMessageQueue()->push(message);
m_forceSettings = false;
m_updateTimer.stop();
}
@ -276,12 +271,6 @@ void RemoteOutputSinkGui::updateStatus()
}
}
void RemoteOutputSinkGui::on_sampleRate_changed(quint64 value)
{
m_settings.m_sampleRate = value;
sendSettings();
}
void RemoteOutputSinkGui::on_nbFECBlocks_valueChanged(int value)
{
m_settings.m_nbFECBlocks = value;
@ -326,9 +315,8 @@ void RemoteOutputSinkGui::on_apiAddress_returnPressed()
m_settings.m_apiAddress = ui->apiAddress->text();
sendSettings();
QString infoURL = QString("http://%1:%2/sdrangel").arg(m_settings.m_apiAddress).arg(m_settings.m_apiPort);
m_networkRequest.setUrl(QUrl(infoURL));
m_networkManager->get(m_networkRequest);
RemoteOutput::MsgRequestFixedData *msg = RemoteOutput::MsgRequestFixedData::create();
m_remoteOutput->getInputMessageQueue()->push(msg);
}
void RemoteOutputSinkGui::on_apiPort_returnPressed()
@ -344,9 +332,8 @@ void RemoteOutputSinkGui::on_apiPort_returnPressed()
sendSettings();
QString infoURL = QString("http://%1:%2/sdrangel").arg(m_settings.m_apiAddress).arg(m_settings.m_apiPort);
m_networkRequest.setUrl(QUrl(infoURL));
m_networkManager->get(m_networkRequest);
RemoteOutput::MsgRequestFixedData *msg = RemoteOutput::MsgRequestFixedData::create();
m_remoteOutput->getInputMessageQueue()->push(msg);
}
void RemoteOutputSinkGui::on_dataAddress_returnPressed()
@ -384,9 +371,8 @@ void RemoteOutputSinkGui::on_apiApplyButton_clicked(bool checked)
sendSettings();
QString infoURL = QString("http://%1:%2/sdrangel").arg(m_settings.m_apiAddress).arg(m_settings.m_apiPort);
m_networkRequest.setUrl(QUrl(infoURL));
m_networkManager->get(m_networkRequest);
RemoteOutput::MsgRequestFixedData *msg = RemoteOutput::MsgRequestFixedData::create();
m_remoteOutput->getInputMessageQueue()->push(msg);
}
void RemoteOutputSinkGui::on_dataApplyButton_clicked(bool checked)
@ -410,7 +396,7 @@ void RemoteOutputSinkGui::on_startStop_toggled(bool checked)
if (m_doApplySettings)
{
RemoteOutput::MsgStartStop *message = RemoteOutput::MsgStartStop::create(checked);
m_deviceSampleSink->getInputMessageQueue()->push(message);
m_remoteOutput->getInputMessageQueue()->push(message);
}
}
@ -460,146 +446,71 @@ void RemoteOutputSinkGui::displayEventTimer()
void RemoteOutputSinkGui::tick()
{
if (++m_tickCount == 20) // once per second
{
QString reportURL;
reportURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/report")
.arg(m_settings.m_apiAddress)
.arg(m_settings.m_apiPort)
.arg(m_settings.m_deviceIndex)
.arg(m_settings.m_channelIndex);
m_networkRequest.setUrl(QUrl(reportURL));
m_networkManager->get(m_networkRequest);
displayEventTimer();
m_tickCount = 0;
}
}
void RemoteOutputSinkGui::networkManagerFinished(QNetworkReply *reply)
{
if (reply->error())
if (++m_tickCount == 20)
{
ui->apiAddressLabel->setStyleSheet("QLabel { background:rgb(79,79,79); }");
ui->statusText->setText(reply->errorString());
}
else
{
QString answer = reply->readAll();
try
{
QByteArray jsonBytes(answer.toStdString().c_str());
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(jsonBytes, &error);
if (error.error == QJsonParseError::NoError)
{
ui->apiAddressLabel->setStyleSheet("QLabel { background-color : green; }");
ui->statusText->setText(QString("API OK"));
analyzeApiReply(doc.object());
}
else
{
ui->apiAddressLabel->setStyleSheet("QLabel { background:rgb(79,79,79); }");
QString errorMsg = QString("Reply JSON error: ") + error.errorString() + QString(" at offset ") + QString::number(error.offset);
ui->statusText->setText(QString("JSON error. See log"));
qInfo().noquote() << "RemoteOutputSinkGui::networkManagerFinished" << errorMsg;
}
}
catch (const std::exception& ex)
{
ui->apiAddressLabel->setStyleSheet("QLabel { background:rgb(79,79,79); }");
QString errorMsg = QString("Error parsing request: ") + ex.what();
ui->statusText->setText("Error parsing request. See log for details");
qInfo().noquote() << "RemoteOutputSinkGui::networkManagerFinished" << errorMsg;
}
}
reply->deleteLater();
}
void RemoteOutputSinkGui::analyzeApiReply(const QJsonObject& jsonObject)
{
QString infoLine;
if (jsonObject.contains("RemoteSourceReport"))
{
QJsonObject report = jsonObject["RemoteSourceReport"].toObject();
m_deviceCenterFrequency = report["deviceCenterFreq"].toInt() * 1000;
m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency);
ui->centerFrequency->setValue(m_deviceCenterFrequency/1000);
int remoteRate = report["deviceSampleRate"].toInt();
ui->remoteRateText->setText(tr("%1k").arg((float)(remoteRate) / 1000));
int queueSize = report["queueSize"].toInt();
queueSize = queueSize == 0 ? 10 : queueSize;
int queueLength = report["queueLength"].toInt();
QString queueLengthText = QString("%1/%2").arg(queueLength).arg(queueSize);
ui->queueLengthText->setText(queueLengthText);
int queueLengthPercent = (queueLength*100)/queueSize;
ui->queueLengthGauge->setValue(queueLengthPercent);
int unrecoverableCount = report["uncorrectableErrorsCount"].toInt();
int recoverableCount = report["correctableErrorsCount"].toInt();
uint64_t timestampUs = report["tvSec"].toInt()*1000000ULL + report["tvUSec"].toInt();
if (!m_resetCounts)
{
int recoverableCountDelta = recoverableCount - m_lastCountRecovered;
int unrecoverableCountDelta = unrecoverableCount - m_lastCountUnrecoverable;
displayEventStatus(recoverableCountDelta, unrecoverableCountDelta);
m_countRecovered += recoverableCountDelta;
m_countUnrecoverable += unrecoverableCountDelta;
displayEventCounts();
}
uint32_t sampleCountDelta, sampleCount;
sampleCount = report["samplesCount"].toInt();
if (sampleCount < m_lastSampleCount) {
sampleCountDelta = (0xFFFFFFFFU - m_lastSampleCount) + sampleCount + 1;
if (m_remoteAPIConnected) {
ui->apiAddressLabel->setStyleSheet("QLabel { background-color: green; }");
} else {
sampleCountDelta = sampleCount - m_lastSampleCount;
ui->apiAddressLabel->setStyleSheet("QLabel { background:rgb(79,79,79); }");
}
if (sampleCountDelta == 0)
{
ui->allFramesDecoded->setStyleSheet("QToolButton { background-color : blue; }");
}
m_remoteAPIConnected = false;
m_tickCount = 0;
}
}
double remoteStreamRate = sampleCountDelta*1e6 / (double) (timestampUs - m_lastTimestampUs);
void RemoteOutputSinkGui::displayRemoteData(const RemoteOutput::MsgReportRemoteData::RemoteData& remoteData)
{
m_deviceCenterFrequency = remoteData.m_centerFrequency;
m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency);
ui->centerFrequency->setText(QString("%L1").arg(m_deviceCenterFrequency));
ui->remoteRateText->setText(tr("%1k").arg((float)(remoteData.m_sampleRate) / 1000));
QString queueLengthText = QString("%1/%2").arg(remoteData.m_queueLength).arg(remoteData.m_queueSize);
ui->queueLengthText->setText(queueLengthText);
int queueLengthPercent = (remoteData.m_queueLength*100)/remoteData.m_queueSize;
ui->queueLengthGauge->setValue(queueLengthPercent);
int recoverableCountDelta = remoteData.m_recoverableCount - m_lastCountRecovered;
int unrecoverableCountDelta = remoteData.m_unrecoverableCount - m_lastCountUnrecoverable;
displayEventStatus(recoverableCountDelta, unrecoverableCountDelta);
m_countRecovered += recoverableCountDelta;
m_countUnrecoverable += unrecoverableCountDelta;
displayEventCounts();
displayEventTimer();
m_remoteAPIConnected = true;
if (remoteStreamRate != 0) {
ui->remoteStreamRateText->setText(QString("%1").arg(remoteStreamRate, 0, 'f', 0));
}
uint32_t sampleCountDelta;
m_resetCounts = false;
m_lastCountRecovered = recoverableCount;
m_lastCountUnrecoverable = unrecoverableCount;
m_lastSampleCount = sampleCount;
m_lastTimestampUs = timestampUs;
if (remoteData.m_sampleCount < m_lastSampleCount) {
sampleCountDelta = (0xFFFFFFFFU - m_lastSampleCount) + remoteData.m_sampleCount + 1;
} else {
sampleCountDelta = remoteData.m_sampleCount - m_lastSampleCount;
}
if (jsonObject.contains("version")) {
infoLine = "v" + jsonObject["version"].toString();
if (sampleCountDelta == 0)
{
ui->allFramesDecoded->setStyleSheet("QToolButton { background-color : blue; }");
}
if (jsonObject.contains("qtVersion")) {
infoLine += " Qt" + jsonObject["qtVersion"].toString();
double remoteStreamRate = sampleCountDelta*1e6 / (double) (remoteData.m_timestampUs - m_lastTimestampUs);
if (remoteStreamRate != 0) {
ui->remoteStreamRateText->setText(QString("%1").arg(remoteStreamRate, 0, 'f', 0));
}
if (jsonObject.contains("architecture")) {
infoLine += " " + jsonObject["architecture"].toString();
}
m_lastCountRecovered = remoteData.m_recoverableCount;
m_lastCountUnrecoverable = remoteData.m_unrecoverableCount;
m_lastSampleCount = remoteData.m_sampleCount;
m_lastTimestampUs = remoteData.m_timestampUs;
}
if (jsonObject.contains("os")) {
infoLine += " " + jsonObject["os"].toString();
}
if (jsonObject.contains("dspRxBits") && jsonObject.contains("dspTxBits")) {
infoLine += QString(" %1/%2b").arg(jsonObject["dspRxBits"].toInt()).arg(jsonObject["dspTxBits"].toInt());
}
void RemoteOutputSinkGui::displayRemoteFixedData(const RemoteOutput::MsgReportRemoteFixedData::RemoteData& remoteData)
{
QString infoLine = "v" + remoteData.m_version;
infoLine += " Qt" + remoteData.m_qtVersion;
infoLine += " " + remoteData.m_architecture;
infoLine += " " + remoteData.m_os;
infoLine += QString(" %1/%2b").arg(remoteData.m_rxBits).arg(remoteData.m_txBits);
m_remoteAPIConnected = true;
if (infoLine.size() > 0) {
ui->infoText->setText(infoLine);

View File

@ -23,7 +23,6 @@
#include <QTimer>
#include <QElapsedTimer>
#include <QWidget>
#include <QNetworkRequest>
#include "device/devicegui.h"
#include "util/messagequeue.h"
@ -32,9 +31,6 @@
#include "remoteoutput.h"
#include "remoteoutputsettings.h"
class QNetworkAccessManager;
class QNetworkReply;
class QJsonObject;
class DeviceSampleSink;
class DeviceUISet;
@ -90,7 +86,7 @@ private:
RemoteOutputSettings m_controlSettings; //!< settings last sent to device via control port
QTimer m_updateTimer;
QTimer m_statusTimer;
DeviceSampleSink* m_deviceSampleSink;
DeviceSampleSink* m_remoteOutput;
int m_sampleRate;
quint64 m_deviceCenterFrequency; //!< Center frequency in device
int m_samplesCount;
@ -99,6 +95,7 @@ private:
int m_lastEngineState;
bool m_doApplySettings;
bool m_forceSettings;
bool m_remoteAPIConnected;
uint32_t m_countUnrecoverable;
uint32_t m_countRecovered;
@ -106,7 +103,6 @@ private:
uint32_t m_lastCountRecovered;
uint32_t m_lastSampleCount;
uint64_t m_lastTimestampUs;
bool m_resetCounts;
QElapsedTimer m_time;
QPalette m_paletteGreenText;
@ -115,24 +111,21 @@ private:
MessageQueue m_inputMessageQueue;
QNetworkAccessManager *m_networkManager;
QNetworkRequest m_networkRequest;
void blockApplySettings(bool block);
void displaySettings();
void displayTime();
void displayRemoteData(const RemoteOutput::MsgReportRemoteData::RemoteData& remoteData);
void displayRemoteFixedData(const RemoteOutput::MsgReportRemoteFixedData::RemoteData& remoteData);
void sendControl(bool force = false);
void sendSettings();
void updateSampleRate();
void displayEventCounts();
void displayEventStatus(int recoverableCount, int unrecoverableCount);
void displayEventTimer();
void analyzeApiReply(const QJsonObject& jsonObject);
bool handleMessage(const Message& message);
private slots:
void handleInputMessages();
void on_sampleRate_changed(quint64 value);
void on_nbFECBlocks_valueChanged(int value);
void on_deviceIndex_returnPressed();
void on_channelIndex_returnPressed();
@ -147,7 +140,6 @@ private slots:
void updateHardware();
void updateStatus();
void tick();
void networkManagerFinished(QNetworkReply *reply);
void openDeviceSettingsDialog(const QPoint& p);
};

View File

@ -115,36 +115,24 @@
</spacer>
</item>
<item>
<widget class="ValueDial" name="centerFrequency" native="true">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<widget class="QLabel" name="centerFrequency">
<property name="minimumSize">
<size>
<width>32</width>
<height>16</height>
<width>170</width>
<height>0</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Mono</family>
<pointsize>20</pointsize>
<family>Liberation Sans</family>
<pointsize>16</pointsize>
</font>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
<property name="text">
<string>10,000,000,000</string>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="toolTip">
<string>Record center frequency in kHz</string>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
@ -155,7 +143,7 @@
<item>
<widget class="QLabel" name="freqUnits">
<property name="text">
<string> kHz</string>
<string>Hz</string>
</property>
</widget>
</item>
@ -200,68 +188,6 @@
<property name="topMargin">
<number>2</number>
</property>
<item>
<widget class="QLabel" name="sampleRateLabel">
<property name="text">
<string>SR</string>
</property>
</widget>
</item>
<item>
<widget class="ValueDial" name="sampleRate" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Mono</family>
<pointsize>12</pointsize>
</font>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="toolTip">
<string>Remote device sample rate</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="sampleRateUnit">
<property name="text">
<string>S/s</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="deviceIndexLabel">
<property name="text">
@ -314,6 +240,19 @@
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
@ -766,12 +705,6 @@
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ValueDial</class>
<extends>QWidget</extends>
<header>gui/valuedial.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ButtonSwitch</class>
<extends>QToolButton</extends>

View File

@ -63,43 +63,43 @@ void RemoteOutputSender::setDestination(const QString& address, uint16_t port)
m_remoteHostAddress.setAddress(address);
}
RemoteDataBlock *RemoteOutputSender::getDataBlock()
RemoteDataFrame *RemoteOutputSender::getDataFrame()
{
return m_fifo.getDataBlock();
return m_fifo.getDataFrame();
}
void RemoteOutputSender::handleData()
{
RemoteDataBlock *dataBlock;
RemoteDataFrame *dataFrame;
unsigned int remainder = m_fifo.getRemainder();
while (remainder != 0)
{
remainder = m_fifo.readDataBlock(&dataBlock);
remainder = m_fifo.readDataFrame(&dataFrame);
if (dataBlock) {
sendDataBlock(dataBlock);
if (dataFrame) {
sendDataFrame(dataFrame);
}
}
}
void RemoteOutputSender::sendDataBlock(RemoteDataBlock *dataBlock)
void RemoteOutputSender::sendDataFrame(RemoteDataFrame *dataFrame)
{
CM256::cm256_encoder_params cm256Params; //!< Main interface with CM256 encoder
CM256::cm256_block descriptorBlocks[256]; //!< Pointers to data for CM256 encoder
RemoteProtectedBlock fecBlocks[256]; //!< FEC data
uint16_t frameIndex = dataBlock->m_txControlBlock.m_frameIndex;
int nbBlocksFEC = dataBlock->m_txControlBlock.m_nbBlocksFEC;
m_remoteHostAddress.setAddress(dataBlock->m_txControlBlock.m_dataAddress);
uint16_t dataPort = dataBlock->m_txControlBlock.m_dataPort;
RemoteSuperBlock *txBlockx = dataBlock->m_superBlocks;
uint16_t frameIndex = dataFrame->m_txControlBlock.m_frameIndex;
int nbBlocksFEC = dataFrame->m_txControlBlock.m_nbBlocksFEC;
m_remoteHostAddress.setAddress(dataFrame->m_txControlBlock.m_dataAddress);
uint16_t dataPort = dataFrame->m_txControlBlock.m_dataPort;
RemoteSuperBlock *txBlockx = dataFrame->m_superBlocks;
if ((nbBlocksFEC == 0) || !m_cm256p) // Do not FEC encode
{
if (m_udpSocket)
{
for (int i = 0; i < RemoteNbOrginalBlocks; i++) { // send block via UDP
for (int i = 0; i < RemoteNbOrginalBlocks; i++) { // send blocks via UDP
m_udpSocket->writeDatagram((const char*)&txBlockx[i], (qint64 ) RemoteUdpSize, m_remoteHostAddress, dataPort);
}
}
@ -130,7 +130,7 @@ void RemoteOutputSender::sendDataBlock(RemoteDataBlock *dataBlock)
{
qWarning("RemoteSinkSender::handleDataBlock: CM256 encode failed. Transmit without FEC.");
cm256Params.RecoveryCount = 0;
RemoteSuperBlock& superBlock = dataBlock->m_superBlocks[0]; // first block
RemoteSuperBlock& superBlock = dataFrame->m_superBlocks[0]; // first block
RemoteMetaDataFEC *destMeta = (RemoteMetaDataFEC *) &superBlock.m_protectedBlock;
destMeta->m_nbFECBlocks = 0;
boost::crc_32_type crc32;
@ -146,11 +146,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 blocks via UDP
m_udpSocket->writeDatagram((const char*)&txBlockx[i], (qint64 ) RemoteUdpSize, m_remoteHostAddress, dataPort);
}
}
}
dataBlock->m_txControlBlock.m_processed = true;
dataFrame->m_txControlBlock.m_processed = true;
}

View File

@ -36,7 +36,7 @@
#include "remoteoutputfifo.h"
class RemoteDataBlock;
class RemoteDataFrame;
class CM256;
class QUdpSocket;
@ -47,7 +47,7 @@ public:
RemoteOutputSender();
~RemoteOutputSender();
RemoteDataBlock *getDataBlock();
RemoteDataFrame *getDataFrame();
void setDestination(const QString& address, uint16_t port);
private:
@ -61,7 +61,7 @@ private:
uint16_t m_remotePort;
QHostAddress m_remoteHostAddress;
void sendDataBlock(RemoteDataBlock *dataBlock);
void sendDataFrame(RemoteDataFrame *dataFrame);
private slots:
void handleData();

View File

@ -25,8 +25,6 @@ RemoteOutputSettings::RemoteOutputSettings()
void RemoteOutputSettings::resetToDefaults()
{
m_centerFrequency = 435000*1000;
m_sampleRate = 48000;
m_nbFECBlocks = 0;
m_apiAddress = "127.0.0.1";
m_apiPort = 9091;
@ -44,8 +42,6 @@ QByteArray RemoteOutputSettings::serialize() const
{
SimpleSerializer s(1);
s.writeU64(1, m_centerFrequency);
s.writeU32(2, m_sampleRate);
s.writeU32(4, m_nbFECBlocks);
s.writeString(5, m_apiAddress);
s.writeU32(6, m_apiPort);
@ -75,8 +71,6 @@ bool RemoteOutputSettings::deserialize(const QByteArray& data)
{
quint32 uintval;
d.readU64(1, &m_centerFrequency, 435000*1000);
d.readU32(2, &m_sampleRate, 48000);
d.readU32(4, &m_nbFECBlocks, 0);
d.readString(5, &m_apiAddress, "127.0.0.1");
d.readU32(6, &uintval, 9090);

View File

@ -22,8 +22,6 @@
#include <QString>
struct RemoteOutputSettings {
quint64 m_centerFrequency;
quint32 m_sampleRate;
quint32 m_nbFECBlocks;
QString m_apiAddress;
quint16 m_apiPort;

View File

@ -120,6 +120,7 @@ void RemoteOutputWorker::tick()
SampleVector& data = m_sampleFifo->getData();
unsigned int iPart1Begin, iPart1End, iPart2Begin, iPart2End;
m_sampleFifo->read(m_samplesChunkSize, iPart1Begin, iPart1End, iPart2Begin, iPart2End);
m_samplesCount += m_samplesChunkSize;
if (iPart1Begin != iPart1End)
{

View File

@ -53,8 +53,8 @@ public:
bool isRunning() const { return m_running; }
uint32_t getSamplesCount() const { return m_samplesCount; }
uint32_t getSamplesCount(uint64_t& ts_usecs) const;
void setSamplesCount(int samplesCount) { m_samplesCount = samplesCount; }
void setChunkCorrection(int chunkCorrection) { m_chunkCorrection = chunkCorrection; }
void connectTimer(const QTimer& timer);

View File

@ -32,7 +32,7 @@ UDPSinkFEC::UDPSinkFEC() :
m_nbSamples(0),
m_nbBlocksFEC(0),
m_txDelayRatio(0.0),
m_dataBlock(nullptr),
m_dataFrame(nullptr),
m_txBlockIndex(0),
m_txBlocksIndex(0),
m_frameCount(0),
@ -115,14 +115,14 @@ void UDPSinkFEC::write(const SampleVector::iterator& begin, uint32_t sampleChunk
metaData.m_tv_sec = nowus / 1000000UL; // tv.tv_sec;
metaData.m_tv_usec = nowus % 1000000UL; // tv.tv_usec;
if (!m_dataBlock) { // on the very first cycle there is no data block allocated
m_dataBlock = m_remoteOutputSender->getDataBlock(); // ask a new block to sender
if (!m_dataFrame) { // on the very first cycle there is no data block allocated
m_dataFrame = m_remoteOutputSender->getDataFrame(); // ask a new block to sender
}
boost::crc_32_type crc32;
crc32.process_bytes(&metaData, sizeof(RemoteMetaDataFEC)-4);
metaData.m_crc32 = crc32.checksum();
RemoteSuperBlock& superBlock = m_dataBlock->m_superBlocks[0]; // first block
RemoteSuperBlock& superBlock = m_dataFrame->m_superBlocks[0]; // first block
superBlock.init();
superBlock.m_header.m_frameIndex = m_frameCount;
superBlock.m_header.m_blockIndex = m_txBlockIndex;
@ -172,18 +172,18 @@ void UDPSinkFEC::write(const SampleVector::iterator& begin, uint32_t sampleChunk
m_superBlock.m_header.m_blockIndex = m_txBlockIndex;
m_superBlock.m_header.m_sampleBytes = (SDR_RX_SAMP_SZ <= 16 ? 2 : 4);
m_superBlock.m_header.m_sampleBits = SDR_RX_SAMP_SZ;
m_dataBlock->m_superBlocks[m_txBlockIndex] = m_superBlock;
m_dataFrame->m_superBlocks[m_txBlockIndex] = m_superBlock;
if (m_txBlockIndex == RemoteNbOrginalBlocks - 1) // frame complete
{
m_dataBlock->m_txControlBlock.m_frameIndex = m_frameCount;
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_dataAddress = m_remoteAddress;
m_dataBlock->m_txControlBlock.m_dataPort = m_remotePort;
m_dataFrame->m_txControlBlock.m_frameIndex = m_frameCount;
m_dataFrame->m_txControlBlock.m_processed = false;
m_dataFrame->m_txControlBlock.m_complete = true;
m_dataFrame->m_txControlBlock.m_nbBlocksFEC = m_nbBlocksFEC;
m_dataFrame->m_txControlBlock.m_dataAddress = m_remoteAddress;
m_dataFrame->m_txControlBlock.m_dataPort = m_remotePort;
m_dataBlock = m_remoteOutputSender->getDataBlock(); // ask a new block to sender
m_dataFrame = m_remoteOutputSender->getDataFrame(); // ask a new block to sender
m_txBlockIndex = 0;
m_frameCount++;

View File

@ -86,7 +86,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
RemoteDataBlock *m_dataBlock;
RemoteDataFrame *m_dataFrame;
RemoteSuperBlock m_superBlock; //!< current super block being built
int m_txBlockIndex; //!< Current index in blocks to transmit in the Tx row
int m_txBlocksIndex; //!< Current index of Tx blocks row

View File

@ -157,13 +157,13 @@ struct RemoteRxControlBlock
}
};
class RemoteDataBlock
class RemoteDataFrame
{
public:
RemoteDataBlock() {
m_superBlocks = new RemoteSuperBlock[256];
RemoteDataFrame() {
m_superBlocks = new RemoteSuperBlock[256]; //!< 128 original bloks + 128 possible recovery blocks
}
~RemoteDataBlock() {
~RemoteDataFrame() {
delete[] m_superBlocks;
}
RemoteTxControlBlock m_txControlBlock;

View File

@ -36,41 +36,38 @@ RemoteDataQueue::RemoteDataQueue(QObject* parent) :
RemoteDataQueue::~RemoteDataQueue()
{
RemoteDataBlock* data;
RemoteDataFrame* data;
while ((data = pop()) != 0)
while ((data = pop()) != nullptr)
{
qDebug() << "RemoteDataQueue::~RemoteDataQueue: data block was still in queue";
delete data;
}
}
void RemoteDataQueue::push(RemoteDataBlock* data, bool emitSignal)
void RemoteDataQueue::push(RemoteDataFrame* data, bool emitSignal)
{
if (data)
{
m_lock.lock();
m_queue.append(data);
m_queue.enqueue(data);
// qDebug("RemoteDataQueue::push: %d", m_queue.size());
m_lock.unlock();
}
if (emitSignal)
{
if (emitSignal) {
emit dataBlockEnqueued();
}
}
RemoteDataBlock* RemoteDataQueue::pop()
RemoteDataFrame* RemoteDataQueue::pop()
{
QMutexLocker locker(&m_lock);
if (m_queue.isEmpty())
{
return 0;
}
else
{
return m_queue.takeFirst();
if (m_queue.isEmpty()) {
return nullptr;
} else {
return m_queue.dequeue();
}
}

View File

@ -31,17 +31,17 @@
#include "export.h"
class RemoteDataBlock;
class RemoteDataFrame;
class SDRBASE_API RemoteDataQueue : public QObject {
Q_OBJECT
public:
RemoteDataQueue(QObject* parent = NULL);
RemoteDataQueue(QObject* parent = nullptr);
~RemoteDataQueue();
void push(RemoteDataBlock* dataBlock, bool emitSignal = true); //!< Push daa block onto queue
RemoteDataBlock* pop(); //!< Pop message from queue
void push(RemoteDataFrame* dataFrame, bool emitSignal = true); //!< Push data frame onto queue
RemoteDataFrame* pop(); //!< Pop frame from queue
int size(); //!< Returns queue size
void clear(); //!< Empty queue
@ -51,7 +51,7 @@ signals:
private:
QMutex m_lock;
QQueue<RemoteDataBlock*> m_queue;
QQueue<RemoteDataFrame*> m_queue;
};
#endif /* CHANNEL_REMOTEDATAQUEUE_H_ */

View File

@ -28,56 +28,45 @@
const uint32_t RemoteDataReadQueue::MinimumMaxSize = 10;
RemoteDataReadQueue::RemoteDataReadQueue() :
m_dataBlock(0),
m_dataFrame(nullptr),
m_maxSize(MinimumMaxSize),
m_blockIndex(1),
m_sampleIndex(0),
m_sampleCount(0),
m_full(false)
m_sampleCount(0)
{}
RemoteDataReadQueue::~RemoteDataReadQueue()
{
RemoteDataBlock* data;
RemoteDataFrame* data;
while ((data = pop()) != 0)
while ((data = pop()) != nullptr)
{
qDebug("RemoteDataReadQueue::~RemoteDataReadQueue: data block was still in queue");
delete data;
}
}
void RemoteDataReadQueue::push(RemoteDataBlock* dataBlock)
void RemoteDataReadQueue::push(RemoteDataFrame* dataFrame)
{
if (length() >= m_maxSize)
{
if (length() < m_maxSize) {
m_dataReadQueue.enqueue(dataFrame);
} else {
qWarning("RemoteDataReadQueue::push: queue is full");
m_full = true; // stop filling the queue
RemoteDataBlock *data = m_dataReadQueue.takeLast();
delete data;
}
if (m_full) {
m_full = (length() > m_maxSize/2); // do not fill queue again before queue is half size
}
if (!m_full) {
m_dataReadQueue.append(dataBlock);
}
}
RemoteDataBlock* RemoteDataReadQueue::pop()
RemoteDataFrame* RemoteDataReadQueue::pop()
{
if (m_dataReadQueue.isEmpty())
{
return 0;
return nullptr;
}
else
{
m_blockIndex = 1;
m_sampleIndex = 0;
return m_dataReadQueue.takeFirst();
return m_dataReadQueue.dequeue();
}
}
@ -91,26 +80,29 @@ void RemoteDataReadQueue::setSize(uint32_t size)
void RemoteDataReadQueue::readSample(Sample& s, bool scaleForTx)
{
// depletion/repletion state
if (m_dataBlock == 0)
if (m_dataFrame == nullptr)
{
if (length() >= m_maxSize/2)
m_dataFrame = pop();
if (m_dataFrame)
{
qDebug("RemoteDataReadQueue::readSample: initial pop new block: queue size: %u", length());
qDebug("RemoteDataReadQueue::readSample: initial pop new frame: queue size: %u", length());
m_blockIndex = 1;
m_dataBlock = m_dataReadQueue.takeFirst();
m_sampleIndex = 0;
convertDataToSample(s, m_blockIndex, m_sampleIndex, scaleForTx);
m_sampleIndex++;
m_sampleCount++;
}
else
{
s = Sample{0, 0};
}
m_sampleCount++;
return;
}
int sampleSize = m_dataBlock->m_superBlocks[m_blockIndex].m_header.m_sampleBytes * 2;
int sampleSize = m_dataFrame->m_superBlocks[m_blockIndex].m_header.m_sampleBytes * 2;
uint32_t samplesPerBlock = RemoteNbBytesPerBlock / sampleSize;
if (m_sampleIndex < samplesPerBlock)
@ -132,29 +124,24 @@ void RemoteDataReadQueue::readSample(Sample& s, bool scaleForTx)
}
else
{
delete m_dataBlock;
m_dataBlock = 0;
delete m_dataFrame;
m_dataFrame = nullptr;
if (length() == 0) {
qWarning("RemoteDataReadQueue::readSample: try to pop new block but queue is empty");
}
m_dataFrame = pop();
if (length() > 0)
if (m_dataFrame)
{
//qDebug("RemoteDataReadQueue::readSample: pop new block: queue size: %u", length());
m_blockIndex = 1;
m_dataBlock = m_dataReadQueue.takeFirst();
m_sampleIndex = 0;
convertDataToSample(s, m_blockIndex, m_sampleIndex, scaleForTx);
m_sampleIndex++;
m_sampleCount++;
}
else
{
qWarning("RemoteDataReadQueue::readSample: try to pop new block but queue is empty");
s = Sample{0, 0};
}
}
}
}

View File

@ -29,7 +29,7 @@
#include "export.h"
class RemoteDataBlock;
class RemoteDataFrame;
struct Sample;
class SDRBASE_API RemoteDataReadQueue
@ -38,8 +38,7 @@ public:
RemoteDataReadQueue();
~RemoteDataReadQueue();
void push(RemoteDataBlock* dataBlock); //!< push block on the queue
RemoteDataBlock* pop(); //!< Pop block from the queue
void push(RemoteDataFrame* dataFrame); //!< push frame on the queue
void readSample(Sample& s, bool scaleForTx = false); //!< Read sample from queue possibly scaling to Tx size
uint32_t length() const { return m_dataReadQueue.size(); } //!< Returns queue length
uint32_t size() const { return m_maxSize; } //!< Returns queue size (max length)
@ -49,24 +48,25 @@ public:
static const uint32_t MinimumMaxSize;
private:
QQueue<RemoteDataBlock*> m_dataReadQueue;
RemoteDataBlock *m_dataBlock;
QQueue<RemoteDataFrame*> m_dataReadQueue;
RemoteDataFrame *m_dataFrame;
uint32_t m_maxSize;
uint32_t m_blockIndex;
uint32_t m_sampleIndex;
uint32_t m_sampleCount; //!< use a counter capped below 2^31 as it is going to be converted to an int in the web interface
bool m_full; //!< full condition was hit
RemoteDataFrame* pop(); //!< Pop frame from the queue
inline void convertDataToSample(Sample& s, uint32_t blockIndex, uint32_t sampleIndex, bool scaleForTx)
{
int sampleSize = m_dataBlock->m_superBlocks[blockIndex].m_header.m_sampleBytes * 2; // I/Q sample size in data block
int samplebits = m_dataBlock->m_superBlocks[blockIndex].m_header.m_sampleBits; // I or Q sample size in bits
int sampleSize = m_dataFrame->m_superBlocks[blockIndex].m_header.m_sampleBytes * 2; // I/Q sample size in data block
int samplebits = m_dataFrame->m_superBlocks[blockIndex].m_header.m_sampleBits; // I or Q sample size in bits
int32_t iconv, qconv;
if ((sizeof(Sample) == 4) && (sampleSize == 8)) // generally 24->16 bits
{
iconv = ((int32_t*) &(m_dataBlock->m_superBlocks[blockIndex].m_protectedBlock.buf[sampleIndex*sampleSize]))[0];
qconv = ((int32_t*) &(m_dataBlock->m_superBlocks[blockIndex].m_protectedBlock.buf[sampleIndex*sampleSize+4]))[0];
iconv = ((int32_t*) &(m_dataFrame->m_superBlocks[blockIndex].m_protectedBlock.buf[sampleIndex*sampleSize]))[0];
qconv = ((int32_t*) &(m_dataFrame->m_superBlocks[blockIndex].m_protectedBlock.buf[sampleIndex*sampleSize+4]))[0];
iconv >>= scaleForTx ? (SDR_TX_SAMP_SZ-SDR_RX_SAMP_SZ) : (samplebits-SDR_RX_SAMP_SZ);
qconv >>= scaleForTx ? (SDR_TX_SAMP_SZ-SDR_RX_SAMP_SZ) : (samplebits-SDR_RX_SAMP_SZ);
s.setReal(iconv);
@ -74,8 +74,8 @@ private:
}
else if ((sizeof(Sample) == 8) && (sampleSize == 4)) // generally 16->24 bits
{
iconv = ((int16_t*) &(m_dataBlock->m_superBlocks[blockIndex].m_protectedBlock.buf[sampleIndex*sampleSize]))[0];
qconv = ((int16_t*) &(m_dataBlock->m_superBlocks[blockIndex].m_protectedBlock.buf[sampleIndex*sampleSize+2]))[0];
iconv = ((int16_t*) &(m_dataFrame->m_superBlocks[blockIndex].m_protectedBlock.buf[sampleIndex*sampleSize]))[0];
qconv = ((int16_t*) &(m_dataFrame->m_superBlocks[blockIndex].m_protectedBlock.buf[sampleIndex*sampleSize+2]))[0];
iconv <<= scaleForTx ? (SDR_TX_SAMP_SZ-samplebits) : (SDR_RX_SAMP_SZ-samplebits);
qconv <<= scaleForTx ? (SDR_TX_SAMP_SZ-samplebits) : (SDR_RX_SAMP_SZ-samplebits);
s.setReal(iconv);
@ -83,7 +83,7 @@ private:
}
else if ((sampleSize == 4) || (sampleSize == 8)) // generally 16->16 or 24->24 bits
{
s = *((Sample*) &(m_dataBlock->m_superBlocks[blockIndex].m_protectedBlock.buf[sampleIndex*sampleSize]));
s = *((Sample*) &(m_dataFrame->m_superBlocks[blockIndex].m_protectedBlock.buf[sampleIndex*sampleSize]));
}
else // invalid size
{

View File

@ -9820,6 +9820,13 @@ margin-bottom: 20px;
};
defs.RemoteOutputReport = {
"properties" : {
"centerFrequency" : {
"type" : "integer",
"format" : "int64"
},
"sampleRate" : {
"type" : "integer"
},
"bufferRWBalance" : {
"type" : "number",
"format" : "float",
@ -9834,13 +9841,6 @@ margin-bottom: 20px;
};
defs.RemoteOutputSettings = {
"properties" : {
"centerFrequency" : {
"type" : "integer",
"format" : "int64"
},
"sampleRate" : {
"type" : "integer"
},
"nbFECBlocks" : {
"type" : "integer"
},
@ -9970,6 +9970,7 @@ margin-bottom: 20px;
},
"centerFreq" : {
"type" : "integer",
"format" : "int64",
"description" : "Stream center frequency setting in kHz"
},
"sampleRate" : {
@ -10003,6 +10004,12 @@ margin-bottom: 20px;
"title" : {
"type" : "string"
},
"log2Interp" : {
"type" : "integer"
},
"filterChainHash" : {
"type" : "integer"
},
"streamIndex" : {
"type" : "integer",
"description" : "MIMO channel. Not relevant when connected to SI (single Rx)."
@ -51596,7 +51603,7 @@ except ApiException as e:
</div>
<div id="generator">
<div class="content">
Generated 2021-12-07T06:38:29.681+01:00
Generated 2021-12-12T22:32:39.234+01:00
</div>
</div>
</div>

View File

@ -1,11 +1,6 @@
RemoteOutputSettings:
description: RemoteOutput
properties:
centerFrequency:
type: integer
format: int64
sampleRate:
type: integer
nbFECBlocks:
type: integer
apiAddress:
@ -35,6 +30,11 @@ RemoteOutputSettings:
RemoteOutputReport:
description: RemoteOutput
properties:
centerFrequency:
type: integer
format: int64
sampleRate:
type: integer
bufferRWBalance:
description: ratio off the mid buffer (positive read leads)
type: number

View File

@ -11,6 +11,10 @@ RemoteSourceSettings:
type: integer
title:
type: string
log2Interp:
type: integer
filterChainHash:
type: integer
streamIndex:
description: MIMO channel. Not relevant when connected to SI (single Rx).
type: integer
@ -61,6 +65,7 @@ RemoteSourceReport:
centerFreq:
description: "Stream center frequency setting in kHz"
type: integer
format: int64
sampleRate:
description: "Stream nominal sample rate in S/s"
type: integer

View File

@ -1,11 +1,6 @@
RemoteOutputSettings:
description: RemoteOutput
properties:
centerFrequency:
type: integer
format: int64
sampleRate:
type: integer
nbFECBlocks:
type: integer
apiAddress:
@ -35,6 +30,11 @@ RemoteOutputSettings:
RemoteOutputReport:
description: RemoteOutput
properties:
centerFrequency:
type: integer
format: int64
sampleRate:
type: integer
bufferRWBalance:
description: ratio off the mid buffer (positive read leads)
type: number

View File

@ -11,6 +11,10 @@ RemoteSourceSettings:
type: integer
title:
type: string
log2Interp:
type: integer
filterChainHash:
type: integer
streamIndex:
description: MIMO channel. Not relevant when connected to SI (single Rx).
type: integer
@ -61,6 +65,7 @@ RemoteSourceReport:
centerFreq:
description: "Stream center frequency setting in kHz"
type: integer
format: int64
sampleRate:
description: "Stream nominal sample rate in S/s"
type: integer

View File

@ -9820,6 +9820,13 @@ margin-bottom: 20px;
};
defs.RemoteOutputReport = {
"properties" : {
"centerFrequency" : {
"type" : "integer",
"format" : "int64"
},
"sampleRate" : {
"type" : "integer"
},
"bufferRWBalance" : {
"type" : "number",
"format" : "float",
@ -9834,13 +9841,6 @@ margin-bottom: 20px;
};
defs.RemoteOutputSettings = {
"properties" : {
"centerFrequency" : {
"type" : "integer",
"format" : "int64"
},
"sampleRate" : {
"type" : "integer"
},
"nbFECBlocks" : {
"type" : "integer"
},
@ -9970,6 +9970,7 @@ margin-bottom: 20px;
},
"centerFreq" : {
"type" : "integer",
"format" : "int64",
"description" : "Stream center frequency setting in kHz"
},
"sampleRate" : {
@ -10003,6 +10004,12 @@ margin-bottom: 20px;
"title" : {
"type" : "string"
},
"log2Interp" : {
"type" : "integer"
},
"filterChainHash" : {
"type" : "integer"
},
"streamIndex" : {
"type" : "integer",
"description" : "MIMO channel. Not relevant when connected to SI (single Rx)."
@ -51596,7 +51603,7 @@ except ApiException as e:
</div>
<div id="generator">
<div class="content">
Generated 2021-12-07T06:38:29.681+01:00
Generated 2021-12-12T22:32:39.234+01:00
</div>
</div>
</div>

View File

@ -28,6 +28,10 @@ SWGRemoteOutputReport::SWGRemoteOutputReport(QString* json) {
}
SWGRemoteOutputReport::SWGRemoteOutputReport() {
center_frequency = 0L;
m_center_frequency_isSet = false;
sample_rate = 0;
m_sample_rate_isSet = false;
buffer_rw_balance = 0.0f;
m_buffer_rw_balance_isSet = false;
sample_count = 0;
@ -40,6 +44,10 @@ SWGRemoteOutputReport::~SWGRemoteOutputReport() {
void
SWGRemoteOutputReport::init() {
center_frequency = 0L;
m_center_frequency_isSet = false;
sample_rate = 0;
m_sample_rate_isSet = false;
buffer_rw_balance = 0.0f;
m_buffer_rw_balance_isSet = false;
sample_count = 0;
@ -50,6 +58,8 @@ void
SWGRemoteOutputReport::cleanup() {
}
SWGRemoteOutputReport*
@ -63,6 +73,10 @@ SWGRemoteOutputReport::fromJson(QString &json) {
void
SWGRemoteOutputReport::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&center_frequency, pJson["centerFrequency"], "qint64", "");
::SWGSDRangel::setValue(&sample_rate, pJson["sampleRate"], "qint32", "");
::SWGSDRangel::setValue(&buffer_rw_balance, pJson["bufferRWBalance"], "float", "");
::SWGSDRangel::setValue(&sample_count, pJson["sampleCount"], "qint32", "");
@ -83,6 +97,12 @@ SWGRemoteOutputReport::asJson ()
QJsonObject*
SWGRemoteOutputReport::asJsonObject() {
QJsonObject* obj = new QJsonObject();
if(m_center_frequency_isSet){
obj->insert("centerFrequency", QJsonValue(center_frequency));
}
if(m_sample_rate_isSet){
obj->insert("sampleRate", QJsonValue(sample_rate));
}
if(m_buffer_rw_balance_isSet){
obj->insert("bufferRWBalance", QJsonValue(buffer_rw_balance));
}
@ -93,6 +113,26 @@ SWGRemoteOutputReport::asJsonObject() {
return obj;
}
qint64
SWGRemoteOutputReport::getCenterFrequency() {
return center_frequency;
}
void
SWGRemoteOutputReport::setCenterFrequency(qint64 center_frequency) {
this->center_frequency = center_frequency;
this->m_center_frequency_isSet = true;
}
qint32
SWGRemoteOutputReport::getSampleRate() {
return sample_rate;
}
void
SWGRemoteOutputReport::setSampleRate(qint32 sample_rate) {
this->sample_rate = sample_rate;
this->m_sample_rate_isSet = true;
}
float
SWGRemoteOutputReport::getBufferRwBalance() {
return buffer_rw_balance;
@ -118,6 +158,12 @@ bool
SWGRemoteOutputReport::isSet(){
bool isObjectUpdated = false;
do{
if(m_center_frequency_isSet){
isObjectUpdated = true; break;
}
if(m_sample_rate_isSet){
isObjectUpdated = true; break;
}
if(m_buffer_rw_balance_isSet){
isObjectUpdated = true; break;
}

View File

@ -41,6 +41,12 @@ public:
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGRemoteOutputReport* fromJson(QString &jsonString) override;
qint64 getCenterFrequency();
void setCenterFrequency(qint64 center_frequency);
qint32 getSampleRate();
void setSampleRate(qint32 sample_rate);
float getBufferRwBalance();
void setBufferRwBalance(float buffer_rw_balance);
@ -51,6 +57,12 @@ public:
virtual bool isSet() override;
private:
qint64 center_frequency;
bool m_center_frequency_isSet;
qint32 sample_rate;
bool m_sample_rate_isSet;
float buffer_rw_balance;
bool m_buffer_rw_balance_isSet;

View File

@ -28,10 +28,6 @@ SWGRemoteOutputSettings::SWGRemoteOutputSettings(QString* json) {
}
SWGRemoteOutputSettings::SWGRemoteOutputSettings() {
center_frequency = 0L;
m_center_frequency_isSet = false;
sample_rate = 0;
m_sample_rate_isSet = false;
nb_fec_blocks = 0;
m_nb_fec_blocks_isSet = false;
api_address = nullptr;
@ -62,10 +58,6 @@ SWGRemoteOutputSettings::~SWGRemoteOutputSettings() {
void
SWGRemoteOutputSettings::init() {
center_frequency = 0L;
m_center_frequency_isSet = false;
sample_rate = 0;
m_sample_rate_isSet = false;
nb_fec_blocks = 0;
m_nb_fec_blocks_isSet = false;
api_address = new QString("");
@ -93,8 +85,6 @@ SWGRemoteOutputSettings::init() {
void
SWGRemoteOutputSettings::cleanup() {
if(api_address != nullptr) {
delete api_address;
}
@ -124,10 +114,6 @@ SWGRemoteOutputSettings::fromJson(QString &json) {
void
SWGRemoteOutputSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&center_frequency, pJson["centerFrequency"], "qint64", "");
::SWGSDRangel::setValue(&sample_rate, pJson["sampleRate"], "qint32", "");
::SWGSDRangel::setValue(&nb_fec_blocks, pJson["nbFECBlocks"], "qint32", "");
::SWGSDRangel::setValue(&api_address, pJson["apiAddress"], "QString", "QString");
@ -166,12 +152,6 @@ SWGRemoteOutputSettings::asJson ()
QJsonObject*
SWGRemoteOutputSettings::asJsonObject() {
QJsonObject* obj = new QJsonObject();
if(m_center_frequency_isSet){
obj->insert("centerFrequency", QJsonValue(center_frequency));
}
if(m_sample_rate_isSet){
obj->insert("sampleRate", QJsonValue(sample_rate));
}
if(m_nb_fec_blocks_isSet){
obj->insert("nbFECBlocks", QJsonValue(nb_fec_blocks));
}
@ -209,26 +189,6 @@ SWGRemoteOutputSettings::asJsonObject() {
return obj;
}
qint64
SWGRemoteOutputSettings::getCenterFrequency() {
return center_frequency;
}
void
SWGRemoteOutputSettings::setCenterFrequency(qint64 center_frequency) {
this->center_frequency = center_frequency;
this->m_center_frequency_isSet = true;
}
qint32
SWGRemoteOutputSettings::getSampleRate() {
return sample_rate;
}
void
SWGRemoteOutputSettings::setSampleRate(qint32 sample_rate) {
this->sample_rate = sample_rate;
this->m_sample_rate_isSet = true;
}
qint32
SWGRemoteOutputSettings::getNbFecBlocks() {
return nb_fec_blocks;
@ -344,12 +304,6 @@ bool
SWGRemoteOutputSettings::isSet(){
bool isObjectUpdated = false;
do{
if(m_center_frequency_isSet){
isObjectUpdated = true; break;
}
if(m_sample_rate_isSet){
isObjectUpdated = true; break;
}
if(m_nb_fec_blocks_isSet){
isObjectUpdated = true; break;
}

View File

@ -42,12 +42,6 @@ public:
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGRemoteOutputSettings* fromJson(QString &jsonString) override;
qint64 getCenterFrequency();
void setCenterFrequency(qint64 center_frequency);
qint32 getSampleRate();
void setSampleRate(qint32 sample_rate);
qint32 getNbFecBlocks();
void setNbFecBlocks(qint32 nb_fec_blocks);
@ -85,12 +79,6 @@ public:
virtual bool isSet() override;
private:
qint64 center_frequency;
bool m_center_frequency_isSet;
qint32 sample_rate;
bool m_sample_rate_isSet;
qint32 nb_fec_blocks;
bool m_nb_fec_blocks_isSet;

View File

@ -46,7 +46,7 @@ SWGRemoteSourceReport::SWGRemoteSourceReport() {
m_nb_original_blocks_isSet = false;
nb_fec_blocks = 0;
m_nb_fec_blocks_isSet = false;
center_freq = 0;
center_freq = 0L;
m_center_freq_isSet = false;
sample_rate = 0;
m_sample_rate_isSet = false;
@ -80,7 +80,7 @@ SWGRemoteSourceReport::init() {
m_nb_original_blocks_isSet = false;
nb_fec_blocks = 0;
m_nb_fec_blocks_isSet = false;
center_freq = 0;
center_freq = 0L;
m_center_freq_isSet = false;
sample_rate = 0;
m_sample_rate_isSet = false;
@ -136,7 +136,7 @@ SWGRemoteSourceReport::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&nb_fec_blocks, pJson["nbFECBlocks"], "qint32", "");
::SWGSDRangel::setValue(&center_freq, pJson["centerFreq"], "qint32", "");
::SWGSDRangel::setValue(&center_freq, pJson["centerFreq"], "qint64", "");
::SWGSDRangel::setValue(&sample_rate, pJson["sampleRate"], "qint32", "");
@ -293,12 +293,12 @@ SWGRemoteSourceReport::setNbFecBlocks(qint32 nb_fec_blocks) {
this->m_nb_fec_blocks_isSet = true;
}
qint32
qint64
SWGRemoteSourceReport::getCenterFreq() {
return center_freq;
}
void
SWGRemoteSourceReport::setCenterFreq(qint32 center_freq) {
SWGRemoteSourceReport::setCenterFreq(qint64 center_freq) {
this->center_freq = center_freq;
this->m_center_freq_isSet = true;
}

View File

@ -68,8 +68,8 @@ public:
qint32 getNbFecBlocks();
void setNbFecBlocks(qint32 nb_fec_blocks);
qint32 getCenterFreq();
void setCenterFreq(qint32 center_freq);
qint64 getCenterFreq();
void setCenterFreq(qint64 center_freq);
qint32 getSampleRate();
void setSampleRate(qint32 sample_rate);
@ -111,7 +111,7 @@ private:
qint32 nb_fec_blocks;
bool m_nb_fec_blocks_isSet;
qint32 center_freq;
qint64 center_freq;
bool m_center_freq_isSet;
qint32 sample_rate;

View File

@ -36,6 +36,10 @@ SWGRemoteSourceSettings::SWGRemoteSourceSettings() {
m_rgb_color_isSet = false;
title = nullptr;
m_title_isSet = false;
log2_interp = 0;
m_log2_interp_isSet = false;
filter_chain_hash = 0;
m_filter_chain_hash_isSet = false;
stream_index = 0;
m_stream_index_isSet = false;
use_reverse_api = 0;
@ -66,6 +70,10 @@ SWGRemoteSourceSettings::init() {
m_rgb_color_isSet = false;
title = new QString("");
m_title_isSet = false;
log2_interp = 0;
m_log2_interp_isSet = false;
filter_chain_hash = 0;
m_filter_chain_hash_isSet = false;
stream_index = 0;
m_stream_index_isSet = false;
use_reverse_api = 0;
@ -94,6 +102,8 @@ SWGRemoteSourceSettings::cleanup() {
}
if(reverse_api_address != nullptr) {
delete reverse_api_address;
}
@ -124,6 +134,10 @@ SWGRemoteSourceSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString");
::SWGSDRangel::setValue(&log2_interp, pJson["log2Interp"], "qint32", "");
::SWGSDRangel::setValue(&filter_chain_hash, pJson["filterChainHash"], "qint32", "");
::SWGSDRangel::setValue(&stream_index, pJson["streamIndex"], "qint32", "");
::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", "");
@ -166,6 +180,12 @@ SWGRemoteSourceSettings::asJsonObject() {
if(title != nullptr && *title != QString("")){
toJsonValue(QString("title"), title, obj, QString("QString"));
}
if(m_log2_interp_isSet){
obj->insert("log2Interp", QJsonValue(log2_interp));
}
if(m_filter_chain_hash_isSet){
obj->insert("filterChainHash", QJsonValue(filter_chain_hash));
}
if(m_stream_index_isSet){
obj->insert("streamIndex", QJsonValue(stream_index));
}
@ -231,6 +251,26 @@ SWGRemoteSourceSettings::setTitle(QString* title) {
this->m_title_isSet = true;
}
qint32
SWGRemoteSourceSettings::getLog2Interp() {
return log2_interp;
}
void
SWGRemoteSourceSettings::setLog2Interp(qint32 log2_interp) {
this->log2_interp = log2_interp;
this->m_log2_interp_isSet = true;
}
qint32
SWGRemoteSourceSettings::getFilterChainHash() {
return filter_chain_hash;
}
void
SWGRemoteSourceSettings::setFilterChainHash(qint32 filter_chain_hash) {
this->filter_chain_hash = filter_chain_hash;
this->m_filter_chain_hash_isSet = true;
}
qint32
SWGRemoteSourceSettings::getStreamIndex() {
return stream_index;
@ -318,6 +358,12 @@ SWGRemoteSourceSettings::isSet(){
if(title && *title != QString("")){
isObjectUpdated = true; break;
}
if(m_log2_interp_isSet){
isObjectUpdated = true; break;
}
if(m_filter_chain_hash_isSet){
isObjectUpdated = true; break;
}
if(m_stream_index_isSet){
isObjectUpdated = true; break;
}

View File

@ -55,6 +55,12 @@ public:
QString* getTitle();
void setTitle(QString* title);
qint32 getLog2Interp();
void setLog2Interp(qint32 log2_interp);
qint32 getFilterChainHash();
void setFilterChainHash(qint32 filter_chain_hash);
qint32 getStreamIndex();
void setStreamIndex(qint32 stream_index);
@ -92,6 +98,12 @@ private:
QString* title;
bool m_title_isSet;
qint32 log2_interp;
bool m_log2_interp_isSet;
qint32 filter_chain_hash;
bool m_filter_chain_hash_isSet;
qint32 stream_index;
bool m_stream_index_isSet;