From 8736f6160aeafe7a177cb6143fea80157e174e52 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Sun, 6 Oct 2024 19:47:19 +0200 Subject: Implement fixed-point symbols, FFT and file output --- src/OfdmGenerator.cpp | 172 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 146 insertions(+), 26 deletions(-) (limited to 'src/OfdmGenerator.cpp') diff --git a/src/OfdmGenerator.cpp b/src/OfdmGenerator.cpp index cb799d3..e679694 100644 --- a/src/OfdmGenerator.cpp +++ b/src/OfdmGenerator.cpp @@ -27,17 +27,19 @@ #include "OfdmGenerator.h" #include "PcDebug.h" -#define FFT_TYPE fftwf_complex - -#include #include #include #include #include +#include +#include +#include static const size_t MAX_CLIP_STATS = 10; -OfdmGenerator::OfdmGenerator(size_t nbSymbols, +using FFTW_TYPE = fftwf_complex; + +OfdmGeneratorCF32::OfdmGeneratorCF32(size_t nbSymbols, size_t nbCarriers, size_t spacing, bool& enableCfr, @@ -102,29 +104,29 @@ OfdmGenerator::OfdmGenerator(size_t nbSymbols, PDEBUG(" myZeroSize: %u\n", myZeroSize); 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); + myFftIn = (FFTW_TYPE*)fftwf_malloc(sizeof(FFTW_TYPE) * N); + myFftOut = (FFTW_TYPE*)fftwf_malloc(sizeof(FFTW_TYPE) * N); fftwf_set_timelimit(2); myFftPlan = fftwf_plan_dft_1d(N, myFftIn, myFftOut, FFTW_BACKWARD, FFTW_MEASURE); - myCfrPostClip = (FFT_TYPE*)fftwf_malloc(sizeof(FFT_TYPE) * N); - myCfrPostFft = (FFT_TYPE*)fftwf_malloc(sizeof(FFT_TYPE) * N); + myCfrPostClip = (FFTW_TYPE*)fftwf_malloc(sizeof(FFTW_TYPE) * N); + myCfrPostFft = (FFTW_TYPE*)fftwf_malloc(sizeof(FFTW_TYPE) * N); myCfrFft = fftwf_plan_dft_1d(N, myCfrPostClip, myCfrPostFft, FFTW_FORWARD, FFTW_MEASURE); - if (sizeof(complexf) != sizeof(FFT_TYPE)) { + if (sizeof(complexf) != sizeof(FFTW_TYPE)) { printf("sizeof(complexf) %zu\n", sizeof(complexf)); - printf("sizeof(FFT_TYPE) %zu\n", sizeof(FFT_TYPE)); + printf("sizeof(FFT_TYPE) %zu\n", sizeof(FFTW_TYPE)); throw std::runtime_error( "OfdmGenerator::process complexf size is not FFT_TYPE size!"); } } -OfdmGenerator::~OfdmGenerator() +OfdmGeneratorCF32::~OfdmGeneratorCF32() { PDEBUG("OfdmGenerator::~OfdmGenerator() @ %p\n", this); @@ -153,15 +155,15 @@ OfdmGenerator::~OfdmGenerator() } } -int OfdmGenerator::process(Buffer* const dataIn, Buffer* dataOut) +int OfdmGeneratorCF32::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()); + FFTW_TYPE* in = reinterpret_cast(dataIn->getData()); + FFTW_TYPE* out = reinterpret_cast(dataOut->getData()); size_t sizeIn = dataIn->getLength() / sizeof(complexf); size_t sizeOut = dataOut->getLength() / sizeof(complexf); @@ -212,17 +214,17 @@ int OfdmGenerator::process(Buffer* const dataIn, Buffer* dataOut) * PosSrc=0 PosDst=1 PosSize=768 * NegSrc=768 NegDst=1280 NegSize=768 */ - memset(&myFftIn[myZeroDst], 0, myZeroSize * sizeof(FFT_TYPE)); + memset(&myFftIn[myZeroDst], 0, myZeroSize * sizeof(FFTW_TYPE)); memcpy(&myFftIn[myPosDst], &in[myPosSrc], - myPosSize * sizeof(FFT_TYPE)); + myPosSize * sizeof(FFTW_TYPE)); memcpy(&myFftIn[myNegDst], &in[myNegSrc], - myNegSize * sizeof(FFT_TYPE)); + myNegSize * sizeof(FFTW_TYPE)); if (myCfr) { reference.resize(mySpacing); memcpy(reinterpret_cast(reference.data()), - myFftIn, mySpacing * sizeof(FFT_TYPE)); + myFftIn, mySpacing * sizeof(FFTW_TYPE)); } fftwf_execute(myFftPlan); // IFFT from myFftIn to myFftOut @@ -235,7 +237,7 @@ int OfdmGenerator::process(Buffer* const dataIn, Buffer* dataOut) if (myMERCalcIndex == i) { before_cfr.resize(mySpacing); memcpy(reinterpret_cast(before_cfr.data()), - myFftOut, mySpacing * sizeof(FFT_TYPE)); + myFftOut, mySpacing * sizeof(FFTW_TYPE)); } /* cfr_one_iteration runs the myFftPlan again at the end, and @@ -277,7 +279,7 @@ int OfdmGenerator::process(Buffer* const dataIn, Buffer* dataOut) num_error_clip += stat.errclip_count; } - memcpy(out, myFftOut, mySpacing * sizeof(FFT_TYPE)); + memcpy(out, myFftOut, mySpacing * sizeof(FFTW_TYPE)); in += myNbCarriers; out += mySpacing; @@ -308,14 +310,14 @@ int OfdmGenerator::process(Buffer* const dataIn, Buffer* dataOut) return sizeOut; } -OfdmGenerator::cfr_iter_stat_t OfdmGenerator::cfr_one_iteration( +OfdmGeneratorCF32::cfr_iter_stat_t OfdmGeneratorCF32::cfr_one_iteration( complexf *symbol, const complexf *reference) { // use std::norm instead of std::abs to avoid calculating the // square roots const float clip_squared = myCfrClip * myCfrClip; - OfdmGenerator::cfr_iter_stat_t ret; + OfdmGeneratorCF32::cfr_iter_stat_t ret; // Clip for (size_t i = 0; i < mySpacing; i++) { @@ -331,7 +333,7 @@ OfdmGenerator::cfr_iter_stat_t OfdmGenerator::cfr_one_iteration( } // Take FFT of our clipped signal - memcpy(myCfrPostClip, symbol, mySpacing * sizeof(FFT_TYPE)); + memcpy(myCfrPostClip, symbol, mySpacing * sizeof(FFTW_TYPE)); fftwf_execute(myCfrFft); // FFT from myCfrPostClip to myCfrPostFft // Calculate the error in frequency domain by subtracting our reference @@ -374,7 +376,7 @@ OfdmGenerator::cfr_iter_stat_t OfdmGenerator::cfr_one_iteration( } -void OfdmGenerator::set_parameter(const std::string& parameter, +void OfdmGeneratorCF32::set_parameter(const std::string& parameter, const std::string& value) { using namespace std; @@ -404,7 +406,7 @@ void OfdmGenerator::set_parameter(const std::string& parameter, } } -const std::string OfdmGenerator::get_parameter(const std::string& parameter) const +const std::string OfdmGeneratorCF32::get_parameter(const std::string& parameter) const { using namespace std; stringstream ss; @@ -458,9 +460,127 @@ const std::string OfdmGenerator::get_parameter(const std::string& parameter) con return ss.str(); } -const json::map_t OfdmGenerator::get_all_values() const +const json::map_t OfdmGeneratorCF32::get_all_values() const { json::map_t map; // TODO needs rework of the values return map; } + +OfdmGeneratorFixed::OfdmGeneratorFixed(size_t nbSymbols, + size_t nbCarriers, + size_t spacing, + bool& enableCfr, + float& cfrClip, + float& cfrErrorClip, + bool inverse) : + ModCodec(), + myNbSymbols(nbSymbols), + myNbCarriers(nbCarriers), + mySpacing(spacing) +{ + PDEBUG("OfdmGenerator::OfdmGenerator(%zu, %zu, %zu, %s) @ %p\n", + nbSymbols, nbCarriers, spacing, inverse ? "true" : "false", this); + + etiLog.level(info) << "Using KISS FFT by Mark Borgerding for fixed-point transform"; + + 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); + + const int N = mySpacing; // The size of the FFT + + const size_t nbytes = N * sizeof(kiss_fft_cpx); + myFftIn = (kiss_fft_cpx*)KISS_FFT_MALLOC(nbytes); + myFftOut = (kiss_fft_cpx*)KISS_FFT_MALLOC(nbytes); + memset(myFftIn, 0, nbytes); + + myKissCfg = kiss_fft_alloc(N, inverse, nullptr, nullptr); +} + +OfdmGeneratorFixed::~OfdmGeneratorFixed() +{ + if (myKissCfg) KISS_FFT_FREE(myKissCfg); + if (myFftIn) KISS_FFT_FREE(myFftIn); + if (myFftOut) KISS_FFT_FREE(myFftOut); +} + +int OfdmGeneratorFixed::process(Buffer* const dataIn, Buffer* dataOut) +{ + dataOut->setLength(myNbSymbols * mySpacing * sizeof(kiss_fft_cpx)); + + kiss_fft_cpx* in = reinterpret_cast(dataIn->getData()); + kiss_fft_cpx* out = reinterpret_cast(dataOut->getData()); + + size_t sizeIn = dataIn->getLength() / sizeof(kiss_fft_cpx); + size_t sizeOut = dataOut->getLength() / sizeof(kiss_fft_cpx); + + 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!"); + } + + for (size_t i = 0; i < myNbSymbols; ++i) { + myFftIn[0].r = 0; + myFftIn[0].i = 0; + + /* For TM I this is: + * ZeroDst=769 ZeroSize=511 + * PosSrc=0 PosDst=1 PosSize=768 + * NegSrc=768 NegDst=1280 NegSize=768 + */ + memset(&myFftIn[myZeroDst], 0, myZeroSize * sizeof(kiss_fft_cpx)); + memcpy(&myFftIn[myPosDst], &in[myPosSrc], myPosSize * sizeof(kiss_fft_cpx)); + memcpy(&myFftIn[myNegDst], &in[myNegSrc], myNegSize * sizeof(kiss_fft_cpx)); + + kiss_fft(myKissCfg, myFftIn, myFftOut); + + memcpy(out, myFftOut, mySpacing * sizeof(kiss_fft_cpx)); + + in += myNbCarriers; + out += mySpacing; + } + + return sizeOut; +} -- cgit v1.2.3 From e836f903ae5e6b6916627142d47227a142879c04 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Sat, 26 Oct 2024 16:30:19 +0200 Subject: Use FFT Accelerator on DEXTER --- src/DabModulator.cpp | 11 ++- src/OfdmGenerator.cpp | 206 +++++++++++++++++++++++++++++++++++++++++++++++--- src/OfdmGenerator.h | 48 +++++++++++- src/output/Dexter.h | 12 +-- 4 files changed, 258 insertions(+), 19 deletions(-) (limited to 'src/OfdmGenerator.cpp') diff --git a/src/DabModulator.cpp b/src/DabModulator.cpp index 9e9d017..757b01f 100644 --- a/src/DabModulator.cpp +++ b/src/DabModulator.cpp @@ -190,7 +190,16 @@ int DabModulator::process(Buffer* dataOut) shared_ptr cifOfdm; - if (m_settings.fixedPoint) { + if (m_settings.useDexterOutput) { + cifOfdm = make_shared( + (1 + m_nbSymbols), + m_nbCarriers, + m_spacing, + m_settings.enableCfr, + m_settings.cfrClip, + m_settings.cfrErrorClip); + } + else if (m_settings.fixedPoint) { cifOfdm = make_shared( (1 + m_nbSymbols), m_nbCarriers, diff --git a/src/OfdmGenerator.cpp b/src/OfdmGenerator.cpp index e679694..198c2fc 100644 --- a/src/OfdmGenerator.cpp +++ b/src/OfdmGenerator.cpp @@ -2,7 +2,7 @@ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2023 + Copyright (C) 2024 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -64,8 +64,7 @@ OfdmGeneratorCF32::OfdmGeneratorCF32(size_t nbSymbols, nbSymbols, nbCarriers, spacing, inverse ? "true" : "false", this); if (nbCarriers > spacing) { - throw std::runtime_error( - "OfdmGenerator::OfdmGenerator nbCarriers > spacing!"); + throw std::runtime_error("OfdmGenerator nbCarriers > spacing!"); } /* register the parameters that can be remote controlled */ @@ -162,8 +161,8 @@ int OfdmGeneratorCF32::process(Buffer* const dataIn, Buffer* dataOut) dataOut->setLength(myNbSymbols * mySpacing * sizeof(complexf)); - FFTW_TYPE* in = reinterpret_cast(dataIn->getData()); - FFTW_TYPE* out = reinterpret_cast(dataOut->getData()); + FFTW_TYPE *in = reinterpret_cast(dataIn->getData()); + FFTW_TYPE *out = reinterpret_cast(dataOut->getData()); size_t sizeIn = dataIn->getLength() / sizeof(complexf); size_t sizeOut = dataOut->getLength() / sizeof(complexf); @@ -205,7 +204,7 @@ int OfdmGeneratorCF32::process(Buffer* const dataIn, Buffer* dataOut) myPaprAfterCFR.clear(); } - for (size_t i = 0; i < myNbSymbols; ++i) { + for (size_t i = 0; i < myNbSymbols; i++) { myFftIn[0][0] = 0; myFftIn[0][1] = 0; @@ -485,8 +484,7 @@ OfdmGeneratorFixed::OfdmGeneratorFixed(size_t nbSymbols, etiLog.level(info) << "Using KISS FFT by Mark Borgerding for fixed-point transform"; if (nbCarriers > spacing) { - throw std::runtime_error( - "OfdmGenerator::OfdmGenerator nbCarriers > spacing!"); + throw std::runtime_error("OfdmGenerator nbCarriers > spacing!"); } if (inverse) { @@ -538,8 +536,8 @@ int OfdmGeneratorFixed::process(Buffer* const dataIn, Buffer* dataOut) { dataOut->setLength(myNbSymbols * mySpacing * sizeof(kiss_fft_cpx)); - kiss_fft_cpx* in = reinterpret_cast(dataIn->getData()); - kiss_fft_cpx* out = reinterpret_cast(dataOut->getData()); + kiss_fft_cpx *in = reinterpret_cast(dataIn->getData()); + kiss_fft_cpx *out = reinterpret_cast(dataOut->getData()); size_t sizeIn = dataIn->getLength() / sizeof(kiss_fft_cpx); size_t sizeOut = dataOut->getLength() / sizeof(kiss_fft_cpx); @@ -561,7 +559,7 @@ int OfdmGeneratorFixed::process(Buffer* const dataIn, Buffer* dataOut) "OfdmGenerator::process output size not valid!"); } - for (size_t i = 0; i < myNbSymbols; ++i) { + for (size_t i = 0; i < myNbSymbols; i++) { myFftIn[0].r = 0; myFftIn[0].i = 0; @@ -584,3 +582,189 @@ int OfdmGeneratorFixed::process(Buffer* const dataIn, Buffer* dataOut) return sizeOut; } + +#ifdef HAVE_DEXTER +OfdmGeneratorDEXTER::OfdmGeneratorDEXTER(size_t nbSymbols, + size_t nbCarriers, + size_t spacing, + bool& enableCfr, + float& cfrClip, + float& cfrErrorClip, + bool inverse) : + ModCodec(), + myNbSymbols(nbSymbols), + myNbCarriers(nbCarriers), + mySpacing(spacing) +{ + PDEBUG("OfdmGeneratorDEXTER::OfdmGeneratorDEXTER(%zu, %zu, %zu, %s) @ %p\n", + nbSymbols, nbCarriers, spacing, inverse ? "true" : "false", this); + + etiLog.level(info) << "Using DEXTER FFT Aceelerator for fixed-point transform"; + + if (nbCarriers > spacing) { + throw std::runtime_error("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); + + const int N = mySpacing; // The size of the FFT + const size_t nbytes = N * sizeof(complexfix); + +#define IIO_ENSURE(expr, err) { \ + if (!(expr)) { \ + etiLog.log(error, "%s (%s:%d)\n", err, __FILE__, __LINE__); \ + throw std::runtime_error("Failed to set FFT for OfdmGeneratorDEXTER"); \ + } \ +} + IIO_ENSURE((m_ctx = iio_create_default_context()), "No context"); + IIO_ENSURE(m_dev_in = iio_context_find_device(m_ctx, "fft-accelerator-in"), "no dev"); + IIO_ENSURE(m_dev_out = iio_context_find_device(m_ctx, "fft-accelerator-out"), "no dev"); + IIO_ENSURE(m_channel_in = iio_device_find_channel(m_dev_in, "voltage0", true), "no channel"); + IIO_ENSURE(m_channel_out = iio_device_find_channel(m_dev_out, "voltage0", false), "no channel"); + + iio_channel_enable(m_channel_in); + iio_channel_enable(m_channel_out); + + m_buf_in = iio_device_create_buffer(m_dev_in, nbytes, false); + if (!m_buf_in) { + throw std::runtime_error("OfdmGeneratorDEXTER could not create in buffer"); + } + + m_buf_out = iio_device_create_buffer(m_dev_out, nbytes, false); + if (!m_buf_out) { + throw std::runtime_error("OfdmGeneratorDEXTER could not create out buffer"); + } +} + +OfdmGeneratorDEXTER::~OfdmGeneratorDEXTER() +{ + if (m_buf_in) { + iio_buffer_destroy(m_buf_in); + m_buf_in = nullptr; + } + + if (m_buf_out) { + iio_buffer_destroy(m_buf_out); + m_buf_out = nullptr; + } + + if (m_channel_in) { + iio_channel_disable(m_channel_in); + m_channel_in = nullptr; + } + + if (m_channel_out) { + iio_channel_disable(m_channel_out); + m_channel_out = nullptr; + } + + if (m_ctx) { + iio_context_destroy(m_ctx); + m_ctx = nullptr; + } +} + +int OfdmGeneratorDEXTER::process(Buffer* const dataIn, Buffer* dataOut) +{ + dataOut->setLength(myNbSymbols * mySpacing * sizeof(complexfix)); + + complexfix *in = reinterpret_cast(dataIn->getData()); + complexfix *out = reinterpret_cast(dataOut->getData()); + + size_t sizeIn = dataIn->getLength() / sizeof(complexfix); + size_t sizeOut = dataOut->getLength() / sizeof(complexfix); + + 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!"); + } + + ptrdiff_t iio_buf_size = (uint8_t*)iio_buffer_end(m_buf_in) - (uint8_t*)iio_buffer_start(m_buf_in); + if (iio_buf_size != (ssize_t)(mySpacing * sizeof(complexfix))) { + throw std::runtime_error("OfdmGenerator::process incorrect iio buffer size!"); + } + + ptrdiff_t p_inc = iio_buffer_step(m_buf_out); + if (p_inc != 1) { + throw std::runtime_error("OfdmGenerator::process Wrong p_inc"); + } + + const uint8_t *fft_out = (const uint8_t*)iio_buffer_first(m_buf_out, m_channel_out); + const uint8_t *fft_out_end = (const uint8_t*)iio_buffer_end(m_buf_out); + if (((fft_out_end - fft_out) != (ssize_t)(mySpacing * sizeof(complexfix))) != 0) { + throw std::runtime_error("OfdmGenerator::process fft_out length invalid!"); + } + + complexfix *fft_in = reinterpret_cast(iio_buffer_start(m_buf_in)); + + fft_in[0] = static_cast(0); + for (size_t i = 0; i < myZeroSize; i++) { + fft_in[myZeroDst + i] = static_cast(0); + } + + for (size_t i = 0; i < myNbSymbols; i++) { + /* For TM I this is: + * ZeroDst=769 ZeroSize=511 + * PosSrc=0 PosDst=1 PosSize=768 + * NegSrc=768 NegDst=1280 NegSize=768 + */ + memcpy(&fft_in[myPosDst], &in[myPosSrc], myPosSize * sizeof(complexfix)); + memcpy(&fft_in[myNegDst], &in[myNegSrc], myNegSize * sizeof(complexfix)); + + ssize_t nbytes_tx = iio_buffer_push(m_buf_in); + if (nbytes_tx < 0) { + throw std::runtime_error("OfdmGenerator::process error pushing IIO buffer!"); + } + + ssize_t nbytes_rx = iio_buffer_refill(m_buf_out); + if (nbytes_rx < 0) { + throw std::runtime_error("OfdmGenerator::process error refilling IIO buffer!"); + } + + memcpy(out, fft_out, mySpacing * sizeof(kiss_fft_cpx)); + + in += myNbCarriers; + out += mySpacing; + } + + return sizeOut; +} + +#endif // HAVE_DEXTER diff --git a/src/OfdmGenerator.h b/src/OfdmGenerator.h index 2e1aa63..b8ec702 100644 --- a/src/OfdmGenerator.h +++ b/src/OfdmGenerator.h @@ -2,7 +2,7 @@ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2023 + Copyright (C) 2024 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -140,3 +140,49 @@ class OfdmGeneratorFixed : public ModCodec unsigned myZeroDst; unsigned myZeroSize; }; + +#ifdef HAVE_DEXTER +#include "iio.h" +// The PrecisionWave DEXTER device contains an FFT accelerator in FPGA +class OfdmGeneratorDEXTER : public ModCodec +{ + public: + OfdmGeneratorDEXTER(size_t nbSymbols, + size_t nbCarriers, + size_t spacing, + bool& enableCfr, + float& cfrClip, + float& cfrErrorClip, + bool inverse = true); + virtual ~OfdmGeneratorDEXTER(); + OfdmGeneratorDEXTER(const OfdmGeneratorDEXTER&) = delete; + OfdmGeneratorDEXTER& operator=(const OfdmGeneratorDEXTER&) = delete; + + int process(Buffer* const dataIn, Buffer* dataOut) override; + const char* name() override { return "OfdmGenerator"; } + + private: + struct iio_context *m_ctx = nullptr; + + // "in" and "out" are from the point of view of the FFT Accelerator block + struct iio_device *m_dev_in = nullptr; + struct iio_channel *m_channel_in = nullptr; + struct iio_buffer *m_buf_in = nullptr; + + struct iio_device *m_dev_out = nullptr; + struct iio_channel *m_channel_out = nullptr; + struct iio_buffer *m_buf_out = nullptr; + + const size_t myNbSymbols; + const size_t myNbCarriers; + const size_t mySpacing; + unsigned myPosSrc; + unsigned myPosDst; + unsigned myPosSize; + unsigned myNegSrc; + unsigned myNegDst; + unsigned myNegSize; + unsigned myZeroDst; + unsigned myZeroSize; +}; +#endif // HAVE_DEXTER diff --git a/src/output/Dexter.h b/src/output/Dexter.h index d4f425f..f8a17ba 100644 --- a/src/output/Dexter.h +++ b/src/output/Dexter.h @@ -98,16 +98,16 @@ class Dexter : public Output::SDRDevice SDRDeviceConfig& m_conf; - struct iio_context* m_ctx = nullptr; - struct iio_device* m_dexter_dsp_tx = nullptr; + struct iio_context *m_ctx = nullptr; + struct iio_device *m_dexter_dsp_tx = nullptr; - struct iio_device* m_ad9957 = nullptr; - struct iio_device* m_ad9957_tx0 = nullptr; - struct iio_channel* m_tx_channel = nullptr; + struct iio_device *m_ad9957 = nullptr; + struct iio_device *m_ad9957_tx0 = nullptr; + struct iio_channel *m_tx_channel = nullptr; struct iio_buffer *m_buffer = nullptr; /* Underflows are counted in a separate thread */ - struct iio_context* m_underflow_ctx = nullptr; + struct iio_context *m_underflow_ctx = nullptr; std::atomic m_running = ATOMIC_VAR_INIT(false); std::thread m_underflow_read_thread; void underflow_read_process(); -- cgit v1.2.3 From 004ff5c13515f61728245ccd3d1f281be987b041 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Sat, 26 Oct 2024 17:00:51 +0200 Subject: Do not launch FFTW planning for dexter output --- src/DabMod.cpp | 3 ++- src/OfdmGenerator.cpp | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'src/OfdmGenerator.cpp') diff --git a/src/DabMod.cpp b/src/DabMod.cpp index 739fef1..b745271 100644 --- a/src/DabMod.cpp +++ b/src/DabMod.cpp @@ -424,7 +424,8 @@ int launch_modulator(int argc, char* argv[]) ModulatorData m; rcs.enrol(&m); - if (not mod_settings.fixedPoint) { + // Neither KISS FFT used for fixedpoint nor the FFT Accelerator used for DEXTER need planning. + if (not (mod_settings.fixedPoint or mod_settings.useDexterOutput)) { // This is mostly useful on ARM systems where FFTW planning takes some time. If we do it here // it will be done before the modulator starts up etiLog.level(debug) << "Running FFTW planning..."; diff --git a/src/OfdmGenerator.cpp b/src/OfdmGenerator.cpp index 198c2fc..62a0ee5 100644 --- a/src/OfdmGenerator.cpp +++ b/src/OfdmGenerator.cpp @@ -599,7 +599,7 @@ OfdmGeneratorDEXTER::OfdmGeneratorDEXTER(size_t nbSymbols, PDEBUG("OfdmGeneratorDEXTER::OfdmGeneratorDEXTER(%zu, %zu, %zu, %s) @ %p\n", nbSymbols, nbCarriers, spacing, inverse ? "true" : "false", this); - etiLog.level(info) << "Using DEXTER FFT Aceelerator for fixed-point transform"; + etiLog.level(info) << "Using DEXTER FFT Accelerator for fixed-point transform"; if (nbCarriers > spacing) { throw std::runtime_error("OfdmGenerator nbCarriers > spacing!"); @@ -758,7 +758,7 @@ int OfdmGeneratorDEXTER::process(Buffer* const dataIn, Buffer* dataOut) throw std::runtime_error("OfdmGenerator::process error refilling IIO buffer!"); } - memcpy(out, fft_out, mySpacing * sizeof(kiss_fft_cpx)); + memcpy(out, fft_out, mySpacing * sizeof(complexfix)); in += myNbCarriers; out += mySpacing; -- cgit v1.2.3 From 933021ed44e6c1bd2cf1dee91fc033c145c061af Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Tue, 29 Oct 2024 22:55:41 +0100 Subject: Change to 16-bit fixed point --- Makefile.am | 2 +- src/Buffer.h | 4 +- src/DabMod.cpp | 3 +- src/DabModulator.cpp | 15 ++++-- src/FormatConverter.cpp | 121 ++++++++++++++++++------------------------------ src/OfdmGenerator.cpp | 30 ++++++------ src/PhaseReference.cpp | 4 +- 7 files changed, 79 insertions(+), 100 deletions(-) (limited to 'src/OfdmGenerator.cpp') diff --git a/Makefile.am b/Makefile.am index 47d6f43..87d553a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -35,7 +35,7 @@ endif bin_PROGRAMS = odr-dabmod -KISS_FLAGS=-DFIXED_POINT=32 +KISS_FLAGS=-DFIXED_POINT=16 odr_dabmod_CFLAGS = -Wall -Isrc -Ilib -Ikiss \ $(GITVERSION_FLAGS) $(KISS_FLAGS) odr_dabmod_CXXFLAGS = -Wall -Isrc -Ilib -Ikiss \ diff --git a/src/Buffer.h b/src/Buffer.h index 711b804..f6c94e0 100644 --- a/src/Buffer.h +++ b/src/Buffer.h @@ -37,7 +37,9 @@ #include "fpm/fixed.hpp" typedef std::complex complexf; -typedef std::complex complexfix; + +using fixed_16 = fpm::fixed; +typedef std::complex complexfix; /* Buffer is a container for a byte array, which is memory-aligned * to 32 bytes for SSE performance. diff --git a/src/DabMod.cpp b/src/DabMod.cpp index b745271..4726df9 100644 --- a/src/DabMod.cpp +++ b/src/DabMod.cpp @@ -313,7 +313,6 @@ static shared_ptr prepare_output(mod_settings_t& s) else if (s.useDexterOutput) { /* We normalise specifically range [-32768; 32767] */ s.normalise = 32767.0f / normalise_factor; - if (s.fixedPoint) throw runtime_error("dexter fixed_point unsupported"); s.sdr_device_config.sampleRate = s.outputRate; auto dexterdevice = make_shared(s.sdr_device_config); output = make_shared(s.sdr_device_config, dexterdevice); @@ -448,7 +447,7 @@ int launch_modulator(int argc, char* argv[]) std::string output_format; if (mod_settings.fixedPoint) { - output_format = "fixedpoint"; + output_format = ""; //fixed point is native sc16, no converter needed } else if (mod_settings.useFileOutput and (mod_settings.fileOutputFormat == "s8" or diff --git a/src/DabModulator.cpp b/src/DabModulator.cpp index 757b01f..5f01725 100644 --- a/src/DabModulator.cpp +++ b/src/DabModulator.cpp @@ -178,11 +178,16 @@ int DabModulator::process(Buffer* dataOut) shared_ptr tii; shared_ptr tiiRef; try { - tii = make_shared( - m_settings.dabMode, - m_settings.tiiConfig); - rcs.enrol(tii.get()); - tiiRef = make_shared(mode, m_settings.fixedPoint); + if (m_settings.fixedPoint) { + etiLog.level(warn) << "TII does not yet support fixed point"; + } + else { + tii = make_shared( + m_settings.dabMode, + m_settings.tiiConfig); + rcs.enrol(tii.get()); + tiiRef = make_shared(mode, m_settings.fixedPoint); + } } catch (const TIIError& e) { etiLog.level(error) << "Could not initialise TII: " << e.what(); diff --git a/src/FormatConverter.cpp b/src/FormatConverter.cpp index 9e59a4a..a52f501 100644 --- a/src/FormatConverter.cpp +++ b/src/FormatConverter.cpp @@ -55,99 +55,68 @@ int FormatConverter::process(Buffer* const dataIn, Buffer* dataOut) size_t num_clipped_samples = 0; - bool source_is_complexf = m_format != "fixedpoint"; - - if (source_is_complexf) { - size_t sizeIn = dataIn->getLength() / sizeof(float); - float* in = reinterpret_cast(dataIn->getData()); - - if (m_format == "s16") { - dataOut->setLength(sizeIn * sizeof(int16_t)); - int16_t* out = reinterpret_cast(dataOut->getData()); - - for (size_t i = 0; i < sizeIn; i++) { - if (in[i] < INT16_MIN) { - out[i] = INT16_MIN; - num_clipped_samples++; - } - else if (in[i] > INT16_MAX) { - out[i] = INT16_MAX; - num_clipped_samples++; - } - else { - out[i] = in[i]; - } - } - } - else if (m_format == "u8") { - dataOut->setLength(sizeIn * sizeof(int8_t)); - uint8_t* out = reinterpret_cast(dataOut->getData()); - - for (size_t i = 0; i < sizeIn; i++) { - const auto samp = in[i] + 128.0f; - if (samp < 0) { - out[i] = 0; - num_clipped_samples++; - } - else if (samp > UINT8_MAX) { - out[i] = UINT8_MAX; - num_clipped_samples++; - } - else { - out[i] = samp; - } + size_t sizeIn = dataIn->getLength() / sizeof(float); + float* in = reinterpret_cast(dataIn->getData()); + + if (m_format == "s16") { + dataOut->setLength(sizeIn * sizeof(int16_t)); + int16_t* out = reinterpret_cast(dataOut->getData()); + for (size_t i = 0; i < sizeIn; i++) { + if (in[i] < INT16_MIN) { + out[i] = INT16_MIN; + num_clipped_samples++; } - } - else if (m_format == "s8") { - dataOut->setLength(sizeIn * sizeof(int8_t)); - int8_t* out = reinterpret_cast(dataOut->getData()); - - for (size_t i = 0; i < sizeIn; i++) { - if (in[i] < INT8_MIN) { - out[i] = INT8_MIN; - num_clipped_samples++; - } - else if (in[i] > INT8_MAX) { - out[i] = INT8_MAX; - num_clipped_samples++; - } - else { - out[i] = in[i]; - } + else if (in[i] > INT16_MAX) { + out[i] = INT16_MAX; + num_clipped_samples++; + } + else { + out[i] = in[i]; } } - else { - throw std::runtime_error("FormatConverter: Invalid format " + m_format); - } - } - else { - // Output is always sc16, because that's what UHD accepts + else if (m_format == "u8") { + dataOut->setLength(sizeIn * sizeof(int8_t)); + uint8_t* out = reinterpret_cast(dataOut->getData()); - using fixed_t = complexfix::value_type; - size_t sizeIn = dataIn->getLength() / sizeof(fixed_t); - fixed_t* in = reinterpret_cast(dataIn->getData()); + for (size_t i = 0; i < sizeIn; i++) { + const auto samp = in[i] + 128.0f; + if (samp < 0) { + out[i] = 0; + num_clipped_samples++; + } + else if (samp > UINT8_MAX) { + out[i] = UINT8_MAX; + num_clipped_samples++; + } + else { + out[i] = samp; + } - dataOut->setLength(sizeIn * sizeof(int16_t)); - int16_t* out = reinterpret_cast(dataOut->getData()); + } + } + else if (m_format == "s8") { + dataOut->setLength(sizeIn * sizeof(int8_t)); + int8_t* out = reinterpret_cast(dataOut->getData()); for (size_t i = 0; i < sizeIn; i++) { - const auto v = (in[i] * 2).raw_value(); - - if (v < INT16_MIN) { - out[i] = INT16_MIN; + if (in[i] < INT8_MIN) { + out[i] = INT8_MIN; num_clipped_samples++; } - else if (v > INT16_MAX) { - out[i] = INT16_MAX; + else if (in[i] > INT8_MAX) { + out[i] = INT8_MAX; num_clipped_samples++; } else { - out[i] = (int16_t)v; + out[i] = in[i]; } } } + else { + throw std::runtime_error("FormatConverter: Invalid format " + m_format); + } m_num_clipped_samples.store(num_clipped_samples); return dataOut->getLength(); diff --git a/src/OfdmGenerator.cpp b/src/OfdmGenerator.cpp index 62a0ee5..4f6eeb9 100644 --- a/src/OfdmGenerator.cpp +++ b/src/OfdmGenerator.cpp @@ -633,8 +633,8 @@ OfdmGeneratorDEXTER::OfdmGeneratorDEXTER(size_t nbSymbols, PDEBUG(" myZeroDst: %u\n", myZeroDst); PDEBUG(" myZeroSize: %u\n", myZeroSize); - const int N = mySpacing; // The size of the FFT - const size_t nbytes = N * sizeof(complexfix); + const size_t nbytes = mySpacing * sizeof(complexfix); + fprintf(stderr, "sizeof(complexfix)=%zu\n", sizeof(complexfix)); #define IIO_ENSURE(expr, err) { \ if (!(expr)) { \ @@ -721,17 +721,6 @@ int OfdmGeneratorDEXTER::process(Buffer* const dataIn, Buffer* dataOut) throw std::runtime_error("OfdmGenerator::process incorrect iio buffer size!"); } - ptrdiff_t p_inc = iio_buffer_step(m_buf_out); - if (p_inc != 1) { - throw std::runtime_error("OfdmGenerator::process Wrong p_inc"); - } - - const uint8_t *fft_out = (const uint8_t*)iio_buffer_first(m_buf_out, m_channel_out); - const uint8_t *fft_out_end = (const uint8_t*)iio_buffer_end(m_buf_out); - if (((fft_out_end - fft_out) != (ssize_t)(mySpacing * sizeof(complexfix))) != 0) { - throw std::runtime_error("OfdmGenerator::process fft_out length invalid!"); - } - complexfix *fft_in = reinterpret_cast(iio_buffer_start(m_buf_in)); fft_in[0] = static_cast(0); @@ -758,6 +747,21 @@ int OfdmGeneratorDEXTER::process(Buffer* const dataIn, Buffer* dataOut) throw std::runtime_error("OfdmGenerator::process error refilling IIO buffer!"); } + fprintf(stderr, "IIO refill %zd\n", nbytes_rx); + + ptrdiff_t p_inc = iio_buffer_step(m_buf_out); + if (p_inc != 1) { + throw std::runtime_error("OfdmGenerator::process Wrong p_inc"); + } + + const uint8_t *fft_out = (const uint8_t*)iio_buffer_first(m_buf_out, m_channel_out); + const uint8_t *fft_out_end = (const uint8_t*)iio_buffer_end(m_buf_out); + if ((fft_out_end - fft_out) != (ssize_t)(mySpacing * sizeof(complexfix))) { + fprintf(stderr, "FFT_OUT: %p %p %zu %zu\n", + fft_out, fft_out_end, (fft_out_end - fft_out), mySpacing * sizeof(complexfix)); + throw std::runtime_error("OfdmGenerator::process fft_out length invalid!"); + } + memcpy(out, fft_out, mySpacing * sizeof(complexfix)); in += myNbCarriers; diff --git a/src/PhaseReference.cpp b/src/PhaseReference.cpp index d7b89bf..e2fb9a9 100644 --- a/src/PhaseReference.cpp +++ b/src/PhaseReference.cpp @@ -137,8 +137,8 @@ complexf PhaseRefGen::convert(uint8_t data) { template <> complexfix PhaseRefGen::convert(uint8_t data) { - constexpr auto one = fpm::fixed_16_16{1}; - constexpr auto zero = fpm::fixed_16_16{0}; + constexpr auto one = fixed_16{1}; + constexpr auto zero = fixed_16{0}; const complexfix value[] = { complexfix(one, zero), -- cgit v1.2.3 From f1a3db6d7dc1461bcf4a8933a77267698fdffd30 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Tue, 29 Oct 2024 23:52:22 +0100 Subject: Rework fixed point config setting --- src/Buffer.h | 1 + src/ConfigParser.cpp | 24 ++++++- src/ConfigParser.h | 8 ++- src/DabMod.cpp | 18 +++--- src/DabModulator.cpp | 96 +++++++++++++++------------- src/FormatConverter.cpp | 142 +++++++++++++++++++++++++----------------- src/FormatConverter.h | 9 ++- src/GuardIntervalInserter.cpp | 23 ++++--- src/GuardIntervalInserter.h | 5 +- src/OfdmGenerator.cpp | 24 ++++--- 10 files changed, 213 insertions(+), 137 deletions(-) (limited to 'src/OfdmGenerator.cpp') diff --git a/src/Buffer.h b/src/Buffer.h index f6c94e0..bf3b5f4 100644 --- a/src/Buffer.h +++ b/src/Buffer.h @@ -40,6 +40,7 @@ typedef std::complex complexf; using fixed_16 = fpm::fixed; typedef std::complex complexfix; +typedef std::complex complexfix_wide; /* Buffer is a container for a byte array, which is memory-aligned * to 32 bytes for SSE performance. diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp index a48f7e7..5d9f6f3 100644 --- a/src/ConfigParser.cpp +++ b/src/ConfigParser.cpp @@ -65,6 +65,27 @@ static GainMode parse_gainmode(const std::string &gainMode_setting) throw std::runtime_error("Configuration error"); } +static FFTEngine parse_fft_engine(const std::string &fft_engine_setting) +{ + string fft_engine_minuscule(fft_engine_setting); + std::transform(fft_engine_minuscule.begin(), fft_engine_minuscule.end(), + fft_engine_minuscule.begin(), ::tolower); + + if (fft_engine_minuscule == "fftw") { + return FFTEngine::FFTW; + } + else if (fft_engine_minuscule == "kiss") { + return FFTEngine::KISS; + } + else if (fft_engine_minuscule == "dexter") { + return FFTEngine::DEXTER; + } + + cerr << "Modulator fft_engine setting '" << fft_engine_setting << + "' not recognised." << endl; + throw std::runtime_error("Configuration error"); +} + static void parse_configfile( const std::string& configuration_file, mod_settings_t& mod_settings) @@ -154,7 +175,8 @@ static void parse_configfile( mod_settings.showProcessTime); // modulator parameters: - mod_settings.fixedPoint = pt.GetInteger("modulator.fixed_point", mod_settings.fixedPoint); + const string fft_engine_setting = pt.Get("modulator.fft_engine", "fftw"); + mod_settings.fftEngine = parse_fft_engine(fft_engine_setting); const string gainMode_setting = pt.Get("modulator.gainmode", "var"); mod_settings.gainMode = parse_gainmode(gainMode_setting); diff --git a/src/ConfigParser.h b/src/ConfigParser.h index f3a2af9..3bacfdd 100644 --- a/src/ConfigParser.h +++ b/src/ConfigParser.h @@ -36,6 +36,12 @@ #include "TII.h" #include "output/SDRDevice.h" +enum class FFTEngine { + FFTW, // floating point in software + KISS, // fixed-point in software + DEXTER // fixed-point in FPGA +}; + struct mod_settings_t { std::string startupCheck; @@ -51,7 +57,7 @@ struct mod_settings_t { bool useLimeOutput = false; bool useBladeRFOutput = false; - bool fixedPoint = false; + FFTEngine fftEngine = FFTEngine::FFTW; size_t outputRate = 2048000; size_t clockRate = 0; diff --git a/src/DabMod.cpp b/src/DabMod.cpp index 4726df9..361e0d4 100644 --- a/src/DabMod.cpp +++ b/src/DabMod.cpp @@ -252,7 +252,7 @@ static shared_ptr prepare_output(mod_settings_t& s) shared_ptr output; if (s.useFileOutput) { - if (s.fixedPoint) { + if (s.fftEngine != FFTEngine::FFTW) { // Intentionally ignore fileOutputFormat, it is always sc16 output = make_shared(s.outputName, s.fileOutputShowMetadata); } @@ -292,7 +292,7 @@ static shared_ptr prepare_output(mod_settings_t& s) else if (s.useUHDOutput) { s.normalise = 1.0f / normalise_factor; s.sdr_device_config.sampleRate = s.outputRate; - s.sdr_device_config.fixedPoint = s.fixedPoint; + s.sdr_device_config.fixedPoint = (s.fftEngine != FFTEngine::FFTW); auto uhddevice = make_shared(s.sdr_device_config); output = make_shared(s.sdr_device_config, uhddevice); rcs.enrol((Output::SDR*)output.get()); @@ -303,7 +303,7 @@ static shared_ptr prepare_output(mod_settings_t& s) /* We normalise the same way as for the UHD output */ s.normalise = 1.0f / normalise_factor; s.sdr_device_config.sampleRate = s.outputRate; - if (s.fixedPoint) throw runtime_error("soapy fixed_point unsupported"); + if (s.fftEngine != FFTEngine::FFTW) throw runtime_error("soapy fixed_point unsupported"); auto soapydevice = make_shared(s.sdr_device_config); output = make_shared(s.sdr_device_config, soapydevice); rcs.enrol((Output::SDR*)output.get()); @@ -323,7 +323,7 @@ static shared_ptr prepare_output(mod_settings_t& s) else if (s.useLimeOutput) { /* We normalise the same way as for the UHD output */ s.normalise = 1.0f / normalise_factor; - if (s.fixedPoint) throw runtime_error("limesdr fixed_point unsupported"); + if (s.fftEngine != FFTEngine::FFTW) throw runtime_error("limesdr fixed_point unsupported"); s.sdr_device_config.sampleRate = s.outputRate; auto limedevice = make_shared(s.sdr_device_config); output = make_shared(s.sdr_device_config, limedevice); @@ -334,7 +334,7 @@ static shared_ptr prepare_output(mod_settings_t& s) else if (s.useBladeRFOutput) { /* We normalise specifically for the BladeRF output : range [-2048; 2047] */ s.normalise = 2047.0f / normalise_factor; - if (s.fixedPoint) throw runtime_error("bladerf fixed_point unsupported"); + if (s.fftEngine != FFTEngine::FFTW) throw runtime_error("bladerf fixed_point unsupported"); s.sdr_device_config.sampleRate = s.outputRate; auto bladerfdevice = make_shared(s.sdr_device_config); output = make_shared(s.sdr_device_config, bladerfdevice); @@ -424,7 +424,7 @@ int launch_modulator(int argc, char* argv[]) rcs.enrol(&m); // Neither KISS FFT used for fixedpoint nor the FFT Accelerator used for DEXTER need planning. - if (not (mod_settings.fixedPoint or mod_settings.useDexterOutput)) { + if (mod_settings.fftEngine == FFTEngine::FFTW) { // This is mostly useful on ARM systems where FFTW planning takes some time. If we do it here // it will be done before the modulator starts up etiLog.level(debug) << "Running FFTW planning..."; @@ -446,9 +446,13 @@ int launch_modulator(int argc, char* argv[]) } std::string output_format; - if (mod_settings.fixedPoint) { + if (mod_settings.fftEngine == FFTEngine::KISS) { output_format = ""; //fixed point is native sc16, no converter needed } + else if (mod_settings.fftEngine == FFTEngine::DEXTER) { + output_format = "s16"; // FPGA FFT Engine outputs s32 + } + // else FFTW, i.e. floating point else if (mod_settings.useFileOutput and (mod_settings.fileOutputFormat == "s8" or mod_settings.fileOutputFormat == "u8" or diff --git a/src/DabModulator.cpp b/src/DabModulator.cpp index 5f01725..4cbd0f5 100644 --- a/src/DabModulator.cpp +++ b/src/DabModulator.cpp @@ -142,13 +142,14 @@ int DabModulator::process(Buffer* dataOut) auto cifMux = make_shared(m_etiSource); auto cifPart = make_shared(mode); - auto cifMap = make_shared(m_nbCarriers, m_settings.fixedPoint); - auto cifRef = make_shared(mode, m_settings.fixedPoint); - auto cifFreq = make_shared(mode, m_settings.fixedPoint); - auto cifDiff = make_shared(m_nbCarriers, m_settings.fixedPoint); + const bool fixedPoint = m_settings.fftEngine != FFTEngine::FFTW; + auto cifMap = make_shared(m_nbCarriers, fixedPoint); + auto cifRef = make_shared(mode, fixedPoint); + auto cifFreq = make_shared(mode, fixedPoint); + auto cifDiff = make_shared(m_nbCarriers, fixedPoint); auto cifNull = make_shared(m_nbCarriers, - m_settings.fixedPoint ? sizeof(complexfix) : sizeof(complexf)); + fixedPoint ? sizeof(complexfix) : sizeof(complexf)); auto cifSig = make_shared(); // TODO this needs a review @@ -178,7 +179,7 @@ int DabModulator::process(Buffer* dataOut) shared_ptr tii; shared_ptr tiiRef; try { - if (m_settings.fixedPoint) { + if (fixedPoint) { etiLog.level(warn) << "TII does not yet support fixed point"; } else { @@ -186,7 +187,7 @@ int DabModulator::process(Buffer* dataOut) m_settings.dabMode, m_settings.tiiConfig); rcs.enrol(tii.get()); - tiiRef = make_shared(mode, m_settings.fixedPoint); + tiiRef = make_shared(mode, fixedPoint); } } catch (const TIIError& e) { @@ -195,40 +196,43 @@ int DabModulator::process(Buffer* dataOut) shared_ptr cifOfdm; - if (m_settings.useDexterOutput) { - cifOfdm = make_shared( - (1 + m_nbSymbols), - m_nbCarriers, - m_spacing, - m_settings.enableCfr, - m_settings.cfrClip, - m_settings.cfrErrorClip); - } - else if (m_settings.fixedPoint) { - cifOfdm = make_shared( - (1 + m_nbSymbols), - m_nbCarriers, - m_spacing, - m_settings.enableCfr, - m_settings.cfrClip, - m_settings.cfrErrorClip); - } - else { - auto ofdm = make_shared( - (1 + m_nbSymbols), - m_nbCarriers, - m_spacing, - m_settings.enableCfr, - m_settings.cfrClip, - m_settings.cfrErrorClip); - - rcs.enrol(ofdm.get()); - cifOfdm = ofdm; + switch (m_settings.fftEngine) { + case FFTEngine::FFTW: + { + auto ofdm = make_shared( + (1 + m_nbSymbols), + m_nbCarriers, + m_spacing, + m_settings.enableCfr, + m_settings.cfrClip, + m_settings.cfrErrorClip); + rcs.enrol(ofdm.get()); + cifOfdm = ofdm; + } + break; + case FFTEngine::KISS: + cifOfdm = make_shared( + (1 + m_nbSymbols), + m_nbCarriers, + m_spacing, + m_settings.enableCfr, + m_settings.cfrClip, + m_settings.cfrErrorClip); + break; + case FFTEngine::DEXTER: + cifOfdm = make_shared( + (1 + m_nbSymbols), + m_nbCarriers, + m_spacing, + m_settings.enableCfr, + m_settings.cfrClip, + m_settings.cfrErrorClip); + break; } shared_ptr cifGain; - if (not m_settings.fixedPoint) { + if (not fixedPoint) { cifGain = make_shared( m_spacing, m_settings.gainMode, @@ -241,12 +245,12 @@ int DabModulator::process(Buffer* dataOut) auto cifGuard = make_shared( m_nbSymbols, m_spacing, m_nullSize, m_symSize, - m_settings.ofdmWindowOverlap, m_settings.fixedPoint); + m_settings.ofdmWindowOverlap, m_settings.fftEngine); rcs.enrol(cifGuard.get()); shared_ptr cifFilter; if (not m_settings.filterTapsFilename.empty()) { - if (m_settings.fixedPoint) throw std::runtime_error("fixed point doesn't support fir filter"); + if (fixedPoint) throw std::runtime_error("fixed point doesn't support fir filter"); cifFilter = make_shared(m_settings.filterTapsFilename); rcs.enrol(cifFilter.get()); @@ -254,7 +258,7 @@ int DabModulator::process(Buffer* dataOut) shared_ptr cifPoly; if (not m_settings.polyCoefFilename.empty()) { - if (m_settings.fixedPoint) throw std::runtime_error("fixed point doesn't support predistortion"); + if (fixedPoint) throw std::runtime_error("fixed point doesn't support predistortion"); cifPoly = make_shared(m_settings.polyCoefFilename, m_settings.polyNumThreads); @@ -263,7 +267,7 @@ int DabModulator::process(Buffer* dataOut) shared_ptr cifRes; if (m_settings.outputRate != 2048000) { - if (m_settings.fixedPoint) throw std::runtime_error("fixed point doesn't support resampler"); + if (fixedPoint) throw std::runtime_error("fixed point doesn't support resampler"); cifRes = make_shared( 2048000, @@ -271,11 +275,13 @@ int DabModulator::process(Buffer* dataOut) m_spacing); } - if (not m_format.empty()) { - // This handles both complexf and fixedpoint: - // Convert from complexfix to interleaved int16_t I/Q - m_formatConverter = make_shared(m_format); + if (m_settings.fftEngine == FFTEngine::FFTW and not m_format.empty()) { + m_formatConverter = make_shared(false, m_format); + } + else if (m_settings.fftEngine == FFTEngine::DEXTER) { + m_formatConverter = make_shared(true, m_format); } + // KISS is already in s16 m_output = make_shared(dataOut); diff --git a/src/FormatConverter.cpp b/src/FormatConverter.cpp index a52f501..1821442 100644 --- a/src/FormatConverter.cpp +++ b/src/FormatConverter.cpp @@ -34,9 +34,10 @@ #include #include -FormatConverter::FormatConverter(const std::string& format) : +FormatConverter::FormatConverter(bool input_is_complexfix_wide, const std::string& format_out) : ModCodec(), - m_format(format) + m_input_complexfix_wide(input_is_complexfix_wide), + m_format_out(format_out) { } FormatConverter::~FormatConverter() @@ -55,67 +56,95 @@ int FormatConverter::process(Buffer* const dataIn, Buffer* dataOut) size_t num_clipped_samples = 0; - size_t sizeIn = dataIn->getLength() / sizeof(float); - float* in = reinterpret_cast(dataIn->getData()); - if (m_format == "s16") { - dataOut->setLength(sizeIn * sizeof(int16_t)); - int16_t* out = reinterpret_cast(dataOut->getData()); - - for (size_t i = 0; i < sizeIn; i++) { - if (in[i] < INT16_MIN) { - out[i] = INT16_MIN; - num_clipped_samples++; - } - else if (in[i] > INT16_MAX) { - out[i] = INT16_MAX; - num_clipped_samples++; - } - else { - out[i] = in[i]; + if (m_input_complexfix_wide) { + size_t sizeIn = dataIn->getLength() / sizeof(int32_t); + int32_t* in = reinterpret_cast(dataIn->getData()); + if (m_format_out == "s16") { + dataOut->setLength(sizeIn * sizeof(int16_t)); + int16_t* out = reinterpret_cast(dataOut->getData()); + + for (size_t i = 0; i < sizeIn; i++) { + if (in[i] < INT16_MIN) { + out[i] = INT16_MIN; + num_clipped_samples++; + } + else if (in[i] > INT16_MAX) { + out[i] = INT16_MAX; + num_clipped_samples++; + } + else { + out[i] = in[i]; + } } } - } - else if (m_format == "u8") { - dataOut->setLength(sizeIn * sizeof(int8_t)); - uint8_t* out = reinterpret_cast(dataOut->getData()); - - for (size_t i = 0; i < sizeIn; i++) { - const auto samp = in[i] + 128.0f; - if (samp < 0) { - out[i] = 0; - num_clipped_samples++; - } - else if (samp > UINT8_MAX) { - out[i] = UINT8_MAX; - num_clipped_samples++; - } - else { - out[i] = samp; - } - + else { + throw std::runtime_error("FormatConverter: Invalid fix format " + m_format_out); } } - else if (m_format == "s8") { - dataOut->setLength(sizeIn * sizeof(int8_t)); - int8_t* out = reinterpret_cast(dataOut->getData()); - - for (size_t i = 0; i < sizeIn; i++) { - if (in[i] < INT8_MIN) { - out[i] = INT8_MIN; - num_clipped_samples++; + else { + size_t sizeIn = dataIn->getLength() / sizeof(float); + float* in = reinterpret_cast(dataIn->getData()); + + if (m_format_out == "s16") { + dataOut->setLength(sizeIn * sizeof(int16_t)); + int16_t* out = reinterpret_cast(dataOut->getData()); + + for (size_t i = 0; i < sizeIn; i++) { + if (in[i] < INT16_MIN) { + out[i] = INT16_MIN; + num_clipped_samples++; + } + else if (in[i] > INT16_MAX) { + out[i] = INT16_MAX; + num_clipped_samples++; + } + else { + out[i] = in[i]; + } } - else if (in[i] > INT8_MAX) { - out[i] = INT8_MAX; - num_clipped_samples++; + } + else if (m_format_out == "u8") { + dataOut->setLength(sizeIn * sizeof(int8_t)); + uint8_t* out = reinterpret_cast(dataOut->getData()); + + for (size_t i = 0; i < sizeIn; i++) { + const auto samp = in[i] + 128.0f; + if (samp < 0) { + out[i] = 0; + num_clipped_samples++; + } + else if (samp > UINT8_MAX) { + out[i] = UINT8_MAX; + num_clipped_samples++; + } + else { + out[i] = samp; + } + } - else { - out[i] = in[i]; + } + else if (m_format_out == "s8") { + dataOut->setLength(sizeIn * sizeof(int8_t)); + int8_t* out = reinterpret_cast(dataOut->getData()); + + for (size_t i = 0; i < sizeIn; i++) { + if (in[i] < INT8_MIN) { + out[i] = INT8_MIN; + num_clipped_samples++; + } + else if (in[i] > INT8_MAX) { + out[i] = INT8_MAX; + num_clipped_samples++; + } + else { + out[i] = in[i]; + } } } - } - else { - throw std::runtime_error("FormatConverter: Invalid format " + m_format); + else { + throw std::runtime_error("FormatConverter: Invalid format " + m_format_out); + } } m_num_clipped_samples.store(num_clipped_samples); @@ -136,10 +165,7 @@ size_t FormatConverter::get_num_clipped_samples() const size_t FormatConverter::get_format_size(const std::string& format) { // Returns 2*sizeof(SAMPLE_TYPE) because we have I + Q - if (format == "fixedpoint") { - return 4; - } - else if (format == "s16") { + if (format == "s16") { return 4; } else if (format == "u8") { diff --git a/src/FormatConverter.h b/src/FormatConverter.h index 27ca0b1..1ed2283 100644 --- a/src/FormatConverter.h +++ b/src/FormatConverter.h @@ -41,8 +41,10 @@ class FormatConverter : public ModCodec public: static size_t get_format_size(const std::string& format); - // Allowed formats: s8, u8 and s16 - FormatConverter(const std::string& format); + // floating-point input allows output formats: s8, u8 and s16 + // complexfix_wide input allows output formats: s16 + // complexfix input is already in s16, and needs no converter + FormatConverter(bool input_is_complexfix_wide, const std::string& format_out); virtual ~FormatConverter(); int process(Buffer* const dataIn, Buffer* dataOut); @@ -51,7 +53,8 @@ class FormatConverter : public ModCodec size_t get_num_clipped_samples() const; private: - std::string m_format; + bool m_input_complexfix_wide; + std::string m_format_out; std::atomic m_num_clipped_samples = 0; }; diff --git a/src/GuardIntervalInserter.cpp b/src/GuardIntervalInserter.cpp index 4e22367..7061e47 100644 --- a/src/GuardIntervalInserter.cpp +++ b/src/GuardIntervalInserter.cpp @@ -49,10 +49,10 @@ GuardIntervalInserter::GuardIntervalInserter( size_t nullSize, size_t symSize, size_t& windowOverlap, - bool fixedPoint) : + FFTEngine fftEngine) : ModCodec(), RemoteControllable("guardinterval"), - m_fixedPoint(fixedPoint), + m_fftEngine(fftEngine), m_params(nbSymbols, spacing, nullSize, symSize, windowOverlap) { if (nullSize == 0) { @@ -277,15 +277,18 @@ int do_process(const GuardIntervalInserter::Params& p, Buffer* const dataIn, Buf int GuardIntervalInserter::process(Buffer* const dataIn, Buffer* dataOut) { - if (m_fixedPoint) { - if (m_params.windowOverlap) { - throw std::runtime_error("fixed point and ofdm windowing not supported"); - } - return do_process(m_params, dataIn, dataOut); - } - else { - return do_process(m_params, dataIn, dataOut); + switch (m_fftEngine) { + case FFTEngine::FFTW: + return do_process(m_params, dataIn, dataOut); + case FFTEngine::KISS: + if (m_params.windowOverlap) { + throw std::runtime_error("fixed point and ofdm windowing not supported"); + } + return do_process(m_params, dataIn, dataOut); + case FFTEngine::DEXTER: + return do_process(m_params, dataIn, dataOut); } + throw std::logic_error("Unhandled fftEngine variant"); } void GuardIntervalInserter::set_parameter( diff --git a/src/GuardIntervalInserter.h b/src/GuardIntervalInserter.h index 380142e..8d329ff 100644 --- a/src/GuardIntervalInserter.h +++ b/src/GuardIntervalInserter.h @@ -30,6 +30,7 @@ # include #endif +#include "ConfigParser.h" #include "ModPlugin.h" #include "RemoteControl.h" #include @@ -51,7 +52,7 @@ class GuardIntervalInserter : public ModCodec, public RemoteControllable size_t nullSize, size_t symSize, size_t& windowOverlap, - bool fixedPoint); + FFTEngine fftEngine); virtual ~GuardIntervalInserter() {} @@ -84,7 +85,7 @@ class GuardIntervalInserter : public ModCodec, public RemoteControllable protected: void update_window(size_t new_window_overlap); - bool m_fixedPoint; + FFTEngine m_fftEngine; Params m_params; diff --git a/src/OfdmGenerator.cpp b/src/OfdmGenerator.cpp index 4f6eeb9..11f5bf1 100644 --- a/src/OfdmGenerator.cpp +++ b/src/OfdmGenerator.cpp @@ -633,8 +633,8 @@ OfdmGeneratorDEXTER::OfdmGeneratorDEXTER(size_t nbSymbols, PDEBUG(" myZeroDst: %u\n", myZeroDst); PDEBUG(" myZeroSize: %u\n", myZeroSize); - const size_t nbytes = mySpacing * sizeof(complexfix); - fprintf(stderr, "sizeof(complexfix)=%zu\n", sizeof(complexfix)); + const size_t nbytes_in = mySpacing * sizeof(complexfix); + const size_t nbytes_out = mySpacing * 2 * sizeof(int32_t); #define IIO_ENSURE(expr, err) { \ if (!(expr)) { \ @@ -651,12 +651,12 @@ OfdmGeneratorDEXTER::OfdmGeneratorDEXTER(size_t nbSymbols, iio_channel_enable(m_channel_in); iio_channel_enable(m_channel_out); - m_buf_in = iio_device_create_buffer(m_dev_in, nbytes, false); + m_buf_in = iio_device_create_buffer(m_dev_in, nbytes_in, false); if (!m_buf_in) { throw std::runtime_error("OfdmGeneratorDEXTER could not create in buffer"); } - m_buf_out = iio_device_create_buffer(m_dev_out, nbytes, false); + m_buf_out = iio_device_create_buffer(m_dev_out, nbytes_out, false); if (!m_buf_out) { throw std::runtime_error("OfdmGeneratorDEXTER could not create out buffer"); } @@ -692,13 +692,13 @@ OfdmGeneratorDEXTER::~OfdmGeneratorDEXTER() int OfdmGeneratorDEXTER::process(Buffer* const dataIn, Buffer* dataOut) { - dataOut->setLength(myNbSymbols * mySpacing * sizeof(complexfix)); + dataOut->setLength(myNbSymbols * mySpacing * sizeof(complexfix_wide)); complexfix *in = reinterpret_cast(dataIn->getData()); - complexfix *out = reinterpret_cast(dataOut->getData()); + complexfix_wide *out = reinterpret_cast(dataOut->getData()); size_t sizeIn = dataIn->getLength() / sizeof(complexfix); - size_t sizeOut = dataOut->getLength() / sizeof(complexfix); + size_t sizeOut = dataOut->getLength() / sizeof(complexfix_wide); if (sizeIn != myNbSymbols * myNbCarriers) { PDEBUG("Nb symbols: %zu\n", myNbSymbols); @@ -754,15 +754,19 @@ int OfdmGeneratorDEXTER::process(Buffer* const dataIn, Buffer* dataOut) throw std::runtime_error("OfdmGenerator::process Wrong p_inc"); } + // The FFT Accelerator takes 16-bit I + 16-bit Q, and outputs 32-bit I and 32-bit Q. + // The formatconvert will take care of this const uint8_t *fft_out = (const uint8_t*)iio_buffer_first(m_buf_out, m_channel_out); const uint8_t *fft_out_end = (const uint8_t*)iio_buffer_end(m_buf_out); - if ((fft_out_end - fft_out) != (ssize_t)(mySpacing * sizeof(complexfix))) { + constexpr size_t sizeof_out_iq = sizeof(complexfix_wide); + if ((fft_out_end - fft_out) != (ssize_t)(mySpacing * sizeof_out_iq)) { fprintf(stderr, "FFT_OUT: %p %p %zu %zu\n", - fft_out, fft_out_end, (fft_out_end - fft_out), mySpacing * sizeof(complexfix)); + fft_out, fft_out_end, (fft_out_end - fft_out), + mySpacing * sizeof_out_iq); throw std::runtime_error("OfdmGenerator::process fft_out length invalid!"); } - memcpy(out, fft_out, mySpacing * sizeof(complexfix)); + memcpy(out, fft_out, mySpacing * sizeof_out_iq); in += myNbCarriers; out += mySpacing; -- cgit v1.2.3 From 15833dc0f2e599dc7d28457ea5c740c51b8562b0 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Wed, 30 Oct 2024 00:02:16 +0100 Subject: Remove IIO refill message --- src/OfdmGenerator.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/OfdmGenerator.cpp') diff --git a/src/OfdmGenerator.cpp b/src/OfdmGenerator.cpp index 11f5bf1..cfdf31d 100644 --- a/src/OfdmGenerator.cpp +++ b/src/OfdmGenerator.cpp @@ -747,8 +747,6 @@ int OfdmGeneratorDEXTER::process(Buffer* const dataIn, Buffer* dataOut) throw std::runtime_error("OfdmGenerator::process error refilling IIO buffer!"); } - fprintf(stderr, "IIO refill %zd\n", nbytes_rx); - ptrdiff_t p_inc = iio_buffer_step(m_buf_out); if (p_inc != 1) { throw std::runtime_error("OfdmGenerator::process Wrong p_inc"); -- cgit v1.2.3 From 7452ff0fd85da423a5be1fdc3873106160c6ccae Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Tue, 5 Nov 2024 16:45:35 +0100 Subject: Debug DEXTER FFT Acccelerator --- src/DabModulator.cpp | 4 + src/DifferentialModulator.cpp | 2 +- src/Flowgraph.cpp | 10 +- src/FrequencyInterleaver.cpp | 8 +- src/GuardIntervalInserter.cpp | 210 ++++++++++++++++++++++-------------------- src/OfdmGenerator.cpp | 16 ++-- src/PhaseReference.cpp | 4 +- src/SignalMultiplexer.cpp | 8 +- 8 files changed, 132 insertions(+), 130 deletions(-) (limited to 'src/OfdmGenerator.cpp') diff --git a/src/DabModulator.cpp b/src/DabModulator.cpp index 4cbd0f5..39c1d4a 100644 --- a/src/DabModulator.cpp +++ b/src/DabModulator.cpp @@ -220,6 +220,7 @@ int DabModulator::process(Buffer* dataOut) m_settings.cfrErrorClip); break; case FFTEngine::DEXTER: +#if defined(HAVE_DEXTER) cifOfdm = make_shared( (1 + m_nbSymbols), m_nbCarriers, @@ -227,6 +228,9 @@ int DabModulator::process(Buffer* dataOut) m_settings.enableCfr, m_settings.cfrClip, m_settings.cfrErrorClip); +#else + throw std::runtime_error("Cannot use DEXTER fft engine without --enable-dexter"); +#endif break; } diff --git a/src/DifferentialModulator.cpp b/src/DifferentialModulator.cpp index cfebf65..21b4c3e 100644 --- a/src/DifferentialModulator.cpp +++ b/src/DifferentialModulator.cpp @@ -43,7 +43,7 @@ DifferentialModulator::~DifferentialModulator() template -void do_process(size_t carriers, std::vector dataIn, Buffer* dataOut) +void do_process(size_t carriers, const std::vector& dataIn, Buffer* dataOut) { size_t phaseSize = dataIn[0]->getLength() / sizeof(T); size_t dataSize = dataIn[1]->getLength() / sizeof(T); diff --git a/src/Flowgraph.cpp b/src/Flowgraph.cpp index 3d4cdcc..339e326 100644 --- a/src/Flowgraph.cpp +++ b/src/Flowgraph.cpp @@ -27,12 +27,10 @@ #include "Flowgraph.h" #include "PcDebug.h" #include "Log.h" -#include #include #include #include #include -#include #include #include @@ -254,15 +252,15 @@ Flowgraph::~Flowgraph() char node_time_sz[1024] = {}; for (const auto &node : nodes) { - snprintf(node_time_sz, 1023, " %30s: %10lu us (%2.2f %%)\n", + snprintf(node_time_sz, 1023, " %30s: %10lld us (%2.2f %%)\n", node->plugin()->name(), - node->processTime(), + (long long)node->processTime(), node->processTime() * 100.0 / myProcessTime); ss << node_time_sz; } - snprintf(node_time_sz, 1023, " %30s: %10lu us (100.00 %%)\n", "total", - myProcessTime); + snprintf(node_time_sz, 1023, " %30s: %10lld us (100.00 %%)\n", "total", + (long long)myProcessTime); ss << node_time_sz; etiLog.level(debug) << ss.str(); diff --git a/src/FrequencyInterleaver.cpp b/src/FrequencyInterleaver.cpp index 856e8d0..6f36dcb 100644 --- a/src/FrequencyInterleaver.cpp +++ b/src/FrequencyInterleaver.cpp @@ -61,7 +61,7 @@ FrequencyInterleaver::FrequencyInterleaver(size_t mode, bool fixedPoint) : beta = 255; break; default: - PDEBUG("Carriers: %zu\n", (d_carriers >> 1) << 1); + PDEBUG("Carriers: %zu\n", (m_carriers >> 1) << 1); throw std::runtime_error("FrequencyInterleaver: invalid dab mode"); } @@ -79,10 +79,10 @@ FrequencyInterleaver::FrequencyInterleaver(size_t mode, bool fixedPoint) : && perm <= (num - (num - m_carriers) / 2) && perm != (num / 2)) { PDEBUG("i: %4zu, R: %4zu, d: %4zu, n: %4zu, k: %5zi, index: %zu\n", - j, perm, perm, index - d_indexes, perm - num / 2, + j, perm, perm, index - m_indices, perm - num / 2, perm > num / 2 ? perm - (1 + (num / 2)) - : perm + (d_carriers - (num / 2))); + : perm + (m_carriers - (num / 2))); *(index++) = perm > num / 2 ? perm - (1 + (num / 2)) : perm + (m_carriers - (num / 2)); } @@ -114,7 +114,7 @@ void do_process(Buffer* const dataIn, Buffer* dataOut, } for (size_t i = 0; i < sizeIn;) { -// memset(out, 0, d_carriers * sizeof(T)); +// memset(out, 0, m_carriers * sizeof(T)); for (size_t j = 0; j < carriers; i += 4, j += 4) { out[indices[j]] = in[i]; out[indices[j + 1]] = in[i + 1]; diff --git a/src/GuardIntervalInserter.cpp b/src/GuardIntervalInserter.cpp index 7061e47..26d4fd1 100644 --- a/src/GuardIntervalInserter.cpp +++ b/src/GuardIntervalInserter.cpp @@ -140,118 +140,123 @@ int do_process(const GuardIntervalInserter::Params& p, Buffer* const dataIn, Buf // windowing too. std::lock_guard lock(p.windowMutex); - if (p.windowOverlap) { if constexpr (std::is_same_v) { - { - // Handle Null symbol separately because it is longer - const size_t prefixlength = p.nullSize - p.spacing; - - // end = spacing - memcpy(out, &in[p.spacing - prefixlength], - prefixlength * sizeof(T)); - - memcpy(&out[prefixlength], in, (p.spacing - p.windowOverlap) * sizeof(T)); - - // The remaining part of the symbol must have half of the window applied, - // sloping down from 1 to 0.5 - for (size_t i = 0; i < p.windowOverlap; i++) { - const size_t out_ix = prefixlength + p.spacing - p.windowOverlap + i; - const size_t in_ix = p.spacing - p.windowOverlap + i; - out[out_ix] = in[in_ix] * p.window[2*p.windowOverlap - (i+1)]; - } - - // Suffix is taken from the beginning of the symbol, and sees the other - // half of the window applied. - for (size_t i = 0; i < p.windowOverlap; i++) { - const size_t out_ix = prefixlength + p.spacing + i; - out[out_ix] = in[i] * p.window[p.windowOverlap - (i+1)]; - } + if (p.windowOverlap) { + if constexpr (std::is_same_v) { + { + // Handle Null symbol separately because it is longer + const size_t prefixlength = p.nullSize - p.spacing; + + // end = spacing + memcpy(out, &in[p.spacing - prefixlength], + prefixlength * sizeof(T)); + + memcpy(&out[prefixlength], in, (p.spacing - p.windowOverlap) * sizeof(T)); + + // The remaining part of the symbol must have half of the window applied, + // sloping down from 1 to 0.5 + for (size_t i = 0; i < p.windowOverlap; i++) { + const size_t out_ix = prefixlength + p.spacing - p.windowOverlap + i; + const size_t in_ix = p.spacing - p.windowOverlap + i; + out[out_ix] = in[in_ix] * p.window[2*p.windowOverlap - (i+1)]; + } - in += p.spacing; - out += p.nullSize; - // out is now pointing to the proper end of symbol. There are - // windowOverlap samples ahead that were already written. - } + // Suffix is taken from the beginning of the symbol, and sees the other + // half of the window applied. + for (size_t i = 0; i < p.windowOverlap; i++) { + const size_t out_ix = prefixlength + p.spacing + i; + out[out_ix] = in[i] * p.window[p.windowOverlap - (i+1)]; + } - // Data symbols - for (size_t sym_ix = 0; sym_ix < p.nbSymbols; sym_ix++) { - /* _ix variables are indices into in[], _ox variables are - * indices for out[] */ - const ssize_t start_rise_ox = -p.windowOverlap; - const size_t start_rise_ix = 2 * p.spacing - p.symSize - p.windowOverlap; - /* - const size_t start_real_symbol_ox = 0; - const size_t start_real_symbol_ix = 2 * p.spacing - p.symSize; - */ - const ssize_t end_rise_ox = p.windowOverlap; - const size_t end_rise_ix = 2 * p.spacing - p.symSize + p.windowOverlap; - const ssize_t end_cyclic_prefix_ox = p.symSize - p.spacing; - /* end_cyclic_prefix_ix = end of symbol - const size_t begin_fall_ox = p.symSize - p.windowOverlap; - const size_t begin_fall_ix = p.spacing - p.windowOverlap; - const size_t end_real_symbol_ox = p.symSize; - end_real_symbol_ix = end of symbol - const size_t end_fall_ox = p.symSize + p.windowOverlap; - const size_t end_fall_ix = p.spacing + p.windowOverlap; - */ - - ssize_t ox = start_rise_ox; - size_t ix = start_rise_ix; - - for (size_t i = 0; ix < end_rise_ix; i++) { - out[ox] += in[ix] * p.window.at(i); - ix++; - ox++; - } - assert(ox == end_rise_ox); - - const size_t remaining_prefix_length = end_cyclic_prefix_ox - end_rise_ox; - memcpy( &out[ox], &in[ix], - remaining_prefix_length * sizeof(T)); - ox += remaining_prefix_length; - assert(ox == end_cyclic_prefix_ox); - ix = 0; - - const bool last_symbol = (sym_ix + 1 >= p.nbSymbols); - if (last_symbol) { - // No windowing at all at end - memcpy(&out[ox], &in[ix], p.spacing * sizeof(T)); - ox += p.spacing; + in += p.spacing; + out += p.nullSize; + // out is now pointing to the proper end of symbol. There are + // windowOverlap samples ahead that were already written. } - else { - // Copy the middle part of the symbol, p.windowOverlap samples - // short of the end. - memcpy( &out[ox], - &in[ix], - (p.spacing - p.windowOverlap) * sizeof(T)); - ox += p.spacing - p.windowOverlap; - ix += p.spacing - p.windowOverlap; - assert(ox == (ssize_t)(p.symSize - p.windowOverlap)); - - // Apply window from 1 to 0.5 for the end of the symbol - for (size_t i = 0; ox < (ssize_t)p.symSize; i++) { - out[ox] = in[ix] * p.window[2*p.windowOverlap - (i+1)]; - ox++; + + // Data symbols + for (size_t sym_ix = 0; sym_ix < p.nbSymbols; sym_ix++) { + /* _ix variables are indices into in[], _ox variables are + * indices for out[] */ + const ssize_t start_rise_ox = -p.windowOverlap; + const size_t start_rise_ix = 2 * p.spacing - p.symSize - p.windowOverlap; + /* + const size_t start_real_symbol_ox = 0; + const size_t start_real_symbol_ix = 2 * p.spacing - p.symSize; + */ + const ssize_t end_rise_ox = p.windowOverlap; + const size_t end_rise_ix = 2 * p.spacing - p.symSize + p.windowOverlap; + const ssize_t end_cyclic_prefix_ox = p.symSize - p.spacing; + /* end_cyclic_prefix_ix = end of symbol + const size_t begin_fall_ox = p.symSize - p.windowOverlap; + const size_t begin_fall_ix = p.spacing - p.windowOverlap; + const size_t end_real_symbol_ox = p.symSize; + end_real_symbol_ix = end of symbol + const size_t end_fall_ox = p.symSize + p.windowOverlap; + const size_t end_fall_ix = p.spacing + p.windowOverlap; + */ + + ssize_t ox = start_rise_ox; + size_t ix = start_rise_ix; + + for (size_t i = 0; ix < end_rise_ix; i++) { + out[ox] += in[ix] * p.window.at(i); ix++; + ox++; } - assert(ix == p.spacing); + assert(ox == end_rise_ox); + const size_t remaining_prefix_length = end_cyclic_prefix_ox - end_rise_ox; + memcpy( &out[ox], &in[ix], + remaining_prefix_length * sizeof(T)); + ox += remaining_prefix_length; + assert(ox == end_cyclic_prefix_ox); ix = 0; - // Cyclic suffix, with window from 0.5 to 0 - for (size_t i = 0; ox < (ssize_t)(p.symSize + p.windowOverlap); i++) { - out[ox] = in[ix] * p.window[p.windowOverlap - (i+1)]; - ox++; - ix++; + + const bool last_symbol = (sym_ix + 1 >= p.nbSymbols); + if (last_symbol) { + // No windowing at all at end + memcpy(&out[ox], &in[ix], p.spacing * sizeof(T)); + ox += p.spacing; + } + else { + // Copy the middle part of the symbol, p.windowOverlap samples + // short of the end. + memcpy( &out[ox], + &in[ix], + (p.spacing - p.windowOverlap) * sizeof(T)); + ox += p.spacing - p.windowOverlap; + ix += p.spacing - p.windowOverlap; + assert(ox == (ssize_t)(p.symSize - p.windowOverlap)); + + // Apply window from 1 to 0.5 for the end of the symbol + for (size_t i = 0; ox < (ssize_t)p.symSize; i++) { + out[ox] = in[ix] * p.window[2*p.windowOverlap - (i+1)]; + ox++; + ix++; + } + assert(ix == p.spacing); + + ix = 0; + // Cyclic suffix, with window from 0.5 to 0 + for (size_t i = 0; ox < (ssize_t)(p.symSize + p.windowOverlap); i++) { + out[ox] = in[ix] * p.window[p.windowOverlap - (i+1)]; + ox++; + ix++; + } + + assert(ix == p.windowOverlap); } - assert(ix == p.windowOverlap); + out += p.symSize; + in += p.spacing; + // out is now pointing to the proper end of symbol. There are + // windowOverlap samples ahead that were already written. } - - out += p.symSize; - in += p.spacing; - // out is now pointing to the proper end of symbol. There are - // windowOverlap samples ahead that were already written. } - } } + else { + throw std::runtime_error("fixed-point doesn't support window overlap"); + } + } else { // Handle Null symbol separately because it is longer // end - (nullSize - spacing) = 2 * spacing - nullSize @@ -272,7 +277,8 @@ int do_process(const GuardIntervalInserter::Params& p, Buffer* const dataIn, Buf } } - return sizeIn; + const auto sizeOut = dataOut->getLength(); + return sizeOut; } int GuardIntervalInserter::process(Buffer* const dataIn, Buffer* dataOut) diff --git a/src/OfdmGenerator.cpp b/src/OfdmGenerator.cpp index cfdf31d..32b5f76 100644 --- a/src/OfdmGenerator.cpp +++ b/src/OfdmGenerator.cpp @@ -219,7 +219,6 @@ int OfdmGeneratorCF32::process(Buffer* const dataIn, Buffer* dataOut) memcpy(&myFftIn[myNegDst], &in[myNegSrc], myNegSize * sizeof(FFTW_TYPE)); - if (myCfr) { reference.resize(mySpacing); memcpy(reinterpret_cast(reference.data()), @@ -228,7 +227,6 @@ int OfdmGeneratorCF32::process(Buffer* const dataIn, Buffer* dataOut) fftwf_execute(myFftPlan); // IFFT from myFftIn to myFftOut - if (myCfr) { complexf *symbol = reinterpret_cast(myFftOut); myPaprBeforeCFR.process_block(symbol, mySpacing); @@ -634,7 +632,7 @@ OfdmGeneratorDEXTER::OfdmGeneratorDEXTER(size_t nbSymbols, PDEBUG(" myZeroSize: %u\n", myZeroSize); const size_t nbytes_in = mySpacing * sizeof(complexfix); - const size_t nbytes_out = mySpacing * 2 * sizeof(int32_t); + const size_t nbytes_out = mySpacing * sizeof(complexfix_wide); #define IIO_ENSURE(expr, err) { \ if (!(expr)) { \ @@ -721,14 +719,14 @@ int OfdmGeneratorDEXTER::process(Buffer* const dataIn, Buffer* dataOut) throw std::runtime_error("OfdmGenerator::process incorrect iio buffer size!"); } - complexfix *fft_in = reinterpret_cast(iio_buffer_start(m_buf_in)); + for (size_t i = 0; i < myNbSymbols; i++) { + complexfix *fft_in = reinterpret_cast(iio_buffer_start(m_buf_in)); - fft_in[0] = static_cast(0); - for (size_t i = 0; i < myZeroSize; i++) { - fft_in[myZeroDst + i] = static_cast(0); - } + fft_in[0] = static_cast(0); + for (size_t i = 0; i < myZeroSize; i++) { + fft_in[myZeroDst + i] = static_cast(0); + } - for (size_t i = 0; i < myNbSymbols; i++) { /* For TM I this is: * ZeroDst=769 ZeroSize=511 * PosSrc=0 PosDst=1 PosSize=768 diff --git a/src/PhaseReference.cpp b/src/PhaseReference.cpp index e2fb9a9..71dec87 100644 --- a/src/PhaseReference.cpp +++ b/src/PhaseReference.cpp @@ -176,10 +176,10 @@ int PhaseReference::process(Buffer* dataOut) PDEBUG("PhaseReference::process(dataOut: %p)\n", dataOut); if (d_fixedPoint) { - dataOut->setData(&d_phaseRefFixed.dataIn[0], d_carriers * sizeof(complexfix)); + dataOut->setData(d_phaseRefFixed.dataIn.data(), d_carriers * sizeof(complexfix)); } else { - dataOut->setData(&d_phaseRefCF32.dataIn[0], d_carriers * sizeof(complexf)); + dataOut->setData(d_phaseRefCF32.dataIn.data(), d_carriers * sizeof(complexf)); } return 1; diff --git a/src/SignalMultiplexer.cpp b/src/SignalMultiplexer.cpp index 8ecbe78..d4955d0 100644 --- a/src/SignalMultiplexer.cpp +++ b/src/SignalMultiplexer.cpp @@ -22,24 +22,20 @@ #include "SignalMultiplexer.h" #include "PcDebug.h" -#include -#include +#include #include -#include SignalMultiplexer::SignalMultiplexer() : ModMux() { - PDEBUG("SignalMultiplexer::SignalMultiplexer(%zu) @ %p\n", framesize, this); - + PDEBUG("SignalMultiplexer::SignalMultiplexer() @ %p\n", this); } SignalMultiplexer::~SignalMultiplexer() { PDEBUG("SignalMultiplexer::~SignalMultiplexer() @ %p\n", this); - } -- cgit v1.2.3 From 51199fd8605368d62edc464e984f847ad5281aab Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 8 Nov 2024 16:56:22 +0100 Subject: OFDM: improve performance of DEXTER FFT --- src/OfdmGenerator.cpp | 83 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 26 deletions(-) (limited to 'src/OfdmGenerator.cpp') diff --git a/src/OfdmGenerator.cpp b/src/OfdmGenerator.cpp index 32b5f76..23cece0 100644 --- a/src/OfdmGenerator.cpp +++ b/src/OfdmGenerator.cpp @@ -722,16 +722,17 @@ int OfdmGeneratorDEXTER::process(Buffer* const dataIn, Buffer* dataOut) for (size_t i = 0; i < myNbSymbols; i++) { complexfix *fft_in = reinterpret_cast(iio_buffer_start(m_buf_in)); - fft_in[0] = static_cast(0); - for (size_t i = 0; i < myZeroSize; i++) { - fft_in[myZeroDst + i] = static_cast(0); - } - /* For TM I this is: * ZeroDst=769 ZeroSize=511 * PosSrc=0 PosDst=1 PosSize=768 * NegSrc=768 NegDst=1280 NegSize=768 */ + + fft_in[0] = static_cast(0); + for (size_t i = 0; i < myZeroSize; i++) { + fft_in[myZeroDst + i] = static_cast(0); + } + memcpy(&fft_in[myPosDst], &in[myPosSrc], myPosSize * sizeof(complexfix)); memcpy(&fft_in[myNegDst], &in[myNegSrc], myNegSize * sizeof(complexfix)); @@ -740,34 +741,64 @@ int OfdmGeneratorDEXTER::process(Buffer* const dataIn, Buffer* dataOut) throw std::runtime_error("OfdmGenerator::process error pushing IIO buffer!"); } - ssize_t nbytes_rx = iio_buffer_refill(m_buf_out); - if (nbytes_rx < 0) { - throw std::runtime_error("OfdmGenerator::process error refilling IIO buffer!"); - } + in += myNbCarriers; - ptrdiff_t p_inc = iio_buffer_step(m_buf_out); - if (p_inc != 1) { - throw std::runtime_error("OfdmGenerator::process Wrong p_inc"); - } + // Keep one buffer in flight while we're doing shuffling data around here, + // this improves performance. + // I believe that, by default, IIO allocates four buffers in total. + if (i > 0) { + ssize_t nbytes_rx = iio_buffer_refill(m_buf_out); + if (nbytes_rx < 0) { + throw std::runtime_error("OfdmGenerator::process error refilling IIO buffer!"); + } + + ptrdiff_t p_inc = iio_buffer_step(m_buf_out); + if (p_inc != 1) { + throw std::runtime_error("OfdmGenerator::process Wrong p_inc"); + } - // The FFT Accelerator takes 16-bit I + 16-bit Q, and outputs 32-bit I and 32-bit Q. - // The formatconvert will take care of this - const uint8_t *fft_out = (const uint8_t*)iio_buffer_first(m_buf_out, m_channel_out); - const uint8_t *fft_out_end = (const uint8_t*)iio_buffer_end(m_buf_out); - constexpr size_t sizeof_out_iq = sizeof(complexfix_wide); - if ((fft_out_end - fft_out) != (ssize_t)(mySpacing * sizeof_out_iq)) { - fprintf(stderr, "FFT_OUT: %p %p %zu %zu\n", - fft_out, fft_out_end, (fft_out_end - fft_out), - mySpacing * sizeof_out_iq); - throw std::runtime_error("OfdmGenerator::process fft_out length invalid!"); + // The FFT Accelerator takes 16-bit I + 16-bit Q, and outputs 32-bit I and 32-bit Q. + // The formatconvert will take care of this + const uint8_t *fft_out = (const uint8_t*)iio_buffer_first(m_buf_out, m_channel_out); + const uint8_t *fft_out_end = (const uint8_t*)iio_buffer_end(m_buf_out); + constexpr size_t sizeof_out_iq = sizeof(complexfix_wide); + if ((fft_out_end - fft_out) != (ssize_t)(mySpacing * sizeof_out_iq)) { + fprintf(stderr, "FFT_OUT: %p %p %zu %zu\n", + fft_out, fft_out_end, (fft_out_end - fft_out), + mySpacing * sizeof_out_iq); + throw std::runtime_error("OfdmGenerator::process fft_out length invalid!"); + } + + memcpy(out, fft_out, mySpacing * sizeof_out_iq); + + out += mySpacing; } + } - memcpy(out, fft_out, mySpacing * sizeof_out_iq); + ssize_t nbytes_rx = iio_buffer_refill(m_buf_out); + if (nbytes_rx < 0) { + throw std::runtime_error("OfdmGenerator::process error refilling IIO buffer!"); + } - in += myNbCarriers; - out += mySpacing; + ptrdiff_t p_inc = iio_buffer_step(m_buf_out); + if (p_inc != 1) { + throw std::runtime_error("OfdmGenerator::process Wrong p_inc"); } + // The FFT Accelerator takes 16-bit I + 16-bit Q, and outputs 32-bit I and 32-bit Q. + // The formatconvert will take care of this + const uint8_t *fft_out = (const uint8_t*)iio_buffer_first(m_buf_out, m_channel_out); + const uint8_t *fft_out_end = (const uint8_t*)iio_buffer_end(m_buf_out); + constexpr size_t sizeof_out_iq = sizeof(complexfix_wide); + if ((fft_out_end - fft_out) != (ssize_t)(mySpacing * sizeof_out_iq)) { + fprintf(stderr, "FFT_OUT: %p %p %zu %zu\n", + fft_out, fft_out_end, (fft_out_end - fft_out), + mySpacing * sizeof_out_iq); + throw std::runtime_error("OfdmGenerator::process fft_out length invalid!"); + } + + memcpy(out, fft_out, mySpacing * sizeof_out_iq); + return sizeOut; } -- cgit v1.2.3 From b6a6cfc5636713e3f7b79d3ac12c9a2b31661549 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Mon, 11 Nov 2024 10:51:29 +0100 Subject: Initialise FFT Accelerator GPIO config --- src/OfdmGenerator.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'src/OfdmGenerator.cpp') diff --git a/src/OfdmGenerator.cpp b/src/OfdmGenerator.cpp index 23cece0..d6698c2 100644 --- a/src/OfdmGenerator.cpp +++ b/src/OfdmGenerator.cpp @@ -599,6 +599,27 @@ OfdmGeneratorDEXTER::OfdmGeneratorDEXTER(size_t nbSymbols, etiLog.level(info) << "Using DEXTER FFT Accelerator for fixed-point transform"; + // I tried to get this to work in code using libgpiod, but life is too short to waste time on that. + // It works through gpioset, so we just call it + // The GPIO is connected to the config AXI bus of the xfft block. + // 15..0 is the config data; 31 is tvalid + if (inverse) { + if (system("gpioset gpiochip0 0=0 31=0") != 0) + throw std::runtime_error("Failed to call gpioset #1"); + if (system("gpioset gpiochip0 0=0 31=1") != 0) + throw std::runtime_error("Failed to call gpioset #2"); + if (system("gpioset gpiochip0 0=0 31=0") != 0) + throw std::runtime_error("Failed to call gpioset #3"); + } + else { + if (system("gpioset gpiochip0 0=1 31=0") != 0) + throw std::runtime_error("Failed to call gpioset #1"); + if (system("gpioset gpiochip0 0=1 31=1") != 0) + throw std::runtime_error("Failed to call gpioset #2"); + if (system("gpioset gpiochip0 0=1 31=0") != 0) + throw std::runtime_error("Failed to call gpioset #3"); + } + if (nbCarriers > spacing) { throw std::runtime_error("OfdmGenerator nbCarriers > spacing!"); } -- cgit v1.2.3 From 9c4683b718fb785ebf7fa5e3cca21af6bd9dc4bb Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Tue, 12 Nov 2024 16:20:54 +0100 Subject: Replace gpioset calls by C code --- configure.ac | 6 ++- src/OfdmGenerator.cpp | 129 ++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 114 insertions(+), 21 deletions(-) (limited to 'src/OfdmGenerator.cpp') diff --git a/configure.ac b/configure.ac index 5c3de90..d0258bc 100644 --- a/configure.ac +++ b/configure.ac @@ -113,13 +113,17 @@ AS_IF([test "x$enable_dexter" = "xyes"], [AC_CHECK_LIB([iio], [iio_create_scan_context], [IIO_LIBS="-liio"], [AC_MSG_ERROR([libiio is required])])]) +AS_IF([test "x$enable_dexter" = "xyes"], + [AC_CHECK_LIB([gpiod], [gpiod_chip_open], [GPIOD_LIBS="-lgpiod"], + [AC_MSG_ERROR([libgpiod is required])])]) + AS_IF([test "x$enable_bladerf" = "xyes"], [AC_CHECK_LIB([bladeRF], [bladerf_open], [BLADERF_LIBS="-lbladeRF"], [AC_MSG_ERROR([BladeRF library is required])])]) AC_SUBST([CFLAGS], ["$CFLAGS $EXTRA $FFTW_CFLAGS $SOAPYSDR_CFLAGS $PTHREAD_CFLAGS"]) AC_SUBST([CXXFLAGS], ["$CXXFLAGS $EXTRA $FFTW_CFLAGS $SOAPYSDR_CFLAGS $PTHREAD_CFLAGS"]) -AC_SUBST([LIBS], ["$FFTW_LIBS $SOAPYSDR_LIBS $PTHREAD_LIBS $ZMQ_LIBS $LIMESDR_LIBS $IIO_LIBS $BLADERF_LIBS"]) +AC_SUBST([LIBS], ["$FFTW_LIBS $SOAPYSDR_LIBS $PTHREAD_LIBS $ZMQ_LIBS $LIMESDR_LIBS $GPIOD_LIBS $IIO_LIBS $BLADERF_LIBS"]) # Checks for UHD. AS_IF([test "x$enable_output_uhd" = "xyes"], diff --git a/src/OfdmGenerator.cpp b/src/OfdmGenerator.cpp index d6698c2..16eb8d9 100644 --- a/src/OfdmGenerator.cpp +++ b/src/OfdmGenerator.cpp @@ -582,6 +582,114 @@ int OfdmGeneratorFixed::process(Buffer* const dataIn, Buffer* dataOut) } #ifdef HAVE_DEXTER +#include + +#define CHIP_PATH "/dev/gpiochip0" +#define LINE_FWD_INV 0 +#define LINE_CONFIG_TDATA_VALID 31 + +// GPIO mapping on write: +// bit 31 config_tdata_tvalid +// bit 30 resets the latches in the xfft_wrapper +// bits 15..0 are 1:1 xfft `config_tdata` +// `GPIO[0] = FWD_INV` according to Vivado + +static struct gpiod_line_request * +request_output_lines(const char *chip_path, const unsigned int *offsets, + enum gpiod_line_value *values, unsigned int num_lines, + const char *consumer) +{ + struct gpiod_request_config *rconfig = NULL; + struct gpiod_line_request *request = NULL; + struct gpiod_line_settings *settings; + struct gpiod_line_config *lconfig; + struct gpiod_chip *chip; + unsigned int i; + int ret; + + chip = gpiod_chip_open(chip_path); + if (!chip) + return NULL; + + settings = gpiod_line_settings_new(); + if (!settings) + goto close_chip; + + gpiod_line_settings_set_direction(settings, + GPIOD_LINE_DIRECTION_OUTPUT); + + lconfig = gpiod_line_config_new(); + if (!lconfig) + goto free_settings; + + for (i = 0; i < num_lines; i++) { + ret = gpiod_line_config_add_line_settings(lconfig, &offsets[i], + 1, settings); + if (ret) + goto free_line_config; + } + gpiod_line_config_set_output_values(lconfig, values, num_lines); + + if (consumer) { + rconfig = gpiod_request_config_new(); + if (!rconfig) + goto free_line_config; + + gpiod_request_config_set_consumer(rconfig, consumer); + } + + request = gpiod_chip_request_lines(chip, rconfig, lconfig); + + gpiod_request_config_free(rconfig); + +free_line_config: + gpiod_line_config_free(lconfig); + +free_settings: + gpiod_line_settings_free(settings); + +close_chip: + gpiod_chip_close(chip); + + return request; +} + +// The GPIO is connected to the config AXI bus of the xfft block. +// 15..0 is the config data; 31 is tvalid +void set_fft_accelerator_config(bool inverse) +{ + constexpr size_t NUM_LINES = 2; + unsigned int line_offsets[NUM_LINES]; + enum gpiod_line_value values[NUM_LINES]; + + line_offsets[0] = LINE_CONFIG_TDATA_VALID; + values[0] = GPIOD_LINE_VALUE_INACTIVE; + + line_offsets[1] = LINE_FWD_INV; + values[1] = inverse ? GPIOD_LINE_VALUE_INACTIVE : GPIOD_LINE_VALUE_ACTIVE; + + struct gpiod_line_request *request; + + request = request_output_lines(CHIP_PATH, line_offsets, values, NUM_LINES, "fft-config"); + if (!request) { + fprintf(stderr, "failed to request line: %s\n", strerror(errno)); + throw std::runtime_error("Request GPIO lines error"); + } + + usleep(100000); + + values[0] = GPIOD_LINE_VALUE_ACTIVE; + gpiod_line_request_set_values(request, values); + + usleep(100000); + + values[0] = GPIOD_LINE_VALUE_INACTIVE; + gpiod_line_request_set_values(request, values); + + gpiod_line_request_release(request); +} + + OfdmGeneratorDEXTER::OfdmGeneratorDEXTER(size_t nbSymbols, size_t nbCarriers, size_t spacing, @@ -599,26 +707,7 @@ OfdmGeneratorDEXTER::OfdmGeneratorDEXTER(size_t nbSymbols, etiLog.level(info) << "Using DEXTER FFT Accelerator for fixed-point transform"; - // I tried to get this to work in code using libgpiod, but life is too short to waste time on that. - // It works through gpioset, so we just call it - // The GPIO is connected to the config AXI bus of the xfft block. - // 15..0 is the config data; 31 is tvalid - if (inverse) { - if (system("gpioset gpiochip0 0=0 31=0") != 0) - throw std::runtime_error("Failed to call gpioset #1"); - if (system("gpioset gpiochip0 0=0 31=1") != 0) - throw std::runtime_error("Failed to call gpioset #2"); - if (system("gpioset gpiochip0 0=0 31=0") != 0) - throw std::runtime_error("Failed to call gpioset #3"); - } - else { - if (system("gpioset gpiochip0 0=1 31=0") != 0) - throw std::runtime_error("Failed to call gpioset #1"); - if (system("gpioset gpiochip0 0=1 31=1") != 0) - throw std::runtime_error("Failed to call gpioset #2"); - if (system("gpioset gpiochip0 0=1 31=0") != 0) - throw std::runtime_error("Failed to call gpioset #3"); - } + set_fft_accelerator_config(inverse); if (nbCarriers > spacing) { throw std::runtime_error("OfdmGenerator nbCarriers > spacing!"); -- cgit v1.2.3 From 46471d4ada2e7f0c12a74f829c8e12bb5c29cf89 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Tue, 19 Nov 2024 14:47:09 +0100 Subject: Simplify OfdmGenerator constructors --- src/DabModulator.cpp | 10 ++-------- src/OfdmGenerator.cpp | 6 ------ src/OfdmGenerator.h | 6 ------ 3 files changed, 2 insertions(+), 20 deletions(-) (limited to 'src/OfdmGenerator.cpp') diff --git a/src/DabModulator.cpp b/src/DabModulator.cpp index e0a613f..5f7aaf6 100644 --- a/src/DabModulator.cpp +++ b/src/DabModulator.cpp @@ -209,20 +209,14 @@ int DabModulator::process(Buffer* dataOut) cifOfdm = make_shared( (1 + m_nbSymbols), m_nbCarriers, - m_spacing, - m_settings.enableCfr, - m_settings.cfrClip, - m_settings.cfrErrorClip); + m_spacing); break; case FFTEngine::DEXTER: #if defined(HAVE_DEXTER) cifOfdm = make_shared( (1 + m_nbSymbols), m_nbCarriers, - m_spacing, - m_settings.enableCfr, - m_settings.cfrClip, - m_settings.cfrErrorClip); + m_spacing); #else throw std::runtime_error("Cannot use DEXTER fft engine without --enable-dexter"); #endif diff --git a/src/OfdmGenerator.cpp b/src/OfdmGenerator.cpp index 16eb8d9..558ec1b 100644 --- a/src/OfdmGenerator.cpp +++ b/src/OfdmGenerator.cpp @@ -467,9 +467,6 @@ const json::map_t OfdmGeneratorCF32::get_all_values() const OfdmGeneratorFixed::OfdmGeneratorFixed(size_t nbSymbols, size_t nbCarriers, size_t spacing, - bool& enableCfr, - float& cfrClip, - float& cfrErrorClip, bool inverse) : ModCodec(), myNbSymbols(nbSymbols), @@ -693,9 +690,6 @@ void set_fft_accelerator_config(bool inverse) OfdmGeneratorDEXTER::OfdmGeneratorDEXTER(size_t nbSymbols, size_t nbCarriers, size_t spacing, - bool& enableCfr, - float& cfrClip, - float& cfrErrorClip, bool inverse) : ModCodec(), myNbSymbols(nbSymbols), diff --git a/src/OfdmGenerator.h b/src/OfdmGenerator.h index b8ec702..56e3cb2 100644 --- a/src/OfdmGenerator.h +++ b/src/OfdmGenerator.h @@ -113,9 +113,6 @@ class OfdmGeneratorFixed : public ModCodec OfdmGeneratorFixed(size_t nbSymbols, size_t nbCarriers, size_t spacing, - bool& enableCfr, - float& cfrClip, - float& cfrErrorClip, bool inverse = true); virtual ~OfdmGeneratorFixed(); OfdmGeneratorFixed(const OfdmGeneratorFixed&) = delete; @@ -150,9 +147,6 @@ class OfdmGeneratorDEXTER : public ModCodec OfdmGeneratorDEXTER(size_t nbSymbols, size_t nbCarriers, size_t spacing, - bool& enableCfr, - float& cfrClip, - float& cfrErrorClip, bool inverse = true); virtual ~OfdmGeneratorDEXTER(); OfdmGeneratorDEXTER(const OfdmGeneratorDEXTER&) = delete; -- cgit v1.2.3 From d45cca6f447c9a72bc9eaeb9d861fa6fcff9e597 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 22 Nov 2024 15:46:27 +0100 Subject: Remove FFT accelerator config --- configure.ac | 6 +-- src/OfdmGenerator.cpp | 140 ++++---------------------------------------------- src/OfdmGenerator.h | 11 ++-- 3 files changed, 18 insertions(+), 139 deletions(-) (limited to 'src/OfdmGenerator.cpp') diff --git a/configure.ac b/configure.ac index d0258bc..5c3de90 100644 --- a/configure.ac +++ b/configure.ac @@ -113,17 +113,13 @@ AS_IF([test "x$enable_dexter" = "xyes"], [AC_CHECK_LIB([iio], [iio_create_scan_context], [IIO_LIBS="-liio"], [AC_MSG_ERROR([libiio is required])])]) -AS_IF([test "x$enable_dexter" = "xyes"], - [AC_CHECK_LIB([gpiod], [gpiod_chip_open], [GPIOD_LIBS="-lgpiod"], - [AC_MSG_ERROR([libgpiod is required])])]) - AS_IF([test "x$enable_bladerf" = "xyes"], [AC_CHECK_LIB([bladeRF], [bladerf_open], [BLADERF_LIBS="-lbladeRF"], [AC_MSG_ERROR([BladeRF library is required])])]) AC_SUBST([CFLAGS], ["$CFLAGS $EXTRA $FFTW_CFLAGS $SOAPYSDR_CFLAGS $PTHREAD_CFLAGS"]) AC_SUBST([CXXFLAGS], ["$CXXFLAGS $EXTRA $FFTW_CFLAGS $SOAPYSDR_CFLAGS $PTHREAD_CFLAGS"]) -AC_SUBST([LIBS], ["$FFTW_LIBS $SOAPYSDR_LIBS $PTHREAD_LIBS $ZMQ_LIBS $LIMESDR_LIBS $GPIOD_LIBS $IIO_LIBS $BLADERF_LIBS"]) +AC_SUBST([LIBS], ["$FFTW_LIBS $SOAPYSDR_LIBS $PTHREAD_LIBS $ZMQ_LIBS $LIMESDR_LIBS $IIO_LIBS $BLADERF_LIBS"]) # Checks for UHD. AS_IF([test "x$enable_output_uhd" = "xyes"], diff --git a/src/OfdmGenerator.cpp b/src/OfdmGenerator.cpp index 558ec1b..38648c9 100644 --- a/src/OfdmGenerator.cpp +++ b/src/OfdmGenerator.cpp @@ -579,150 +579,30 @@ int OfdmGeneratorFixed::process(Buffer* const dataIn, Buffer* dataOut) } #ifdef HAVE_DEXTER -#include - -#define CHIP_PATH "/dev/gpiochip0" -#define LINE_FWD_INV 0 -#define LINE_CONFIG_TDATA_VALID 31 - -// GPIO mapping on write: -// bit 31 config_tdata_tvalid -// bit 30 resets the latches in the xfft_wrapper -// bits 15..0 are 1:1 xfft `config_tdata` -// `GPIO[0] = FWD_INV` according to Vivado - -static struct gpiod_line_request * -request_output_lines(const char *chip_path, const unsigned int *offsets, - enum gpiod_line_value *values, unsigned int num_lines, - const char *consumer) -{ - struct gpiod_request_config *rconfig = NULL; - struct gpiod_line_request *request = NULL; - struct gpiod_line_settings *settings; - struct gpiod_line_config *lconfig; - struct gpiod_chip *chip; - unsigned int i; - int ret; - - chip = gpiod_chip_open(chip_path); - if (!chip) - return NULL; - - settings = gpiod_line_settings_new(); - if (!settings) - goto close_chip; - - gpiod_line_settings_set_direction(settings, - GPIOD_LINE_DIRECTION_OUTPUT); - - lconfig = gpiod_line_config_new(); - if (!lconfig) - goto free_settings; - - for (i = 0; i < num_lines; i++) { - ret = gpiod_line_config_add_line_settings(lconfig, &offsets[i], - 1, settings); - if (ret) - goto free_line_config; - } - gpiod_line_config_set_output_values(lconfig, values, num_lines); - - if (consumer) { - rconfig = gpiod_request_config_new(); - if (!rconfig) - goto free_line_config; - - gpiod_request_config_set_consumer(rconfig, consumer); - } - - request = gpiod_chip_request_lines(chip, rconfig, lconfig); - - gpiod_request_config_free(rconfig); - -free_line_config: - gpiod_line_config_free(lconfig); - -free_settings: - gpiod_line_settings_free(settings); - -close_chip: - gpiod_chip_close(chip); - - return request; -} - -// The GPIO is connected to the config AXI bus of the xfft block. -// 15..0 is the config data; 31 is tvalid -void set_fft_accelerator_config(bool inverse) -{ - constexpr size_t NUM_LINES = 2; - unsigned int line_offsets[NUM_LINES]; - enum gpiod_line_value values[NUM_LINES]; - - line_offsets[0] = LINE_CONFIG_TDATA_VALID; - values[0] = GPIOD_LINE_VALUE_INACTIVE; - - line_offsets[1] = LINE_FWD_INV; - values[1] = inverse ? GPIOD_LINE_VALUE_INACTIVE : GPIOD_LINE_VALUE_ACTIVE; - - struct gpiod_line_request *request; - - request = request_output_lines(CHIP_PATH, line_offsets, values, NUM_LINES, "fft-config"); - if (!request) { - fprintf(stderr, "failed to request line: %s\n", strerror(errno)); - throw std::runtime_error("Request GPIO lines error"); - } - - usleep(100000); - - values[0] = GPIOD_LINE_VALUE_ACTIVE; - gpiod_line_request_set_values(request, values); - - usleep(100000); - - values[0] = GPIOD_LINE_VALUE_INACTIVE; - gpiod_line_request_set_values(request, values); - - gpiod_line_request_release(request); -} - - OfdmGeneratorDEXTER::OfdmGeneratorDEXTER(size_t nbSymbols, size_t nbCarriers, - size_t spacing, - bool inverse) : + size_t spacing) : ModCodec(), myNbSymbols(nbSymbols), myNbCarriers(nbCarriers), mySpacing(spacing) { - PDEBUG("OfdmGeneratorDEXTER::OfdmGeneratorDEXTER(%zu, %zu, %zu, %s) @ %p\n", - nbSymbols, nbCarriers, spacing, inverse ? "true" : "false", this); + PDEBUG("OfdmGeneratorDEXTER::OfdmGeneratorDEXTER(%zu, %zu, %zu) @ %p\n", + nbSymbols, nbCarriers, spacing, this); etiLog.level(info) << "Using DEXTER FFT Accelerator for fixed-point transform"; - set_fft_accelerator_config(inverse); - if (nbCarriers > spacing) { throw std::runtime_error("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; - } + myPosDst = (nbCarriers & 1 ? 0 : 1); + myPosSrc = 0; + myPosSize = (nbCarriers + 1) / 2; + myNegDst = spacing - (nbCarriers / 2); + myNegSrc = (nbCarriers + 1) / 2; + myNegSize = nbCarriers / 2; + myZeroDst = myPosDst + myPosSize; myZeroSize = myNegDst - myZeroDst; diff --git a/src/OfdmGenerator.h b/src/OfdmGenerator.h index 56e3cb2..475b2a4 100644 --- a/src/OfdmGenerator.h +++ b/src/OfdmGenerator.h @@ -35,9 +35,13 @@ #include "PAPRStats.h" #include "kiss_fft.h" -#include #include #include +#include + +#ifdef HAVE_DEXTER +# include +#endif // Complex Float uses FFTW class OfdmGeneratorCF32 : public ModCodec, public RemoteControllable @@ -139,15 +143,14 @@ class OfdmGeneratorFixed : public ModCodec }; #ifdef HAVE_DEXTER -#include "iio.h" // The PrecisionWave DEXTER device contains an FFT accelerator in FPGA +// It only does inverse FFTs class OfdmGeneratorDEXTER : public ModCodec { public: OfdmGeneratorDEXTER(size_t nbSymbols, size_t nbCarriers, - size_t spacing, - bool inverse = true); + size_t spacing); virtual ~OfdmGeneratorDEXTER(); OfdmGeneratorDEXTER(const OfdmGeneratorDEXTER&) = delete; OfdmGeneratorDEXTER& operator=(const OfdmGeneratorDEXTER&) = delete; -- cgit v1.2.3