mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-21 23:55:13 -05:00
Local Sink: FFT filter bands
This commit is contained in:
parent
4d1ab5d413
commit
bf765a00ec
@ -76,6 +76,7 @@ bool LocalSinkGUI::handleMessage(const Message& message)
|
||||
m_basebandSampleRate = notif.getSampleRate();
|
||||
updateAbsoluteCenterFrequency();
|
||||
displayRateAndShift();
|
||||
displayFFTBand();
|
||||
return true;
|
||||
}
|
||||
else if (LocalSink::MsgConfigureLocalSink::match(message))
|
||||
@ -106,6 +107,8 @@ LocalSinkGUI::LocalSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb
|
||||
ui(new Ui::LocalSinkGUI),
|
||||
m_pluginAPI(pluginAPI),
|
||||
m_deviceUISet(deviceUISet),
|
||||
m_currentBandIndex(-1),
|
||||
m_showFilterHighCut(false),
|
||||
m_deviceCenterFrequency(0),
|
||||
m_basebandSampleRate(0),
|
||||
m_tickCount(0)
|
||||
@ -194,9 +197,15 @@ void LocalSinkGUI::displaySettings()
|
||||
ui->localDevicePlay->setChecked(m_settings.m_play);
|
||||
ui->decimationFactor->setCurrentIndex(m_settings.m_log2Decim);
|
||||
ui->dsp->setChecked(m_settings.m_dsp);
|
||||
ui->gain->setValue(m_settings.m_gaindB);
|
||||
ui->gainText->setText(tr("%1").arg(m_settings.m_gaindB));
|
||||
ui->fft->setChecked(m_settings.m_fftOn);
|
||||
ui->fftSize->setCurrentIndex(m_settings.m_log2FFT-6);
|
||||
ui->fftWindow->setCurrentIndex((int) m_settings.m_fftWindow);
|
||||
ui->filterF2orW->setChecked(m_showFilterHighCut);
|
||||
applyDecimation();
|
||||
updateIndexLabel();
|
||||
displayFFTBand(false);
|
||||
|
||||
getRollupContents()->restoreState(m_rollupState);
|
||||
blockApplySettings(false);
|
||||
@ -215,6 +224,51 @@ void LocalSinkGUI::displayRateAndShift()
|
||||
m_channelMarker.setBandwidth(channelSampleRate);
|
||||
}
|
||||
|
||||
void LocalSinkGUI::displayFFTBand(bool blockApplySettings)
|
||||
{
|
||||
if (blockApplySettings) {
|
||||
this->blockApplySettings(true);
|
||||
}
|
||||
|
||||
ui->bandIndex->setMaximum(m_settings.m_fftBands.size() != 0 ? m_settings.m_fftBands.size() - 1 : 0);
|
||||
ui->bandIndex->setEnabled(m_settings.m_fftBands.size() != 0);
|
||||
ui->f1->setEnabled(m_settings.m_fftBands.size() != 0);
|
||||
ui->bandWidth->setEnabled(m_settings.m_fftBands.size() != 0);
|
||||
|
||||
if ((m_settings.m_fftBands.size() != 0) && (m_currentBandIndex < 0)) {
|
||||
m_currentBandIndex = 0;
|
||||
}
|
||||
|
||||
if (m_currentBandIndex >= 0)
|
||||
{
|
||||
ui->bandIndex->setValue(m_currentBandIndex);
|
||||
m_currentBandIndex = ui->bandIndex->value();
|
||||
ui->bandIndexText->setText(tr("%1").arg(m_currentBandIndex));
|
||||
ui->f1->setValue(m_settings.m_fftBands[m_currentBandIndex].first*1000);
|
||||
ui->bandWidth->setValue(m_settings.m_fftBands[m_currentBandIndex].second*1000);
|
||||
double channelSampleRate = ((double) m_basebandSampleRate) / (1<<m_settings.m_log2Decim);
|
||||
double f1 = (m_settings.m_fftBands[m_currentBandIndex].first)*channelSampleRate;
|
||||
double w = (m_settings.m_fftBands[m_currentBandIndex].second)*channelSampleRate;
|
||||
ui->f1Text->setText(displayScaled(f1, 5));
|
||||
|
||||
if (m_showFilterHighCut)
|
||||
{
|
||||
ui->bandwidthText->setToolTip("Filter high cut frequency");
|
||||
double f2 = f1 + w;
|
||||
ui->bandwidthText->setText(displayScaled(f2, 5));
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->bandwidthText->setToolTip("Filter width");
|
||||
ui->bandwidthText->setText(displayScaled(w, 5));
|
||||
}
|
||||
}
|
||||
|
||||
if (blockApplySettings) {
|
||||
this->blockApplySettings(false);
|
||||
}
|
||||
}
|
||||
|
||||
int LocalSinkGUI::getLocalDeviceIndexInCombo(int localDeviceIndex)
|
||||
{
|
||||
int index = 0;
|
||||
@ -363,6 +417,75 @@ void LocalSinkGUI::on_gain_valueChanged(int value)
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void LocalSinkGUI::on_fft_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_fftOn = checked;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void LocalSinkGUI::on_fftBandAdd_clicked()
|
||||
{
|
||||
if (m_settings.m_fftBands.size() == m_settings.m_maxFFTBands) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_settings.m_fftBands.push_back(std::pair<float,float>{-0.1f, 0.2f});
|
||||
m_currentBandIndex = m_settings.m_fftBands.size()-1;
|
||||
displayFFTBand();
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void LocalSinkGUI::on_fftBandDel_clicked()
|
||||
{
|
||||
m_settings.m_fftBands.erase(m_settings.m_fftBands.begin() + m_currentBandIndex);
|
||||
m_currentBandIndex--;
|
||||
displayFFTBand();
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void LocalSinkGUI::on_bandIndex_valueChanged(int value)
|
||||
{
|
||||
ui->bandIndexText->setText(tr("%1").arg(value));
|
||||
m_currentBandIndex = value;
|
||||
displayFFTBand();
|
||||
}
|
||||
|
||||
void LocalSinkGUI::on_f1_valueChanged(int value)
|
||||
{
|
||||
float f1 = value / 1000.0f;
|
||||
m_settings.m_fftBands[m_currentBandIndex].first = f1;
|
||||
float maxWidth = 0.5f - f1;
|
||||
|
||||
if (m_settings.m_fftBands[m_currentBandIndex].second > maxWidth) {
|
||||
m_settings.m_fftBands[m_currentBandIndex].second = maxWidth;
|
||||
}
|
||||
|
||||
displayFFTBand();
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void LocalSinkGUI::on_bandWidth_valueChanged(int value)
|
||||
{
|
||||
float w = value / 1000.0f;
|
||||
const float& f1 = m_settings.m_fftBands[m_currentBandIndex].first;
|
||||
float maxWidth = 0.5f - f1;
|
||||
|
||||
if (w > maxWidth) {
|
||||
m_settings.m_fftBands[m_currentBandIndex].second = maxWidth;
|
||||
} else {
|
||||
m_settings.m_fftBands[m_currentBandIndex].second = w;
|
||||
}
|
||||
|
||||
displayFFTBand();
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void LocalSinkGUI::on_filterF2orW_toggled(bool checked)
|
||||
{
|
||||
m_showFilterHighCut = checked;
|
||||
displayFFTBand();
|
||||
}
|
||||
|
||||
void LocalSinkGUI::applyDecimation()
|
||||
{
|
||||
uint32_t maxHash = 1;
|
||||
@ -386,6 +509,7 @@ void LocalSinkGUI::applyPosition()
|
||||
|
||||
updateAbsoluteCenterFrequency();
|
||||
displayRateAndShift();
|
||||
displayFFTBand();
|
||||
applySettings();
|
||||
}
|
||||
|
||||
@ -403,7 +527,14 @@ void LocalSinkGUI::makeUIConnections()
|
||||
QObject::connect(ui->localDevice, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &LocalSinkGUI::on_localDevice_currentIndexChanged);
|
||||
QObject::connect(ui->localDevicePlay, &ButtonSwitch::toggled, this, &LocalSinkGUI::on_localDevicePlay_toggled);
|
||||
QObject::connect(ui->dsp, &ButtonSwitch::toggled, this, &LocalSinkGUI::on_dsp_toggled);
|
||||
QObject::connect(ui->gain, &QSlider::valueChanged, this, &LocalSinkGUI::on_gain_valueChanged);
|
||||
QObject::connect(ui->gain, &QDial::valueChanged, this, &LocalSinkGUI::on_gain_valueChanged);
|
||||
QObject::connect(ui->fft, &ButtonSwitch::toggled, this, &LocalSinkGUI::on_fft_toggled);
|
||||
QObject::connect(ui->fftBandAdd, &QPushButton::clicked, this, &LocalSinkGUI::on_fftBandAdd_clicked);
|
||||
QObject::connect(ui->fftBandDel, &QPushButton::clicked, this, &LocalSinkGUI::on_fftBandDel_clicked);
|
||||
QObject::connect(ui->bandIndex, &QSlider::valueChanged, this, &LocalSinkGUI::on_bandIndex_valueChanged);
|
||||
QObject::connect(ui->f1, &QDial::valueChanged, this, &LocalSinkGUI::on_f1_valueChanged);
|
||||
QObject::connect(ui->bandWidth, &QDial::valueChanged, this, &LocalSinkGUI::on_bandWidth_valueChanged);
|
||||
QObject::connect(ui->filterF2orW, &ButtonSwitch::toggled, this, &LocalSinkGUI::on_filterF2orW_toggled);
|
||||
}
|
||||
|
||||
void LocalSinkGUI::updateAbsoluteCenterFrequency()
|
||||
@ -411,3 +542,20 @@ void LocalSinkGUI::updateAbsoluteCenterFrequency()
|
||||
int shift = m_shiftFrequencyFactor * m_basebandSampleRate;
|
||||
setStatusFrequency(m_deviceCenterFrequency + shift);
|
||||
}
|
||||
|
||||
QString LocalSinkGUI::displayScaled(int64_t value, int precision)
|
||||
{
|
||||
int64_t posValue = (value < 0) ? -value : value;
|
||||
|
||||
if (posValue < 1000) {
|
||||
return tr("%1").arg(QString::number(value, 'g', precision));
|
||||
} else if (posValue < 1000000) {
|
||||
return tr("%1k").arg(QString::number(value / 1000.0, 'g', precision));
|
||||
} else if (posValue < 1000000000) {
|
||||
return tr("%1M").arg(QString::number(value / 1000000.0, 'g', precision));
|
||||
} else if (posValue < 1000000000000) {
|
||||
return tr("%1%2").arg(QString::number(value / 1000000000.0, 'g', precision)).arg("G");
|
||||
} else {
|
||||
return tr("%1").arg(QString::number(value, 'e', precision));
|
||||
}
|
||||
}
|
||||
|
@ -68,6 +68,8 @@ private:
|
||||
ChannelMarker m_channelMarker;
|
||||
RollupState m_rollupState;
|
||||
LocalSinkSettings m_settings;
|
||||
int m_currentBandIndex;
|
||||
bool m_showFilterHighCut;
|
||||
qint64 m_deviceCenterFrequency;
|
||||
int m_basebandSampleRate;
|
||||
double m_shiftFrequencyFactor; //!< Channel frequency shift factor
|
||||
@ -86,11 +88,13 @@ private:
|
||||
void applySettings(bool force = false);
|
||||
void displaySettings();
|
||||
void displayRateAndShift();
|
||||
void displayFFTBand(bool blockApplySettings = true);
|
||||
bool handleMessage(const Message& message);
|
||||
void makeUIConnections();
|
||||
void updateAbsoluteCenterFrequency();
|
||||
void updateDeviceSetList(const QList<int>& deviceSetIndexes);
|
||||
int getLocalDeviceIndexInCombo(int localDeviceIndex);
|
||||
QString displayScaled(int64_t value, int precision);
|
||||
|
||||
void leaveEvent(QEvent*);
|
||||
void enterEvent(EnterEventType*);
|
||||
@ -106,6 +110,13 @@ private slots:
|
||||
void on_localDevicePlay_toggled(bool checked);
|
||||
void on_dsp_toggled(bool checked);
|
||||
void on_gain_valueChanged(int value);
|
||||
void on_fft_toggled(bool checked);
|
||||
void on_fftBandAdd_clicked();
|
||||
void on_fftBandDel_clicked();
|
||||
void on_bandIndex_valueChanged(int value);
|
||||
void on_f1_valueChanged(int value);
|
||||
void on_bandWidth_valueChanged(int value);
|
||||
void on_filterF2orW_toggled(bool checked);
|
||||
void onWidgetRolled(QWidget* widget, bool rollDown);
|
||||
void onMenuDialogCalled(const QPoint& p);
|
||||
void tick();
|
||||
|
@ -6,7 +6,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>452</width>
|
||||
<width>480</width>
|
||||
<height>467</height>
|
||||
</rect>
|
||||
</property>
|
||||
@ -18,7 +18,7 @@
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>414</width>
|
||||
<width>480</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
@ -36,7 +36,7 @@
|
||||
<rect>
|
||||
<x>1</x>
|
||||
<y>1</y>
|
||||
<width>451</width>
|
||||
<width>481</width>
|
||||
<height>141</height>
|
||||
</rect>
|
||||
</property>
|
||||
@ -277,6 +277,12 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="localDevicePlay">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Start/Stop processing</string>
|
||||
</property>
|
||||
@ -292,6 +298,15 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="dsp">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Toggle DSP processing</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>DSP</string>
|
||||
</property>
|
||||
@ -353,6 +368,15 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="fft">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Toggle FFT filter</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>FFT</string>
|
||||
</property>
|
||||
@ -372,12 +396,6 @@
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>FFT size</string>
|
||||
</property>
|
||||
@ -440,14 +458,8 @@
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>FFT filter window function</string>
|
||||
<string>FFT filter bands window function</string>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QComboBox::AdjustToContents</enum>
|
||||
@ -499,6 +511,22 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="fftFilterReverse">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Reverse filter bands (pass <-> cut)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>R</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
@ -517,9 +545,12 @@
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="fftLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<widget class="QLabel" name="fftBandLabel">
|
||||
<property name="toolTip">
|
||||
<string>FFT filter bands</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Band</string>
|
||||
<string>FFT</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -551,6 +582,131 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="fftBandAddDel">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="fftBandAdd">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>18</width>
|
||||
<height>18</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="palette">
|
||||
<palette>
|
||||
<active>
|
||||
<colorrole role="ButtonText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</active>
|
||||
<inactive>
|
||||
<colorrole role="ButtonText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</inactive>
|
||||
<disabled>
|
||||
<colorrole role="ButtonText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>190</red>
|
||||
<green>190</green>
|
||||
<blue>190</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</disabled>
|
||||
</palette>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Sans</family>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Add a new FFT band</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>+</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="fftBandDel">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>18</width>
|
||||
<height>18</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="palette">
|
||||
<palette>
|
||||
<active>
|
||||
<colorrole role="ButtonText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</active>
|
||||
<inactive>
|
||||
<colorrole role="ButtonText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</inactive>
|
||||
<disabled>
|
||||
<colorrole role="ButtonText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>190</red>
|
||||
<green>190</green>
|
||||
<blue>190</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</disabled>
|
||||
</palette>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Sans</family>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Remove current FFT band</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>-</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
@ -572,17 +728,20 @@
|
||||
<property name="text">
|
||||
<string>00</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="f1Label">
|
||||
<property name="text">
|
||||
<string>f1</string>
|
||||
<string>F1</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDial" name="f1Bin">
|
||||
<widget class="QDial" name="f1">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
@ -596,13 +755,13 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Frequency limit #1 bin</string>
|
||||
<string>Filter low cut frequency</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
<number>-500</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>1023</number>
|
||||
<number>500</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
@ -618,7 +777,7 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Frequency limit #1 frequency</string>
|
||||
<string>Filter low cut frequency</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>100.00k</string>
|
||||
@ -636,14 +795,14 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="f2Label">
|
||||
<widget class="QLabel" name="widthLabel">
|
||||
<property name="text">
|
||||
<string>f2</string>
|
||||
<string>W</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDial" name="f2Bin">
|
||||
<widget class="QDial" name="bandWidth">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
@ -657,13 +816,13 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Frequency limit #2 bin</string>
|
||||
<string>FFT band width</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>1023</number>
|
||||
<number>1000</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
@ -671,7 +830,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="f2Text">
|
||||
<widget class="QLabel" name="bandwidthText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
@ -679,7 +838,7 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Frequency limit #2 frequency</string>
|
||||
<string>Filter width</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>100.00k</string>
|
||||
@ -689,6 +848,30 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="filterF2orW">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Filter width / high cut display toggle</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/arrow_2head_h.png</normaloff>
|
||||
<normalon>:/arrow_down.png</normalon>:/arrow_2head_h.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
|
@ -40,6 +40,9 @@ void LocalSinkSettings::resetToDefaults()
|
||||
m_play = false;
|
||||
m_dsp = false;
|
||||
m_gaindB = 0;
|
||||
m_fftOn = false;
|
||||
m_log2FFT = 10;
|
||||
m_fftWindow = FFTWindow::Function::Bartlett;
|
||||
m_streamIndex = 0;
|
||||
m_useReverseAPI = false;
|
||||
m_reverseAPIAddress = "127.0.0.1";
|
||||
@ -84,6 +87,23 @@ QByteArray LocalSinkSettings::serialize() const
|
||||
s.writeBlob(21, m_spectrumGUI->serialize());
|
||||
}
|
||||
|
||||
s.writeBool(22, m_fftOn);
|
||||
s.writeU32(23, (int) m_fftWindow);
|
||||
|
||||
s.writeU32(99, m_fftBands.size());
|
||||
int i = 0;
|
||||
|
||||
for (auto fftBand : m_fftBands)
|
||||
{
|
||||
s.writeFloat(100 + 2*i, fftBand.first);
|
||||
s.writeFloat(101 + 2*i, fftBand.second);
|
||||
i++;
|
||||
|
||||
if (i == m_maxFFTBands) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return s.final();
|
||||
}
|
||||
|
||||
@ -99,7 +119,7 @@ bool LocalSinkSettings::deserialize(const QByteArray& data)
|
||||
|
||||
if(d.getVersion() == 1)
|
||||
{
|
||||
uint32_t tmp;
|
||||
uint32_t utmp;
|
||||
QString strtmp;
|
||||
QByteArray bytetmp;
|
||||
|
||||
@ -115,20 +135,20 @@ bool LocalSinkSettings::deserialize(const QByteArray& data)
|
||||
d.readString(6, &m_title, "Local sink");
|
||||
d.readBool(7, &m_useReverseAPI, false);
|
||||
d.readString(8, &m_reverseAPIAddress, "127.0.0.1");
|
||||
d.readU32(9, &tmp, 0);
|
||||
d.readU32(9, &utmp, 0);
|
||||
|
||||
if ((tmp > 1023) && (tmp < 65535)) {
|
||||
m_reverseAPIPort = tmp;
|
||||
if ((utmp > 1023) && (utmp < 65535)) {
|
||||
m_reverseAPIPort = utmp;
|
||||
} else {
|
||||
m_reverseAPIPort = 8888;
|
||||
}
|
||||
|
||||
d.readU32(10, &tmp, 0);
|
||||
m_reverseAPIDeviceIndex = tmp > 99 ? 99 : tmp;
|
||||
d.readU32(11, &tmp, 0);
|
||||
m_reverseAPIChannelIndex = tmp > 99 ? 99 : tmp;
|
||||
d.readU32(12, &tmp, 0);
|
||||
m_log2Decim = tmp > 6 ? 6 : tmp;
|
||||
d.readU32(10, &utmp, 0);
|
||||
m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp;
|
||||
d.readU32(11, &utmp, 0);
|
||||
m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp;
|
||||
d.readU32(12, &utmp, 0);
|
||||
m_log2Decim = utmp > 6 ? 6 : utmp;
|
||||
d.readU32(13, &m_filterChainHash, 0);
|
||||
d.readS32(14, &m_streamIndex, 0);
|
||||
|
||||
@ -150,6 +170,24 @@ bool LocalSinkSettings::deserialize(const QByteArray& data)
|
||||
m_spectrumGUI->deserialize(bytetmp);
|
||||
}
|
||||
|
||||
d.readBool(22, &m_fftOn, false);
|
||||
d.readU32(23, &utmp, 0);
|
||||
m_fftWindow = (utmp > (uint32_t) FFTWindow::Function::BlackmanHarris7) ?
|
||||
FFTWindow::Function::BlackmanHarris7 :
|
||||
(FFTWindow::Function) utmp;
|
||||
|
||||
uint32_t nbBands;
|
||||
d.readU32(99, &nbBands, 0);
|
||||
m_fftBands.clear();
|
||||
|
||||
for (uint32_t i = 0; i < std::min(nbBands, m_maxFFTBands); i++)
|
||||
{
|
||||
float f1, w;
|
||||
d.readFloat(100 + 2*i, &f1, 0);
|
||||
d.readFloat(101 + 2*i, &w, 0);
|
||||
m_fftBands.push_back(std::pair<float, float>{f1, w});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
@ -21,6 +21,8 @@
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
|
||||
#include "dsp/fftwindow.h"
|
||||
|
||||
class Serializable;
|
||||
|
||||
struct LocalSinkSettings
|
||||
@ -33,6 +35,11 @@ struct LocalSinkSettings
|
||||
bool m_play;
|
||||
bool m_dsp;
|
||||
int m_gaindB;
|
||||
bool m_fftOn;
|
||||
uint32_t m_log2FFT;
|
||||
FFTWindow::Function m_fftWindow;
|
||||
static const uint32_t m_maxFFTBands = 20;
|
||||
std::vector<std::pair<float, float>> m_fftBands;
|
||||
int m_streamIndex; //!< MIMO channel. Not relevant when connected to SI (single Rx).
|
||||
bool m_useReverseAPI;
|
||||
QString m_reverseAPIAddress;
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "dsp/devicesamplesource.h"
|
||||
#include "dsp/hbfilterchainconverter.h"
|
||||
#include "dsp/spectrumvis.h"
|
||||
#include "dsp/fftfilt.h"
|
||||
#include "util/db.h"
|
||||
|
||||
#include "localsinkworker.h"
|
||||
@ -39,16 +40,49 @@ LocalSinkSink::LocalSinkSink() :
|
||||
m_deviceSampleRate(48000)
|
||||
{
|
||||
m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(4000000));
|
||||
// m_fftFilter = new fftfilt(0.1f, 0.4f, 1<<m_settings.m_log2FFT);
|
||||
m_fftFilter = new fftfilt(1<<m_settings.m_log2FFT);
|
||||
applySettings(m_settings, true);
|
||||
}
|
||||
|
||||
LocalSinkSink::~LocalSinkSink()
|
||||
{
|
||||
delete m_fftFilter;
|
||||
}
|
||||
|
||||
void LocalSinkSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
|
||||
{
|
||||
if (m_settings.m_dsp && (m_settings.m_gaindB != 0))
|
||||
if (m_settings.m_dsp && m_settings.m_fftOn)
|
||||
{
|
||||
fftfilt::cmplx *rf;
|
||||
int rf_out;
|
||||
|
||||
for (SampleVector::const_iterator it = begin; it != end; ++it)
|
||||
{
|
||||
Complex c(it->real(), it->imag());
|
||||
rf_out = m_fftFilter->runFilt(c, &rf); // filter RF
|
||||
|
||||
if (rf_out > 0)
|
||||
{
|
||||
m_spectrumBuffer.resize(rf_out);
|
||||
std::transform(
|
||||
rf,
|
||||
rf + rf_out,
|
||||
m_spectrumBuffer.begin(),
|
||||
[this](const fftfilt::cmplx& c) -> Sample {
|
||||
return Sample(c.real()*m_gain, c.imag()*m_gain);
|
||||
}
|
||||
);
|
||||
if (m_running && m_deviceSource) {
|
||||
m_deviceSource->getSampleFifo()->write(m_spectrumBuffer.begin(), m_spectrumBuffer.begin() + rf_out);
|
||||
}
|
||||
if (m_spectrumSink) {
|
||||
m_spectrumSink->feed(m_spectrumBuffer.begin(), m_spectrumBuffer.begin() + rf_out, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_settings.m_dsp && (m_settings.m_gaindB != 0))
|
||||
{
|
||||
m_spectrumBuffer.resize(end - begin);
|
||||
std::transform(
|
||||
@ -74,11 +108,11 @@ void LocalSinkSink::feed(const SampleVector::const_iterator& begin, const Sample
|
||||
if (m_running && m_deviceSource) {
|
||||
m_deviceSource->getSampleFifo()->write(begin, end);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_spectrumSink) {
|
||||
m_spectrumSink->feed(begin, end, false);
|
||||
}
|
||||
}
|
||||
|
||||
// m_sampleFifo.write(begin, end);
|
||||
}
|
||||
|
||||
@ -153,12 +187,22 @@ void LocalSinkSink::applySettings(const LocalSinkSettings& settings, bool force)
|
||||
qDebug() << "LocalSinkSink::applySettings:"
|
||||
<< " m_localDeviceIndex: " << settings.m_localDeviceIndex
|
||||
<< " m_streamIndex: " << settings.m_streamIndex
|
||||
<< " m_dsp: " << settings.m_dsp
|
||||
<< " m_gaindB: " << settings.m_gaindB
|
||||
<< " m_fftOn: " << settings.m_fftOn
|
||||
<< " force: " << force;
|
||||
|
||||
if ((settings.m_gaindB != m_settings.m_gaindB) || force) {
|
||||
m_gain = CalcDb::powerFromdB(settings.m_gaindB/2.0); // Amplitude gain
|
||||
}
|
||||
|
||||
if ((settings.m_fftOn != m_settings.m_fftOn) || force)
|
||||
{
|
||||
if (settings.m_fftOn) {
|
||||
m_fftFilter->create_filter(m_settings.m_fftBands, true, FFTWindow::Function::Rectangle);
|
||||
}
|
||||
}
|
||||
|
||||
m_settings = settings;
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@
|
||||
class DeviceSampleSource;
|
||||
class LocalSinkWorker;
|
||||
class SpectrumVis;
|
||||
class fftfilt;
|
||||
|
||||
class LocalSinkSink : public QObject, public ChannelSampleSink {
|
||||
Q_OBJECT
|
||||
@ -54,6 +55,7 @@ private:
|
||||
SampleVector m_spectrumBuffer;
|
||||
bool m_running;
|
||||
float m_gain; //!< Amplitude gain
|
||||
fftfilt* m_fftFilter;
|
||||
|
||||
uint64_t m_centerFrequency;
|
||||
int64_t m_frequencyOffset;
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
#include <typeinfo>
|
||||
#include <array>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
@ -156,6 +157,87 @@ void fftfilt::create_filter(float f1, float f2, FFTWindow::Function wf)
|
||||
}
|
||||
}
|
||||
|
||||
void fftfilt::create_filter(const std::vector<std::pair<float, float>>& limits, bool pass, FFTWindow::Function wf)
|
||||
{
|
||||
// initialize the filter canvas
|
||||
std::vector<int> canvas(flen, pass ? 0 : 1);
|
||||
fftfilt::cmplx* xfilter = new fftfilt::cmplx[flen];
|
||||
|
||||
for (const auto& fs : limits)
|
||||
{
|
||||
const float& f1 = fs.first + 0.5;
|
||||
const float& w = fs.second > 0.0 ? fs.second : 0.0;
|
||||
const float& f2 = f1 + w;
|
||||
|
||||
for (int i = 0; i < flen; i++)
|
||||
{
|
||||
if (pass) // pass
|
||||
{
|
||||
if ((i >= f1*flen) && (i <= f2*flen)) {
|
||||
canvas[i] = 1;
|
||||
}
|
||||
}
|
||||
else // reject
|
||||
{
|
||||
if ((i >= f1*flen) && (i <= f2*flen)) {
|
||||
canvas[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<int,int>> indexes;
|
||||
int c = 0;
|
||||
|
||||
for (int i = 0; i < flen; i++)
|
||||
{
|
||||
if ((canvas[i] == 1) && (c == 0)) {
|
||||
indexes.push_back(std::pair<int,int>{i, 0});
|
||||
}
|
||||
|
||||
if ((canvas[i] == 0) && (c == 1)) {
|
||||
indexes.back().second = i;
|
||||
}
|
||||
|
||||
xfilter[i] = cmplx(canvas[i], 0);
|
||||
c = canvas[i];
|
||||
}
|
||||
|
||||
// Apply window
|
||||
for (const auto& wband : indexes)
|
||||
{
|
||||
FFTWindow fwin;
|
||||
fwin.create(wf, wband.second - wband.first);
|
||||
fwin.apply(&xfilter[wband.first]);
|
||||
}
|
||||
|
||||
// Rearrange
|
||||
std::copy(&xfilter[flen2], &xfilter[flen-1], filter);
|
||||
std::copy(&xfilter[0], &xfilter[flen2-1], &filter[flen2]);
|
||||
|
||||
|
||||
// // normalize the output filter for unity gain
|
||||
// float scale = 0, mag;
|
||||
|
||||
// for (int i = 0; i < flen2; i++)
|
||||
// {
|
||||
// mag = abs(filter[i]);
|
||||
|
||||
// if (mag > scale) {
|
||||
// scale = mag;
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (scale != 0)
|
||||
// {
|
||||
// for (int i = 0; i < flen; i++) {
|
||||
// filter[i] /= scale;
|
||||
// }
|
||||
// }
|
||||
|
||||
delete[] xfilter;
|
||||
}
|
||||
|
||||
// Double the size of FFT used for equivalent SSB filter or assume FFT is half the size of the one used for SSB
|
||||
void fftfilt::create_dsb_filter(float f2, FFTWindow::Function wf)
|
||||
{
|
||||
|
@ -27,6 +27,7 @@ public:
|
||||
// f1 < f2 ==> bandpass
|
||||
// f1 > f2 ==> band reject
|
||||
void create_filter(float f1, float f2, FFTWindow::Function wf = FFTWindow::Blackman);
|
||||
void create_filter(const std::vector<std::pair<float, float>>& limits, bool pass = true, FFTWindow::Function wf = FFTWindow::Blackman);
|
||||
void create_dsb_filter(float f2, FFTWindow::Function wf = FFTWindow::Blackman);
|
||||
void create_asym_filter(float fopp, float fin, FFTWindow::Function wf = FFTWindow::Blackman); //!< two different filters for in band and opposite band
|
||||
void create_rrc_filter(float fb, float a); //!< root raised cosine. fb is half the band pass
|
||||
|
BIN
sdrgui/resources/arrow_2head_h.png
Normal file
BIN
sdrgui/resources/arrow_2head_h.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 774 B |
@ -1,5 +1,6 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>arrow_2head_h.png</file>
|
||||
<file>truncate.png</file>
|
||||
<file>caliper.png</file>
|
||||
<file>flip_windows.png</file>
|
||||
|
Loading…
Reference in New Issue
Block a user