add support for pluggable FFT engines, add KissFFT and FFTW as available engines

This commit is contained in:
Christian Daniel 2013-02-14 23:18:38 +01:00
parent 7020d69967
commit b84314c233
12 changed files with 277 additions and 19 deletions

View File

@ -10,10 +10,12 @@ set(QT_USE_QTOPENGL TRUE)
find_package(Qt4 REQUIRED)
find_package(OpenGL REQUIRED)
find_package(PkgConfig)
find_package(LibOsmoSDR REQUIRED)
find_package(LibRTLSDR REQUIRED)
find_package(Portaudio REQUIRED)
find_package(LibUSB REQUIRED)
find_package(FFTW3F)
set(sdrangelove_SOURCES
main.cpp
@ -24,6 +26,7 @@ set(sdrangelove_SOURCES
dsp/channelizer.cpp
dsp/dspcommands.cpp
dsp/dspengine.cpp
dsp/fftengine.cpp
dsp/fftwindow.cpp
dsp/interpolator.cpp
dsp/inthalfbandfilter.cpp
@ -75,10 +78,10 @@ set(sdrangelove_HEADERS
dsp/dspcommands.h
dsp/dspengine.h
dsp/dsptypes.h
dsp/fftengine.h
dsp/fftwindow.h
dsp/interpolator.h
dsp/inthalfbandfilter.h
dsp/kissfft.h
dsp/lowpass.h
dsp/nco.h
dsp/nfmdemod.h
@ -145,6 +148,30 @@ include_directories(
${LIBUSB_INCLUDE_DIR}
)
if(FFTW3F_FOUND)
set(sdrangelove_SOURCES
${sdrangelove_SOURCES}
dsp/fftwengine.cpp
)
set(sdrangelove_HEADERS
${sdrangelove_HEADERS}
dsp/fftwengine.h
)
add_definitions(-DUSE_FFTW)
include_directories(${FFTW3F_INCLUDE_DIRS})
else(FFTW3F_FOUND)
set(sdrangelove_SOURCES
${sdrangelove_SOURCES}
dsp/kissengine.cpp
dsp/kissfft.h
)
set(sdrangelove_HEADERS
${sdrangelove_HEADERS}
dsp/kissengine.h
)
add_definitions(-DUSE_KISSFFT)
endif(FFTW3F_FOUND)
include(${QT_USE_FILE})
add_definitions(${QT_DEFINITIONS})
@ -172,6 +199,10 @@ target_link_libraries(sdrangelove
${LIBUSB_LIBRARIES}
)
if(FFTW3F_FOUND)
target_link_libraries(sdrangelove ${FFTW3F_LIBRARIES})
endif(FFTW3F_FOUND)
if(WIN32)
set_target_properties(sdrangelove PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE")
set_target_properties(sdrangelove PROPERTIES COMPILE_DEFINITIONS_DEBUG "_CONSOLE")

View File

@ -0,0 +1,40 @@
# http://tim.klingt.org/code/projects/supernova/repository/revisions/d336dd6f400e381bcfd720e96139656de0c53b6a/entry/cmake_modules/FindFFTW3f.cmake
# Modified to use pkg config and use standard var names
# Find single-precision (float) version of FFTW3
INCLUDE(FindPkgConfig)
PKG_CHECK_MODULES(PC_FFTW3F "fftw3f >= 3.0")
FIND_PATH(
FFTW3F_INCLUDE_DIRS
NAMES fftw3.h
HINTS $ENV{FFTW3_DIR}/include
${PC_FFTW3F_INCLUDE_DIR}
PATHS /usr/local/include
/usr/include
)
FIND_LIBRARY(
FFTW3F_LIBRARIES
NAMES fftw3f libfftw3f
HINTS $ENV{FFTW3_DIR}/lib
${PC_FFTW3F_LIBDIR}
PATHS /usr/local/lib
/usr/lib
/usr/lib64
)
FIND_LIBRARY(
FFTW3F_THREADS_LIBRARIES
NAMES fftw3f_threads libfftw3f_threads
HINTS $ENV{FFTW3_DIR}/lib
${PC_FFTW3F_LIBDIR}
PATHS /usr/local/lib
/usr/lib
/usr/lib64
)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(FFTW3F DEFAULT_MSG FFTW3F_LIBRARIES FFTW3F_INCLUDE_DIRS)
MARK_AS_ADVANCED(FFTW3F_LIBRARIES FFTW3F_INCLUDE_DIRS FFTW3F_THREADS_LIBRARIES)

26
dsp/fftengine.cpp Normal file
View File

@ -0,0 +1,26 @@
#include "fftengine.h"
#ifdef USE_KISSFFT
#include "kissengine.h"
#endif
#ifdef USE_FFTW
#include "fftwengine.h"
#endif // USE_FFTW
FFTEngine::~FFTEngine()
{
}
FFTEngine* FFTEngine::create()
{
#ifdef USE_FFTW
qDebug("FFT: using FFTW engine");
return new FFTWEngine;
#endif // USE_FFTW
#ifdef USE_KISSFFT
qDebug("FFT: using KissFFT engine");
return new KissEngine;
#endif // USE_KISSFFT
qCritical("FFT: no engine built");
return NULL;
}

19
dsp/fftengine.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef INCLUDE_FFTENGINE_H
#define INCLUDE_FFTENGINE_H
#include "dsptypes.h"
class FFTEngine {
public:
virtual ~FFTEngine();
virtual void configure(int n, bool inverse) = 0;
virtual void transform() = 0;
virtual Complex* in() = 0;
virtual Complex* out() = 0;
static FFTEngine* create();
};
#endif // INCLUDE_FFTENGINE_H

56
dsp/fftwengine.cpp Normal file
View File

@ -0,0 +1,56 @@
#include "fftwengine.h"
FFTWEngine::FFTWEngine() :
m_plans(),
m_currentPlan(NULL)
{
}
FFTWEngine::~FFTWEngine()
{
freeAll();
}
void FFTWEngine::configure(int n, bool inverse)
{
for(Plans::const_iterator it = m_plans.begin(); it != m_plans.end(); ++it) {
if(((*it)->n == n) && ((*it)->inverse == inverse)) {
m_currentPlan = *it;
return;
}
}
m_currentPlan = new Plan;
m_currentPlan->n = n;
m_currentPlan->inverse = inverse;
m_currentPlan->in = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * n);
m_currentPlan->out = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * n);
m_currentPlan->plan = fftwf_plan_dft_1d(n, m_currentPlan->in, m_currentPlan->out, inverse ? FFTW_BACKWARD : FFTW_FORWARD, FFTW_PATIENT);
m_plans.push_back(m_currentPlan);
}
void FFTWEngine::transform()
{
fftwf_execute(m_currentPlan->plan);
}
Complex* FFTWEngine::in()
{
return reinterpret_cast<Complex*>(m_currentPlan->in);
}
Complex* FFTWEngine::out()
{
return reinterpret_cast<Complex*>(m_currentPlan->out);
}
void FFTWEngine::freeAll()
{
for(Plans::iterator it = m_plans.begin(); it != m_plans.end(); ++it) {
fftwf_destroy_plan((*it)->plan);
fftwf_free((*it)->in);
fftwf_free((*it)->out);
delete *it;
}
m_plans.clear();
}

34
dsp/fftwengine.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef INCLUDE_FFTWENGINE_H
#define INCLUDE_FFTWENGINE_H
#include <fftw3.h>
#include <list>
#include "fftengine.h"
class FFTWEngine : public FFTEngine {
public:
FFTWEngine();
~FFTWEngine();
void configure(int n, bool inverse);
void transform();
Complex* in();
Complex* out();
protected:
struct Plan {
int n;
bool inverse;
fftwf_plan plan;
fftwf_complex* in;
fftwf_complex* out;
};
typedef std::list<Plan*> Plans;
Plans m_plans;
Plan* m_currentPlan;
void freeAll();
};
#endif // INCLUDE_FFTWENGINE_H

View File

@ -66,3 +66,8 @@ void FFTWindow::apply(const std::vector<Complex>& in, std::vector<Complex>* out)
(*out)[i] = in[i] * m_window[i];
}
void FFTWindow::apply(const Complex* in, Complex* out)
{
for(size_t i = 0; i < m_window.size(); i++)
out[i] = in[i] * m_window[i];
}

View File

@ -37,6 +37,7 @@ public:
void create(Function function, int n);
void apply(const std::vector<Real>& in, std::vector<Real>* out);
void apply(const std::vector<Complex>& in, std::vector<Complex>* out);
void apply(const Complex* in, Complex* out);
private:
std::vector<float> m_window;

25
dsp/kissengine.cpp Normal file
View File

@ -0,0 +1,25 @@
#include "kissengine.h"
void KissEngine::configure(int n, bool inverse)
{
m_fft.configure(n, inverse);
if(n > m_in.size())
m_in.resize(n);
if(n > m_out.size())
m_out.resize(n);
}
void KissEngine::transform()
{
m_fft.transform(&m_in[0], &m_out[0]);
}
Complex* KissEngine::in()
{
return &m_in[0];
}
Complex* KissEngine::out()
{
return &m_out[0];
}

23
dsp/kissengine.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef INCLUDE_KISSENGINE_H
#define INCLUDE_KISSENGINE_H
#include "fftengine.h"
#include "kissfft.h"
class KissEngine : public FFTEngine {
public:
void configure(int n, bool inverse);
void transform();
Complex* in();
Complex* out();
protected:
typedef kissfft<Real, Complex> KissFFT;
KissFFT m_fft;
std::vector<Complex> m_in;
std::vector<Complex> m_out;
};
#endif // INCLUDE_KISSENGINE_H

View File

@ -6,29 +6,28 @@
#define MAX_FFT_SIZE 4096
#ifdef _WIN32
double log2f(double n)
{
return log(n) / log(2.0);
double log2f(double n)
{
return log(n) / log(2.0);
}
#endif
SpectrumVis::SpectrumVis(GLSpectrum* glSpectrum) :
SampleSink(),
m_fft(FFTEngine::create()),
m_fftBuffer(MAX_FFT_SIZE),
m_fftIn(MAX_FFT_SIZE),
m_fftOut(MAX_FFT_SIZE),
m_logPowerSpectrum(MAX_FFT_SIZE),
m_fftBufferFill(0),
m_glSpectrum(glSpectrum)
{
handleConfigure(1024, 10, FFTWindow::BlackmanHarris);
}
/*
void SpectrumVis::setGLSpectrum(GLSpectrum* glSpectrum)
SpectrumVis::~SpectrumVis()
{
m_glSpectrum = glSpectrum;
delete m_fft;
}
*/
void SpectrumVis::configure(MessageQueue* msgQueue, int fftSize, int overlapPercent, FFTWindow::Function window)
{
Message* cmd = DSPCmdConfigureSpectrumVis::create(fftSize, overlapPercent, window);
@ -52,16 +51,17 @@ void SpectrumVis::feed(SampleVector::const_iterator begin, SampleVector::const_i
*it++ = Complex(begin->real() / 32768.0, begin->imag() / 32768.0);
// apply fft window (and copy from m_fftBuffer to m_fftIn)
m_window.apply(m_fftBuffer, &m_fftIn);
m_window.apply(&m_fftBuffer[0], m_fft->in());
// calculate FFT
m_fft.transform(&m_fftIn[0], &m_fftOut[0]);
m_fft->transform();
// extract power spectrum and reorder buckets
Real ofs = 20.0f * log10f(1.0f / m_fftSize);
Real mult = (10.0f / log2f(10.0f));
const Complex* fftOut = m_fft->out();
for(size_t i = 0; i < m_fftSize; i++) {
Complex c = m_fftOut[((i + (m_fftSize >> 1)) & (m_fftSize - 1))];
Complex c = fftOut[((i + (m_fftSize >> 1)) & (m_fftSize - 1))];
Real v = c.real() * c.real() + c.imag() * c.imag();
v = mult * log2f(v) + ofs;
m_logPowerSpectrum[i] = v;
@ -123,7 +123,7 @@ void SpectrumVis::handleConfigure(int fftSize, int overlapPercent, FFTWindow::Fu
m_fftSize = fftSize;
m_overlapPercent = overlapPercent;
m_fft.configure(m_fftSize, false);
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

@ -2,7 +2,7 @@
#define INCLUDE_SPECTRUMVIS_H
#include "samplesink.h"
#include "kissfft.h"
#include "fftengine.h"
#include "fftwindow.h"
class GLSpectrum;
@ -11,6 +11,7 @@ class MessageQueue;
class SpectrumVis : public SampleSink {
public:
SpectrumVis(GLSpectrum* glSpectrum = NULL);
~SpectrumVis();
void configure(MessageQueue* msgQueue, int fftSize, int overlapPercent, FFTWindow::Function window);
@ -21,13 +22,10 @@ public:
void handleMessage(Message* cmd);
private:
typedef kissfft<Real, Complex> KissFFT;
KissFFT m_fft;
FFTEngine* m_fft;
FFTWindow m_window;
std::vector<Complex> m_fftBuffer;
std::vector<Complex> m_fftIn;
std::vector<Complex> m_fftOut;
std::vector<Real> m_logPowerSpectrum;
size_t m_fftSize;