mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-25 01:18:38 -05:00
Compare commits
6 Commits
8715f4c037
...
54866a1a1e
Author | SHA1 | Date | |
---|---|---|---|
|
54866a1a1e | ||
|
1168eefcc9 | ||
|
56ba6f3d80 | ||
|
a5948c8b24 | ||
|
96411edd3c | ||
|
f7f5f4b2dd |
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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++;
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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'));
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -22,8 +22,6 @@
|
||||
#include <QString>
|
||||
|
||||
struct RemoteOutputSettings {
|
||||
quint64 m_centerFrequency;
|
||||
quint32 m_sampleRate;
|
||||
quint32 m_nbFECBlocks;
|
||||
QString m_apiAddress;
|
||||
quint16 m_apiPort;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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++;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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_ */
|
||||
|
@ -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};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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(¢er_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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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(¢er_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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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(¢er_freq, pJson["centerFreq"], "qint32", "");
|
||||
::SWGSDRangel::setValue(¢er_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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user