mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-17 13:51:47 -05:00
FFT factory: implementation base
This commit is contained in:
parent
8e8323e925
commit
926f45d9fb
@ -93,6 +93,7 @@ set(sdrbase_SOURCES
|
||||
dsp/dspdevicemimoengine.cpp
|
||||
dsp/fftcorr.cpp
|
||||
dsp/fftengine.cpp
|
||||
dsp/fftfactory.cpp
|
||||
dsp/fftfilt.cpp
|
||||
dsp/fftwindow.cpp
|
||||
dsp/filterrc.cpp
|
||||
@ -216,6 +217,7 @@ set(sdrbase_HEADERS
|
||||
dsp/dsptypes.h
|
||||
dsp/fftcorr.h
|
||||
dsp/fftengine.h
|
||||
dsp/fftfactory.h
|
||||
dsp/fftfilt.h
|
||||
dsp/fftwengine.h
|
||||
dsp/fftwindow.h
|
||||
|
@ -23,13 +23,15 @@
|
||||
#include "dsp/dspdevicesourceengine.h"
|
||||
#include "dsp/dspdevicesinkengine.h"
|
||||
#include "dsp/dspdevicemimoengine.h"
|
||||
#include "dsp/fftfactory.h"
|
||||
|
||||
DSPEngine::DSPEngine() :
|
||||
m_deviceSourceEnginesUIDSequence(0),
|
||||
m_deviceSinkEnginesUIDSequence(0),
|
||||
m_deviceMIMOEnginesUIDSequence(0),
|
||||
m_audioInputDeviceIndex(-1), // default device
|
||||
m_audioOutputDeviceIndex(-1) // default device
|
||||
m_audioOutputDeviceIndex(-1), // default device
|
||||
m_fftFactory(nullptr)
|
||||
{
|
||||
m_dvSerialSupport = false;
|
||||
m_mimoSupport = false;
|
||||
@ -45,6 +47,10 @@ DSPEngine::~DSPEngine()
|
||||
delete *it;
|
||||
++it;
|
||||
}
|
||||
|
||||
if (m_fftFactory) {
|
||||
delete m_fftFactory;
|
||||
}
|
||||
}
|
||||
|
||||
Q_GLOBAL_STATIC(DSPEngine, dspEngine)
|
||||
@ -185,3 +191,13 @@ void DSPEngine::pushMbeFrame(
|
||||
{
|
||||
m_ambeEngine.pushMbeFrame(mbeFrame, mbeRateIndex, mbeVolumeIndex, channels, useHP, upsampling, audioFifo);
|
||||
}
|
||||
|
||||
void DSPEngine::createFFTFactory(const QString& fftWisdomFileName)
|
||||
{
|
||||
m_fftFactory = new FFTFactory(fftWisdomFileName);
|
||||
}
|
||||
|
||||
void DSPEngine::preAllocateFFTs()
|
||||
{
|
||||
m_fftFactory->preallocate(7, 12, 1, 0); // pre-acllocate forward FFT only 1 per size from 128 to 4096
|
||||
}
|
@ -32,6 +32,7 @@
|
||||
class DSPDeviceSourceEngine;
|
||||
class DSPDeviceSinkEngine;
|
||||
class DSPDeviceMIMOEngine;
|
||||
class FFTFactory;
|
||||
|
||||
class SDRBASE_API DSPEngine : public QObject {
|
||||
Q_OBJECT
|
||||
@ -84,6 +85,9 @@ public:
|
||||
const QTimer& getMasterTimer() const { return m_masterTimer; }
|
||||
void setMIMOSupport(bool mimoSupport) { m_mimoSupport = mimoSupport; }
|
||||
bool getMIMOSupport() const { return m_mimoSupport; }
|
||||
void createFFTFactory(const QString& fftWisdomFileName);
|
||||
void preAllocateFFTs();
|
||||
FFTFactory *getFFTFactory() { return m_fftFactory; }
|
||||
|
||||
private:
|
||||
std::vector<DSPDeviceSourceEngine*> m_deviceSourceEngines;
|
||||
@ -99,6 +103,7 @@ private:
|
||||
bool m_dvSerialSupport;
|
||||
bool m_mimoSupport;
|
||||
AMBEEngine m_ambeEngine;
|
||||
FFTFactory *m_fftFactory;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_DSPENGINE_H
|
||||
|
@ -46,9 +46,9 @@ void fftcorr::init_fft()
|
||||
fftcorr::fftcorr(int len) :
|
||||
flen(len),
|
||||
flen2(len>>1),
|
||||
fftA(FFTEngine::create()),
|
||||
fftB(FFTEngine::create()),
|
||||
fftInvA(FFTEngine::create())
|
||||
fftA(FFTEngine::create(QString(""))), // TODO: use factory
|
||||
fftB(FFTEngine::create(QString(""))), // TODO: use factory
|
||||
fftInvA(FFTEngine::create(QString(""))) // TODO: use factory
|
||||
{
|
||||
init_fft();
|
||||
}
|
||||
|
@ -10,13 +10,14 @@ FFTEngine::~FFTEngine()
|
||||
{
|
||||
}
|
||||
|
||||
FFTEngine* FFTEngine::create()
|
||||
FFTEngine* FFTEngine::create(const QString& fftWisdomFileName)
|
||||
{
|
||||
#ifdef USE_FFTW
|
||||
qDebug("FFTEngine::create: using FFTW engine");
|
||||
return new FFTWEngine;
|
||||
return new FFTWEngine(fftWisdomFileName);
|
||||
#elif USE_KISSFFT
|
||||
qDebug("FFTEngine::create: using KissFFT engine");
|
||||
(void) fftWisdomFileName;
|
||||
return new KissEngine;
|
||||
#else // USE_KISSFFT
|
||||
qCritical("FFTEngine::create: no engine built");
|
||||
|
@ -1,6 +1,8 @@
|
||||
#ifndef INCLUDE_FFTENGINE_H
|
||||
#define INCLUDE_FFTENGINE_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include "dsp/dsptypes.h"
|
||||
#include "export.h"
|
||||
|
||||
@ -14,7 +16,7 @@ public:
|
||||
virtual Complex* in() = 0;
|
||||
virtual Complex* out() = 0;
|
||||
|
||||
static FFTEngine* create();
|
||||
static FFTEngine* create(const QString& fftWisdomFileName);
|
||||
};
|
||||
|
||||
#endif // INCLUDE_FFTENGINE_H
|
||||
|
128
sdrbase/dsp/fftfactory.cpp
Normal file
128
sdrbase/dsp/fftfactory.cpp
Normal file
@ -0,0 +1,128 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// 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 "fftfactory.h"
|
||||
|
||||
FFTFactory::FFTFactory(const QString& fftwWisdomFileName) :
|
||||
m_fftwWisdomFileName(fftwWisdomFileName)
|
||||
{}
|
||||
|
||||
FFTFactory::~FFTFactory()
|
||||
{
|
||||
qDebug("FFTFactory::~FFTFactory: deleting FFTs");
|
||||
|
||||
for (auto mIt = m_fftEngineBySize.begin(); mIt != m_fftEngineBySize.end(); ++mIt)
|
||||
{
|
||||
for (auto eIt = mIt->second.begin(); eIt != mIt->second.end(); ++eIt) {
|
||||
delete eIt->m_engine;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FFTFactory::preallocate(
|
||||
unsigned int minLog2Size,
|
||||
unsigned int maxLog2Size,
|
||||
unsigned int numberFFT,
|
||||
unsigned int numberInvFFT)
|
||||
{
|
||||
if (minLog2Size <= maxLog2Size)
|
||||
{
|
||||
for (unsigned int log2Size = minLog2Size; log2Size <= maxLog2Size; log2Size++)
|
||||
{
|
||||
unsigned int fftSize = 1<<log2Size;
|
||||
m_fftEngineBySize.insert(std::pair<unsigned int, std::vector<AllocatedEngine>>(fftSize, std::vector<AllocatedEngine>()));
|
||||
m_invFFTEngineBySize.insert(std::pair<unsigned int, std::vector<AllocatedEngine>>(fftSize, std::vector<AllocatedEngine>()));
|
||||
std::vector<AllocatedEngine>& fftEngines = m_fftEngineBySize[fftSize];
|
||||
std::vector<AllocatedEngine>& invFFTEngines = m_invFFTEngineBySize[fftSize];
|
||||
|
||||
for (unsigned int i = 0; i < numberFFT; i++)
|
||||
{
|
||||
fftEngines.push_back(AllocatedEngine());
|
||||
fftEngines.back().m_engine = FFTEngine::create(m_fftwWisdomFileName);
|
||||
fftEngines.back().m_engine->configure(fftSize, false);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < numberInvFFT; i++)
|
||||
{
|
||||
invFFTEngines.push_back(AllocatedEngine());
|
||||
invFFTEngines.back().m_engine = FFTEngine::create(m_fftwWisdomFileName);
|
||||
invFFTEngines.back().m_engine->configure(fftSize, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int FFTFactory::getEngine(unsigned int fftSize, bool inverse, FFTEngine **engine)
|
||||
{
|
||||
std::map<unsigned int, std::vector<AllocatedEngine>>& enginesBySize = inverse ?
|
||||
m_invFFTEngineBySize : m_fftEngineBySize;
|
||||
|
||||
if (enginesBySize.find(fftSize) == enginesBySize.end())
|
||||
{
|
||||
enginesBySize.insert(std::pair<unsigned int, std::vector<AllocatedEngine>>(fftSize, std::vector<AllocatedEngine>()));
|
||||
std::vector<AllocatedEngine>& engines = enginesBySize[fftSize];
|
||||
engines.push_back(AllocatedEngine());
|
||||
engines.back().m_inUse = true;
|
||||
engines.back().m_engine = FFTEngine::create(m_fftwWisdomFileName);
|
||||
engines.back().m_engine->configure(fftSize, true);
|
||||
*engine = engines.back().m_engine;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int i = 0;
|
||||
|
||||
for (; i < enginesBySize[fftSize].size(); i++)
|
||||
{
|
||||
if (!enginesBySize[fftSize][i].m_inUse) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i < enginesBySize[fftSize].size())
|
||||
{
|
||||
enginesBySize[fftSize][i].m_inUse = true;
|
||||
*engine = enginesBySize[fftSize][i].m_engine;
|
||||
return i;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<AllocatedEngine>& engines = enginesBySize[fftSize];
|
||||
engines.push_back(AllocatedEngine());
|
||||
engines.back().m_inUse = true;
|
||||
engines.back().m_engine = FFTEngine::create(m_fftwWisdomFileName);
|
||||
engines.back().m_engine->configure(fftSize, true);
|
||||
*engine = engines.back().m_engine;
|
||||
return engines.size() - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FFTFactory::releaseEngine(unsigned int fftSize, bool inverse, int engineSequence)
|
||||
{
|
||||
std::map<unsigned int, std::vector<AllocatedEngine>>& enginesBySize = inverse ?
|
||||
m_invFFTEngineBySize : m_fftEngineBySize;
|
||||
|
||||
if (enginesBySize.find(fftSize) != enginesBySize.end())
|
||||
{
|
||||
std::vector<AllocatedEngine>& engines = enginesBySize[fftSize];
|
||||
|
||||
if (engineSequence < engines.size()) {
|
||||
engines[engineSequence].m_inUse = false;
|
||||
}
|
||||
}
|
||||
}
|
56
sdrbase/dsp/fftfactory.h
Normal file
56
sdrbase/dsp/fftfactory.h
Normal file
@ -0,0 +1,56 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// 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_FFTWFACTORY_H
|
||||
#define _SDRBASE_FFTWFACTORY_H
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include "export.h"
|
||||
#include "dsp/dsptypes.h"
|
||||
#include "fftengine.h"
|
||||
|
||||
class SDRBASE_API FFTFactory {
|
||||
public:
|
||||
FFTFactory(const QString& fftwWisdomFileName);
|
||||
~FFTFactory();
|
||||
|
||||
void preallocate(unsigned int minLog2Size, unsigned int maxLog2Size, unsigned int numberFFT, unsigned int numberInvFFT);
|
||||
unsigned int getEngine(unsigned int fftSize, bool inverse, FFTEngine **engine); //!< returns an engine sequence
|
||||
void releaseEngine(unsigned int fftSize, bool inverse, int engineSequence);
|
||||
|
||||
private:
|
||||
struct AllocatedEngine
|
||||
{
|
||||
FFTEngine *m_engine;
|
||||
bool m_inUse;
|
||||
|
||||
AllocatedEngine() :
|
||||
m_engine(nullptr),
|
||||
m_inUse(false)
|
||||
{}
|
||||
};
|
||||
|
||||
QString m_fftwWisdomFileName;
|
||||
std::map<unsigned int, std::vector<AllocatedEngine>> m_fftEngineBySize;
|
||||
std::map<unsigned int, std::vector<AllocatedEngine>> m_invFFTEngineBySize;
|
||||
};
|
||||
|
||||
#endif // _SDRBASE_FFTWFACTORY_H
|
@ -1,7 +1,8 @@
|
||||
#include <QTime>
|
||||
#include "dsp/fftwengine.h"
|
||||
|
||||
FFTWEngine::FFTWEngine() :
|
||||
FFTWEngine::FFTWEngine(const QString& fftWisdomFileName) :
|
||||
m_fftWisdomFileName(fftWisdomFileName),
|
||||
m_plans(),
|
||||
m_currentPlan(NULL)
|
||||
{
|
||||
|
@ -2,6 +2,8 @@
|
||||
#define INCLUDE_FFTWENGINE_H
|
||||
|
||||
#include <QMutex>
|
||||
#include <QString>
|
||||
|
||||
#include <fftw3.h>
|
||||
#include <list>
|
||||
#include "dsp/fftengine.h"
|
||||
@ -9,7 +11,7 @@
|
||||
|
||||
class SDRBASE_API FFTWEngine : public FFTEngine {
|
||||
public:
|
||||
FFTWEngine();
|
||||
FFTWEngine(const QString& fftWisdomFileName);
|
||||
~FFTWEngine();
|
||||
|
||||
void configure(int n, bool inverse);
|
||||
@ -20,6 +22,7 @@ public:
|
||||
|
||||
protected:
|
||||
static QMutex m_globalPlanMutex;
|
||||
QString m_fftWisdomFileName;
|
||||
|
||||
struct Plan {
|
||||
int n;
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include "dsp/spectrumvis.h"
|
||||
#include "gui/glspectrum.h"
|
||||
#include "dsp/dspcommands.h"
|
||||
#include "dsp/dspengine.h"
|
||||
#include "dsp/fftfactory.h"
|
||||
#include "util/messagequeue.h"
|
||||
|
||||
#define MAX_FFT_SIZE 4096
|
||||
@ -19,7 +21,8 @@ const Real SpectrumVis::m_mult = (10.0f / log2f(10.0f));
|
||||
|
||||
SpectrumVis::SpectrumVis(Real scalef, GLSpectrum* glSpectrum) :
|
||||
BasebandSampleSink(),
|
||||
m_fft(FFTEngine::create()),
|
||||
m_fft(nullptr),
|
||||
m_fftEngineSequence(0),
|
||||
m_fftBuffer(MAX_FFT_SIZE),
|
||||
m_powerSpectrum(MAX_FFT_SIZE),
|
||||
m_fftBufferFill(0),
|
||||
@ -39,7 +42,8 @@ SpectrumVis::SpectrumVis(Real scalef, GLSpectrum* glSpectrum) :
|
||||
|
||||
SpectrumVis::~SpectrumVis()
|
||||
{
|
||||
delete m_fft;
|
||||
FFTFactory *fftFactory = DSPEngine::instance()->getFFTFactory();
|
||||
fftFactory->releaseEngine(m_fftSize, false, m_fftEngineSequence);
|
||||
}
|
||||
|
||||
void SpectrumVis::configure(MessageQueue* msgQueue,
|
||||
@ -374,8 +378,10 @@ void SpectrumVis::handleConfigure(int fftSize,
|
||||
m_overlapPercent = overlapPercent;
|
||||
}
|
||||
|
||||
FFTFactory *fftFactory = DSPEngine::instance()->getFFTFactory();
|
||||
fftFactory->releaseEngine(m_fftSize, false, m_fftEngineSequence);
|
||||
m_fftEngineSequence = fftFactory->getEngine(fftSize, false, &m_fft);
|
||||
m_fftSize = fftSize;
|
||||
m_fft->configure(m_fftSize, false);
|
||||
m_window.create(window, m_fftSize);
|
||||
m_overlapSize = (m_fftSize * m_overlapPercent) / 100;
|
||||
m_refillSize = m_fftSize - m_overlapSize;
|
||||
|
@ -98,6 +98,7 @@ public:
|
||||
private:
|
||||
FFTEngine* m_fft;
|
||||
FFTWindow m_window;
|
||||
unsigned int m_fftEngineSequence;
|
||||
|
||||
std::vector<Complex> m_fftBuffer;
|
||||
std::vector<Real> m_powerSpectrum;
|
||||
|
@ -187,6 +187,11 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
|
||||
m_masterTimer.setTimerType(Qt::PreciseTimer);
|
||||
m_masterTimer.start(50);
|
||||
|
||||
splash->showStatusMessage("allocate FFTs...", Qt::white);
|
||||
splash->showStatusMessage("allocate FFTs...", Qt::white);
|
||||
m_dspEngine->createFFTFactory(parser.getFFTWFWisdomFileName());
|
||||
m_dspEngine->preAllocateFFTs();
|
||||
|
||||
splash->showStatusMessage("load settings...", Qt::white);
|
||||
qDebug() << "MainWindow::MainWindow: load settings...";
|
||||
|
||||
|
@ -63,14 +63,20 @@ MainCore::MainCore(qtwebapp::LoggerWithFile *logger, const MainParser& parser, Q
|
||||
m_settings.setAudioDeviceManager(m_dspEngine->getAudioDeviceManager());
|
||||
m_settings.setAMBEEngine(m_dspEngine->getAMBEEngine());
|
||||
|
||||
qDebug() << "MainCore::MainCore: create FFT factory...";
|
||||
m_dspEngine->createFFTFactory(parser.getFFTWFWisdomFileName());
|
||||
|
||||
qDebug() << "MainCore::MainCore: load plugins...";
|
||||
m_pluginManager = new PluginManager(this);
|
||||
m_pluginManager->loadPlugins(QString("pluginssrv"));
|
||||
|
||||
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleMessages()), Qt::QueuedConnection);
|
||||
m_masterTimer.start(50);
|
||||
|
||||
qDebug() << "MainCore::MainCore: load setings...";
|
||||
loadSettings();
|
||||
|
||||
qDebug() << "MainCore::MainCore: finishing...";
|
||||
QString applicationDirPath = QCoreApplication::instance()->applicationDirPath();
|
||||
|
||||
m_apiAdapter = new WebAPIAdapterSrv(*this);
|
||||
|
Loading…
Reference in New Issue
Block a user