mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-25 09:18:54 -05:00
Remote output/source: use queue langth for rate control and derive rate from Tx side. Other fixes
This commit is contained in:
parent
1168eefcc9
commit
54866a1a1e
@ -28,6 +28,8 @@
|
|||||||
#include "SWGRemoteSourceReport.h"
|
#include "SWGRemoteSourceReport.h"
|
||||||
|
|
||||||
#include "dsp/devicesamplesink.h"
|
#include "dsp/devicesamplesink.h"
|
||||||
|
#include "dsp/hbfilterchainconverter.h"
|
||||||
|
#include "dsp/dspcommands.h"
|
||||||
#include "device/deviceapi.h"
|
#include "device/deviceapi.h"
|
||||||
#include "feature/feature.h"
|
#include "feature/feature.h"
|
||||||
#include "settings/serializable.h"
|
#include "settings/serializable.h"
|
||||||
@ -40,13 +42,17 @@
|
|||||||
MESSAGE_CLASS_DEFINITION(RemoteSource::MsgConfigureRemoteSource, Message)
|
MESSAGE_CLASS_DEFINITION(RemoteSource::MsgConfigureRemoteSource, Message)
|
||||||
MESSAGE_CLASS_DEFINITION(RemoteSource::MsgQueryStreamData, Message)
|
MESSAGE_CLASS_DEFINITION(RemoteSource::MsgQueryStreamData, Message)
|
||||||
MESSAGE_CLASS_DEFINITION(RemoteSource::MsgReportStreamData, 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_channelIdURI = "sdrangel.channeltx.remotesource";
|
||||||
const char* const RemoteSource::m_channelId ="RemoteSource";
|
const char* const RemoteSource::m_channelId ="RemoteSource";
|
||||||
|
|
||||||
RemoteSource::RemoteSource(DeviceAPI *deviceAPI) :
|
RemoteSource::RemoteSource(DeviceAPI *deviceAPI) :
|
||||||
ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSource),
|
ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSource),
|
||||||
m_deviceAPI(deviceAPI)
|
m_deviceAPI(deviceAPI),
|
||||||
|
m_centerFrequency(0),
|
||||||
|
m_frequencyOffset(0),
|
||||||
|
m_basebandSampleRate(48000)
|
||||||
{
|
{
|
||||||
setObjectName(m_channelId);
|
setObjectName(m_channelId);
|
||||||
|
|
||||||
@ -96,7 +102,27 @@ void RemoteSource::pull(SampleVector::iterator& begin, unsigned int nbSamples)
|
|||||||
|
|
||||||
bool RemoteSource::handleMessage(const Message& cmd)
|
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;
|
MsgConfigureRemoteSource& cfg = (MsgConfigureRemoteSource&) cmd;
|
||||||
qDebug() << "MsgConfigureRemoteSource::handleMessage: MsgConfigureRemoteSource";
|
qDebug() << "MsgConfigureRemoteSource::handleMessage: MsgConfigureRemoteSource";
|
||||||
@ -158,6 +184,8 @@ bool RemoteSource::deserialize(const QByteArray& data)
|
|||||||
void RemoteSource::applySettings(const RemoteSourceSettings& settings, bool force)
|
void RemoteSource::applySettings(const RemoteSourceSettings& settings, bool force)
|
||||||
{
|
{
|
||||||
qDebug() << "RemoteSource::applySettings:"
|
qDebug() << "RemoteSource::applySettings:"
|
||||||
|
<< "m_log2Interp:" << settings.m_log2Interp
|
||||||
|
<< "m_filterChainHash:" << settings.m_filterChainHash
|
||||||
<< "m_dataAddress:" << settings.m_dataAddress
|
<< "m_dataAddress:" << settings.m_dataAddress
|
||||||
<< "m_dataPort:" << settings.m_dataPort
|
<< "m_dataPort:" << settings.m_dataPort
|
||||||
<< "m_rgbColor:" << settings.m_rgbColor
|
<< "m_rgbColor:" << settings.m_rgbColor
|
||||||
@ -171,14 +199,24 @@ void RemoteSource::applySettings(const RemoteSourceSettings& settings, bool forc
|
|||||||
|
|
||||||
QList<QString> reverseAPIKeys;
|
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) {
|
if ((m_settings.m_dataAddress != settings.m_dataAddress) || force) {
|
||||||
reverseAPIKeys.append("dataAddress");
|
reverseAPIKeys.append("dataAddress");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((m_settings.m_dataPort != settings.m_dataPort) || force) {
|
if ((m_settings.m_dataPort != settings.m_dataPort) || force) {
|
||||||
reverseAPIKeys.append("dataPort");
|
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_settings.m_streamIndex != settings.m_streamIndex)
|
||||||
{
|
{
|
||||||
if (m_deviceAPI->getSampleMIMO()) // change of stream is possible for MIMO devices only
|
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;
|
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(
|
int RemoteSource::webapiSettingsGet(
|
||||||
SWGSDRangel::SWGChannelSettings& response,
|
SWGSDRangel::SWGChannelSettings& response,
|
||||||
QString& errorMessage)
|
QString& errorMessage)
|
||||||
@ -274,6 +329,12 @@ void RemoteSource::webapiUpdateChannelSettings(
|
|||||||
if (channelSettingsKeys.contains("title")) {
|
if (channelSettingsKeys.contains("title")) {
|
||||||
settings.m_title = *response.getRemoteSourceSettings()->getTitle();
|
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")) {
|
if (channelSettingsKeys.contains("streamIndex")) {
|
||||||
settings.m_streamIndex = response.getRemoteSourceSettings()->getStreamIndex();
|
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()->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);
|
response.getRemoteSourceSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
|
||||||
|
|
||||||
if (response.getRemoteSourceSettings()->getReverseApiAddress()) {
|
if (response.getRemoteSourceSettings()->getReverseApiAddress()) {
|
||||||
@ -367,8 +430,9 @@ void RemoteSource::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& resp
|
|||||||
response.getRemoteSourceReport()->setUncorrectableErrorsCount(m_basebandSource->getNbUncorrectableErrors());
|
response.getRemoteSourceReport()->setUncorrectableErrorsCount(m_basebandSource->getNbUncorrectableErrors());
|
||||||
response.getRemoteSourceReport()->setNbOriginalBlocks(currentMeta.m_nbOriginalBlocks);
|
response.getRemoteSourceReport()->setNbOriginalBlocks(currentMeta.m_nbOriginalBlocks);
|
||||||
response.getRemoteSourceReport()->setNbFecBlocks(currentMeta.m_nbFECBlocks);
|
response.getRemoteSourceReport()->setNbFecBlocks(currentMeta.m_nbFECBlocks);
|
||||||
response.getRemoteSourceReport()->setCenterFreq(currentMeta.m_centerFrequency);
|
response.getRemoteSourceReport()->setCenterFreq(m_frequencyOffset);
|
||||||
response.getRemoteSourceReport()->setSampleRate(currentMeta.m_sampleRate);
|
double channelSampleRate = ((double) m_basebandSampleRate) / (1<<m_settings.m_log2Interp);
|
||||||
|
response.getRemoteSourceReport()->setSampleRate(channelSampleRate);
|
||||||
response.getRemoteSourceReport()->setDeviceCenterFreq(m_deviceAPI->getSampleSink()->getCenterFrequency());
|
response.getRemoteSourceReport()->setDeviceCenterFreq(m_deviceAPI->getSampleSink()->getCenterFrequency());
|
||||||
response.getRemoteSourceReport()->setDeviceSampleRate(m_deviceAPI->getSampleSink()->getSampleRate());
|
response.getRemoteSourceReport()->setDeviceSampleRate(m_deviceAPI->getSampleSink()->getSampleRate());
|
||||||
}
|
}
|
||||||
@ -445,6 +509,12 @@ void RemoteSource::webapiFormatChannelSettings(
|
|||||||
if (channelSettingsKeys.contains("rgbColor") || force) {
|
if (channelSettingsKeys.contains("rgbColor") || force) {
|
||||||
swgRemoteSourceSettings->setRgbColor(settings.m_rgbColor);
|
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) {
|
if (channelSettingsKeys.contains("title") || force) {
|
||||||
swgRemoteSourceSettings->setTitle(new QString(settings.m_title));
|
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);
|
RemoteSource(DeviceAPI *deviceAPI);
|
||||||
virtual ~RemoteSource();
|
virtual ~RemoteSource();
|
||||||
|
|
||||||
@ -218,7 +239,13 @@ private:
|
|||||||
QNetworkAccessManager *m_networkManager;
|
QNetworkAccessManager *m_networkManager;
|
||||||
QNetworkRequest m_networkRequest;
|
QNetworkRequest m_networkRequest;
|
||||||
|
|
||||||
|
uint64_t m_centerFrequency;
|
||||||
|
int64_t m_frequencyOffset;
|
||||||
|
uint32_t m_basebandSampleRate;
|
||||||
|
|
||||||
void applySettings(const RemoteSourceSettings& settings, bool force = false);
|
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 webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response);
|
||||||
void webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const RemoteSourceSettings& settings, bool force);
|
void webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const RemoteSourceSettings& settings, bool force);
|
||||||
void sendChannelSettings(
|
void sendChannelSettings(
|
||||||
|
@ -164,6 +164,8 @@ bool RemoteSourceBaseband::handleMessage(const Message& cmd)
|
|||||||
void RemoteSourceBaseband::applySettings(const RemoteSourceSettings& settings, bool force)
|
void RemoteSourceBaseband::applySettings(const RemoteSourceSettings& settings, bool force)
|
||||||
{
|
{
|
||||||
qDebug() << "RemoteSourceBaseband::applySettings:"
|
qDebug() << "RemoteSourceBaseband::applySettings:"
|
||||||
|
<< "m_log2Interp:" << settings.m_log2Interp
|
||||||
|
<< "m_filterChainHash:" << settings.m_filterChainHash
|
||||||
<< "m_dataAddress:" << settings.m_dataAddress
|
<< "m_dataAddress:" << settings.m_dataAddress
|
||||||
<< "m_dataPort:" << settings.m_dataPort
|
<< "m_dataPort:" << settings.m_dataPort
|
||||||
<< "force:" << force;
|
<< "force:" << force;
|
||||||
@ -173,6 +175,12 @@ void RemoteSourceBaseband::applySettings(const RemoteSourceSettings& settings, b
|
|||||||
m_source.dataBind(settings.m_dataAddress, settings.m_dataPort);
|
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;
|
m_settings = settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "device/deviceapi.h"
|
#include "device/deviceapi.h"
|
||||||
#include "device/deviceuiset.h"
|
#include "device/deviceuiset.h"
|
||||||
|
#include "dsp/hbfilterchainconverter.h"
|
||||||
#include "gui/basicchannelsettingsdialog.h"
|
#include "gui/basicchannelsettingsdialog.h"
|
||||||
#include "gui/devicestreamselectiondialog.h"
|
#include "gui/devicestreamselectiondialog.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
@ -65,7 +66,14 @@ bool RemoteSourceGUI::deserialize(const QByteArray& data)
|
|||||||
|
|
||||||
bool RemoteSourceGUI::handleMessage(const Message& message)
|
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;
|
const RemoteSource::MsgConfigureRemoteSource& cfg = (RemoteSource::MsgConfigureRemoteSource&) message;
|
||||||
m_settings = cfg.getSettings();
|
m_settings = cfg.getSettings();
|
||||||
@ -149,6 +157,8 @@ RemoteSourceGUI::RemoteSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet,
|
|||||||
m_pluginAPI(pluginAPI),
|
m_pluginAPI(pluginAPI),
|
||||||
m_deviceUISet(deviceUISet),
|
m_deviceUISet(deviceUISet),
|
||||||
m_remoteSampleRate(48000),
|
m_remoteSampleRate(48000),
|
||||||
|
m_basebandSampleRate(48000),
|
||||||
|
m_shiftFrequencyFactor(0.0),
|
||||||
m_countUnrecoverable(0),
|
m_countUnrecoverable(0),
|
||||||
m_countRecovered(0),
|
m_countRecovered(0),
|
||||||
m_lastCountUnrecoverable(0),
|
m_lastCountUnrecoverable(0),
|
||||||
@ -189,6 +199,8 @@ RemoteSourceGUI::RemoteSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet,
|
|||||||
m_time.start();
|
m_time.start();
|
||||||
|
|
||||||
displaySettings();
|
displaySettings();
|
||||||
|
displayPosition();
|
||||||
|
displayRateAndShift();
|
||||||
applySettings(true);
|
applySettings(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,6 +245,25 @@ void RemoteSourceGUI::displaySettings()
|
|||||||
blockApplySettings(false);
|
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()
|
void RemoteSourceGUI::displayStreamIndex()
|
||||||
{
|
{
|
||||||
if (m_deviceUISet->m_deviceMIMOEngine) {
|
if (m_deviceUISet->m_deviceMIMOEngine) {
|
||||||
@ -319,6 +350,18 @@ void RemoteSourceGUI::onMenuDialogCalled(const QPoint &p)
|
|||||||
resetContextMenuType();
|
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()
|
void RemoteSourceGUI::on_dataAddress_returnPressed()
|
||||||
{
|
{
|
||||||
m_settings.m_dataAddress = ui->dataAddress->text();
|
m_settings.m_dataAddress = ui->dataAddress->text();
|
||||||
@ -364,6 +407,31 @@ void RemoteSourceGUI::on_eventCountsReset_clicked(bool checked)
|
|||||||
displayEventTimer();
|
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()
|
void RemoteSourceGUI::displayEventCounts()
|
||||||
{
|
{
|
||||||
QString nstr = QString("%1").arg(m_countUnrecoverable, 3, 10, QChar('0'));
|
QString nstr = QString("%1").arg(m_countUnrecoverable, 3, 10, QChar('0'));
|
||||||
|
@ -57,7 +57,9 @@ private:
|
|||||||
ChannelMarker m_channelMarker;
|
ChannelMarker m_channelMarker;
|
||||||
RemoteSourceSettings m_settings;
|
RemoteSourceSettings m_settings;
|
||||||
int m_remoteSampleRate;
|
int m_remoteSampleRate;
|
||||||
|
int m_basebandSampleRate;
|
||||||
bool m_doApplySettings;
|
bool m_doApplySettings;
|
||||||
|
double m_shiftFrequencyFactor; //!< Channel frequency shift factor
|
||||||
|
|
||||||
RemoteSource* m_remoteSrc;
|
RemoteSource* m_remoteSrc;
|
||||||
MessageQueue m_inputMessageQueue;
|
MessageQueue m_inputMessageQueue;
|
||||||
@ -78,6 +80,8 @@ private:
|
|||||||
void blockApplySettings(bool block);
|
void blockApplySettings(bool block);
|
||||||
void applySettings(bool force = false);
|
void applySettings(bool force = false);
|
||||||
void displaySettings();
|
void displaySettings();
|
||||||
|
void displayRateAndShift();
|
||||||
|
void displayPosition();
|
||||||
void displayStreamIndex();
|
void displayStreamIndex();
|
||||||
bool handleMessage(const Message& message);
|
bool handleMessage(const Message& message);
|
||||||
|
|
||||||
@ -88,8 +92,13 @@ private:
|
|||||||
void displayEventStatus(int recoverableCount, int unrecoverableCount);
|
void displayEventStatus(int recoverableCount, int unrecoverableCount);
|
||||||
void displayEventTimer();
|
void displayEventTimer();
|
||||||
|
|
||||||
|
void applyInterpolation();
|
||||||
|
void applyPosition();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void handleSourceMessages();
|
void handleSourceMessages();
|
||||||
|
void on_interpolationFactor_currentIndexChanged(int index);
|
||||||
|
void on_position_valueChanged(int value);
|
||||||
void on_dataAddress_returnPressed();
|
void on_dataAddress_returnPressed();
|
||||||
void on_dataPort_returnPressed();
|
void on_dataPort_returnPressed();
|
||||||
void on_dataApplyButton_clicked(bool checked);
|
void on_dataApplyButton_clicked(bool checked);
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>320</width>
|
<width>320</width>
|
||||||
<height>140</height>
|
<height>221</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@ -43,7 +43,7 @@
|
|||||||
<x>10</x>
|
<x>10</x>
|
||||||
<y>10</y>
|
<y>10</y>
|
||||||
<width>301</width>
|
<width>301</width>
|
||||||
<height>121</height>
|
<height>191</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@ -156,6 +156,188 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</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>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="nominalValuesLayout">
|
<layout class="QHBoxLayout" name="nominalValuesLayout">
|
||||||
<item>
|
<item>
|
||||||
|
@ -34,6 +34,8 @@ void RemoteSourceSettings::resetToDefaults()
|
|||||||
m_dataPort = 9090;
|
m_dataPort = 9090;
|
||||||
m_rgbColor = QColor(140, 4, 4).rgb();
|
m_rgbColor = QColor(140, 4, 4).rgb();
|
||||||
m_title = "Remote source";
|
m_title = "Remote source";
|
||||||
|
m_log2Interp = 0;
|
||||||
|
m_filterChainHash = 0;
|
||||||
m_channelMarker = nullptr;
|
m_channelMarker = nullptr;
|
||||||
m_streamIndex = 0;
|
m_streamIndex = 0;
|
||||||
m_useReverseAPI = false;
|
m_useReverseAPI = false;
|
||||||
@ -57,6 +59,8 @@ QByteArray RemoteSourceSettings::serialize() const
|
|||||||
s.writeU32(9, m_reverseAPIChannelIndex);
|
s.writeU32(9, m_reverseAPIChannelIndex);
|
||||||
s.writeS32(10, m_streamIndex);
|
s.writeS32(10, m_streamIndex);
|
||||||
s.writeBlob(11, m_rollupState);
|
s.writeBlob(11, m_rollupState);
|
||||||
|
s.writeU32(12, m_log2Interp);
|
||||||
|
s.writeU32(13, m_filterChainHash);
|
||||||
|
|
||||||
return s.final();
|
return s.final();
|
||||||
}
|
}
|
||||||
@ -103,6 +107,8 @@ bool RemoteSourceSettings::deserialize(const QByteArray& data)
|
|||||||
m_reverseAPIChannelIndex = tmp > 99 ? 99 : tmp;
|
m_reverseAPIChannelIndex = tmp > 99 ? 99 : tmp;
|
||||||
d.readS32(10, &m_streamIndex, 0);
|
d.readS32(10, &m_streamIndex, 0);
|
||||||
d.readBlob(11, &m_rollupState);
|
d.readBlob(11, &m_rollupState);
|
||||||
|
d.readU32(13, &m_filterChainHash, 0);
|
||||||
|
d.readS32(14, &m_streamIndex, 0);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,8 @@ struct RemoteSourceSettings
|
|||||||
uint16_t m_dataPort; //!< Listening data port
|
uint16_t m_dataPort; //!< Listening data port
|
||||||
quint32 m_rgbColor;
|
quint32 m_rgbColor;
|
||||||
QString m_title;
|
QString m_title;
|
||||||
|
uint32_t m_log2Interp;
|
||||||
|
uint32_t m_filterChainHash;
|
||||||
int m_streamIndex;
|
int m_streamIndex;
|
||||||
bool m_useReverseAPI;
|
bool m_useReverseAPI;
|
||||||
QString m_reverseAPIAddress;
|
QString m_reverseAPIAddress;
|
||||||
|
@ -46,8 +46,6 @@ MESSAGE_CLASS_DEFINITION(RemoteOutput::MsgReportRemoteData, Message)
|
|||||||
MESSAGE_CLASS_DEFINITION(RemoteOutput::MsgReportRemoteFixedData, Message)
|
MESSAGE_CLASS_DEFINITION(RemoteOutput::MsgReportRemoteFixedData, Message)
|
||||||
MESSAGE_CLASS_DEFINITION(RemoteOutput::MsgRequestFixedData, Message)
|
MESSAGE_CLASS_DEFINITION(RemoteOutput::MsgRequestFixedData, Message)
|
||||||
|
|
||||||
const uint32_t RemoteOutput::NbSamplesForRateCorrection = 5000000;
|
|
||||||
|
|
||||||
RemoteOutput::RemoteOutput(DeviceAPI *deviceAPI) :
|
RemoteOutput::RemoteOutput(DeviceAPI *deviceAPI) :
|
||||||
m_deviceAPI(deviceAPI),
|
m_deviceAPI(deviceAPI),
|
||||||
m_settings(),
|
m_settings(),
|
||||||
@ -59,15 +57,7 @@ RemoteOutput::RemoteOutput(DeviceAPI *deviceAPI) :
|
|||||||
m_masterTimer(deviceAPI->getMasterTimer()),
|
m_masterTimer(deviceAPI->getMasterTimer()),
|
||||||
m_tickCount(0),
|
m_tickCount(0),
|
||||||
m_greaterTickCount(0),
|
m_greaterTickCount(0),
|
||||||
m_tickMultiplier(1),
|
m_tickMultiplier(1)
|
||||||
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_deviceAPI->setNbSinkStreams(1);
|
m_deviceAPI->setNbSinkStreams(1);
|
||||||
m_networkManager = new QNetworkAccessManager();
|
m_networkManager = new QNetworkAccessManager();
|
||||||
@ -102,14 +92,9 @@ bool RemoteOutput::start()
|
|||||||
m_remoteOutputWorker->connectTimer(m_masterTimer);
|
m_remoteOutputWorker->connectTimer(m_masterTimer);
|
||||||
startWorker();
|
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();
|
mutexLocker.unlock();
|
||||||
//applySettings(m_generalSettings, m_settings, true);
|
applySampleRate();
|
||||||
|
|
||||||
qDebug("RemoteOutput::start: started");
|
qDebug("RemoteOutput::start: started");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -337,9 +322,9 @@ void RemoteOutput::applySampleRate()
|
|||||||
m_remoteOutputWorker->setSamplerate(m_sampleRate);
|
m_remoteOutputWorker->setSamplerate(m_sampleRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_tickMultiplier = (21*NbSamplesForRateCorrection) / (2*m_sampleRate); // two times per sample filling period plus small extension
|
m_tickMultiplier = 480000 / m_sampleRate;
|
||||||
m_tickMultiplier /= 20; // greter tick (one per second)
|
m_tickMultiplier = m_tickMultiplier < 1 ? 1 : m_tickMultiplier > 10 ? 10 : m_tickMultiplier;
|
||||||
m_tickMultiplier = m_tickMultiplier < 1 ? 1 : m_tickMultiplier; // not below 1 second
|
m_greaterTickCount = 0;
|
||||||
|
|
||||||
DSPSignalNotification *notif = new DSPSignalNotification(m_sampleRate, m_centerFrequency);
|
DSPSignalNotification *notif = new DSPSignalNotification(m_sampleRate, m_centerFrequency);
|
||||||
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
|
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
|
||||||
@ -548,7 +533,7 @@ void RemoteOutput::analyzeApiReply(const QJsonObject& jsonObject, const QString&
|
|||||||
{
|
{
|
||||||
MsgReportRemoteData::RemoteData msgRemoteData;
|
MsgReportRemoteData::RemoteData msgRemoteData;
|
||||||
QJsonObject report = jsonObject["RemoteSourceReport"].toObject();
|
QJsonObject report = jsonObject["RemoteSourceReport"].toObject();
|
||||||
uint64_t centerFrequency = report["deviceCenterFreq"].toInt();
|
uint64_t centerFrequency = report["deviceCenterFreq"].toInt() + report["centerFreq"].toInt();
|
||||||
|
|
||||||
if (centerFrequency != m_centerFrequency)
|
if (centerFrequency != m_centerFrequency)
|
||||||
{
|
{
|
||||||
@ -556,7 +541,7 @@ void RemoteOutput::analyzeApiReply(const QJsonObject& jsonObject, const QString&
|
|||||||
applyCenterFrequency();
|
applyCenterFrequency();
|
||||||
}
|
}
|
||||||
|
|
||||||
int remoteRate = report["deviceSampleRate"].toInt();
|
int remoteRate = report["sampleRate"].toInt();
|
||||||
|
|
||||||
if (remoteRate != m_sampleRate)
|
if (remoteRate != m_sampleRate)
|
||||||
{
|
{
|
||||||
@ -572,7 +557,6 @@ void RemoteOutput::analyzeApiReply(const QJsonObject& jsonObject, const QString&
|
|||||||
msgRemoteData.m_queueSize = queueSize;
|
msgRemoteData.m_queueSize = queueSize;
|
||||||
int queueLength = report["queueLength"].toInt();
|
int queueLength = report["queueLength"].toInt();
|
||||||
msgRemoteData.m_queueLength = queueLength;
|
msgRemoteData.m_queueLength = queueLength;
|
||||||
int queueLengthPercent = (queueLength*100)/queueSize;
|
|
||||||
uint64_t remoteTimestampUs = report["tvSec"].toInt()*1000000ULL + report["tvUSec"].toInt();
|
uint64_t remoteTimestampUs = report["tvSec"].toInt()*1000000ULL + report["tvUSec"].toInt();
|
||||||
msgRemoteData.m_timestampUs = remoteTimestampUs;
|
msgRemoteData.m_timestampUs = remoteTimestampUs;
|
||||||
int intRemoteSampleCount = report["samplesCount"].toInt();
|
int intRemoteSampleCount = report["samplesCount"].toInt();
|
||||||
@ -593,63 +577,12 @@ void RemoteOutput::analyzeApiReply(const QJsonObject& jsonObject, const QString&
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (++m_greaterTickCount != m_tickMultiplier) {
|
if (++m_greaterTickCount == m_tickMultiplier)
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t remoteSampleCountDelta;
|
|
||||||
|
|
||||||
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))
|
|
||||||
{
|
{
|
||||||
m_lastRemoteTimestampRateCorrection = remoteTimestampUs;
|
queueLengthCompensation(m_sampleRate, queueLength, queueSize);
|
||||||
m_lastTimestampRateCorrection = timestampUs;
|
|
||||||
m_nbRemoteSamplesSinceRateCorrection = 0;
|
|
||||||
m_nbSamplesSinceRateCorrection = 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;
|
|
||||||
m_greaterTickCount = 0;
|
m_greaterTickCount = 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (jsonObject.contains("remoteOutputSettings"))
|
else if (jsonObject.contains("remoteOutputSettings"))
|
||||||
{
|
{
|
||||||
qDebug("RemoteOutput::analyzeApiReply: reply:\n%s", answer.toStdString().c_str());
|
qDebug("RemoteOutput::analyzeApiReply: reply:\n%s", answer.toStdString().c_str());
|
||||||
@ -685,16 +618,18 @@ void RemoteOutput::analyzeApiReply(const QJsonObject& jsonObject, const QString&
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
int deltaQueueBlocks = (queueSize/2) - queueLength;
|
||||||
double chunkCorr = 50000 * deltaSR; // for 50ms chunk intervals (50000us)
|
int blockMultiplier = nominalSR / 4000;
|
||||||
m_chunkSizeCorrection += roundf(chunkCorr);
|
blockMultiplier = blockMultiplier < 12 ? 12 : blockMultiplier;
|
||||||
|
int corr = deltaQueueBlocks * blockMultiplier;
|
||||||
qDebug("RemoteOutput::sampleRateCorrection: remote: %u / %f us local: %u / %f us corr: %d (%f) samples",
|
qDebug("RemoteOutput::queueLengthCompensation: deltaQueueBlocks: %d corr: %d", deltaQueueBlocks, corr);
|
||||||
remoteSampleCount, remoteTimeDeltaUs, sampleCount, timeDeltaUs, m_chunkSizeCorrection, chunkCorr);
|
MsgConfigureRemoteOutputChunkCorrection* message = MsgConfigureRemoteOutputChunkCorrection::create(corr);
|
||||||
|
|
||||||
MsgConfigureRemoteOutputChunkCorrection* message = MsgConfigureRemoteOutputChunkCorrection::create(m_chunkSizeCorrection);
|
|
||||||
getInputMessageQueue()->push(message);
|
getInputMessageQueue()->push(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,16 +288,6 @@ private:
|
|||||||
QNetworkAccessManager *m_networkManager;
|
QNetworkAccessManager *m_networkManager;
|
||||||
QNetworkRequest m_networkRequest;
|
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 startWorker();
|
||||||
void stopWorker();
|
void stopWorker();
|
||||||
void applySettings(const RemoteOutputSettings& settings, bool force = false);
|
void applySettings(const RemoteOutputSettings& settings, bool force = false);
|
||||||
@ -306,7 +296,11 @@ private:
|
|||||||
void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response);
|
void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response);
|
||||||
|
|
||||||
void analyzeApiReply(const QJsonObject& jsonObject, const QString& answer);
|
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 webapiReverseSendSettings(QList<QString>& deviceSettingsKeys, const RemoteOutputSettings& settings, bool force);
|
||||||
void webapiReverseSendStartStop(bool start);
|
void webapiReverseSendStartStop(bool start);
|
||||||
|
|
||||||
|
@ -91,13 +91,14 @@ void RemoteDataReadQueue::readSample(Sample& s, bool scaleForTx)
|
|||||||
m_sampleIndex = 0;
|
m_sampleIndex = 0;
|
||||||
convertDataToSample(s, m_blockIndex, m_sampleIndex, scaleForTx);
|
convertDataToSample(s, m_blockIndex, m_sampleIndex, scaleForTx);
|
||||||
m_sampleIndex++;
|
m_sampleIndex++;
|
||||||
m_sampleCount++;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
s = Sample{0, 0};
|
s = Sample{0, 0};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_sampleCount++;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10004,6 +10004,12 @@ margin-bottom: 20px;
|
|||||||
"title" : {
|
"title" : {
|
||||||
"type" : "string"
|
"type" : "string"
|
||||||
},
|
},
|
||||||
|
"log2Interp" : {
|
||||||
|
"type" : "integer"
|
||||||
|
},
|
||||||
|
"filterChainHash" : {
|
||||||
|
"type" : "integer"
|
||||||
|
},
|
||||||
"streamIndex" : {
|
"streamIndex" : {
|
||||||
"type" : "integer",
|
"type" : "integer",
|
||||||
"description" : "MIMO channel. Not relevant when connected to SI (single Rx)."
|
"description" : "MIMO channel. Not relevant when connected to SI (single Rx)."
|
||||||
@ -51597,7 +51603,7 @@ except ApiException as e:
|
|||||||
</div>
|
</div>
|
||||||
<div id="generator">
|
<div id="generator">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
Generated 2021-12-12T19:10:03.240+01:00
|
Generated 2021-12-12T22:32:39.234+01:00
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -11,6 +11,10 @@ RemoteSourceSettings:
|
|||||||
type: integer
|
type: integer
|
||||||
title:
|
title:
|
||||||
type: string
|
type: string
|
||||||
|
log2Interp:
|
||||||
|
type: integer
|
||||||
|
filterChainHash:
|
||||||
|
type: integer
|
||||||
streamIndex:
|
streamIndex:
|
||||||
description: MIMO channel. Not relevant when connected to SI (single Rx).
|
description: MIMO channel. Not relevant when connected to SI (single Rx).
|
||||||
type: integer
|
type: integer
|
||||||
|
@ -11,6 +11,10 @@ RemoteSourceSettings:
|
|||||||
type: integer
|
type: integer
|
||||||
title:
|
title:
|
||||||
type: string
|
type: string
|
||||||
|
log2Interp:
|
||||||
|
type: integer
|
||||||
|
filterChainHash:
|
||||||
|
type: integer
|
||||||
streamIndex:
|
streamIndex:
|
||||||
description: MIMO channel. Not relevant when connected to SI (single Rx).
|
description: MIMO channel. Not relevant when connected to SI (single Rx).
|
||||||
type: integer
|
type: integer
|
||||||
|
@ -10004,6 +10004,12 @@ margin-bottom: 20px;
|
|||||||
"title" : {
|
"title" : {
|
||||||
"type" : "string"
|
"type" : "string"
|
||||||
},
|
},
|
||||||
|
"log2Interp" : {
|
||||||
|
"type" : "integer"
|
||||||
|
},
|
||||||
|
"filterChainHash" : {
|
||||||
|
"type" : "integer"
|
||||||
|
},
|
||||||
"streamIndex" : {
|
"streamIndex" : {
|
||||||
"type" : "integer",
|
"type" : "integer",
|
||||||
"description" : "MIMO channel. Not relevant when connected to SI (single Rx)."
|
"description" : "MIMO channel. Not relevant when connected to SI (single Rx)."
|
||||||
@ -51597,7 +51603,7 @@ except ApiException as e:
|
|||||||
</div>
|
</div>
|
||||||
<div id="generator">
|
<div id="generator">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
Generated 2021-12-12T19:10:03.240+01:00
|
Generated 2021-12-12T22:32:39.234+01:00
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -36,6 +36,10 @@ SWGRemoteSourceSettings::SWGRemoteSourceSettings() {
|
|||||||
m_rgb_color_isSet = false;
|
m_rgb_color_isSet = false;
|
||||||
title = nullptr;
|
title = nullptr;
|
||||||
m_title_isSet = false;
|
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;
|
stream_index = 0;
|
||||||
m_stream_index_isSet = false;
|
m_stream_index_isSet = false;
|
||||||
use_reverse_api = 0;
|
use_reverse_api = 0;
|
||||||
@ -66,6 +70,10 @@ SWGRemoteSourceSettings::init() {
|
|||||||
m_rgb_color_isSet = false;
|
m_rgb_color_isSet = false;
|
||||||
title = new QString("");
|
title = new QString("");
|
||||||
m_title_isSet = false;
|
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;
|
stream_index = 0;
|
||||||
m_stream_index_isSet = false;
|
m_stream_index_isSet = false;
|
||||||
use_reverse_api = 0;
|
use_reverse_api = 0;
|
||||||
@ -94,6 +102,8 @@ SWGRemoteSourceSettings::cleanup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if(reverse_api_address != nullptr) {
|
if(reverse_api_address != nullptr) {
|
||||||
delete reverse_api_address;
|
delete reverse_api_address;
|
||||||
}
|
}
|
||||||
@ -124,6 +134,10 @@ SWGRemoteSourceSettings::fromJsonObject(QJsonObject &pJson) {
|
|||||||
|
|
||||||
::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString");
|
::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(&stream_index, pJson["streamIndex"], "qint32", "");
|
||||||
|
|
||||||
::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", "");
|
::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", "");
|
||||||
@ -166,6 +180,12 @@ SWGRemoteSourceSettings::asJsonObject() {
|
|||||||
if(title != nullptr && *title != QString("")){
|
if(title != nullptr && *title != QString("")){
|
||||||
toJsonValue(QString("title"), title, obj, QString("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){
|
if(m_stream_index_isSet){
|
||||||
obj->insert("streamIndex", QJsonValue(stream_index));
|
obj->insert("streamIndex", QJsonValue(stream_index));
|
||||||
}
|
}
|
||||||
@ -231,6 +251,26 @@ SWGRemoteSourceSettings::setTitle(QString* title) {
|
|||||||
this->m_title_isSet = true;
|
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
|
qint32
|
||||||
SWGRemoteSourceSettings::getStreamIndex() {
|
SWGRemoteSourceSettings::getStreamIndex() {
|
||||||
return stream_index;
|
return stream_index;
|
||||||
@ -318,6 +358,12 @@ SWGRemoteSourceSettings::isSet(){
|
|||||||
if(title && *title != QString("")){
|
if(title && *title != QString("")){
|
||||||
isObjectUpdated = true; break;
|
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){
|
if(m_stream_index_isSet){
|
||||||
isObjectUpdated = true; break;
|
isObjectUpdated = true; break;
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,12 @@ public:
|
|||||||
QString* getTitle();
|
QString* getTitle();
|
||||||
void setTitle(QString* title);
|
void setTitle(QString* title);
|
||||||
|
|
||||||
|
qint32 getLog2Interp();
|
||||||
|
void setLog2Interp(qint32 log2_interp);
|
||||||
|
|
||||||
|
qint32 getFilterChainHash();
|
||||||
|
void setFilterChainHash(qint32 filter_chain_hash);
|
||||||
|
|
||||||
qint32 getStreamIndex();
|
qint32 getStreamIndex();
|
||||||
void setStreamIndex(qint32 stream_index);
|
void setStreamIndex(qint32 stream_index);
|
||||||
|
|
||||||
@ -92,6 +98,12 @@ private:
|
|||||||
QString* title;
|
QString* title;
|
||||||
bool m_title_isSet;
|
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;
|
qint32 stream_index;
|
||||||
bool m_stream_index_isSet;
|
bool m_stream_index_isSet;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user