add support for pluggable FFT engines, add KissFFT and FFTW as available engines
This commit is contained in:
parent
7020d69967
commit
b84314c233
|
@ -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")
|
||||
|
|
|
@ -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)
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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];
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue