mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-17 13:51:47 -05:00
Merge pull request #797 from srcejon/chan_an_costas_loop
Add Costas loop PLL to channel analyzer
This commit is contained in:
commit
7b13abe0d8
@ -139,7 +139,11 @@ void ChannelAnalyzer::applySettings(const ChannelAnalyzerSettings& settings, boo
|
|||||||
<< " m_ssb: " << settings.m_ssb
|
<< " m_ssb: " << settings.m_ssb
|
||||||
<< " m_pll: " << settings.m_pll
|
<< " m_pll: " << settings.m_pll
|
||||||
<< " m_fll: " << settings.m_fll
|
<< " m_fll: " << settings.m_fll
|
||||||
|
<< " m_costasLoop: " << settings.m_costasLoop
|
||||||
<< " m_pllPskOrder: " << settings.m_pllPskOrder
|
<< " m_pllPskOrder: " << settings.m_pllPskOrder
|
||||||
|
<< " m_pllBandwidth: " << settings.m_pllBandwidth
|
||||||
|
<< " m_pllDampingFactor: " << settings.m_pllDampingFactor
|
||||||
|
<< " m_pllLoopGain: " << settings.m_pllLoopGain
|
||||||
<< " m_inputType: " << (int) settings.m_inputType;
|
<< " m_inputType: " << (int) settings.m_inputType;
|
||||||
|
|
||||||
ChannelAnalyzerBaseband::MsgConfigureChannelAnalyzerBaseband *msg
|
ChannelAnalyzerBaseband::MsgConfigureChannelAnalyzerBaseband *msg
|
||||||
|
@ -103,18 +103,85 @@ void ChannelAnalyzerGUI::displaySettings()
|
|||||||
|
|
||||||
void ChannelAnalyzerGUI::displayPLLSettings()
|
void ChannelAnalyzerGUI::displayPLLSettings()
|
||||||
{
|
{
|
||||||
if (m_settings.m_fll)
|
if (m_settings.m_costasLoop)
|
||||||
{
|
ui->pllType->setCurrentIndex(2);
|
||||||
ui->pllPskOrder->setCurrentIndex(5);
|
else if (m_settings.m_fll)
|
||||||
}
|
ui->pllType->setCurrentIndex(1);
|
||||||
else
|
else
|
||||||
{
|
ui->pllType->setCurrentIndex(0);
|
||||||
|
setPLLVisibility();
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for(; ((m_settings.m_pllPskOrder>>i) & 1) == 0; i++);
|
for(; ((m_settings.m_pllPskOrder>>i) & 1) == 0; i++);
|
||||||
|
if (m_settings.m_costasLoop)
|
||||||
|
ui->pllPskOrder->setCurrentIndex(i==0 ? 0 : i-1);
|
||||||
|
else
|
||||||
ui->pllPskOrder->setCurrentIndex(i);
|
ui->pllPskOrder->setCurrentIndex(i);
|
||||||
}
|
|
||||||
|
|
||||||
ui->pll->setChecked(m_settings.m_pll);
|
ui->pll->setChecked(m_settings.m_pll);
|
||||||
|
ui->pllBandwidth->setValue((int)(m_settings.m_pllBandwidth*1000.0));
|
||||||
|
QString bandwidthStr = QString::number(m_settings.m_pllBandwidth, 'f', 3);
|
||||||
|
ui->pllBandwidthText->setText(bandwidthStr);
|
||||||
|
ui->pllDampingFactor->setValue((int)(m_settings.m_pllDampingFactor*10.0));
|
||||||
|
QString factorStr = QString::number(m_settings.m_pllDampingFactor, 'f', 1);
|
||||||
|
ui->pllDampingFactorText->setText(factorStr);
|
||||||
|
ui->pllLoopGain->setValue((int)(m_settings.m_pllLoopGain));
|
||||||
|
QString gainStr = QString::number(m_settings.m_pllLoopGain, 'f', 0);
|
||||||
|
ui->pllLoopGainText->setText(gainStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChannelAnalyzerGUI::setPLLVisibility()
|
||||||
|
{
|
||||||
|
ui->pllToolbar->setVisible(m_settings.m_pll);
|
||||||
|
|
||||||
|
// BW
|
||||||
|
ui->pllPskOrder->setVisible(!m_settings.m_fll);
|
||||||
|
ui->pllLine1->setVisible(!m_settings.m_fll);
|
||||||
|
ui->pllBandwidthLabel->setVisible(!m_settings.m_fll);
|
||||||
|
ui->pllBandwidth->setVisible(!m_settings.m_fll);
|
||||||
|
ui->pllBandwidthText->setVisible(!m_settings.m_fll);
|
||||||
|
ui->pllLine2->setVisible(!m_settings.m_fll);
|
||||||
|
|
||||||
|
// Damping factor and gain
|
||||||
|
bool stdPll = !m_settings.m_fll && !m_settings.m_costasLoop;
|
||||||
|
ui->pllDamplingFactor->setVisible(stdPll);
|
||||||
|
ui->pllDampingFactor->setVisible(stdPll);
|
||||||
|
ui->pllDampingFactorText->setVisible(stdPll);
|
||||||
|
ui->pllLine3->setVisible(stdPll);
|
||||||
|
ui->pllLoopGainLabel->setVisible(stdPll);
|
||||||
|
ui->pllLoopGain->setVisible(stdPll);
|
||||||
|
ui->pllLoopGainText->setVisible(stdPll);
|
||||||
|
ui->pllLine4->setVisible(stdPll);
|
||||||
|
|
||||||
|
// Order
|
||||||
|
ui->pllPskOrder->blockSignals(true);
|
||||||
|
ui->pllPskOrder->clear();
|
||||||
|
if (stdPll)
|
||||||
|
{
|
||||||
|
ui->pllPskOrder->addItem("CW");
|
||||||
|
ui->pllPskOrder->addItem("BPSK");
|
||||||
|
ui->pllPskOrder->addItem("QPSK");
|
||||||
|
ui->pllPskOrder->addItem("8PSK");
|
||||||
|
ui->pllPskOrder->addItem("16PSK");
|
||||||
|
}
|
||||||
|
else if (m_settings.m_costasLoop)
|
||||||
|
{
|
||||||
|
ui->pllPskOrder->addItem("BPSK");
|
||||||
|
ui->pllPskOrder->addItem("QPSK");
|
||||||
|
ui->pllPskOrder->addItem("8PSK");
|
||||||
|
if (m_settings.m_pllPskOrder < 2)
|
||||||
|
m_settings.m_pllPskOrder = 2;
|
||||||
|
else if (m_settings.m_pllPskOrder > 8)
|
||||||
|
m_settings.m_pllPskOrder = 8;
|
||||||
|
}
|
||||||
|
int i = 0;
|
||||||
|
for(; ((m_settings.m_pllPskOrder>>i) & 1) == 0; i++);
|
||||||
|
if (m_settings.m_costasLoop)
|
||||||
|
ui->pllPskOrder->setCurrentIndex(i==0 ? 0 : i-1);
|
||||||
|
else
|
||||||
|
ui->pllPskOrder->setCurrentIndex(i);
|
||||||
|
ui->pllPskOrder->blockSignals(false);
|
||||||
|
arrangeRollups();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChannelAnalyzerGUI::setSpectrumDisplay()
|
void ChannelAnalyzerGUI::setSpectrumDisplay()
|
||||||
@ -212,9 +279,10 @@ void ChannelAnalyzerGUI::tick()
|
|||||||
|
|
||||||
if (ui->pll->isChecked())
|
if (ui->pll->isChecked())
|
||||||
{
|
{
|
||||||
double sampleRate = ((double) m_channelAnalyzer->getChannelSampleRate()) / m_channelAnalyzer->getDecimation();
|
double sampleRate = (double) m_channelAnalyzer->getChannelSampleRate();
|
||||||
int freq = (m_channelAnalyzer->getPllFrequency() * sampleRate) / (2.0*M_PI);
|
int freq = (m_channelAnalyzer->getPllFrequency() * sampleRate) / (2.0*M_PI);
|
||||||
ui->pll->setToolTip(tr("PLL lock. Freq = %1 Hz").arg(freq));
|
ui->pll->setToolTip(tr("PLL lock. Freq = %1 Hz").arg(freq));
|
||||||
|
ui->pllLockFrequency->setText(tr("%1 Hz").arg(freq));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,16 +300,48 @@ void ChannelAnalyzerGUI::on_pll_toggled(bool checked)
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_settings.m_pll = checked;
|
m_settings.m_pll = checked;
|
||||||
|
setPLLVisibility();
|
||||||
|
applySettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChannelAnalyzerGUI::on_pllType_currentIndexChanged(int index)
|
||||||
|
{
|
||||||
|
m_settings.m_fll = (index == 1);
|
||||||
|
m_settings.m_costasLoop = (index == 2);
|
||||||
|
setPLLVisibility();
|
||||||
applySettings();
|
applySettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChannelAnalyzerGUI::on_pllPskOrder_currentIndexChanged(int index)
|
void ChannelAnalyzerGUI::on_pllPskOrder_currentIndexChanged(int index)
|
||||||
{
|
{
|
||||||
if (index < 5) {
|
if (m_settings.m_costasLoop)
|
||||||
|
m_settings.m_pllPskOrder = (1<<(index+1));
|
||||||
|
else
|
||||||
m_settings.m_pllPskOrder = (1<<index);
|
m_settings.m_pllPskOrder = (1<<index);
|
||||||
|
applySettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_settings.m_fll = (index == 5);
|
void ChannelAnalyzerGUI::on_pllBandwidth_valueChanged(int value)
|
||||||
|
{
|
||||||
|
m_settings.m_pllBandwidth = value/1000.0;
|
||||||
|
QString bandwidthStr = QString::number(m_settings.m_pllBandwidth, 'f', 3);
|
||||||
|
ui->pllBandwidthText->setText(bandwidthStr);
|
||||||
|
applySettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChannelAnalyzerGUI::on_pllDampingFactor_valueChanged(int value)
|
||||||
|
{
|
||||||
|
m_settings.m_pllDampingFactor = value/10.0;
|
||||||
|
QString factorStr = QString::number(m_settings.m_pllDampingFactor, 'f', 1);
|
||||||
|
ui->pllDampingFactorText->setText(factorStr);
|
||||||
|
applySettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChannelAnalyzerGUI::on_pllLoopGain_valueChanged(int value)
|
||||||
|
{
|
||||||
|
m_settings.m_pllLoopGain = value;
|
||||||
|
QString gainStr = QString::number(m_settings.m_pllLoopGain, 'f', 0);
|
||||||
|
ui->pllLoopGainText->setText(gainStr);
|
||||||
applySettings();
|
applySettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +81,7 @@ private:
|
|||||||
void applySettings(bool force = false);
|
void applySettings(bool force = false);
|
||||||
void displaySettings();
|
void displaySettings();
|
||||||
void displayPLLSettings();
|
void displayPLLSettings();
|
||||||
|
void setPLLVisibility();
|
||||||
void setSpectrumDisplay();
|
void setSpectrumDisplay();
|
||||||
bool handleMessage(const Message& message);
|
bool handleMessage(const Message& message);
|
||||||
|
|
||||||
@ -91,7 +92,11 @@ private slots:
|
|||||||
void on_deltaFrequency_changed(qint64 value);
|
void on_deltaFrequency_changed(qint64 value);
|
||||||
void on_rationalDownSamplerRate_changed(quint64 value);
|
void on_rationalDownSamplerRate_changed(quint64 value);
|
||||||
void on_pll_toggled(bool checked);
|
void on_pll_toggled(bool checked);
|
||||||
|
void on_pllType_currentIndexChanged(int index);
|
||||||
void on_pllPskOrder_currentIndexChanged(int index);
|
void on_pllPskOrder_currentIndexChanged(int index);
|
||||||
|
void on_pllBandwidth_valueChanged(int value);
|
||||||
|
void on_pllDampingFactor_valueChanged(int value);
|
||||||
|
void on_pllLoopGain_valueChanged(int value);
|
||||||
void on_useRationalDownsampler_toggled(bool checked);
|
void on_useRationalDownsampler_toggled(bool checked);
|
||||||
void on_signalSelect_currentIndexChanged(int index);
|
void on_signalSelect_currentIndexChanged(int index);
|
||||||
void on_rrcFilter_toggled(bool checked);
|
void on_rrcFilter_toggled(bool checked);
|
||||||
|
@ -29,9 +29,9 @@
|
|||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>10</y>
|
<y>0</y>
|
||||||
<width>631</width>
|
<width>524</width>
|
||||||
<height>81</height>
|
<height>101</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@ -335,49 +335,6 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<widget class="QComboBox" name="pllPskOrder">
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>PLL PSK order (1 for CW)</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>F</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
@ -592,6 +549,274 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="pllToolbar" native="true">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="pllType">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>PLL type</string>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>PLL</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>FLL</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Costas Loop</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="pllPskOrder">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>70</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>70</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>PLL PSK order (1 for CW)</string>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>CW</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>BPSK</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>QPSK</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>8PSK</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>16PSK</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Line" name="pllLine1">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="pllBandwidthLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>BW</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDial" name="pllBandwidth">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>24</width>
|
||||||
|
<height>24</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>PLL loop bandwidth</string>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>100</number>
|
||||||
|
</property>
|
||||||
|
<property name="pageStep">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="pllBandwidthText">
|
||||||
|
<property name="text">
|
||||||
|
<string>0.002</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Line" name="pllLine2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="pllDamplingFactor">
|
||||||
|
<property name="text">
|
||||||
|
<string>D</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDial" name="pllDampingFactor">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>24</width>
|
||||||
|
<height>24</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>PLL damping factor</string>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
<property name="pageStep">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="pllDampingFactorText">
|
||||||
|
<property name="text">
|
||||||
|
<string>0.5</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Line" name="pllLine3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="pllLoopGainLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>G</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDial" name="pllLoopGain">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>24</width>
|
||||||
|
<height>24</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>PLL loop gain</string>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>1000</number>
|
||||||
|
</property>
|
||||||
|
<property name="pageStep">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="pllLoopGainText">
|
||||||
|
<property name="text">
|
||||||
|
<string>10</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Line" name="pllLine4">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="pllHorizontalSpacer">
|
||||||
|
<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="pllLockFrequencyLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Freq</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="pllLockFrequency">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>60</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>PLL lock frequency</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>-100000Hz</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="spectrumContainer" native="true">
|
<widget class="QWidget" name="spectrumContainer" native="true">
|
||||||
|
@ -42,9 +42,13 @@ void ChannelAnalyzerSettings::resetToDefaults()
|
|||||||
m_ssb = false;
|
m_ssb = false;
|
||||||
m_pll = false;
|
m_pll = false;
|
||||||
m_fll = false;
|
m_fll = false;
|
||||||
|
m_costasLoop = false;
|
||||||
m_rrc = false;
|
m_rrc = false;
|
||||||
m_rrcRolloff = 35; // 0.35
|
m_rrcRolloff = 35; // 0.35
|
||||||
m_pllPskOrder = 1;
|
m_pllPskOrder = 1;
|
||||||
|
m_pllBandwidth = 0.002f;
|
||||||
|
m_pllDampingFactor = 0.5f;
|
||||||
|
m_pllLoopGain = 10.0f;
|
||||||
m_inputType = InputSignal;
|
m_inputType = InputSignal;
|
||||||
m_rgbColor = QColor(128, 128, 128).rgb();
|
m_rgbColor = QColor(128, 128, 128).rgb();
|
||||||
m_title = "Channel Analyzer";
|
m_title = "Channel Analyzer";
|
||||||
@ -71,6 +75,10 @@ QByteArray ChannelAnalyzerSettings::serialize() const
|
|||||||
s.writeString(15, m_title);
|
s.writeString(15, m_title);
|
||||||
s.writeBool(16, m_rrc);
|
s.writeBool(16, m_rrc);
|
||||||
s.writeU32(17, m_rrcRolloff);
|
s.writeU32(17, m_rrcRolloff);
|
||||||
|
s.writeFloat(18, m_pllBandwidth);
|
||||||
|
s.writeFloat(19, m_pllDampingFactor);
|
||||||
|
s.writeFloat(20, m_pllLoopGain);
|
||||||
|
s.writeBool(21, m_costasLoop);
|
||||||
|
|
||||||
return s.final();
|
return s.final();
|
||||||
}
|
}
|
||||||
@ -118,6 +126,10 @@ bool ChannelAnalyzerSettings::deserialize(const QByteArray& data)
|
|||||||
d.readString(15, &m_title, "Channel Analyzer");
|
d.readString(15, &m_title, "Channel Analyzer");
|
||||||
d.readBool(16, &m_rrc, false);
|
d.readBool(16, &m_rrc, false);
|
||||||
d.readU32(17, &m_rrcRolloff, 35);
|
d.readU32(17, &m_rrcRolloff, 35);
|
||||||
|
d.readFloat(18, &m_pllBandwidth, 0.002f);
|
||||||
|
d.readFloat(19, &m_pllDampingFactor, 0.5f);
|
||||||
|
d.readFloat(20, &m_pllLoopGain, 10.0f);
|
||||||
|
d.readBool(21, &m_costasLoop, false);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -40,9 +40,13 @@ struct ChannelAnalyzerSettings
|
|||||||
bool m_ssb;
|
bool m_ssb;
|
||||||
bool m_pll;
|
bool m_pll;
|
||||||
bool m_fll;
|
bool m_fll;
|
||||||
|
bool m_costasLoop;
|
||||||
bool m_rrc;
|
bool m_rrc;
|
||||||
quint32 m_rrcRolloff; //!< in 100ths
|
quint32 m_rrcRolloff; //!< in 100ths
|
||||||
unsigned int m_pllPskOrder;
|
unsigned int m_pllPskOrder;
|
||||||
|
float m_pllBandwidth;
|
||||||
|
float m_pllDampingFactor;
|
||||||
|
float m_pllLoopGain;
|
||||||
InputType m_inputType;
|
InputType m_inputType;
|
||||||
quint32 m_rgbColor;
|
quint32 m_rgbColor;
|
||||||
QString m_title;
|
QString m_title;
|
||||||
|
@ -30,6 +30,7 @@ ChannelAnalyzerSink::ChannelAnalyzerSink() :
|
|||||||
m_channelSampleRate(48000),
|
m_channelSampleRate(48000),
|
||||||
m_channelFrequencyOffset(0),
|
m_channelFrequencyOffset(0),
|
||||||
m_sinkSampleRate(48000),
|
m_sinkSampleRate(48000),
|
||||||
|
m_costasLoop(0.002, 2),
|
||||||
m_sampleSink(nullptr)
|
m_sampleSink(nullptr)
|
||||||
{
|
{
|
||||||
m_usb = true;
|
m_usb = true;
|
||||||
@ -38,7 +39,8 @@ ChannelAnalyzerSink::ChannelAnalyzerSink() :
|
|||||||
DSBFilter = new fftfilt(m_settings.m_bandwidth / m_channelSampleRate, 2*m_ssbFftLen);
|
DSBFilter = new fftfilt(m_settings.m_bandwidth / m_channelSampleRate, 2*m_ssbFftLen);
|
||||||
RRCFilter = new fftfilt(m_settings.m_bandwidth / m_channelSampleRate, 2*m_ssbFftLen);
|
RRCFilter = new fftfilt(m_settings.m_bandwidth / m_channelSampleRate, 2*m_ssbFftLen);
|
||||||
m_corr = new fftcorr(2*m_corrFFTLen); // 8k for 4k effective samples
|
m_corr = new fftcorr(2*m_corrFFTLen); // 8k for 4k effective samples
|
||||||
m_pll.computeCoefficients(0.002f, 0.5f, 10.0f); // bandwidth, damping factor, loop gain
|
m_pll.computeCoefficients(m_settings.m_pllBandwidth, m_settings.m_pllDampingFactor, m_settings.m_pllLoopGain);
|
||||||
|
m_costasLoop.computeCoefficients(m_settings.m_pllBandwidth);
|
||||||
|
|
||||||
applyChannelSettings(m_channelSampleRate, m_sinkSampleRate, m_channelFrequencyOffset, true);
|
applyChannelSettings(m_channelSampleRate, m_sinkSampleRate, m_channelFrequencyOffset, true);
|
||||||
applySettings(m_settings, true);
|
applySettings(m_settings, true);
|
||||||
@ -123,21 +125,28 @@ void ChannelAnalyzerSink::processOneSample(Complex& c, fftfilt::cmplx *sideband)
|
|||||||
|
|
||||||
if (m_settings.m_pll)
|
if (m_settings.m_pll)
|
||||||
{
|
{
|
||||||
if (m_settings.m_fll)
|
// Use -fPLL to mix (exchange PLL real and image in the complex multiplication)
|
||||||
|
if (m_settings.m_costasLoop)
|
||||||
|
{
|
||||||
|
m_costasLoop.feed(re, im);
|
||||||
|
mix = si * std::conj(m_costasLoop.getComplex());
|
||||||
|
feedOneSample(mix, m_costasLoop.getComplex());
|
||||||
|
}
|
||||||
|
else if (m_settings.m_fll)
|
||||||
{
|
{
|
||||||
m_fll.feed(re, im);
|
m_fll.feed(re, im);
|
||||||
// Use -fPLL to mix (exchange PLL real and image in the complex multiplication)
|
|
||||||
mix = si * std::conj(m_fll.getComplex());
|
mix = si * std::conj(m_fll.getComplex());
|
||||||
|
feedOneSample(mix, m_fll.getComplex());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_pll.feed(re, im);
|
m_pll.feed(re, im);
|
||||||
// Use -fPLL to mix (exchange PLL real and image in the complex multiplication)
|
|
||||||
mix = si * std::conj(m_pll.getComplex());
|
mix = si * std::conj(m_pll.getComplex());
|
||||||
|
feedOneSample(mix, m_pll.getComplex());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
feedOneSample(m_settings.m_pll ? mix : si, m_settings.m_fll ? m_fll.getComplex() : m_pll.getComplex());
|
feedOneSample(si, si);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,7 +239,11 @@ void ChannelAnalyzerSink::applySettings(const ChannelAnalyzerSettings& settings,
|
|||||||
<< " m_ssb: " << settings.m_ssb
|
<< " m_ssb: " << settings.m_ssb
|
||||||
<< " m_pll: " << settings.m_pll
|
<< " m_pll: " << settings.m_pll
|
||||||
<< " m_fll: " << settings.m_fll
|
<< " m_fll: " << settings.m_fll
|
||||||
|
<< " m_costasLoop: " << settings.m_costasLoop
|
||||||
<< " m_pllPskOrder: " << settings.m_pllPskOrder
|
<< " m_pllPskOrder: " << settings.m_pllPskOrder
|
||||||
|
<< " m_pllBandwidth: " << settings.m_pllBandwidth
|
||||||
|
<< " m_pllDampingFactor: " << settings.m_pllDampingFactor
|
||||||
|
<< " m_pllLoopGain: " << settings.m_pllLoopGain
|
||||||
<< " m_inputType: " << (int) settings.m_inputType;
|
<< " m_inputType: " << (int) settings.m_inputType;
|
||||||
bool doApplySampleRate = false;
|
bool doApplySampleRate = false;
|
||||||
|
|
||||||
@ -247,6 +260,7 @@ void ChannelAnalyzerSink::applySettings(const ChannelAnalyzerSettings& settings,
|
|||||||
{
|
{
|
||||||
m_pll.reset();
|
m_pll.reset();
|
||||||
m_fll.reset();
|
m_fll.reset();
|
||||||
|
m_costasLoop.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,11 +271,30 @@ void ChannelAnalyzerSink::applySettings(const ChannelAnalyzerSettings& settings,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (settings.m_costasLoop != m_settings.m_costasLoop || force)
|
||||||
|
{
|
||||||
|
if (settings.m_costasLoop) {
|
||||||
|
m_costasLoop.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (settings.m_pllPskOrder != m_settings.m_pllPskOrder || force)
|
if (settings.m_pllPskOrder != m_settings.m_pllPskOrder || force)
|
||||||
{
|
{
|
||||||
if (settings.m_pllPskOrder < 32) {
|
if (settings.m_pllPskOrder < 32) {
|
||||||
m_pll.setPskOrder(settings.m_pllPskOrder);
|
m_pll.setPskOrder(settings.m_pllPskOrder);
|
||||||
}
|
}
|
||||||
|
if (settings.m_pllPskOrder < 16) {
|
||||||
|
m_costasLoop.setPskOrder(settings.m_pllPskOrder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((settings.m_pllBandwidth != m_settings.m_pllBandwidth)
|
||||||
|
|| (settings.m_pllDampingFactor != m_settings.m_pllDampingFactor)
|
||||||
|
|| (settings.m_pllLoopGain != m_settings.m_pllLoopGain)
|
||||||
|
|| force)
|
||||||
|
{
|
||||||
|
m_pll.computeCoefficients(settings.m_pllBandwidth, settings.m_pllDampingFactor, settings.m_pllLoopGain);
|
||||||
|
m_costasLoop.computeCoefficients(settings.m_pllBandwidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((settings.m_rationalDownSample != m_settings.m_rationalDownSample) ||
|
if ((settings.m_rationalDownSample != m_settings.m_rationalDownSample) ||
|
||||||
@ -280,15 +313,42 @@ void ChannelAnalyzerSink::applySettings(const ChannelAnalyzerSettings& settings,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ChannelAnalyzerSink::isPllLocked() const
|
||||||
|
{
|
||||||
|
if (m_settings.m_pll)
|
||||||
|
return m_pll.locked();
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Real ChannelAnalyzerSink::getPllFrequency() const
|
Real ChannelAnalyzerSink::getPllFrequency() const
|
||||||
{
|
{
|
||||||
if (m_settings.m_fll) {
|
if (m_settings.m_costasLoop)
|
||||||
|
return m_costasLoop.getFreq();
|
||||||
|
else if (m_settings.m_fll)
|
||||||
return m_fll.getFreq();
|
return m_fll.getFreq();
|
||||||
} else if (m_settings.m_pll) {
|
else if (m_settings.m_pll)
|
||||||
return m_pll.getFreq();
|
return m_pll.getFreq();
|
||||||
} else {
|
else
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Real ChannelAnalyzerSink::getPllPhase() const
|
||||||
|
{
|
||||||
|
if (m_settings.m_costasLoop)
|
||||||
|
return m_costasLoop.getPhiHat();
|
||||||
|
else if (m_settings.m_pll)
|
||||||
|
return m_pll.getPhiHat();
|
||||||
|
else
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
Real ChannelAnalyzerSink::getPllDeltaPhase() const
|
||||||
|
{
|
||||||
|
if (m_settings.m_pll)
|
||||||
|
return m_pll.getDeltaPhi();
|
||||||
|
else
|
||||||
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ChannelAnalyzerSink::getActualSampleRate()
|
int ChannelAnalyzerSink::getActualSampleRate()
|
||||||
@ -307,5 +367,6 @@ void ChannelAnalyzerSink::applySampleRate()
|
|||||||
setFilters(sampleRate, m_settings.m_bandwidth, m_settings.m_lowCutoff);
|
setFilters(sampleRate, m_settings.m_bandwidth, m_settings.m_lowCutoff);
|
||||||
m_pll.setSampleRate(sampleRate);
|
m_pll.setSampleRate(sampleRate);
|
||||||
m_fll.setSampleRate(sampleRate);
|
m_fll.setSampleRate(sampleRate);
|
||||||
|
m_costasLoop.setSampleRate(sampleRate);
|
||||||
RRCFilter->create_rrc_filter(m_settings.m_bandwidth / (float) sampleRate, m_settings.m_rrcRolloff / 100.0);
|
RRCFilter->create_rrc_filter(m_settings.m_bandwidth / (float) sampleRate, m_settings.m_rrcRolloff / 100.0);
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "dsp/fftfilt.h"
|
#include "dsp/fftfilt.h"
|
||||||
#include "dsp/phaselockcomplex.h"
|
#include "dsp/phaselockcomplex.h"
|
||||||
#include "dsp/freqlockcomplex.h"
|
#include "dsp/freqlockcomplex.h"
|
||||||
|
#include "dsp/costasloop.h"
|
||||||
#include "audio/audiofifo.h"
|
#include "audio/audiofifo.h"
|
||||||
|
|
||||||
#include "util/movingaverage.h"
|
#include "util/movingaverage.h"
|
||||||
@ -46,10 +47,10 @@ public:
|
|||||||
|
|
||||||
double getMagSq() const { return m_magsq; }
|
double getMagSq() const { return m_magsq; }
|
||||||
double getMagSqAvg() const { return (double) m_channelPowerAvg; }
|
double getMagSqAvg() const { return (double) m_channelPowerAvg; }
|
||||||
bool isPllLocked() const { return m_settings.m_pll && m_pll.locked(); }
|
bool isPllLocked() const;
|
||||||
Real getPllFrequency() const;
|
Real getPllFrequency() const;
|
||||||
Real getPllDeltaPhase() const { return m_pll.getDeltaPhi(); }
|
Real getPllDeltaPhase() const;
|
||||||
Real getPllPhase() const { return m_pll.getPhiHat(); }
|
Real getPllPhase() const;
|
||||||
void setSampleSink(BasebandSampleSink* sampleSink) { m_sampleSink = sampleSink; }
|
void setSampleSink(BasebandSampleSink* sampleSink) { m_sampleSink = sampleSink; }
|
||||||
|
|
||||||
static const unsigned int m_corrFFTLen;
|
static const unsigned int m_corrFFTLen;
|
||||||
@ -70,6 +71,7 @@ private:
|
|||||||
Real m_interpolatorDistanceRemain;
|
Real m_interpolatorDistanceRemain;
|
||||||
PhaseLockComplex m_pll;
|
PhaseLockComplex m_pll;
|
||||||
FreqLockComplex m_fll;
|
FreqLockComplex m_fll;
|
||||||
|
CostasLoop m_costasLoop;
|
||||||
DecimatorC m_decimator;
|
DecimatorC m_decimator;
|
||||||
|
|
||||||
fftfilt* SSBFilter;
|
fftfilt* SSBFilter;
|
||||||
|
@ -55,9 +55,13 @@ void ChannelAnalyzerWebAPIAdapter::webapiFormatChannelSettings(
|
|||||||
response.getChannelAnalyzerSettings()->setSsb(settings.m_ssb ? 1 : 0);
|
response.getChannelAnalyzerSettings()->setSsb(settings.m_ssb ? 1 : 0);
|
||||||
response.getChannelAnalyzerSettings()->setPll(settings.m_pll ? 1 : 0);
|
response.getChannelAnalyzerSettings()->setPll(settings.m_pll ? 1 : 0);
|
||||||
response.getChannelAnalyzerSettings()->setFll(settings.m_fll ? 1 : 0);
|
response.getChannelAnalyzerSettings()->setFll(settings.m_fll ? 1 : 0);
|
||||||
|
response.getChannelAnalyzerSettings()->setCostasLoop(settings.m_costasLoop ? 1 : 0);
|
||||||
response.getChannelAnalyzerSettings()->setRrc(settings.m_rrc ? 1 : 0);
|
response.getChannelAnalyzerSettings()->setRrc(settings.m_rrc ? 1 : 0);
|
||||||
response.getChannelAnalyzerSettings()->setRrcRolloff(settings.m_rrcRolloff);
|
response.getChannelAnalyzerSettings()->setRrcRolloff(settings.m_rrcRolloff);
|
||||||
response.getChannelAnalyzerSettings()->setPllPskOrder(settings.m_pllPskOrder);
|
response.getChannelAnalyzerSettings()->setPllPskOrder(settings.m_pllPskOrder);
|
||||||
|
response.getChannelAnalyzerSettings()->setPllBandwidth(settings.m_pllBandwidth);
|
||||||
|
response.getChannelAnalyzerSettings()->setPllDampingFactor(settings.m_pllBandwidth);
|
||||||
|
response.getChannelAnalyzerSettings()->setPllLoopGain(settings.m_pllLoopGain);
|
||||||
response.getChannelAnalyzerSettings()->setInputType((int) settings.m_inputType);
|
response.getChannelAnalyzerSettings()->setInputType((int) settings.m_inputType);
|
||||||
response.getChannelAnalyzerSettings()->setRgbColor(settings.m_rgbColor);
|
response.getChannelAnalyzerSettings()->setRgbColor(settings.m_rgbColor);
|
||||||
response.getChannelAnalyzerSettings()->setTitle(new QString(settings.m_title));
|
response.getChannelAnalyzerSettings()->setTitle(new QString(settings.m_title));
|
||||||
@ -190,9 +194,21 @@ void ChannelAnalyzerWebAPIAdapter::webapiUpdateChannelSettings(
|
|||||||
if (channelSettingsKeys.contains("pll")) {
|
if (channelSettingsKeys.contains("pll")) {
|
||||||
settings.m_pll = response.getChannelAnalyzerSettings()->getPll() != 0;
|
settings.m_pll = response.getChannelAnalyzerSettings()->getPll() != 0;
|
||||||
}
|
}
|
||||||
|
if (channelSettingsKeys.contains("costasLoop")) {
|
||||||
|
settings.m_costasLoop = response.getChannelAnalyzerSettings()->getCostasLoop() != 0;
|
||||||
|
}
|
||||||
if (channelSettingsKeys.contains("pllPskOrder")) {
|
if (channelSettingsKeys.contains("pllPskOrder")) {
|
||||||
settings.m_pllPskOrder = response.getChannelAnalyzerSettings()->getPllPskOrder();
|
settings.m_pllPskOrder = response.getChannelAnalyzerSettings()->getPllPskOrder();
|
||||||
}
|
}
|
||||||
|
if (channelSettingsKeys.contains("pllBandwidth")) {
|
||||||
|
settings.m_pllBandwidth = response.getChannelAnalyzerSettings()->getPllBandwidth();
|
||||||
|
}
|
||||||
|
if (channelSettingsKeys.contains("pllDampingFactor")) {
|
||||||
|
settings.m_pllDampingFactor = response.getChannelAnalyzerSettings()->getPllDampingFactor();
|
||||||
|
}
|
||||||
|
if (channelSettingsKeys.contains("pllLoopGain")) {
|
||||||
|
settings.m_pllLoopGain = response.getChannelAnalyzerSettings()->getPllLoopGain();
|
||||||
|
}
|
||||||
if (channelSettingsKeys.contains("rgbColor")) {
|
if (channelSettingsKeys.contains("rgbColor")) {
|
||||||
settings.m_rgbColor = response.getChannelAnalyzerSettings()->getRgbColor();
|
settings.m_rgbColor = response.getChannelAnalyzerSettings()->getRgbColor();
|
||||||
}
|
}
|
||||||
|
@ -97,6 +97,7 @@ set(sdrbase_SOURCES
|
|||||||
dsp/ctcssfrequencies.cpp
|
dsp/ctcssfrequencies.cpp
|
||||||
dsp/channelsamplesink.cpp
|
dsp/channelsamplesink.cpp
|
||||||
dsp/channelsamplesource.cpp
|
dsp/channelsamplesource.cpp
|
||||||
|
dsp/costasloop.cpp
|
||||||
dsp/cwkeyer.cpp
|
dsp/cwkeyer.cpp
|
||||||
dsp/cwkeyersettings.cpp
|
dsp/cwkeyersettings.cpp
|
||||||
dsp/datafifo.cpp
|
dsp/datafifo.cpp
|
||||||
@ -255,6 +256,7 @@ set(sdrbase_HEADERS
|
|||||||
dsp/channelsamplesink.h
|
dsp/channelsamplesink.h
|
||||||
dsp/channelsamplesource.h
|
dsp/channelsamplesource.h
|
||||||
dsp/complex.h
|
dsp/complex.h
|
||||||
|
dsp/costasloop.h
|
||||||
dsp/ctcssdetector.h
|
dsp/ctcssdetector.h
|
||||||
dsp/ctcssfrequencies.h
|
dsp/ctcssfrequencies.h
|
||||||
dsp/cwkeyer.h
|
dsp/cwkeyer.h
|
||||||
|
112
sdrbase/dsp/costasloop.cpp
Normal file
112
sdrbase/dsp/costasloop.cpp
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2006-2021 Free Software Foundation, Inc. //
|
||||||
|
// Copyright (C) 2018 Edouard Griffiths, F4EXB //
|
||||||
|
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||||
|
// //
|
||||||
|
// Based on the Costas Loop from GNU Radio //
|
||||||
|
// //
|
||||||
|
// This program is free software; you can redistribute it and/or modify //
|
||||||
|
// it under the terms of the GNU General Public License as published by //
|
||||||
|
// the Free Software Foundation as version 3 of the License, or //
|
||||||
|
// (at your option) any later version. //
|
||||||
|
// //
|
||||||
|
// This program is distributed in the hope that it will be useful, //
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||||
|
// GNU General Public License V3 for more details. //
|
||||||
|
// //
|
||||||
|
// You should have received a copy of the GNU General Public License //
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "costasloop.h"
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
// Loop bandwidth supposedly ~ 2pi/100 rads/sample
|
||||||
|
// pskOrder 2, 4 or 8
|
||||||
|
CostasLoop::CostasLoop(float loopBW, unsigned int pskOrder) :
|
||||||
|
m_maxFreq(1.0f),
|
||||||
|
m_minFreq(-1.0f),
|
||||||
|
m_pskOrder(pskOrder)
|
||||||
|
{
|
||||||
|
computeCoefficients(loopBW);
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
CostasLoop::~CostasLoop()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CostasLoop::reset()
|
||||||
|
{
|
||||||
|
m_y.real(1.0f);
|
||||||
|
m_y.imag(0.0f);
|
||||||
|
m_freq = 0.0f;
|
||||||
|
m_phase = 0.0f;
|
||||||
|
m_freq = 0.0f;
|
||||||
|
m_error = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2nd order loop with critical damping
|
||||||
|
void CostasLoop::computeCoefficients(float loopBW)
|
||||||
|
{
|
||||||
|
float damping = sqrtf(2.0f) / 2.0f;
|
||||||
|
float denom = (1.0 + 2.0 * damping * loopBW + loopBW * loopBW);
|
||||||
|
m_alpha = (4 * damping * loopBW) / denom;
|
||||||
|
m_beta = (4 * loopBW * loopBW) / denom;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CostasLoop::setSampleRate(unsigned int sampleRate)
|
||||||
|
{
|
||||||
|
(void) sampleRate;
|
||||||
|
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
static float branchlessClip(float x, float clip)
|
||||||
|
{
|
||||||
|
return 0.5f * (std::abs(x + clip) - std::abs(x - clip));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't use built-in complex.h multiply to avoid NaN/INF checking
|
||||||
|
static void fastComplexMultiply(std::complex<float> &out, const std::complex<float> cc1, const std::complex<float> cc2)
|
||||||
|
{
|
||||||
|
float o_r, o_i;
|
||||||
|
|
||||||
|
o_r = (cc1.real() * cc2.real()) - (cc1.imag() * cc2.imag());
|
||||||
|
o_i = (cc1.real() * cc2.imag()) + (cc1.imag() * cc2.real());
|
||||||
|
|
||||||
|
out.real(o_r);
|
||||||
|
out.imag(o_i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CostasLoop::feed(float re, float im)
|
||||||
|
{
|
||||||
|
std::complex<float> nco(::cosf(-m_phase), ::sinf(-m_phase));
|
||||||
|
|
||||||
|
std::complex<float> in, out;
|
||||||
|
in.real(re);
|
||||||
|
in.imag(im);
|
||||||
|
fastComplexMultiply(out, in, nco);
|
||||||
|
|
||||||
|
switch (m_pskOrder)
|
||||||
|
{
|
||||||
|
case 2:
|
||||||
|
m_error = phaseDetector2(out);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
m_error = phaseDetector4(out);
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
m_error = phaseDetector8(out);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
m_error = branchlessClip(m_error, 1.0f);
|
||||||
|
|
||||||
|
advanceLoop(m_error);
|
||||||
|
phaseWrap();
|
||||||
|
frequencyLimit();
|
||||||
|
|
||||||
|
m_y.real(-nco.real());
|
||||||
|
m_y.imag(nco.imag());
|
||||||
|
}
|
122
sdrbase/dsp/costasloop.h
Normal file
122
sdrbase/dsp/costasloop.h
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Copyright 2006-2021 Free Software Foundation, Inc. //
|
||||||
|
// Copyright (C) 2018 Edouard Griffiths, F4EXB //
|
||||||
|
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||||
|
// //
|
||||||
|
// Based on the Costas Loop from GNU Radio //
|
||||||
|
// //
|
||||||
|
// This program is free software; you can redistribute it and/or modify //
|
||||||
|
// it under the terms of the GNU General Public License as published by //
|
||||||
|
// the Free Software Foundation as version 3 of the License, or //
|
||||||
|
// (at your option) any later version. //
|
||||||
|
// //
|
||||||
|
// This program is distributed in the hope that it will be useful, //
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||||
|
// GNU General Public License V3 for more details. //
|
||||||
|
// //
|
||||||
|
// You should have received a copy of the GNU General Public License //
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef SDRBASE_DSP_COSTASLOOP_H_
|
||||||
|
#define SDRBASE_DSP_COSTASLOOP_H_
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "dsp/dsptypes.h"
|
||||||
|
#include "export.h"
|
||||||
|
|
||||||
|
/** Costas Loop for phase and frequency tracking. */
|
||||||
|
class SDRBASE_API CostasLoop
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CostasLoop(float loopBW, unsigned int pskOrder);
|
||||||
|
~CostasLoop();
|
||||||
|
|
||||||
|
void computeCoefficients(float loopBW);
|
||||||
|
void setPskOrder(unsigned int pskOrder) { m_pskOrder = pskOrder; }
|
||||||
|
void reset();
|
||||||
|
void setSampleRate(unsigned int sampleRate);
|
||||||
|
void feed(float re, float im);
|
||||||
|
const std::complex<float>& getComplex() const { return m_y; }
|
||||||
|
float getReal() const { return m_y.real(); }
|
||||||
|
float getImag() const { return m_y.imag(); }
|
||||||
|
float getFreq() const { return m_freq; }
|
||||||
|
float getPhiHat() const { return m_phase; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::complex<float> m_y;
|
||||||
|
float m_phase;
|
||||||
|
float m_freq;
|
||||||
|
float m_error;
|
||||||
|
float m_maxFreq;
|
||||||
|
float m_minFreq;
|
||||||
|
float m_alpha;
|
||||||
|
float m_beta;
|
||||||
|
unsigned int m_pskOrder;
|
||||||
|
|
||||||
|
void advanceLoop(float error)
|
||||||
|
{
|
||||||
|
m_freq = m_freq + m_beta * error;
|
||||||
|
m_phase = m_phase + m_freq + m_alpha * error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void phaseWrap()
|
||||||
|
{
|
||||||
|
const float two_pi = (float)(2.0 * M_PI);
|
||||||
|
|
||||||
|
while (m_phase > two_pi)
|
||||||
|
m_phase -= two_pi;
|
||||||
|
while (m_phase < -two_pi)
|
||||||
|
m_phase += two_pi;
|
||||||
|
}
|
||||||
|
|
||||||
|
void frequencyLimit()
|
||||||
|
{
|
||||||
|
if (m_freq > m_maxFreq)
|
||||||
|
m_freq = m_maxFreq;
|
||||||
|
else if (m_freq < m_minFreq)
|
||||||
|
m_freq = m_minFreq;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMaxFreq(float freq)
|
||||||
|
{
|
||||||
|
m_maxFreq = freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMinFreq(float freq)
|
||||||
|
{
|
||||||
|
m_minFreq = freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
float phaseDetector2(std::complex<float> sample) const // for BPSK
|
||||||
|
{
|
||||||
|
return (sample.real() * sample.imag());
|
||||||
|
}
|
||||||
|
|
||||||
|
float phaseDetector4(std::complex<float> sample) const // for QPSK
|
||||||
|
{
|
||||||
|
return ((sample.real() > 0.0f ? 1.0f : -1.0f) * sample.imag() -
|
||||||
|
(sample.imag() > 0.0f ? 1.0f : -1.0f) * sample.real());
|
||||||
|
};
|
||||||
|
|
||||||
|
float phaseDetector8(std::complex<float> sample) const // for 8PSK
|
||||||
|
{
|
||||||
|
const float K = (sqrtf(2.0) - 1);
|
||||||
|
if (fabsf(sample.real()) >= fabsf(sample.imag()))
|
||||||
|
{
|
||||||
|
return ((sample.real() > 0.0f ? 1.0f : -1.0f) * sample.imag() -
|
||||||
|
(sample.imag() > 0.0f ? 1.0f : -1.0f) * sample.real() * K);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ((sample.real() > 0.0f ? 1.0f : -1.0f) * sample.imag() * K -
|
||||||
|
(sample.imag() > 0.0f ? 1.0f : -1.0f) * sample.real());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* SDRBASE_DSP_COSTASLOOP_H_ */
|
@ -2586,6 +2586,10 @@ margin-bottom: 20px;
|
|||||||
"type" : "integer",
|
"type" : "integer",
|
||||||
"description" : "Boolean"
|
"description" : "Boolean"
|
||||||
},
|
},
|
||||||
|
"costasLoop" : {
|
||||||
|
"type" : "integer",
|
||||||
|
"description" : "Boolean"
|
||||||
|
},
|
||||||
"rrc" : {
|
"rrc" : {
|
||||||
"type" : "integer",
|
"type" : "integer",
|
||||||
"description" : "Boolean"
|
"description" : "Boolean"
|
||||||
@ -2597,6 +2601,18 @@ margin-bottom: 20px;
|
|||||||
"pllPskOrder" : {
|
"pllPskOrder" : {
|
||||||
"type" : "integer"
|
"type" : "integer"
|
||||||
},
|
},
|
||||||
|
"pllBandwidth" : {
|
||||||
|
"type" : "number",
|
||||||
|
"format" : "float"
|
||||||
|
},
|
||||||
|
"pllDampingFactor" : {
|
||||||
|
"type" : "number",
|
||||||
|
"format" : "float"
|
||||||
|
},
|
||||||
|
"pllLoopGain" : {
|
||||||
|
"type" : "number",
|
||||||
|
"format" : "float"
|
||||||
|
},
|
||||||
"inputType" : {
|
"inputType" : {
|
||||||
"type" : "integer",
|
"type" : "integer",
|
||||||
"description" : "see ChannelAnalyzerSettings::InputType"
|
"description" : "see ChannelAnalyzerSettings::InputType"
|
||||||
@ -45623,7 +45639,7 @@ except ApiException as e:
|
|||||||
</div>
|
</div>
|
||||||
<div id="generator">
|
<div id="generator">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
Generated 2021-03-01T10:47:56.898+01:00
|
Generated 2021-03-05T14:04:36.302+01:00
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -23,6 +23,9 @@ ChannelAnalyzerSettings:
|
|||||||
fll:
|
fll:
|
||||||
description: Boolean
|
description: Boolean
|
||||||
type: integer
|
type: integer
|
||||||
|
costasLoop:
|
||||||
|
description: Boolean
|
||||||
|
type: integer
|
||||||
rrc:
|
rrc:
|
||||||
description: Boolean
|
description: Boolean
|
||||||
type: integer
|
type: integer
|
||||||
@ -31,6 +34,15 @@ ChannelAnalyzerSettings:
|
|||||||
type: integer
|
type: integer
|
||||||
pllPskOrder:
|
pllPskOrder:
|
||||||
type: integer
|
type: integer
|
||||||
|
pllBandwidth:
|
||||||
|
type: number
|
||||||
|
format: float
|
||||||
|
pllDampingFactor:
|
||||||
|
type: number
|
||||||
|
format: float
|
||||||
|
pllLoopGain:
|
||||||
|
type: number
|
||||||
|
format: float
|
||||||
inputType:
|
inputType:
|
||||||
description: see ChannelAnalyzerSettings::InputType
|
description: see ChannelAnalyzerSettings::InputType
|
||||||
type: integer
|
type: integer
|
||||||
|
@ -23,6 +23,9 @@ ChannelAnalyzerSettings:
|
|||||||
fll:
|
fll:
|
||||||
description: Boolean
|
description: Boolean
|
||||||
type: integer
|
type: integer
|
||||||
|
costasLoop:
|
||||||
|
description: Boolean
|
||||||
|
type: integer
|
||||||
rrc:
|
rrc:
|
||||||
description: Boolean
|
description: Boolean
|
||||||
type: integer
|
type: integer
|
||||||
@ -31,6 +34,15 @@ ChannelAnalyzerSettings:
|
|||||||
type: integer
|
type: integer
|
||||||
pllPskOrder:
|
pllPskOrder:
|
||||||
type: integer
|
type: integer
|
||||||
|
pllBandwidth:
|
||||||
|
type: number
|
||||||
|
format: float
|
||||||
|
pllDampingFactor:
|
||||||
|
type: number
|
||||||
|
format: float
|
||||||
|
pllLoopGain:
|
||||||
|
type: number
|
||||||
|
format: float
|
||||||
inputType:
|
inputType:
|
||||||
description: see ChannelAnalyzerSettings::InputType
|
description: see ChannelAnalyzerSettings::InputType
|
||||||
type: integer
|
type: integer
|
||||||
|
@ -2586,6 +2586,10 @@ margin-bottom: 20px;
|
|||||||
"type" : "integer",
|
"type" : "integer",
|
||||||
"description" : "Boolean"
|
"description" : "Boolean"
|
||||||
},
|
},
|
||||||
|
"costasLoop" : {
|
||||||
|
"type" : "integer",
|
||||||
|
"description" : "Boolean"
|
||||||
|
},
|
||||||
"rrc" : {
|
"rrc" : {
|
||||||
"type" : "integer",
|
"type" : "integer",
|
||||||
"description" : "Boolean"
|
"description" : "Boolean"
|
||||||
@ -2597,6 +2601,18 @@ margin-bottom: 20px;
|
|||||||
"pllPskOrder" : {
|
"pllPskOrder" : {
|
||||||
"type" : "integer"
|
"type" : "integer"
|
||||||
},
|
},
|
||||||
|
"pllBandwidth" : {
|
||||||
|
"type" : "number",
|
||||||
|
"format" : "float"
|
||||||
|
},
|
||||||
|
"pllDampingFactor" : {
|
||||||
|
"type" : "number",
|
||||||
|
"format" : "float"
|
||||||
|
},
|
||||||
|
"pllLoopGain" : {
|
||||||
|
"type" : "number",
|
||||||
|
"format" : "float"
|
||||||
|
},
|
||||||
"inputType" : {
|
"inputType" : {
|
||||||
"type" : "integer",
|
"type" : "integer",
|
||||||
"description" : "see ChannelAnalyzerSettings::InputType"
|
"description" : "see ChannelAnalyzerSettings::InputType"
|
||||||
@ -45623,7 +45639,7 @@ except ApiException as e:
|
|||||||
</div>
|
</div>
|
||||||
<div id="generator">
|
<div id="generator">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
Generated 2021-03-01T10:47:56.898+01:00
|
Generated 2021-03-05T14:04:36.302+01:00
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -46,12 +46,20 @@ SWGChannelAnalyzerSettings::SWGChannelAnalyzerSettings() {
|
|||||||
m_pll_isSet = false;
|
m_pll_isSet = false;
|
||||||
fll = 0;
|
fll = 0;
|
||||||
m_fll_isSet = false;
|
m_fll_isSet = false;
|
||||||
|
costas_loop = 0;
|
||||||
|
m_costas_loop_isSet = false;
|
||||||
rrc = 0;
|
rrc = 0;
|
||||||
m_rrc_isSet = false;
|
m_rrc_isSet = false;
|
||||||
rrc_rolloff = 0;
|
rrc_rolloff = 0;
|
||||||
m_rrc_rolloff_isSet = false;
|
m_rrc_rolloff_isSet = false;
|
||||||
pll_psk_order = 0;
|
pll_psk_order = 0;
|
||||||
m_pll_psk_order_isSet = false;
|
m_pll_psk_order_isSet = false;
|
||||||
|
pll_bandwidth = 0.0f;
|
||||||
|
m_pll_bandwidth_isSet = false;
|
||||||
|
pll_damping_factor = 0.0f;
|
||||||
|
m_pll_damping_factor_isSet = false;
|
||||||
|
pll_loop_gain = 0.0f;
|
||||||
|
m_pll_loop_gain_isSet = false;
|
||||||
input_type = 0;
|
input_type = 0;
|
||||||
m_input_type_isSet = false;
|
m_input_type_isSet = false;
|
||||||
rgb_color = 0;
|
rgb_color = 0;
|
||||||
@ -88,12 +96,20 @@ SWGChannelAnalyzerSettings::init() {
|
|||||||
m_pll_isSet = false;
|
m_pll_isSet = false;
|
||||||
fll = 0;
|
fll = 0;
|
||||||
m_fll_isSet = false;
|
m_fll_isSet = false;
|
||||||
|
costas_loop = 0;
|
||||||
|
m_costas_loop_isSet = false;
|
||||||
rrc = 0;
|
rrc = 0;
|
||||||
m_rrc_isSet = false;
|
m_rrc_isSet = false;
|
||||||
rrc_rolloff = 0;
|
rrc_rolloff = 0;
|
||||||
m_rrc_rolloff_isSet = false;
|
m_rrc_rolloff_isSet = false;
|
||||||
pll_psk_order = 0;
|
pll_psk_order = 0;
|
||||||
m_pll_psk_order_isSet = false;
|
m_pll_psk_order_isSet = false;
|
||||||
|
pll_bandwidth = 0.0f;
|
||||||
|
m_pll_bandwidth_isSet = false;
|
||||||
|
pll_damping_factor = 0.0f;
|
||||||
|
m_pll_damping_factor_isSet = false;
|
||||||
|
pll_loop_gain = 0.0f;
|
||||||
|
m_pll_loop_gain_isSet = false;
|
||||||
input_type = 0;
|
input_type = 0;
|
||||||
m_input_type_isSet = false;
|
m_input_type_isSet = false;
|
||||||
rgb_color = 0;
|
rgb_color = 0;
|
||||||
@ -122,6 +138,10 @@ SWGChannelAnalyzerSettings::cleanup() {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if(title != nullptr) {
|
if(title != nullptr) {
|
||||||
delete title;
|
delete title;
|
||||||
}
|
}
|
||||||
@ -162,12 +182,20 @@ SWGChannelAnalyzerSettings::fromJsonObject(QJsonObject &pJson) {
|
|||||||
|
|
||||||
::SWGSDRangel::setValue(&fll, pJson["fll"], "qint32", "");
|
::SWGSDRangel::setValue(&fll, pJson["fll"], "qint32", "");
|
||||||
|
|
||||||
|
::SWGSDRangel::setValue(&costas_loop, pJson["costasLoop"], "qint32", "");
|
||||||
|
|
||||||
::SWGSDRangel::setValue(&rrc, pJson["rrc"], "qint32", "");
|
::SWGSDRangel::setValue(&rrc, pJson["rrc"], "qint32", "");
|
||||||
|
|
||||||
::SWGSDRangel::setValue(&rrc_rolloff, pJson["rrcRolloff"], "qint32", "");
|
::SWGSDRangel::setValue(&rrc_rolloff, pJson["rrcRolloff"], "qint32", "");
|
||||||
|
|
||||||
::SWGSDRangel::setValue(&pll_psk_order, pJson["pllPskOrder"], "qint32", "");
|
::SWGSDRangel::setValue(&pll_psk_order, pJson["pllPskOrder"], "qint32", "");
|
||||||
|
|
||||||
|
::SWGSDRangel::setValue(&pll_bandwidth, pJson["pllBandwidth"], "float", "");
|
||||||
|
|
||||||
|
::SWGSDRangel::setValue(&pll_damping_factor, pJson["pllDampingFactor"], "float", "");
|
||||||
|
|
||||||
|
::SWGSDRangel::setValue(&pll_loop_gain, pJson["pllLoopGain"], "float", "");
|
||||||
|
|
||||||
::SWGSDRangel::setValue(&input_type, pJson["inputType"], "qint32", "");
|
::SWGSDRangel::setValue(&input_type, pJson["inputType"], "qint32", "");
|
||||||
|
|
||||||
::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", "");
|
::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", "");
|
||||||
@ -221,6 +249,9 @@ SWGChannelAnalyzerSettings::asJsonObject() {
|
|||||||
if(m_fll_isSet){
|
if(m_fll_isSet){
|
||||||
obj->insert("fll", QJsonValue(fll));
|
obj->insert("fll", QJsonValue(fll));
|
||||||
}
|
}
|
||||||
|
if(m_costas_loop_isSet){
|
||||||
|
obj->insert("costasLoop", QJsonValue(costas_loop));
|
||||||
|
}
|
||||||
if(m_rrc_isSet){
|
if(m_rrc_isSet){
|
||||||
obj->insert("rrc", QJsonValue(rrc));
|
obj->insert("rrc", QJsonValue(rrc));
|
||||||
}
|
}
|
||||||
@ -230,6 +261,15 @@ SWGChannelAnalyzerSettings::asJsonObject() {
|
|||||||
if(m_pll_psk_order_isSet){
|
if(m_pll_psk_order_isSet){
|
||||||
obj->insert("pllPskOrder", QJsonValue(pll_psk_order));
|
obj->insert("pllPskOrder", QJsonValue(pll_psk_order));
|
||||||
}
|
}
|
||||||
|
if(m_pll_bandwidth_isSet){
|
||||||
|
obj->insert("pllBandwidth", QJsonValue(pll_bandwidth));
|
||||||
|
}
|
||||||
|
if(m_pll_damping_factor_isSet){
|
||||||
|
obj->insert("pllDampingFactor", QJsonValue(pll_damping_factor));
|
||||||
|
}
|
||||||
|
if(m_pll_loop_gain_isSet){
|
||||||
|
obj->insert("pllLoopGain", QJsonValue(pll_loop_gain));
|
||||||
|
}
|
||||||
if(m_input_type_isSet){
|
if(m_input_type_isSet){
|
||||||
obj->insert("inputType", QJsonValue(input_type));
|
obj->insert("inputType", QJsonValue(input_type));
|
||||||
}
|
}
|
||||||
@ -339,6 +379,16 @@ SWGChannelAnalyzerSettings::setFll(qint32 fll) {
|
|||||||
this->m_fll_isSet = true;
|
this->m_fll_isSet = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qint32
|
||||||
|
SWGChannelAnalyzerSettings::getCostasLoop() {
|
||||||
|
return costas_loop;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
SWGChannelAnalyzerSettings::setCostasLoop(qint32 costas_loop) {
|
||||||
|
this->costas_loop = costas_loop;
|
||||||
|
this->m_costas_loop_isSet = true;
|
||||||
|
}
|
||||||
|
|
||||||
qint32
|
qint32
|
||||||
SWGChannelAnalyzerSettings::getRrc() {
|
SWGChannelAnalyzerSettings::getRrc() {
|
||||||
return rrc;
|
return rrc;
|
||||||
@ -369,6 +419,36 @@ SWGChannelAnalyzerSettings::setPllPskOrder(qint32 pll_psk_order) {
|
|||||||
this->m_pll_psk_order_isSet = true;
|
this->m_pll_psk_order_isSet = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float
|
||||||
|
SWGChannelAnalyzerSettings::getPllBandwidth() {
|
||||||
|
return pll_bandwidth;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
SWGChannelAnalyzerSettings::setPllBandwidth(float pll_bandwidth) {
|
||||||
|
this->pll_bandwidth = pll_bandwidth;
|
||||||
|
this->m_pll_bandwidth_isSet = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
float
|
||||||
|
SWGChannelAnalyzerSettings::getPllDampingFactor() {
|
||||||
|
return pll_damping_factor;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
SWGChannelAnalyzerSettings::setPllDampingFactor(float pll_damping_factor) {
|
||||||
|
this->pll_damping_factor = pll_damping_factor;
|
||||||
|
this->m_pll_damping_factor_isSet = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
float
|
||||||
|
SWGChannelAnalyzerSettings::getPllLoopGain() {
|
||||||
|
return pll_loop_gain;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
SWGChannelAnalyzerSettings::setPllLoopGain(float pll_loop_gain) {
|
||||||
|
this->pll_loop_gain = pll_loop_gain;
|
||||||
|
this->m_pll_loop_gain_isSet = true;
|
||||||
|
}
|
||||||
|
|
||||||
qint32
|
qint32
|
||||||
SWGChannelAnalyzerSettings::getInputType() {
|
SWGChannelAnalyzerSettings::getInputType() {
|
||||||
return input_type;
|
return input_type;
|
||||||
@ -451,6 +531,9 @@ SWGChannelAnalyzerSettings::isSet(){
|
|||||||
if(m_fll_isSet){
|
if(m_fll_isSet){
|
||||||
isObjectUpdated = true; break;
|
isObjectUpdated = true; break;
|
||||||
}
|
}
|
||||||
|
if(m_costas_loop_isSet){
|
||||||
|
isObjectUpdated = true; break;
|
||||||
|
}
|
||||||
if(m_rrc_isSet){
|
if(m_rrc_isSet){
|
||||||
isObjectUpdated = true; break;
|
isObjectUpdated = true; break;
|
||||||
}
|
}
|
||||||
@ -460,6 +543,15 @@ SWGChannelAnalyzerSettings::isSet(){
|
|||||||
if(m_pll_psk_order_isSet){
|
if(m_pll_psk_order_isSet){
|
||||||
isObjectUpdated = true; break;
|
isObjectUpdated = true; break;
|
||||||
}
|
}
|
||||||
|
if(m_pll_bandwidth_isSet){
|
||||||
|
isObjectUpdated = true; break;
|
||||||
|
}
|
||||||
|
if(m_pll_damping_factor_isSet){
|
||||||
|
isObjectUpdated = true; break;
|
||||||
|
}
|
||||||
|
if(m_pll_loop_gain_isSet){
|
||||||
|
isObjectUpdated = true; break;
|
||||||
|
}
|
||||||
if(m_input_type_isSet){
|
if(m_input_type_isSet){
|
||||||
isObjectUpdated = true; break;
|
isObjectUpdated = true; break;
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,9 @@ public:
|
|||||||
qint32 getFll();
|
qint32 getFll();
|
||||||
void setFll(qint32 fll);
|
void setFll(qint32 fll);
|
||||||
|
|
||||||
|
qint32 getCostasLoop();
|
||||||
|
void setCostasLoop(qint32 costas_loop);
|
||||||
|
|
||||||
qint32 getRrc();
|
qint32 getRrc();
|
||||||
void setRrc(qint32 rrc);
|
void setRrc(qint32 rrc);
|
||||||
|
|
||||||
@ -80,6 +83,15 @@ public:
|
|||||||
qint32 getPllPskOrder();
|
qint32 getPllPskOrder();
|
||||||
void setPllPskOrder(qint32 pll_psk_order);
|
void setPllPskOrder(qint32 pll_psk_order);
|
||||||
|
|
||||||
|
float getPllBandwidth();
|
||||||
|
void setPllBandwidth(float pll_bandwidth);
|
||||||
|
|
||||||
|
float getPllDampingFactor();
|
||||||
|
void setPllDampingFactor(float pll_damping_factor);
|
||||||
|
|
||||||
|
float getPllLoopGain();
|
||||||
|
void setPllLoopGain(float pll_loop_gain);
|
||||||
|
|
||||||
qint32 getInputType();
|
qint32 getInputType();
|
||||||
void setInputType(qint32 input_type);
|
void setInputType(qint32 input_type);
|
||||||
|
|
||||||
@ -126,6 +138,9 @@ private:
|
|||||||
qint32 fll;
|
qint32 fll;
|
||||||
bool m_fll_isSet;
|
bool m_fll_isSet;
|
||||||
|
|
||||||
|
qint32 costas_loop;
|
||||||
|
bool m_costas_loop_isSet;
|
||||||
|
|
||||||
qint32 rrc;
|
qint32 rrc;
|
||||||
bool m_rrc_isSet;
|
bool m_rrc_isSet;
|
||||||
|
|
||||||
@ -135,6 +150,15 @@ private:
|
|||||||
qint32 pll_psk_order;
|
qint32 pll_psk_order;
|
||||||
bool m_pll_psk_order_isSet;
|
bool m_pll_psk_order_isSet;
|
||||||
|
|
||||||
|
float pll_bandwidth;
|
||||||
|
bool m_pll_bandwidth_isSet;
|
||||||
|
|
||||||
|
float pll_damping_factor;
|
||||||
|
bool m_pll_damping_factor_isSet;
|
||||||
|
|
||||||
|
float pll_loop_gain;
|
||||||
|
bool m_pll_loop_gain_isSet;
|
||||||
|
|
||||||
qint32 input_type;
|
qint32 input_type;
|
||||||
bool m_input_type_isSet;
|
bool m_input_type_isSet;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user