From 45b180cd1a0ccbd12b5fb1baf82a9430e4567d2b Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 22 Dec 2017 09:08:42 +0100 Subject: Fix some PDEBUG calls --- src/DabModulator.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/DabModulator.cpp') diff --git a/src/DabModulator.cpp b/src/DabModulator.cpp index 0914469..be140d3 100644 --- a/src/DabModulator.cpp +++ b/src/DabModulator.cpp @@ -62,8 +62,7 @@ DabModulator::DabModulator(EtiSource& etiSource, myEtiSource(etiSource), myFlowgraph() { - PDEBUG("DabModulator::DabModulator(%u, %u, %u, %zu) @ %p\n", - outputRate, clockRate, dabMode, (size_t)gainMode, this); + PDEBUG("DabModulator::DabModulator() @ %p\n", this); if (m_settings.dabMode == 0) { setMode(2); -- cgit v1.2.3 From ce490c8c25bdbb1fc40fc43d8631a807f6effa6a Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 22 Dec 2017 09:10:17 +0100 Subject: Add OFDM windowing support to GuardIntervalInserter --- src/DabModulator.cpp | 1 + src/GuardIntervalInserter.cpp | 232 ++++++++++++++++++++++++++++++++++++++---- src/GuardIntervalInserter.h | 60 +++++++---- 3 files changed, 257 insertions(+), 36 deletions(-) (limited to 'src/DabModulator.cpp') diff --git a/src/DabModulator.cpp b/src/DabModulator.cpp index be140d3..cded280 100644 --- a/src/DabModulator.cpp +++ b/src/DabModulator.cpp @@ -202,6 +202,7 @@ int DabModulator::process(Buffer* dataOut) auto cifGuard = make_shared( myNbSymbols, mySpacing, myNullSize, mySymSize); + rcs.enrol(cifGuard.get()); shared_ptr cifFilter; if (not m_settings.filterTapsFilename.empty()) { diff --git a/src/GuardIntervalInserter.cpp b/src/GuardIntervalInserter.cpp index a2542f7..1381a3c 100644 --- a/src/GuardIntervalInserter.cpp +++ b/src/GuardIntervalInserter.cpp @@ -1,6 +1,11 @@ /* Copyright (C) 2005, 2206, 2007, 2008, 2009, 2010, 2011 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) + + Copyright (C) 2017 + Matthias P. Braendli, matthias.braendli@mpb.li + + http://opendigitalradio.org */ /* This file is part of ODR-DabMod. @@ -24,37 +29,98 @@ #include #include #include +#include typedef std::complex complexf; -GuardIntervalInserter::GuardIntervalInserter(size_t nbSymbols, +GuardIntervalInserter::GuardIntervalInserter( + size_t nbSymbols, size_t spacing, size_t nullSize, - size_t symSize) : + size_t symSize, + size_t windowOverlap) : ModCodec(), + RemoteControllable("guardinterval"), d_nbSymbols(nbSymbols), d_spacing(spacing), d_nullSize(nullSize), - d_symSize(symSize) + d_symSize(symSize), + d_windowOverlap(0) { - PDEBUG("GuardIntervalInserter::GuardIntervalInserter(%zu, %zu, %zu, %zu)" - " @ %p\n", nbSymbols, spacing, nullSize, symSize, this); + if (d_nullSize == 0) { + throw std::logic_error("NULL symbol must be present"); + } + + RC_ADD_PARAMETER(windowlen, "Window length for OFDM windowng [0 to disable]"); + + /* We use a raised-cosine window for the OFDM windowing. + * Each symbol is extended on both sides by d_windowOverlap samples. + * + * + * Sym n |####################| + * Sym n+1 |####################| + * + * We now extend the symbols by d_windowOverlap (one dash) + * + * Sym n extended -|####################|- + * Sym n+1 extended -|####################|- + * + * The windows are raised-cosine: + * ____________________ + * Sym n window / \ + * ... ____/ \___________ ... + * + * Sym n+1 window ____________________ + * / \ + * ... ________________/ \__ ... + * + * The window length is 2*d_windowOverlap. + */ + + update_window(windowOverlap); + + PDEBUG("GuardIntervalInserter::GuardIntervalInserter" + "(%zu, %zu, %zu, %zu, %zu) @ %p\n", + nbSymbols, spacing, nullSize, symSize, windowOverlap, this); } +void GuardIntervalInserter::update_window(size_t new_window_overlap) +{ + std::lock_guard lock(d_windowMutex); + + d_windowOverlap = new_window_overlap; + + // d_window only contains the rising window edge. + d_window.resize(2*d_windowOverlap); + for (size_t i = 0; i < 2*d_windowOverlap; i++) { + d_window[i] = (float)(0.5 * (1.0 - cos(M_PI * i / (2*d_windowOverlap - 1)))); + } +} + +#pragma GCC optimize ("O0") int GuardIntervalInserter::process(Buffer* const dataIn, Buffer* dataOut) { PDEBUG("GuardIntervalInserter::process(dataIn: %p, dataOut: %p)\n", dataIn, dataOut); - dataOut->setLength((d_nullSize + (d_nbSymbols * d_symSize)) - * sizeof(complexf)); + std::lock_guard lock(d_windowMutex); + + // Every symbol overlaps over a length of d_windowOverlap with + // the previous symbol, and with the next symbol. First symbol + // receives no prefix window, because we don't remember the + // last symbol from the previous TF (yet). Last symbol also + // receives no suffix window, for the same reason. + // Overall output buffer length must stay independent of the windowing. + dataOut->setLength((d_nullSize + (d_nbSymbols * d_symSize)) * sizeof(complexf)); const complexf* in = reinterpret_cast(dataIn->getData()); complexf* out = reinterpret_cast(dataOut->getData()); size_t sizeIn = dataIn->getLength() / sizeof(complexf); - if (sizeIn != (d_nbSymbols + (d_nullSize ? 1 : 0)) * d_spacing) { + const size_t num_symbols = d_nbSymbols + 1; + if (sizeIn != num_symbols * d_spacing) + { PDEBUG("Nb symbols: %zu\n", d_nbSymbols); PDEBUG("Spacing: %zu\n", d_spacing); PDEBUG("Null size: %zu\n", d_nullSize); @@ -64,25 +130,153 @@ int GuardIntervalInserter::process(Buffer* const dataIn, Buffer* dataOut) "GuardIntervalInserter::process input size not valid!"); } - // Handle Null symbol separately because it is longer - if (d_nullSize) { + // TODO remember the end of the last TF so that we can do some + // windowing too. + + if (d_windowOverlap) { + { + // Handle Null symbol separately because it is longer + const size_t prefixlength = d_nullSize - d_spacing; + + // end = spacing + memcpy(out, &in[d_spacing - prefixlength], + prefixlength * sizeof(complexf)); + + memcpy(&out[prefixlength], in, (d_spacing - d_windowOverlap) * sizeof(complexf)); + + // 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 < d_windowOverlap; i++) { + const size_t out_ix = prefixlength + d_spacing - d_windowOverlap + i; + const size_t in_ix = d_spacing - d_windowOverlap + i; + out[out_ix] = in[in_ix] * d_window[2*d_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 < d_windowOverlap; i++) { + const size_t out_ix = prefixlength + d_spacing + i; + out[out_ix] = in[i] * d_window[d_windowOverlap - (i+1)]; + } + + in += d_spacing; + out += d_nullSize; + // out is now pointing to the proper end of symbol. There are + // d_windowOverlap samples ahead that were already written. + } + + // Data symbols + for (size_t sym_ix = 0; sym_ix < d_nbSymbols; sym_ix++) { + // end = spacing + const size_t prefix_start_ix = 2*d_spacing - d_symSize - d_windowOverlap; + + // From out[-d_windowOverlap] to out[d_windowOverlap], we need to + // apply the window from 0 to 0.5, with our additional + // prefix, and add it to existing data. + for (size_t i = 0; i < 2*d_windowOverlap; i++) { + out[-i] += in[prefix_start_ix + i] * d_window[i]; + } + + const size_t remaining_prefix_length = d_symSize - d_spacing - d_windowOverlap; + + // After out[d_windowOverlap], no need to accumulate, we can copy. + memcpy( &out[d_windowOverlap], + &in[prefix_start_ix + 2*d_windowOverlap], + remaining_prefix_length * sizeof(complexf)); + + const size_t prefixlength = d_symSize - d_spacing + d_windowOverlap; + + const bool last_symbol = (sym_ix + 1 < d_nbSymbols); + if (last_symbol) { + // No windowing at all at end + memcpy(&out[prefixlength], in, d_spacing * sizeof(complexf)); + } + else { + // Copy the middle part of the symbol, d_windowOverlap samples + // short of the end. + memcpy( &out[prefixlength], + in, + (d_spacing - d_windowOverlap) * sizeof(complexf)); + + const size_t out_start = prefixlength + d_spacing - d_windowOverlap; + const size_t in_start = d_spacing - d_windowOverlap; + + // Apply window from 1 to 0.5 for the end of the symbol + for (size_t i = 0; i < d_windowOverlap; i++) { + out[out_start + i] = + in[in_start + i] * d_window[2*d_windowOverlap - (i+1)]; + } + + const size_t out_start_suffix = prefixlength + d_spacing; + + // Cyclic suffix, with window from 0.5 to 0 + for (size_t i = 0; i < d_windowOverlap; i++) { + out[out_start_suffix + i] = + in[i] * d_window[d_windowOverlap - (i+1)]; + } + } + + in += d_spacing; + out += d_symSize; + // out is now pointing to the proper end of symbol. There are + // d_windowOverlap samples ahead that were already written. + } + } + else { + // Handle Null symbol separately because it is longer // end - (nullSize - spacing) = 2 * spacing - nullSize memcpy(out, &in[2 * d_spacing - d_nullSize], (d_nullSize - d_spacing) * sizeof(complexf)); memcpy(&out[d_nullSize - d_spacing], in, d_spacing * sizeof(complexf)); in += d_spacing; out += d_nullSize; - } - // Data symbols - for (size_t i = 0; i < d_nbSymbols; ++i) { - // end - (symSize - spacing) = 2 * spacing - symSize - memcpy(out, &in[2 * d_spacing - d_symSize], - (d_symSize - d_spacing) * sizeof(complexf)); - memcpy(&out[d_symSize - d_spacing], in, d_spacing * sizeof(complexf)); - in += d_spacing; - out += d_symSize; + // Data symbols + for (size_t i = 0; i < d_nbSymbols; ++i) { + // end - (symSize - spacing) = 2 * spacing - symSize + memcpy(out, &in[2 * d_spacing - d_symSize], + (d_symSize - d_spacing) * sizeof(complexf)); + memcpy(&out[d_symSize - d_spacing], in, d_spacing * sizeof(complexf)); + in += d_spacing; + out += d_symSize; + } } return sizeIn; } + +void GuardIntervalInserter::set_parameter( + const std::string& parameter, + const std::string& value) +{ + using namespace std; + stringstream ss(value); + ss.exceptions ( stringstream::failbit | stringstream::badbit ); + + if (parameter == "windowlen") { + size_t new_window_overlap = 0; + ss >> new_window_overlap; + update_window(new_window_overlap); + } + else { + stringstream ss_err; + ss_err << "Parameter '" << parameter << + "' is not exported by controllable " << get_rc_name(); + throw ParameterError(ss_err.str()); + } +} + +const std::string GuardIntervalInserter::get_parameter(const std::string& parameter) const +{ + using namespace std; + stringstream ss; + if (parameter == "windowlen") { + ss << d_windowOverlap; + } + else { + ss << "Parameter '" << parameter << + "' is not exported by controllable " << get_rc_name(); + throw ParameterError(ss.str()); + } + return ss.str(); +} diff --git a/src/GuardIntervalInserter.h b/src/GuardIntervalInserter.h index e6b3b64..b2ac782 100644 --- a/src/GuardIntervalInserter.h +++ b/src/GuardIntervalInserter.h @@ -1,6 +1,11 @@ /* Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) + + Copyright (C) 2017 + Matthias P. Braendli, matthias.braendli@mpb.li + + http://opendigitalradio.org */ /* This file is part of ODR-DabMod. @@ -26,26 +31,47 @@ #endif #include "ModPlugin.h" +#include "RemoteControl.h" #include +#include /* The GuardIntervalInserter prepends the cyclic prefix to all - * symbols in the transmission frame. */ -class GuardIntervalInserter : public ModCodec + * symbols in the transmission frame. + * + * If windowOverlap is non-zero, it will also add a cyclic suffix of + * that length, enlarge the cyclic prefix too, and make symbols + * overlap using a raised cosine window. + * */ +class GuardIntervalInserter : public ModCodec , public RemoteControllable { -public: - GuardIntervalInserter( - size_t nbSymbols, - size_t spacing, - size_t nullSize, - size_t symSize); - - int process(Buffer* const dataIn, Buffer* dataOut); - const char* name() { return "GuardIntervalInserter"; } - -protected: - size_t d_nbSymbols; - size_t d_spacing; - size_t d_nullSize; - size_t d_symSize; + public: + GuardIntervalInserter( + size_t nbSymbols, + size_t spacing, + size_t nullSize, + size_t symSize, + size_t windowOverlap = 0); + + int process(Buffer* const dataIn, Buffer* dataOut); + const char* name() { return "GuardIntervalInserter"; } + + /******* REMOTE CONTROL ********/ + virtual void set_parameter(const std::string& parameter, + const std::string& value); + + virtual const std::string get_parameter( + const std::string& parameter) const; + + protected: + void update_window(size_t new_window_overlap); + + size_t d_nbSymbols; + size_t d_spacing; + size_t d_nullSize; + size_t d_symSize; + + mutable std::mutex d_windowMutex; + size_t d_windowOverlap; + std::vector d_window; }; -- cgit v1.2.3 From 215e8143e3feefd69a76d0b6eaf36ed42a7a904f Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Sat, 23 Dec 2017 07:59:14 +0100 Subject: Add OFDM windowing to config file --- doc/example.ini | 7 +++++++ src/ConfigParser.cpp | 6 +++++- src/ConfigParser.h | 2 ++ src/DabModulator.cpp | 3 ++- 4 files changed, 16 insertions(+), 2 deletions(-) (limited to 'src/DabModulator.cpp') diff --git a/doc/example.ini b/doc/example.ini index cf56720..d356ee1 100644 --- a/doc/example.ini +++ b/doc/example.ini @@ -128,6 +128,13 @@ rate=2048000 ; and ;dac_clk_rate=128000000 +; When nonzero, overlap ofdmwindowing samples from each OFDM symbol +; onto the previous and next symbol, using a raised cosine window function. +; This has the effect of smoothing the transition from one symbol to the next, +; which improves spectrum shape. +; In Transmission Mode I, every data symbol is composed of 2552 samples. +;ofdmwindowing=10 + ; Settings for crest factor reduction. Statistics for ratio of ; samples that were clipped are available through the RC. [cfr] diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp index 1cc94c0..2c93a57 100644 --- a/src/ConfigParser.cpp +++ b/src/ConfigParser.cpp @@ -159,8 +159,12 @@ static void parse_configfile( mod_settings.dabMode = pt.get("modulator.mode", mod_settings.dabMode); mod_settings.clockRate = pt.get("modulator.dac_clk_rate", (size_t)0); - mod_settings.digitalgain = pt.get("modulator.digital_gain", mod_settings.digitalgain); + mod_settings.digitalgain = pt.get("modulator.digital_gain", + mod_settings.digitalgain); + mod_settings.outputRate = pt.get("modulator.rate", mod_settings.outputRate); + mod_settings.ofdmWindowOverlap = pt.get("modulator.ofdmwindowing", + mod_settings.ofdmWindowOverlap); // FIR Filter parameters: if (pt.get("firfilter.enabled", 0) == 1) { diff --git a/src/ConfigParser.h b/src/ConfigParser.h index a8d7837..0be3558 100644 --- a/src/ConfigParser.h +++ b/src/ConfigParser.h @@ -82,6 +82,8 @@ struct mod_settings_t { float cfrClip = 1.0f; float cfrErrorClip = 1.0f; + // Settings for the OFDM windowing + unsigned ofdmWindowOverlap = 0; #if defined(HAVE_OUTPUT_UHD) OutputUHDConfig outputuhd_conf; diff --git a/src/DabModulator.cpp b/src/DabModulator.cpp index cded280..0818f4f 100644 --- a/src/DabModulator.cpp +++ b/src/DabModulator.cpp @@ -201,7 +201,8 @@ int DabModulator::process(Buffer* dataOut) rcs.enrol(cifGain.get()); auto cifGuard = make_shared( - myNbSymbols, mySpacing, myNullSize, mySymSize); + myNbSymbols, mySpacing, myNullSize, mySymSize, + m_settings.ofdmWindowOverlap); rcs.enrol(cifGuard.get()); shared_ptr cifFilter; -- cgit v1.2.3