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/DabModulator.cpp | 74 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 24 deletions(-) (limited to 'src/DabModulator.cpp') diff --git a/src/DabModulator.cpp b/src/DabModulator.cpp index 4a29132..d48f1a2 100644 --- a/src/DabModulator.cpp +++ b/src/DabModulator.cpp @@ -142,14 +142,14 @@ int DabModulator::process(Buffer* dataOut) auto cifMux = make_shared(m_etiSource); auto cifPart = make_shared(mode); - auto cifMap = make_shared(m_nbCarriers); - auto cifRef = make_shared(mode); - auto cifFreq = make_shared(mode); - auto cifDiff = make_shared(m_nbCarriers); + 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); - auto cifNull = make_shared(m_nbCarriers); - auto cifSig = make_shared( - (1 + m_nbSymbols) * m_nbCarriers * sizeof(complexf)); + auto cifNull = make_shared(m_nbCarriers, + m_settings.fixedPoint ? sizeof(complexfix) : sizeof(complexf)); + auto cifSig = make_shared(); // TODO this needs a review bool useCicEq = false; @@ -182,44 +182,66 @@ int DabModulator::process(Buffer* dataOut) m_settings.dabMode, m_settings.tiiConfig); rcs.enrol(tii.get()); - tiiRef = make_shared(mode); + tiiRef = make_shared(mode, m_settings.fixedPoint); } catch (const TIIError& e) { etiLog.level(error) << "Could not initialise TII: " << e.what(); } - auto cifOfdm = make_shared( - (1 + m_nbSymbols), - m_nbCarriers, - m_spacing, - m_settings.enableCfr, - m_settings.cfrClip, - m_settings.cfrErrorClip); + shared_ptr cifOfdm; + + 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; + } - rcs.enrol(cifOfdm.get()); + shared_ptr cifGain; - auto cifGain = make_shared( - m_spacing, - m_settings.gainMode, - m_settings.digitalgain, - m_settings.normalise, - m_settings.gainmodeVariance); + if (not m_settings.fixedPoint) { + cifGain = make_shared( + m_spacing, + m_settings.gainMode, + m_settings.digitalgain, + m_settings.normalise, + m_settings.gainmodeVariance); - rcs.enrol(cifGain.get()); + rcs.enrol(cifGain.get()); + } auto cifGuard = make_shared( m_nbSymbols, m_spacing, m_nullSize, m_symSize, - m_settings.ofdmWindowOverlap); + m_settings.ofdmWindowOverlap, m_settings.fixedPoint); 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"); + cifFilter = make_shared(m_settings.filterTapsFilename); rcs.enrol(cifFilter.get()); } shared_ptr cifPoly; if (not m_settings.polyCoefFilename.empty()) { + if (m_settings.fixedPoint) throw std::runtime_error("fixed point doesn't support predistortion"); + cifPoly = make_shared(m_settings.polyCoefFilename, m_settings.polyNumThreads); rcs.enrol(cifPoly.get()); @@ -227,6 +249,8 @@ 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"); + cifRes = make_shared( 2048000, m_settings.outputRate, @@ -234,6 +258,8 @@ int DabModulator::process(Buffer* dataOut) } if (not m_format.empty()) { + if (m_settings.fixedPoint) throw std::runtime_error("fixed point doesn't support format converter"); + m_formatConverter = make_shared(m_format); } -- cgit v1.2.3 From 1d83a2b247f8e83bbce802a272ffa165bbc6333f Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Tue, 8 Oct 2024 15:25:45 +0200 Subject: Get fixed_point working with USRP --- src/DabMod.cpp | 26 ++++---- src/DabModulator.cpp | 4 +- src/FormatConverter.cpp | 135 +++++++++++++++++++++++++++--------------- src/FormatConverter.h | 3 +- src/GuardIntervalInserter.cpp | 4 +- src/OutputMemory.cpp | 20 ++----- src/OutputMemory.h | 4 +- src/output/SDR.cpp | 1 + src/output/SDR.h | 4 -- src/output/SDRDevice.h | 4 +- src/output/UHD.cpp | 23 ++++--- src/output/UHD.h | 3 - 12 files changed, 129 insertions(+), 102 deletions(-) (limited to 'src/DabModulator.cpp') diff --git a/src/DabMod.cpp b/src/DabMod.cpp index 5f8412b..739fef1 100644 --- a/src/DabMod.cpp +++ b/src/DabMod.cpp @@ -72,10 +72,12 @@ * samples can have peaks up to about 48000. The value of 50000 * should guarantee that with a digital gain of 1.0, UHD never clips * our samples. + * + * This only applies when fixed_point == false. */ static const float normalise_factor = 50000.0f; -//Empirical normalisation factors used to normalise the samples to amplitude 1. +// Empirical normalisation factors used to normalise the samples to amplitude 1. static const float normalise_factor_file_fix = 81000.0f; static const float normalise_factor_file_var = 46000.0f; static const float normalise_factor_file_max = 46000.0f; @@ -249,16 +251,12 @@ static shared_ptr prepare_output(mod_settings_t& s) { shared_ptr output; - if (s.fixedPoint) { - if (s.useFileOutput) { + if (s.useFileOutput) { + if (s.fixedPoint) { + // Intentionally ignore fileOutputFormat, it is always sc16 output = make_shared(s.outputName, s.fileOutputShowMetadata); } - else { - throw runtime_error("Fixed point only works with file output"); - } - } - else if (s.useFileOutput) { - if (s.fileOutputFormat == "complexf") { + else if (s.fileOutputFormat == "complexf") { output = make_shared(s.outputName, s.fileOutputShowMetadata); } else if (s.fileOutputFormat == "complexf_normalised") { @@ -294,6 +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; auto uhddevice = make_shared(s.sdr_device_config); output = make_shared(s.sdr_device_config, uhddevice); rcs.enrol((Output::SDR*)output.get()); @@ -304,6 +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"); auto soapydevice = make_shared(s.sdr_device_config); output = make_shared(s.sdr_device_config, soapydevice); rcs.enrol((Output::SDR*)output.get()); @@ -313,6 +313,7 @@ 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); @@ -323,6 +324,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"); s.sdr_device_config.sampleRate = s.outputRate; auto limedevice = make_shared(s.sdr_device_config); output = make_shared(s.sdr_device_config, limedevice); @@ -333,6 +335,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"); s.sdr_device_config.sampleRate = s.outputRate; auto bladerfdevice = make_shared(s.sdr_device_config); output = make_shared(s.sdr_device_config, bladerfdevice); @@ -443,7 +446,10 @@ int launch_modulator(int argc, char* argv[]) } std::string output_format; - if (mod_settings.useFileOutput and + if (mod_settings.fixedPoint) { + output_format = "fixedpoint"; + } + else if (mod_settings.useFileOutput and (mod_settings.fileOutputFormat == "s8" or mod_settings.fileOutputFormat == "u8" or mod_settings.fileOutputFormat == "s16")) { diff --git a/src/DabModulator.cpp b/src/DabModulator.cpp index d48f1a2..9e9d017 100644 --- a/src/DabModulator.cpp +++ b/src/DabModulator.cpp @@ -258,8 +258,8 @@ int DabModulator::process(Buffer* dataOut) } if (not m_format.empty()) { - if (m_settings.fixedPoint) throw std::runtime_error("fixed point doesn't support format converter"); - + // This handles both complexf and fixedpoint: + // Convert from complexfix to interleaved int16_t I/Q m_formatConverter = make_shared(m_format); } diff --git a/src/FormatConverter.cpp b/src/FormatConverter.cpp index e8e76ed..9e59a4a 100644 --- a/src/FormatConverter.cpp +++ b/src/FormatConverter.cpp @@ -39,6 +39,14 @@ FormatConverter::FormatConverter(const std::string& format) : m_format(format) { } +FormatConverter::~FormatConverter() +{ + etiLog.level(debug) << "FormatConverter: " + << m_num_clipped_samples.load() << + " clipped samples"; +} + + /* Expect the input samples to be in the correct range for the required format */ int FormatConverter::process(Buffer* const dataIn, Buffer* dataOut) { @@ -47,71 +55,101 @@ 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]; + 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()); + 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; + } - 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 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 { + throw std::runtime_error("FormatConverter: Invalid format " + m_format); + } + } - else if (m_format == "s8") { - dataOut->setLength(sizeIn * sizeof(int8_t)); - int8_t* out = reinterpret_cast(dataOut->getData()); + else { + // Output is always sc16, because that's what UHD accepts + + using fixed_t = complexfix::value_type; + size_t sizeIn = dataIn->getLength() / sizeof(fixed_t); + fixed_t* in = reinterpret_cast(dataIn->getData()); + + dataOut->setLength(sizeIn * sizeof(int16_t)); + int16_t* out = reinterpret_cast(dataOut->getData()); for (size_t i = 0; i < sizeIn; i++) { - if (in[i] < INT8_MIN) { - out[i] = INT8_MIN; + const auto v = (in[i] * 2).raw_value(); + + if (v < INT16_MIN) { + out[i] = INT16_MIN; num_clipped_samples++; } - else if (in[i] > INT8_MAX) { - out[i] = INT8_MAX; + else if (v > INT16_MAX) { + out[i] = INT16_MAX; num_clipped_samples++; } else { - out[i] = in[i]; + out[i] = (int16_t)v; } } } - else { - throw std::runtime_error("FormatConverter: Invalid format " + m_format); - } m_num_clipped_samples.store(num_clipped_samples); - return dataOut->getLength(); } @@ -129,7 +167,10 @@ 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 == "s16") { + if (format == "fixedpoint") { + return 4; + } + else if (format == "s16") { return 4; } else if (format == "u8") { diff --git a/src/FormatConverter.h b/src/FormatConverter.h index 05511c0..27ca0b1 100644 --- a/src/FormatConverter.h +++ b/src/FormatConverter.h @@ -33,10 +33,8 @@ #endif #include "ModPlugin.h" -#include #include #include -#include class FormatConverter : public ModCodec { @@ -45,6 +43,7 @@ class FormatConverter : public ModCodec // Allowed formats: s8, u8 and s16 FormatConverter(const std::string& format); + virtual ~FormatConverter(); int process(Buffer* const dataIn, Buffer* dataOut); const char* name(); diff --git a/src/GuardIntervalInserter.cpp b/src/GuardIntervalInserter.cpp index 0b39de8..4e22367 100644 --- a/src/GuardIntervalInserter.cpp +++ b/src/GuardIntervalInserter.cpp @@ -112,8 +112,6 @@ int do_process(const GuardIntervalInserter::Params& p, Buffer* const dataIn, Buf PDEBUG("GuardIntervalInserter do_process(dataIn: %p, dataOut: %p)\n", dataIn, dataOut); - std::lock_guard lock(p.windowMutex); - // Every symbol overlaps over a length of windowOverlap with // the previous symbol, and with the next symbol. First symbol // receives no prefix window, because we don't remember the @@ -141,7 +139,7 @@ int do_process(const GuardIntervalInserter::Params& p, Buffer* const dataIn, Buf // TODO remember the end of the last TF so that we can do some // 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 diff --git a/src/OutputMemory.cpp b/src/OutputMemory.cpp index ac8a67b..f673555 100644 --- a/src/OutputMemory.cpp +++ b/src/OutputMemory.cpp @@ -26,20 +26,14 @@ #include "OutputMemory.h" #include "PcDebug.h" -#include "Log.h" -#include "TimestampDecoder.h" - -#include -#include -#include - +#include OutputMemory::OutputMemory(Buffer* dataOut) : ModOutput() { PDEBUG("OutputMemory::OutputMemory(%p) @ %p\n", dataOut, this); - myDataOut = dataOut; + m_dataOut = dataOut; #if OUTPUT_MEM_HISTOGRAM myMax = 0.0f; @@ -49,7 +43,6 @@ OutputMemory::OutputMemory(Buffer* dataOut) #endif } - OutputMemory::~OutputMemory() { #if OUTPUT_MEM_HISTOGRAM @@ -66,13 +59,12 @@ OutputMemory::~OutputMemory() PDEBUG("OutputMemory::~OutputMemory() @ %p\n", this); } - int OutputMemory::process(Buffer* dataIn) { PDEBUG("OutputMemory::process(dataIn: %p)\n", dataIn); - *myDataOut = *dataIn; + *m_dataOut = *dataIn; #if OUTPUT_MEM_HISTOGRAM const float* in = (const float*)dataIn->getData(); @@ -87,17 +79,17 @@ int OutputMemory::process(Buffer* dataIn) } #endif - return myDataOut->getLength(); + return m_dataOut->getLength(); } meta_vec_t OutputMemory::process_metadata(const meta_vec_t& metadataIn) { - myMetadata = metadataIn; + m_metadata = metadataIn; return {}; } meta_vec_t OutputMemory::get_latest_metadata() { - return myMetadata; + return m_metadata; } diff --git a/src/OutputMemory.h b/src/OutputMemory.h index e7252d3..299d31d 100644 --- a/src/OutputMemory.h +++ b/src/OutputMemory.h @@ -62,8 +62,8 @@ public: meta_vec_t get_latest_metadata(void); protected: - Buffer* myDataOut; - meta_vec_t myMetadata; + Buffer* m_dataOut; + meta_vec_t m_metadata; #if OUTPUT_MEM_HISTOGRAM // keep track of max value diff --git a/src/output/SDR.cpp b/src/output/SDR.cpp index 594171f..22398c7 100644 --- a/src/output/SDR.cpp +++ b/src/output/SDR.cpp @@ -34,6 +34,7 @@ #include "RemoteControl.h" #include "Utils.h" +#include #include #include #include diff --git a/src/output/SDR.h b/src/output/SDR.h index 960de0c..86bf295 100644 --- a/src/output/SDR.h +++ b/src/output/SDR.h @@ -34,16 +34,12 @@ DESCRIPTION: # include #endif -#include #include "ModPlugin.h" -#include "EtiReader.h" #include "output/SDRDevice.h" #include "output/Feedback.h" namespace Output { -using complexf = std::complex; - class SDR : public ModOutput, public ModMetadata, public RemoteControllable { public: SDR(SDRDeviceConfig& config, std::shared_ptr device); diff --git a/src/output/SDRDevice.h b/src/output/SDRDevice.h index 378829c..ec9373d 100644 --- a/src/output/SDRDevice.h +++ b/src/output/SDRDevice.h @@ -38,9 +38,7 @@ DESCRIPTION: #include #include #include -#include #include -#include #include "TimestampDecoder.h" @@ -59,6 +57,8 @@ struct SDRDeviceConfig { std::string tx_antenna; std::string rx_antenna; + bool fixedPoint = false; + long masterClockRate = 32768000; unsigned sampleRate = 2048000; double frequency = 0.0; diff --git a/src/output/UHD.cpp b/src/output/UHD.cpp index e097692..b30f9e1 100644 --- a/src/output/UHD.cpp +++ b/src/output/UHD.cpp @@ -31,10 +31,7 @@ //#define MDEBUG(fmt, args...) fprintf(LOG, fmt , ## args) #define MDEBUG(fmt, args...) -#include "PcDebug.h" #include "Log.h" -#include "RemoteControl.h" -#include "Utils.h" #include #include @@ -52,14 +49,12 @@ # include #endif - -#include #include -#include +#include +#include #include -#include +#include #include -#include #include #include @@ -235,7 +230,8 @@ UHD::UHD(SDRDeviceConfig& config) : m_usrp->set_rx_gain(m_conf.rxgain); etiLog.log(debug, "OutputUHD:Actual RX Gain: %f", m_usrp->get_rx_gain()); - const uhd::stream_args_t stream_args("fc32"); //complex floats + const uhd::stream_args_t stream_args( + m_conf.fixedPoint ? "sc16" : "fc32"); m_rx_stream = m_usrp->get_rx_stream(stream_args); m_tx_stream = m_usrp->get_tx_stream(stream_args); @@ -319,8 +315,9 @@ double UHD::get_bandwidth(void) const void UHD::transmit_frame(struct FrameData&& frame) { const double tx_timeout = 20.0; - const size_t sizeIn = frame.buf.size() / sizeof(complexf); - const complexf* in_data = reinterpret_cast(&frame.buf[0]); + + const size_t sample_size = m_conf.fixedPoint ? (2 * sizeof(int16_t)) : sizeof(complexf); + const size_t sizeIn = frame.buf.size() / sample_size; uhd::tx_metadata_t md_tx; @@ -353,9 +350,9 @@ void UHD::transmit_frame(struct FrameData&& frame) samps_to_send <= usrp_max_num_samps ); m_require_timestamp_refresh = false; - //send a single packet + // send a single packet size_t num_tx_samps = m_tx_stream->send( - &in_data[num_acc_samps], + frame.buf.data() + sample_size * num_acc_samps, samps_to_send, md_tx, tx_timeout); etiLog.log(trace, "UHD,sent %zu of %zu", num_tx_samps, samps_to_send); diff --git a/src/output/UHD.h b/src/output/UHD.h index 9891c7a..c4f1a45 100644 --- a/src/output/UHD.h +++ b/src/output/UHD.h @@ -45,12 +45,9 @@ DESCRIPTION: #include #include -#include "Log.h" #include "output/SDR.h" #include "output/USRPTime.h" #include "TimestampDecoder.h" -#include "RemoteControl.h" -#include "ThreadsafeQueue.h" #include #include -- 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/DabModulator.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 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/DabModulator.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/DabModulator.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 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/DabModulator.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 101952b8277dede9c70a72a7f9e7a75ed3a291e0 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Mon, 18 Nov 2024 17:06:49 +0100 Subject: Make TII compatible with fixed point --- src/DabModulator.cpp | 19 ++++------ src/TII.cpp | 99 ++++++++++++++++++++++++++++++---------------------- src/TII.h | 8 ++--- 3 files changed, 69 insertions(+), 57 deletions(-) (limited to 'src/DabModulator.cpp') diff --git a/src/DabModulator.cpp b/src/DabModulator.cpp index 39c1d4a..e0a613f 100644 --- a/src/DabModulator.cpp +++ b/src/DabModulator.cpp @@ -3,7 +3,7 @@ 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 @@ -54,7 +54,6 @@ #include "SignalMultiplexer.h" #include "TII.h" #include "TimeInterleaver.h" -#include "TimestampDecoder.h" using namespace std; @@ -179,16 +178,12 @@ int DabModulator::process(Buffer* dataOut) shared_ptr tii; shared_ptr tiiRef; try { - if (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, fixedPoint); - } + tii = make_shared( + m_settings.dabMode, + m_settings.tiiConfig, + fixedPoint); + rcs.enrol(tii.get()); + tiiRef = make_shared(mode, fixedPoint); } catch (const TIIError& e) { etiLog.level(error) << "Could not initialise TII: " << e.what(); diff --git a/src/TII.cpp b/src/TII.cpp index 155d1c9..bce15aa 100644 --- a/src/TII.cpp +++ b/src/TII.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 @@ -103,11 +103,12 @@ const int pattern_tm1_2_4[][8] = { // {{{ {1,1,1,0,1,0,0,0}, {1,1,1,1,0,0,0,0} }; // }}} -TII::TII(unsigned int dabmode, tii_config_t& tii_config) : +TII::TII(unsigned int dabmode, tii_config_t& tii_config, bool fixedPoint) : ModCodec(), RemoteControllable("tii"), m_dabmode(dabmode), - m_conf(tii_config) + m_conf(tii_config), + m_fixedPoint(fixedPoint) { PDEBUG("TII::TII(%u) @ %p\n", dabmode, this); @@ -168,56 +169,72 @@ const char* TII::name() return m_name.c_str(); } +template +void do_process(size_t carriers, bool old_variant, const std::vector& Acp, Buffer* dataIn, Buffer* dataOut) +{ + const T* in = reinterpret_cast(dataIn->getData()); + T* out = reinterpret_cast(dataOut->getData()); + + /* Normalise the TII carrier power according to ETSI TR 101 496-3 + * Clause 5.4.2.2 Paragraph 7: + * + * > The ratio of carriers in a TII symbol to a normal DAB symbol + * > is 1:48 for all Modes, so that the signal power in a TII symbol is + * > 16 dB below the signal power of the other symbols. + * + * This is because we only enable 32 out of 1536 carriers, not because + * every carrier is lower power. + */ + for (size_t i = 0; i < Acp.size(); i++) { + /* See header file for an explanation of the old variant. + * + * A_{c,p}(k) and A_{c,p}(k-1) are never both simultaneously true, + * so instead of doing the sum inside z_{m,0,k}, we could do + * + * if (m_Acp[i]) out[i] = in[i]; + * if (m_Acp[i-1]) out[i] = in[i-1] + * + * (Considering only the new variant) + * + * To avoid messing with indices, we substitute j = i-1 + * + * if (m_Acp[i]) out[i] = in[i]; + * if (m_Acp[j]) out[j+1] = in[j] + * + * and fuse the two conditionals together: + */ + if (Acp[i]) { + out[i] = in[i]; + out[i+1] = (old_variant ? in[i+1] : in[i]); + } + } +} int TII::process(Buffer* dataIn, Buffer* dataOut) { + const size_t sizeof_samples = m_fixedPoint ? sizeof(complexfix) : sizeof(complexf); + PDEBUG("TII::process(dataOut: %p)\n", dataOut); if ( (dataIn == NULL) or - (dataIn->getLength() != m_carriers * sizeof(complexf))) { + (dataIn->getLength() != m_carriers * sizeof_samples)) { throw TIIError("TII::process input size not valid!"); } - dataOut->setLength(m_carriers * sizeof(complexf)); - memset(dataOut->getData(), 0, dataOut->getLength()); + dataOut->setLength(m_carriers * sizeof_samples); + memset(dataOut->getData(), 0, dataOut->getLength()); if (m_conf.enable and m_insert) { std::lock_guard lock(m_enabled_carriers_mutex); - complexf* in = reinterpret_cast(dataIn->getData()); - complexf* out = reinterpret_cast(dataOut->getData()); - - /* Normalise the TII carrier power according to ETSI TR 101 496-3 - * Clause 5.4.2.2 Paragraph 7: - * - * > The ratio of carriers in a TII symbol to a normal DAB symbol - * > is 1:48 for all Modes, so that the signal power in a TII symbol is - * > 16 dB below the signal power of the other symbols. - * - * This is because we only enable 32 out of 1536 carriers, not because - * every carrier is lower power. - */ - for (size_t i = 0; i < m_Acp.size(); i++) { - /* See header file for an explanation of the old variant. - * - * A_{c,p}(k) and A_{c,p}(k-1) are never both simultaneously true, - * so instead of doing the sum inside z_{m,0,k}, we could do - * - * if (m_Acp[i]) out[i] = in[i]; - * if (m_Acp[i-1]) out[i] = in[i-1] - * - * (Considering only the new variant) - * - * To avoid messing with indices, we substitute j = i-1 - * - * if (m_Acp[i]) out[i] = in[i]; - * if (m_Acp[j]) out[j+1] = in[j] - * - * and fuse the two conditionals together: - */ - if (m_Acp[i]) { - out[i] = in[i]; - out[i+1] = (m_conf.old_variant ? in[i+1] : in[i]); - } + if (m_fixedPoint) { + do_process( + m_carriers, m_conf.old_variant, m_Acp, + dataIn, dataOut); + } + else { + do_process( + m_carriers, m_conf.old_variant, m_Acp, + dataIn, dataOut); } } diff --git a/src/TII.h b/src/TII.h index f6de70b..6fe4d4f 100644 --- a/src/TII.h +++ b/src/TII.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 @@ -36,8 +36,6 @@ #include "RemoteControl.h" #include -#include -#include #include #include @@ -81,7 +79,7 @@ class TIIError : public std::runtime_error { class TII : public ModCodec, public RemoteControllable { public: - TII(unsigned int dabmode, tii_config_t& tii_config); + TII(unsigned int dabmode, tii_config_t& tii_config, bool fixedPoint); virtual ~TII() {} int process(Buffer* dataIn, Buffer* dataOut) override; @@ -106,6 +104,8 @@ class TII : public ModCodec, public RemoteControllable // Remote-controllable settings tii_config_t& m_conf; + bool m_fixedPoint = false; + // Internal flag when to insert TII bool m_insert = true; -- 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/DabModulator.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