From bf765a00ec071147373ce29e188681742b084029 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 10 Dec 2022 19:02:38 +0100 Subject: [PATCH] Local Sink: FFT filter bands --- plugins/channelrx/localsink/localsinkgui.cpp | 150 ++++++++++- plugins/channelrx/localsink/localsinkgui.h | 11 + plugins/channelrx/localsink/localsinkgui.ui | 245 +++++++++++++++--- .../channelrx/localsink/localsinksettings.cpp | 58 ++++- .../channelrx/localsink/localsinksettings.h | 7 + plugins/channelrx/localsink/localsinksink.cpp | 52 +++- plugins/channelrx/localsink/localsinksink.h | 2 + sdrbase/dsp/fftfilt.cpp | 82 ++++++ sdrbase/dsp/fftfilt.h | 1 + sdrgui/resources/arrow_2head_h.png | Bin 0 -> 774 bytes sdrgui/resources/res.qrc | 1 + 11 files changed, 563 insertions(+), 46 deletions(-) create mode 100644 sdrgui/resources/arrow_2head_h.png diff --git a/plugins/channelrx/localsink/localsinkgui.cpp b/plugins/channelrx/localsink/localsinkgui.cpp index a8cf78ae4..08f520213 100644 --- a/plugins/channelrx/localsink/localsinkgui.cpp +++ b/plugins/channelrx/localsink/localsinkgui.cpp @@ -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<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{-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::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)); + } +} diff --git a/plugins/channelrx/localsink/localsinkgui.h b/plugins/channelrx/localsink/localsinkgui.h index 4fb56f625..f79a4cb41 100644 --- a/plugins/channelrx/localsink/localsinkgui.h +++ b/plugins/channelrx/localsink/localsinkgui.h @@ -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& 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(); diff --git a/plugins/channelrx/localsink/localsinkgui.ui b/plugins/channelrx/localsink/localsinkgui.ui index 41b218f24..f29e5cea7 100644 --- a/plugins/channelrx/localsink/localsinkgui.ui +++ b/plugins/channelrx/localsink/localsinkgui.ui @@ -6,7 +6,7 @@ 0 0 - 452 + 480 467 @@ -18,7 +18,7 @@ - 414 + 480 0 @@ -36,7 +36,7 @@ 1 1 - 451 + 481 141 @@ -277,6 +277,12 @@ + + + 24 + 24 + + Start/Stop processing @@ -292,6 +298,15 @@ + + + 16777215 + 24 + + + + Toggle DSP processing + DSP @@ -353,6 +368,15 @@ + + + 16777215 + 24 + + + + Toggle FFT filter + FFT @@ -372,12 +396,6 @@ 0 - - - 50 - 16777215 - - FFT size @@ -440,14 +458,8 @@ 0 - - - 60 - 16777215 - - - FFT filter window function + FFT filter bands window function QComboBox::AdjustToContents @@ -499,6 +511,22 @@ + + + + + 24 + 24 + + + + Reverse filter bands (pass <-> cut) + + + R + + + @@ -517,9 +545,12 @@ - + + + FFT filter bands + - Band + FFT @@ -551,6 +582,131 @@ + + + + 0 + + + + + + 18 + 18 + + + + + + + + + 255 + 255 + 255 + + + + + + + + + 255 + 255 + 255 + + + + + + + + + 190 + 190 + 190 + + + + + + + + + Liberation Sans + 10 + + + + Add a new FFT band + + + + + + + + + + + + 18 + 18 + + + + + + + + + 255 + 255 + 255 + + + + + + + + + 255 + 255 + 255 + + + + + + + + + 190 + 190 + 190 + + + + + + + + + Liberation Sans + 10 + + + + Remove current FFT band + + + - + + + + + @@ -572,17 +728,20 @@ 00 + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + - f1 + F1 - + 24 @@ -596,13 +755,13 @@ - Frequency limit #1 bin + Filter low cut frequency - 0 + -500 - 1023 + 500 1 @@ -618,7 +777,7 @@ - Frequency limit #1 frequency + Filter low cut frequency 100.00k @@ -636,14 +795,14 @@ - + - f2 + W - + 24 @@ -657,13 +816,13 @@ - Frequency limit #2 bin + FFT band width 0 - 1023 + 1000 1 @@ -671,7 +830,7 @@ - + 50 @@ -679,7 +838,7 @@ - Frequency limit #2 frequency + Filter width 100.00k @@ -689,6 +848,30 @@ + + + + + 24 + 24 + + + + Filter width / high cut display toggle + + + + + + + :/arrow_2head_h.png + :/arrow_down.png:/arrow_2head_h.png + + + true + + + diff --git a/plugins/channelrx/localsink/localsinksettings.cpp b/plugins/channelrx/localsink/localsinksettings.cpp index 8a7b3478e..d433fb6ee 100644 --- a/plugins/channelrx/localsink/localsinksettings.cpp +++ b/plugins/channelrx/localsink/localsinksettings.cpp @@ -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{f1, w}); + } + return true; } else diff --git a/plugins/channelrx/localsink/localsinksettings.h b/plugins/channelrx/localsink/localsinksettings.h index 6502e1115..eda4b8190 100644 --- a/plugins/channelrx/localsink/localsinksettings.h +++ b/plugins/channelrx/localsink/localsinksettings.h @@ -21,6 +21,8 @@ #include #include +#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> m_fftBands; int m_streamIndex; //!< MIMO channel. Not relevant when connected to SI (single Rx). bool m_useReverseAPI; QString m_reverseAPIAddress; diff --git a/plugins/channelrx/localsink/localsinksink.cpp b/plugins/channelrx/localsink/localsinksink.cpp index 7e1125775..01e451abd 100644 --- a/plugins/channelrx/localsink/localsinksink.cpp +++ b/plugins/channelrx/localsink/localsinksink.cpp @@ -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<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); + } } - 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; } diff --git a/plugins/channelrx/localsink/localsinksink.h b/plugins/channelrx/localsink/localsinksink.h index 0aa61234c..741706efc 100644 --- a/plugins/channelrx/localsink/localsinksink.h +++ b/plugins/channelrx/localsink/localsinksink.h @@ -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; diff --git a/sdrbase/dsp/fftfilt.cpp b/sdrbase/dsp/fftfilt.cpp index 8ca8a28d6..3fabae3be 100644 --- a/sdrbase/dsp/fftfilt.cpp +++ b/sdrbase/dsp/fftfilt.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -156,6 +157,87 @@ void fftfilt::create_filter(float f1, float f2, FFTWindow::Function wf) } } +void fftfilt::create_filter(const std::vector>& limits, bool pass, FFTWindow::Function wf) +{ + // initialize the filter canvas + std::vector 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> indexes; + int c = 0; + + for (int i = 0; i < flen; i++) + { + if ((canvas[i] == 1) && (c == 0)) { + indexes.push_back(std::pair{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) { diff --git a/sdrbase/dsp/fftfilt.h b/sdrbase/dsp/fftfilt.h index 1aba83b90..806c5f8c4 100644 --- a/sdrbase/dsp/fftfilt.h +++ b/sdrbase/dsp/fftfilt.h @@ -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>& 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 diff --git a/sdrgui/resources/arrow_2head_h.png b/sdrgui/resources/arrow_2head_h.png new file mode 100644 index 0000000000000000000000000000000000000000..3e51a02df25fc6352edc9d0fa607eaf99ff64900 GIT binary patch literal 774 zcmV+h1Nr=kP)EX>4Tx04R}tkv&MmKpe$iTcuJf4i*u0$WWauh>AFB6^c+H)C#RSm|Xe=O&XFE z7e~Rh;NZt%)xpJCR|i)?5c~jfbaGO3krMxx6k5c1aNLh~_a1le0DryARI_6oP&La) zCE`LRyD9`<5k?5z=*N)6Onpuilkgm0_we!cF2=LG&;2=il$^-`pFljzbi*RvAfDc| zbk6(45muBG;&b9LgDyz?$aUG}H_ioz{X8>bq*L?65n`dx#&R38qM;H`5l0nOqkMnH zWrgz=XSG~q&3p0}hI87=GS_JiBY{OML4*JqRg_SMg&3_GDJD|1ANTMNI)0H{GPz1% z zj20++-Q(R|?Y;ebrrF;QJS%dMgVax300006VoOIv0001c0Pq}I&e8w?010qNS#tmY z4#WTe4#WYKD-Ig~000McNliru<_ro884sau1Tp{s02y>eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{007=eL_t(Y$L*B64Z<)GhW|qpGz@^0-XlQ42s{cTcE}pc zkqIafBOoC$0u#^&3aXC+OPGKoD}*keWXX^J?(Fl~Qb)&M0dinovpxs%CXNR{!IM>u zZsj!vFi3Tt0Y_e>1i;&5RZYKQFqO2GGzz92u#CiTL4P9Y1gy>M5X<+S-?@L>OOWq2 zDI}@qW9%edTP(n<_xW}T2b{m` + arrow_2head_h.png truncate.png caliper.png flip_windows.png