1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-17 05:41:56 -05:00

FFT factory: implementation base

This commit is contained in:
f4exb 2020-03-12 06:27:38 +01:00
parent 8e8323e925
commit 926f45d9fb
14 changed files with 244 additions and 12 deletions

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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();
}

View File

@ -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");

View File

@ -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
View 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
View 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

View File

@ -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)
{

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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...";

View File

@ -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);