/*
Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Her Majesty
the Queen in Right of Canada (Communications Research Center Canada)
Copyright (C) 2014
Matthias P. Braendli, matthias.braendli@mpb.li
http://opendigitalradio.org
*/
/*
This file is part of ODR-DabMod.
ODR-DabMod 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, either version 3 of the
License, or (at your option) any later version.
ODR-DabMod 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 for more details.
You should have received a copy of the GNU General Public License
along with ODR-DabMod. If not, see .
*/
#include "OfdmGenerator.h"
#include "PcDebug.h"
#if USE_FFTW
# include "fftw3.h"
# define FFT_TYPE fftwf_complex
#else
# include "kiss_fftsimd.h"
#endif
#include
#include
#include
#include
#include
typedef std::complex complexf;
OfdmGenerator::OfdmGenerator(size_t nbSymbols,
size_t nbCarriers,
size_t spacing,
bool inverse) :
ModCodec(ModFormat(nbSymbols * nbCarriers * sizeof(FFT_TYPE)),
ModFormat(nbSymbols * spacing * sizeof(FFT_TYPE))),
myFftPlan(NULL),
#if USE_FFTW
myFftIn(NULL), myFftOut(NULL),
#else
myFftBuffer(NULL),
#endif
myNbSymbols(nbSymbols),
myNbCarriers(nbCarriers),
mySpacing(spacing)
{
PDEBUG("OfdmGenerator::OfdmGenerator(%zu, %zu, %zu, %s) @ %p\n",
nbSymbols, nbCarriers, spacing, inverse ? "true" : "false", this);
if (nbCarriers > spacing) {
throw std::runtime_error(
"OfdmGenerator::OfdmGenerator nbCarriers > spacing!");
}
if (inverse) {
myPosDst = (nbCarriers & 1 ? 0 : 1);
myPosSrc = 0;
myPosSize = (nbCarriers + 1) / 2;
myNegDst = spacing - (nbCarriers / 2);
myNegSrc = (nbCarriers + 1) / 2;
myNegSize = nbCarriers / 2;
}
else {
myPosDst = (nbCarriers & 1 ? 0 : 1);
myPosSrc = nbCarriers / 2;
myPosSize = (nbCarriers + 1) / 2;
myNegDst = spacing - (nbCarriers / 2);
myNegSrc = 0;
myNegSize = nbCarriers / 2;
}
myZeroDst = myPosDst + myPosSize;
myZeroSize = myNegDst - myZeroDst;
PDEBUG(" myPosDst: %u\n", myPosDst);
PDEBUG(" myPosSrc: %u\n", myPosSrc);
PDEBUG(" myPosSize: %u\n", myPosSize);
PDEBUG(" myNegDst: %u\n", myNegDst);
PDEBUG(" myNegSrc: %u\n", myNegSrc);
PDEBUG(" myNegSize: %u\n", myNegSize);
PDEBUG(" myZeroDst: %u\n", myZeroDst);
PDEBUG(" myZeroSize: %u\n", myZeroSize);
#if USE_FFTW
const int N = mySpacing; // The size of the FFT
myFftIn = (FFT_TYPE*)fftwf_malloc(sizeof(FFT_TYPE) * N);
myFftOut = (FFT_TYPE*)fftwf_malloc(sizeof(FFT_TYPE) * N);
myFftPlan = fftwf_plan_dft_1d(N,
myFftIn, myFftOut,
FFTW_BACKWARD, FFTW_MEASURE);
if (sizeof(complexf) != sizeof(FFT_TYPE)) {
printf("sizeof(complexf) %d\n", sizeof(complexf));
printf("sizeof(FFT_TYPE) %d\n", sizeof(FFT_TYPE));
throw std::runtime_error(
"OfdmGenerator::process complexf size is not FFT_TYPE size!");
}
#else
myFftPlan = kiss_fft_alloc(mySpacing, 1, NULL, NULL);
myFftBuffer = (FFT_TYPE*)memalign(16, mySpacing * sizeof(FFT_TYPE));
#endif
}
OfdmGenerator::~OfdmGenerator()
{
PDEBUG("OfdmGenerator::~OfdmGenerator() @ %p\n", this);
#if USE_FFTW
if (myFftIn) {
fftwf_free(myFftIn);
}
if (myFftOut) {
fftwf_free(myFftOut);
}
if (myFftPlan) {
fftwf_destroy_plan(myFftPlan);
}
#else
if (myFftPlan != NULL) {
kiss_fft_free(myFftPlan);
}
if (myFftBuffer != NULL) {
free(myFftBuffer);
}
kiss_fft_cleanup();
#endif
}
int OfdmGenerator::process(Buffer* const dataIn, Buffer* dataOut)
{
PDEBUG("OfdmGenerator::process(dataIn: %p, dataOut: %p)\n",
dataIn, dataOut);
dataOut->setLength(myNbSymbols * mySpacing * sizeof(complexf));
FFT_TYPE* in = reinterpret_cast(dataIn->getData());
FFT_TYPE* out = reinterpret_cast(dataOut->getData());
size_t sizeIn = dataIn->getLength() / sizeof(complexf);
size_t sizeOut = dataOut->getLength() / sizeof(complexf);
if (sizeIn != myNbSymbols * myNbCarriers) {
PDEBUG("Nb symbols: %zu\n", myNbSymbols);
PDEBUG("Nb carriers: %zu\n", myNbCarriers);
PDEBUG("Spacing: %zu\n", mySpacing);
PDEBUG("\n%zu != %zu\n", sizeIn, myNbSymbols * myNbCarriers);
throw std::runtime_error(
"OfdmGenerator::process input size not valid!");
}
if (sizeOut != myNbSymbols * mySpacing) {
PDEBUG("Nb symbols: %zu\n", myNbSymbols);
PDEBUG("Nb carriers: %zu\n", myNbCarriers);
PDEBUG("Spacing: %zu\n", mySpacing);
PDEBUG("\n%zu != %zu\n", sizeIn, myNbSymbols * mySpacing);
throw std::runtime_error(
"OfdmGenerator::process output size not valid!");
}
#if USE_FFTW
// No SIMD/no-SIMD distinction, it's too early to optimize anything
for (size_t i = 0; i < myNbSymbols; ++i) {
myFftIn[0][0] = 0;
myFftIn[0][1] = 0;
bzero(&myFftIn[myZeroDst], myZeroSize * sizeof(FFT_TYPE));
memcpy(&myFftIn[myPosDst], &in[myPosSrc],
myPosSize * sizeof(FFT_TYPE));
memcpy(&myFftIn[myNegDst], &in[myNegSrc],
myNegSize * sizeof(FFT_TYPE));
fftwf_execute(myFftPlan);
memcpy(out, myFftOut, mySpacing * sizeof(FFT_TYPE));
in += myNbCarriers;
out += mySpacing;
}
#else
# ifdef USE_SIMD
for (size_t i = 0, j = 0; i < sizeIn; ) {
// Pack 4 fft operations
typedef struct {
float r[4];
float i[4];
} fft_data;
assert(sizeof(FFT_TYPE) == sizeof(fft_data));
complexf *cplxIn = (complexf*)in;
complexf *cplxOut = (complexf*)out;
fft_data *dataBuffer = (fft_data*)myFftBuffer;
FFT_REAL(myFftBuffer[0]) = _mm_setzero_ps();
FFT_IMAG(myFftBuffer[0]) = _mm_setzero_ps();
for (size_t k = 0; k < myZeroSize; ++k) {
FFT_REAL(myFftBuffer[myZeroDst + k]) = _mm_setzero_ps();
FFT_IMAG(myFftBuffer[myZeroDst + k]) = _mm_setzero_ps();
}
for (int k = 0; k < 4; ++k) {
if (i < sizeIn) {
for (size_t l = 0; l < myPosSize; ++l) {
dataBuffer[myPosDst + l].r[k] = cplxIn[i + myPosSrc + l].real();
dataBuffer[myPosDst + l].i[k] = cplxIn[i + myPosSrc + l].imag();
}
for (size_t l = 0; l < myNegSize; ++l) {
dataBuffer[myNegDst + l].r[k] = cplxIn[i + myNegSrc + l].real();
dataBuffer[myNegDst + l].i[k] = cplxIn[i + myNegSrc + l].imag();
}
i += myNbCarriers;
}
else {
for (size_t l = 0; l < myNbCarriers; ++l) {
dataBuffer[l].r[k] = 0.0f;
dataBuffer[l].i[k] = 0.0f;
}
}
}
kiss_fft(myFftPlan, myFftBuffer, myFftBuffer);
for (int k = 0; k < 4; ++k) {
if (j < sizeOut) {
for (size_t l = 0; l < mySpacing; ++l) {
cplxOut[j + l].real() = dataBuffer[l].r[k];
cplxOut[j + l].imag() = dataBuffer[l].i[k];
}
j += mySpacing;
}
}
}
# else
for (size_t i = 0; i < myNbSymbols; ++i) {
FFT_REAL(myFftBuffer[0]) = 0;
FFT_IMAG(myFftBuffer[0]) = 0;
bzero(&myFftBuffer[myZeroDst], myZeroSize * sizeof(FFT_TYPE));
memcpy(&myFftBuffer[myPosDst], &in[myPosSrc],
myPosSize * sizeof(FFT_TYPE));
memcpy(&myFftBuffer[myNegDst], &in[myNegSrc],
myNegSize * sizeof(FFT_TYPE));
kiss_fft(myFftPlan, myFftBuffer, out);
in += myNbCarriers;
out += mySpacing;
}
# endif
#endif
return sizeOut;
}