mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-21 23:55:13 -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 "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,6 +184,8 @@ bool RemoteSource::deserialize(const QByteArray& data)
|
||||
void RemoteSource::applySettings(const RemoteSourceSettings& settings, bool force)
|
||||
{
|
||||
qDebug() << "RemoteSource::applySettings:"
|
||||
<< "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
|
||||
@ -171,14 +199,24 @@ void RemoteSource::applySettings(const RemoteSourceSettings& settings, bool forc
|
||||
|
||||
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,8 +430,9 @@ 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()->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,6 +164,8 @@ bool RemoteSourceBaseband::handleMessage(const Message& cmd)
|
||||
void RemoteSourceBaseband::applySettings(const RemoteSourceSettings& settings, bool force)
|
||||
{
|
||||
qDebug() << "RemoteSourceBaseband::applySettings:"
|
||||
<< "m_log2Interp:" << settings.m_log2Interp
|
||||
<< "m_filterChainHash:" << settings.m_filterChainHash
|
||||
<< "m_dataAddress:" << settings.m_dataAddress
|
||||
<< "m_dataPort:" << settings.m_dataPort
|
||||
<< "force:" << force;
|
||||
@ -173,6 +175,12 @@ void RemoteSourceBaseband::applySettings(const RemoteSourceSettings& settings, b
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -46,8 +46,6 @@ MESSAGE_CLASS_DEFINITION(RemoteOutput::MsgReportRemoteData, Message)
|
||||
MESSAGE_CLASS_DEFINITION(RemoteOutput::MsgReportRemoteFixedData, Message)
|
||||
MESSAGE_CLASS_DEFINITION(RemoteOutput::MsgRequestFixedData, Message)
|
||||
|
||||
const uint32_t RemoteOutput::NbSamplesForRateCorrection = 5000000;
|
||||
|
||||
RemoteOutput::RemoteOutput(DeviceAPI *deviceAPI) :
|
||||
m_deviceAPI(deviceAPI),
|
||||
m_settings(),
|
||||
@ -59,15 +57,7 @@ RemoteOutput::RemoteOutput(DeviceAPI *deviceAPI) :
|
||||
m_masterTimer(deviceAPI->getMasterTimer()),
|
||||
m_tickCount(0),
|
||||
m_greaterTickCount(0),
|
||||
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_tickMultiplier(1)
|
||||
{
|
||||
m_deviceAPI->setNbSinkStreams(1);
|
||||
m_networkManager = new QNetworkAccessManager();
|
||||
@ -102,14 +92,9 @@ bool RemoteOutput::start()
|
||||
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;
|
||||
@ -337,9 +322,9 @@ void RemoteOutput::applySampleRate()
|
||||
m_remoteOutputWorker->setSamplerate(m_sampleRate);
|
||||
}
|
||||
|
||||
m_tickMultiplier = (21*NbSamplesForRateCorrection) / (2*m_sampleRate); // two times per sample filling period plus small extension
|
||||
m_tickMultiplier /= 20; // greter tick (one per second)
|
||||
m_tickMultiplier = m_tickMultiplier < 1 ? 1 : m_tickMultiplier; // not below 1 second
|
||||
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);
|
||||
@ -548,7 +533,7 @@ void RemoteOutput::analyzeApiReply(const QJsonObject& jsonObject, const QString&
|
||||
{
|
||||
MsgReportRemoteData::RemoteData msgRemoteData;
|
||||
QJsonObject report = jsonObject["RemoteSourceReport"].toObject();
|
||||
uint64_t centerFrequency = report["deviceCenterFreq"].toInt();
|
||||
uint64_t centerFrequency = report["deviceCenterFreq"].toInt() + report["centerFreq"].toInt();
|
||||
|
||||
if (centerFrequency != m_centerFrequency)
|
||||
{
|
||||
@ -556,7 +541,7 @@ void RemoteOutput::analyzeApiReply(const QJsonObject& jsonObject, const QString&
|
||||
applyCenterFrequency();
|
||||
}
|
||||
|
||||
int remoteRate = report["deviceSampleRate"].toInt();
|
||||
int remoteRate = report["sampleRate"].toInt();
|
||||
|
||||
if (remoteRate != m_sampleRate)
|
||||
{
|
||||
@ -572,7 +557,6 @@ void RemoteOutput::analyzeApiReply(const QJsonObject& jsonObject, const QString&
|
||||
msgRemoteData.m_queueSize = queueSize;
|
||||
int queueLength = report["queueLength"].toInt();
|
||||
msgRemoteData.m_queueLength = queueLength;
|
||||
int queueLengthPercent = (queueLength*100)/queueSize;
|
||||
uint64_t remoteTimestampUs = report["tvSec"].toInt()*1000000ULL + report["tvUSec"].toInt();
|
||||
msgRemoteData.m_timestampUs = remoteTimestampUs;
|
||||
int intRemoteSampleCount = report["samplesCount"].toInt();
|
||||
@ -593,63 +577,12 @@ void RemoteOutput::analyzeApiReply(const QJsonObject& jsonObject, const QString&
|
||||
return;
|
||||
}
|
||||
|
||||
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))
|
||||
if (++m_greaterTickCount == m_tickMultiplier)
|
||||
{
|
||||
m_lastRemoteTimestampRateCorrection = remoteTimestampUs;
|
||||
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;
|
||||
queueLengthCompensation(m_sampleRate, queueLength, queueSize);
|
||||
m_greaterTickCount = 0;
|
||||
}
|
||||
}
|
||||
else if (jsonObject.contains("remoteOutputSettings"))
|
||||
{
|
||||
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);
|
||||
double chunkCorr = 50000 * deltaSR; // for 50ms chunk intervals (50000us)
|
||||
m_chunkSizeCorrection += roundf(chunkCorr);
|
||||
|
||||
qDebug("RemoteOutput::sampleRateCorrection: remote: %u / %f us local: %u / %f us corr: %d (%f) samples",
|
||||
remoteSampleCount, remoteTimeDeltaUs, sampleCount, timeDeltaUs, 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);
|
||||
}
|
||||
|
||||
|
@ -288,16 +288,6 @@ private:
|
||||
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);
|
||||
@ -306,7 +296,11 @@ private:
|
||||
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);
|
||||
|
||||
|
@ -91,13 +91,14 @@ void RemoteDataReadQueue::readSample(Sample& s, bool scaleForTx)
|
||||
m_sampleIndex = 0;
|
||||
convertDataToSample(s, m_blockIndex, m_sampleIndex, scaleForTx);
|
||||
m_sampleIndex++;
|
||||
m_sampleCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
s = Sample{0, 0};
|
||||
}
|
||||
|
||||
m_sampleCount++;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -10004,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)."
|
||||
@ -51597,7 +51603,7 @@ except ApiException as e:
|
||||
</div>
|
||||
<div id="generator">
|
||||
<div class="content">
|
||||
Generated 2021-12-12T19:10:03.240+01:00
|
||||
Generated 2021-12-12T22:32:39.234+01:00
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -10004,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)."
|
||||
@ -51597,7 +51603,7 @@ except ApiException as e:
|
||||
</div>
|
||||
<div id="generator">
|
||||
<div class="content">
|
||||
Generated 2021-12-12T19:10:03.240+01:00
|
||||
Generated 2021-12-12T22:32:39.234+01:00
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -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