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

Frequency Tracker: added spectrum span log2

This commit is contained in:
f4exb 2020-10-25 23:07:15 +01:00
parent 23cebe596f
commit 573aa9b559
11 changed files with 191 additions and 17 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

View File

@ -182,6 +182,7 @@ void FreqTracker::applySettings(const FreqTrackerSettings& settings, bool force)
<< " m_rrc: " << settings.m_rrc
<< " m_rrcRolloff: " << settings.m_rrcRolloff
<< " m_streamIndex: " << settings.m_streamIndex
<< " m_spanLog2: " << settings.m_spanLog2
<< " m_useReverseAPI: " << settings.m_useReverseAPI
<< " m_reverseAPIAddress: " << settings.m_reverseAPIAddress
<< " m_reverseAPIPort: " << settings.m_reverseAPIPort
@ -215,6 +216,9 @@ void FreqTracker::applySettings(const FreqTrackerSettings& settings, bool force)
if ((m_settings.m_alphaEMA != settings.m_alphaEMA) || force) {
reverseAPIKeys.append("alphaEMA");
}
if ((m_settings.m_spanLog2 != settings.m_spanLog2) || force) {
reverseAPIKeys.append("spanLog2");
}
if ((m_settings.m_tracking != settings.m_tracking) || force) {
reverseAPIKeys.append("tracking");
}
@ -354,6 +358,9 @@ void FreqTracker::webapiUpdateChannelSettings(
if (channelSettingsKeys.contains("title")) {
settings.m_title = *response.getFreqTrackerSettings()->getTitle();
}
if (channelSettingsKeys.contains("spanLog2")) {
settings.m_spanLog2 = response.getFreqTrackerSettings()->getSpanLog2();
}
if (channelSettingsKeys.contains("alphaEMA")) {
float alphaEMA = response.getFreqTrackerSettings()->getAlphaEma();
settings.m_alphaEMA = alphaEMA < 0.01 ? 0.01 : alphaEMA > 1.0 ? 1.0 : alphaEMA;
@ -425,6 +432,7 @@ void FreqTracker::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& r
response.getFreqTrackerSettings()->setTitle(new QString(settings.m_title));
}
response.getFreqTrackerSettings()->setSpanLog2(settings.m_spanLog2);
response.getFreqTrackerSettings()->setAlphaEma(settings.m_alphaEMA);
response.getFreqTrackerSettings()->setTracking(settings.m_tracking ? 1 : 0);
response.getFreqTrackerSettings()->setTrackerType((int) settings.m_trackerType);
@ -533,18 +541,42 @@ void FreqTracker::webapiFormatChannelSettings(
if (channelSettingsKeys.contains("rfBandwidth") || force) {
swgFreqTrackerSettings->setRfBandwidth(settings.m_rfBandwidth);
}
if (channelSettingsKeys.contains("rgbColor") || force) {
swgFreqTrackerSettings->setRgbColor(settings.m_rgbColor);
if (channelSettingsKeys.contains("log2Decim") || force) {
swgFreqTrackerSettings->setLog2Decim(settings.m_log2Decim);
}
if (channelSettingsKeys.contains("squelch") || force) {
swgFreqTrackerSettings->setSquelch(settings.m_squelch);
}
if (channelSettingsKeys.contains("rgbColor") || force) {
swgFreqTrackerSettings->setRgbColor(settings.m_rgbColor);
}
if (channelSettingsKeys.contains("title") || force) {
swgFreqTrackerSettings->setTitle(new QString(settings.m_title));
}
if (channelSettingsKeys.contains("spanLog2") || force) {
swgFreqTrackerSettings->setSpanLog2(settings.m_spanLog2);
}
if (channelSettingsKeys.contains("alphaEMA") || force) {
swgFreqTrackerSettings->setAlphaEma(settings.m_alphaEMA);
}
if (channelSettingsKeys.contains("tracking") || force) {
swgFreqTrackerSettings->setTracking(settings.m_tracking ? 1 : 0);
}
if (channelSettingsKeys.contains("trackerType") || force) {
swgFreqTrackerSettings->setTrackerType((int) settings.m_trackerType);
}
if (channelSettingsKeys.contains("pllPskOrder") || force) {
swgFreqTrackerSettings->setPllPskOrder(settings.m_pllPskOrder);
}
if (channelSettingsKeys.contains("rrc") || force) {
swgFreqTrackerSettings->setRrc(settings.m_rrc ? 1 : 0);
}
if (channelSettingsKeys.contains("rrcRolloff") || force) {
swgFreqTrackerSettings->setRrcRolloff(settings.m_rrcRolloff);
}
if (channelSettingsKeys.contains("squelchGate") || force) {
swgFreqTrackerSettings->setSquelchGate(settings.m_squelchGate);
}
if (channelSettingsKeys.contains("streamIndex") || force) {
swgFreqTrackerSettings->setStreamIndex(settings.m_streamIndex);
}

View File

@ -91,8 +91,8 @@ bool FreqTrackerGUI::handleMessage(const Message& message)
m_basebandSampleRate = cfg.getSampleRate();
int sinkSampleRate = m_basebandSampleRate / (1<<m_settings.m_log2Decim);
ui->channelSampleRateText->setText(tr("%1k").arg(QString::number(sinkSampleRate / 1000.0f, 'g', 5)));
ui->glSpectrum->setSampleRate(sinkSampleRate);
m_pllChannelMarker.setBandwidth(sinkSampleRate/1000);
displaySpectrumBandwidth(m_settings.m_spanLog2);
m_pllChannelMarker.setBandwidth(sinkSampleRate/500);
if (sinkSampleRate > 1000) {
ui->rfBW->setMaximum(sinkSampleRate/100);
@ -153,8 +153,8 @@ void FreqTrackerGUI::on_log2Decim_currentIndexChanged(int index)
m_settings.m_log2Decim = index < 0 ? 0 : index > 6 ? 6 : index;
int sinkSampleRate = m_basebandSampleRate / (1<<m_settings.m_log2Decim);
ui->channelSampleRateText->setText(tr("%1k").arg(QString::number(sinkSampleRate / 1000.0f, 'g', 5)));
ui->glSpectrum->setSampleRate(sinkSampleRate);
m_pllChannelMarker.setBandwidth(sinkSampleRate/1000);
displaySpectrumBandwidth(m_settings.m_spanLog2);
m_pllChannelMarker.setBandwidth(sinkSampleRate/500);
if (sinkSampleRate > 1000) {
ui->rfBW->setMaximum(sinkSampleRate/100);
@ -235,6 +235,15 @@ void FreqTrackerGUI::on_squelchGate_valueChanged(int value)
applySettings();
}
void FreqTrackerGUI::on_spanLog2_valueChanged(int value)
{
if ((value < 0) || (value > 6)) {
return;
}
applySpectrumBandwidth(ui->spanLog2->value());
}
void FreqTrackerGUI::onWidgetRolled(QWidget* widget, bool rollDown)
{
(void) widget;
@ -332,9 +341,9 @@ FreqTrackerGUI::FreqTrackerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B
ui->glSpectrum->setCenterFrequency(0);
m_pllChannelMarker.blockSignals(true);
m_pllChannelMarker.setColor(Qt::gray);
m_pllChannelMarker.setColor(Qt::white);
m_pllChannelMarker.setCenterFrequency(0);
m_pllChannelMarker.setBandwidth(35);
m_pllChannelMarker.setBandwidth(70);
m_pllChannelMarker.setTitle("Tracker");
m_pllChannelMarker.setMovable(false);
m_pllChannelMarker.blockSignals(false);
@ -369,6 +378,13 @@ void FreqTrackerGUI::applySettings(bool force)
}
}
void FreqTrackerGUI::applySpectrumBandwidth(int spanLog2, bool force)
{
displaySpectrumBandwidth(spanLog2);
m_settings.m_spanLog2 = spanLog2;
applySettings(force);
}
void FreqTrackerGUI::displaySettings()
{
m_channelMarker.blockSignals(true);
@ -407,11 +423,29 @@ void FreqTrackerGUI::displaySettings()
ui->squelchGateText->setText(QString("%1").arg(m_settings.m_squelchGate * 10.0f, 0, 'f', 0));
ui->squelchGate->setValue(m_settings.m_squelchGate);
displaySpectrumBandwidth(m_settings.m_spanLog2);
displayStreamIndex();
blockApplySettings(false);
}
void FreqTrackerGUI::displaySpectrumBandwidth(int spanLog2)
{
int spectrumRate = (m_basebandSampleRate / (1<<m_settings.m_log2Decim)) / (1<<spanLog2);
qDebug() << "FreqTrackerGUI::displaySpectrumBandwidth:"
<< " spanLog2: " << spanLog2
<< " spectrumRate: " << spectrumRate;
QString spanStr = QString::number(spectrumRate/1000.0, 'f', 1);
ui->spanLog2->blockSignals(true);
ui->spanLog2->setValue(spanLog2);
ui->spanLog2->blockSignals(false);
ui->spanText->setText(tr("%1k").arg(spanStr));
ui->glSpectrum->setSampleRate(spectrumRate);
}
void FreqTrackerGUI::displayStreamIndex()
{
if (m_deviceUISet->m_deviceMIMOEngine) {

View File

@ -73,7 +73,9 @@ private:
void blockApplySettings(bool block);
void applySettings(bool force = false);
void applySpectrumBandwidth(int spanLog2, bool force = false);
void displaySettings();
void displaySpectrumBandwidth(int spanLog2);
void displayStreamIndex();
bool handleMessage(const Message& message);
@ -92,6 +94,7 @@ private slots:
void on_rrcRolloff_valueChanged(int value);
void on_squelch_valueChanged(int value);
void on_squelchGate_valueChanged(int value);
void on_spanLog2_valueChanged(int value);
void onWidgetRolled(QWidget* widget, bool rollDown);
void onMenuDialogCalled(const QPoint& p);
void handleInputMessages();

View File

@ -40,7 +40,7 @@
<x>0</x>
<y>0</y>
<width>401</width>
<height>140</height>
<height>151</height>
</rect>
</property>
<property name="windowTitle">
@ -673,15 +673,88 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="spanLayout">
<item>
<widget class="QLabel" name="spanLabel">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Span</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="spanLog2">
<property name="toolTip">
<string>Spectrum display frequency span</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>6</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>0</number>
</property>
<property name="sliderPosition">
<number>0</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="spanText">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Spectrum display frequency span (kHz)</string>
</property>
<property name="text">
<string>6.0k</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="spectrumContainer" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>150</y>
<width>391</width>
<height>171</height>
<y>160</y>
<width>401</width>
<height>161</height>
</rect>
</property>
<property name="windowTitle">

View File

@ -37,6 +37,7 @@ void FreqTrackerSettings::resetToDefaults()
m_squelch = -40.0;
m_rgbColor = QColor(200, 244, 66).rgb();
m_title = "Frequency Tracker";
m_spanLog2 = 0;
m_alphaEMA = 0.1;
m_tracking = false;
m_trackerType = TrackerFLL;
@ -72,6 +73,7 @@ QByteArray FreqTrackerSettings::serialize() const
s.writeFloat(8, m_alphaEMA);
s.writeString(9, m_title);
s.writeBool(10, m_tracking);
s.writeS32(11, m_spanLog2);
s.writeS32(12, (int) m_trackerType);
s.writeU32(13, m_pllPskOrder);
s.writeBool(14, m_rrc);
@ -130,6 +132,7 @@ bool FreqTrackerSettings::deserialize(const QByteArray& data)
m_alphaEMA = ftmp < 0.01 ? 0.01 : ftmp > 1.0 ? 1.0 : ftmp;
d.readString(9, &m_title, "Frequency Tracker");
d.readBool(10, &m_tracking, false);
d.readS32(11, &m_spanLog2, 0);
d.readS32(12, &tmp, 0);
m_trackerType = tmp < 0 ? TrackerFLL : tmp > 2 ? TrackerPLL : (TrackerType) tmp;
d.readU32(13, &utmp, 2);

View File

@ -42,6 +42,7 @@ struct FreqTrackerSettings
QString m_title;
Serializable *m_channelMarker;
Serializable *m_spectrumGUI;
int m_spanLog2;
float m_alphaEMA; //!< alpha factor for delta frequency EMA
bool m_tracking;
TrackerType m_trackerType;

View File

@ -36,6 +36,7 @@ FreqTrackerSink::FreqTrackerSink() :
m_sinkSampleRate(48000),
m_spectrumSink(nullptr),
m_sampleBufferCount(0),
m_undersampleCount(0),
m_squelchOpen(false),
m_squelchGate(0),
m_magsqSum(0.0f),
@ -57,6 +58,7 @@ FreqTrackerSink::FreqTrackerSink() :
m_magsq = 0.0;
m_sampleBufferSize = m_sinkSampleRate / 20; // 50 ms
m_sampleBuffer.resize(m_sampleBufferSize);
m_sum = Complex{0.0, 0.0};
m_rrcFilter = new fftfilt(m_settings.m_rfBandwidth / m_sinkSampleRate, 2*1024);
m_pll.computeCoefficients(0.002f, 0.5f, 10.0f); // bandwidth, damping factor, loop gain
@ -107,7 +109,18 @@ void FreqTrackerSink::processOneSample(Complex &ci)
{
fftfilt::cmplx *sideband;
int n_out;
m_sampleBuffer[m_sampleBufferCount++] = Sample(ci.real(), ci.imag());
int decim = 1<<m_settings.m_spanLog2;
m_sum += ci;
if (m_undersampleCount++ == decim)
{
Real avgr = m_sum.real() / decim;
Real avgi = m_sum.imag() / decim;
m_sampleBuffer[m_sampleBufferCount++] = Sample(avgr, avgi);
m_sum.real(0.0);
m_sum.imag(0.0);
m_undersampleCount = 0;
}
if (m_settings.m_rrc)
{
@ -230,9 +243,10 @@ void FreqTrackerSink::applyChannelSettings(int sinkSampleRate, int channelSample
setInterpolator();
}
m_sampleBufferSize = m_sinkSampleRate / 20; // 50 ms
m_sampleBufferSize = (m_sinkSampleRate/(1<<m_settings.m_spanLog2)) / 20; // 50 ms
m_sampleBuffer.resize(m_sampleBufferSize);
m_sampleBufferCount = 0;
m_undersampleCount = 0;
}
void FreqTrackerSink::applySettings(const FreqTrackerSettings& settings, bool force)
@ -311,6 +325,14 @@ void FreqTrackerSink::applySettings(const FreqTrackerSettings& settings, bool fo
useInterpolator = true;
}
if ((settings.m_spanLog2 != m_settings.m_spanLog2) || force)
{
m_sampleBufferSize = (m_sinkSampleRate/(1<<settings.m_spanLog2)) / 20; // 50 ms
m_sampleBuffer.resize(m_sampleBufferSize);
m_sampleBufferCount = 0;
m_undersampleCount = 0;
}
m_settings = settings;
if (useInterpolator) {

View File

@ -105,6 +105,8 @@ private:
SampleVector m_sampleBuffer;
unsigned int m_sampleBufferCount;
unsigned int m_sampleBufferSize;
Complex m_sum;
int m_undersampleCount;
NCOF m_nco;
PhaseLockComplex m_pll;

View File

@ -96,10 +96,14 @@ This indicator lights in green when the squelch is open. When the squelch is clo
This is the squelch threshold in dB. The average total power received in the signal bandwidth before demodulation is compared to this value and the squelch input is open above this value. It can be varied continuously in 0.1 dB steps from 0.0 to -100.0 dB using the dial button.
<h4>10: Squelch time gate</h4>
<h3>10: Squelch time gate</h3>
Number of milliseconds following squelch gate opening after which the signal is declared open. 0 means squelch is declared open with no delay and is suitable for burst signals. The value can be varied in steps of 10 ms from 0 to 990 ms.
<h4>11: Channel spectrum</h4>
<h3>11: Spectrum display frequency span</h3>
This is the spectrum display of the tracker channel. When the tracker is locked to the signal the center of the channel should fall almost in the middle of the signal spectrum (ideally in the middle when the tracker error is zero). Thus the locking can be followed dynamically and it can be more reliable than the lock indicator. A channel marker shows the tracker offset from the channel center frequency (tracker error). Its width is the tracker error tolerance but is hardly visible since it is 1/1000th of the channel width. Controls on the bottom of the panel are identical to the ones of the main spectrum display.
The channel signal is decimated by a power of two before being applied to the channel spectrum display. It is a kind of zoom on the center of the spectrum.
<h3>12: Channel spectrum</h3>
This is the spectrum display of the tracker channel. When the tracker is locked to the signal the center of the channel should fall almost in the middle of the signal spectrum (ideally in the middle when the tracker error is zero). Thus the locking can be followed dynamically and it can be more reliable than the lock indicator. A channel marker shows the tracker offset from the channel center frequency (tracker error). Its width is the tracker error tolerance and is &plusmn;1/1000th of the channel width. Controls on the bottom of the panel are identical to the ones of the main spectrum display.