aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--doc/example.ini15
-rw-r--r--src/ConfigParser.cpp6
-rw-r--r--src/ConfigParser.h2
-rw-r--r--src/DabMod.cpp6
-rw-r--r--src/DabModulator.cpp66
-rw-r--r--src/DabModulator.h4
-rw-r--r--src/MemlessPoly.cpp187
-rw-r--r--src/MemlessPoly.h78
9 files changed, 343 insertions, 23 deletions
diff --git a/Makefile.am b/Makefile.am
index f4e8e00..d4365fd 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -62,6 +62,8 @@ odr_dabmod_SOURCES = src/DabMod.cpp \
src/FicSource.h \
src/FIRFilter.cpp \
src/FIRFilter.h \
+ src/MemlessPoly.cpp \
+ src/MemlessPoly.h \
src/PuncturingRule.cpp \
src/PuncturingRule.h \
src/PuncturingEncoder.cpp \
diff --git a/doc/example.ini b/doc/example.ini
index f8cec36..ffb20d7 100644
--- a/doc/example.ini
+++ b/doc/example.ini
@@ -135,6 +135,21 @@ enabled=1
; If filtertapsfile is not given, the default taps are used.
;filtertapsfile=simple_taps.txt
+[poly]
+;Predistortion using memoryless polynom
+enabled=1
+polycoeffile=polyCoefs
+;eg:
+;echo "8
+;0.1
+;0
+;0
+;0
+;0
+;0
+;0
+;0" > polyCoefs
+
[output]
; choose output: possible values: uhd, file, zmq, soapysdr
output=uhd
diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp
index 393f58a..b7649df 100644
--- a/src/ConfigParser.cpp
+++ b/src/ConfigParser.cpp
@@ -168,6 +168,12 @@ static void parse_configfile(
pt.get<std::string>("firfilter.filtertapsfile", "default");
}
+ // Poly coefficients:
+ if (pt.get("poly.enabled", 0) == 1) {
+ mod_settings.polyCoefFilename =
+ pt.get<std::string>("poly.polycoeffile", "default");
+ }
+
// Output options
std::string output_selected;
try {
diff --git a/src/ConfigParser.h b/src/ConfigParser.h
index 02b798a..fede801 100644
--- a/src/ConfigParser.h
+++ b/src/ConfigParser.h
@@ -74,6 +74,8 @@ struct mod_settings_t {
std::string filterTapsFilename = "";
+ std::string polyCoefFilename = "";
+
#if defined(HAVE_OUTPUT_UHD)
OutputUHDConfig outputuhd_conf;
diff --git a/src/DabMod.cpp b/src/DabMod.cpp
index 4e4cdab..adc4cf2 100644
--- a/src/DabMod.cpp
+++ b/src/DabMod.cpp
@@ -322,7 +322,8 @@ int launch_modulator(int argc, char* argv[])
mod_settings.digitalgain,
mod_settings.normalise,
mod_settings.gainmodeVariance,
- mod_settings.filterTapsFilename);
+ mod_settings.filterTapsFilename,
+ mod_settings.polyCoefFilename);
if (format_converter) {
flowgraph.connect(modulator, format_converter);
@@ -426,7 +427,8 @@ int launch_modulator(int argc, char* argv[])
mod_settings.digitalgain,
mod_settings.normalise,
mod_settings.gainmodeVariance,
- mod_settings.filterTapsFilename);
+ mod_settings.filterTapsFilename,
+ mod_settings.polyCoefFilename);
if (format_converter) {
flowgraph.connect(modulator, format_converter);
diff --git a/src/DabModulator.cpp b/src/DabModulator.cpp
index c41b8fc..8e3af8a 100644
--- a/src/DabModulator.cpp
+++ b/src/DabModulator.cpp
@@ -47,6 +47,7 @@
#include "Resampler.h"
#include "ConvEncoder.h"
#include "FIRFilter.h"
+#include "MemlessPoly.h"
#include "TII.h"
#include "PuncturingEncoder.h"
#include "TimeInterleaver.h"
@@ -61,7 +62,8 @@ DabModulator::DabModulator(
unsigned dabMode, GainMode gainMode,
float& digGain, float normalise,
float gainmodeVariance,
- const std::string& filterTapsFilename
+ const std::string& filterTapsFilename,
+ const std::string& polyCoefFilename
) :
ModInput(),
myOutputRate(outputRate),
@@ -74,6 +76,7 @@ DabModulator::DabModulator(
myEtiSource(etiSource),
myFlowgraph(NULL),
myFilterTapsFilename(filterTapsFilename),
+ myPolyCoefFilename(polyCoefFilename),
myTiiConfig(tiiConfig)
{
PDEBUG("DabModulator::DabModulator(%u, %u, %u, %zu) @ %p\n",
@@ -203,7 +206,8 @@ int DabModulator::process(Buffer* dataOut)
(1 + myNbSymbols), myNbCarriers, mySpacing);
auto cifGain = make_shared<GainControl>(
- mySpacing, myGainMode, myDigGain, myNormalise, myGainmodeVariance);
+ mySpacing, myGainMode, myDigGain, myNormalise,
+ myGainmodeVariance);
rcs.enrol(cifGain.get());
@@ -215,6 +219,19 @@ int DabModulator::process(Buffer* dataOut)
cifFilter = make_shared<FIRFilter>(myFilterTapsFilename);
rcs.enrol(cifFilter.get());
}
+
+ shared_ptr<MemlessPoly> cifPoly;
+ if (not myPolyCoefFilename.empty()) {
+ cifPoly = make_shared<MemlessPoly>(myPolyCoefFilename);
+ etiLog.level(debug) << myPolyCoefFilename << "\n";
+ etiLog.level(debug) << cifPoly->m_coefs[0] << " " <<
+ cifPoly->m_coefs[1] << " "<< cifPoly->m_coefs[2] << " "<<
+ cifPoly->m_coefs[3] << " "<< cifPoly->m_coefs[4] << " "<<
+ cifPoly->m_coefs[5] << " "<< cifPoly->m_coefs[6] << " "<<
+ cifPoly->m_coefs[7] << "\n";
+ rcs.enrol(cifPoly.get());
+ }
+
auto myOutput = make_shared<OutputMemory>(dataOut);
shared_ptr<Resampler> cifRes;
@@ -306,7 +323,8 @@ int DabModulator::process(Buffer* dataOut)
auto subchConv = make_shared<ConvEncoder>(subchSizeIn);
// Configuring puncturing encoder
- auto subchPunc = make_shared<PuncturingEncoder>(subchannel->framesizeCu());
+ auto subchPunc =
+ make_shared<PuncturingEncoder>(subchannel->framesizeCu());
for (const auto& rule : subchannel->get_rules()) {
PDEBUG(" Adding rule:\n");
@@ -348,23 +366,31 @@ int DabModulator::process(Buffer* dataOut)
myFlowgraph->connect(cifOfdm, cifGain);
myFlowgraph->connect(cifGain, cifGuard);
- if (cifFilter) {
- myFlowgraph->connect(cifGuard, cifFilter);
- if (cifRes) {
- myFlowgraph->connect(cifFilter, cifRes);
- myFlowgraph->connect(cifRes, myOutput);
- } else {
- myFlowgraph->connect(cifFilter, myOutput);
- }
- }
- else { //no filtering
- if (cifRes) {
- myFlowgraph->connect(cifGuard, cifRes);
- myFlowgraph->connect(cifRes, myOutput);
- } else {
- myFlowgraph->connect(cifGuard, myOutput);
- }
-
+#warning "Flowgraph logic incomplete (skips FIRFilter)!"
+ //if (cifFilter) {
+ // myFlowgraph->connect(cifGuard, cifFilter);
+ // if (cifRes) {
+ // myFlowgraph->connect(cifFilter, cifRes);
+ // myFlowgraph->connect(cifRes, myOutput);
+ // } else {
+ // myFlowgraph->connect(cifFilter, myOutput);
+ // }
+ //}
+ //else { //no filtering
+ // if (cifRes) {
+ // myFlowgraph->connect(cifGuard, cifRes);
+ // myFlowgraph->connect(cifRes, myOutput);
+ // } else {
+ // myFlowgraph->connect(cifGuard, myOutput);
+ // }
+ //}
+ if (cifRes) {
+ myFlowgraph->connect(cifGuard, cifRes);
+ myFlowgraph->connect(cifRes, cifPoly);
+ myFlowgraph->connect(cifPoly, myOutput);
+ } else {
+ myFlowgraph->connect(cifGuard, cifPoly);
+ myFlowgraph->connect(cifPoly, myOutput);
}
}
diff --git a/src/DabModulator.h b/src/DabModulator.h
index c9bdbe1..0c691dd 100644
--- a/src/DabModulator.h
+++ b/src/DabModulator.h
@@ -55,7 +55,8 @@ public:
unsigned dabMode, GainMode gainMode,
float& digGain, float normalise,
float gainmodeVariance,
- const std::string& filterTapsFilename);
+ const std::string& filterTapsFilename,
+ const std::string& polyCoefFilename);
DabModulator(const DabModulator& other) = delete;
DabModulator& operator=(const DabModulator& other) = delete;
virtual ~DabModulator();
@@ -80,6 +81,7 @@ protected:
Flowgraph* myFlowgraph;
OutputMemory* myOutput;
std::string myFilterTapsFilename;
+ std::string myPolyCoefFilename;
tii_config_t& myTiiConfig;
size_t myNbSymbols;
diff --git a/src/MemlessPoly.cpp b/src/MemlessPoly.cpp
new file mode 100644
index 0000000..7e074eb
--- /dev/null
+++ b/src/MemlessPoly.cpp
@@ -0,0 +1,187 @@
+/*
+ Copyright (C) 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
+ Andreas Steger, andreas.steger@digris.ch
+
+ http://opendigitalradio.org
+
+ This block implements a memoryless polynom for digital predistortion.
+ For better performance, multiplying is done in another thread, leading
+ to a pipeline delay of two calls to MemlessPoly::process
+ */
+/*
+ This file is part of ODR-DabMod.
+
+ ODR-DabMod is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ ODR-DabMod is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ODR-DabMod. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "MemlessPoly.h"
+#include "PcDebug.h"
+#include "Utils.h"
+
+#include <stdio.h>
+#include <stdexcept>
+
+#include <array>
+#include <iostream>
+#include <fstream>
+#include <memory>
+
+using namespace std;
+
+
+// By default the signal is unchanged
+static const std::array<float, 8> default_coefficients({
+ 1, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 0.0
+ });
+
+
+MemlessPoly::MemlessPoly(const std::string& coefs_file) :
+ PipelinedModCodec(),
+ RemoteControllable("memlesspoly"),
+ m_coefs_file(coefs_file)
+{
+ PDEBUG("MemlessPoly::MemlessPoly(%s) @ %p\n",
+ coefs_file.c_str(), this);
+
+ RC_ADD_PARAMETER(ncoefs, "(Read-only) number of coefficients.");
+ RC_ADD_PARAMETER(coeffile, "Filename containing coefficients. When written to, the new file gets automatically loaded.");
+
+ load_coefficients(m_coefs_file);
+
+ start_pipeline_thread();
+}
+
+void MemlessPoly::load_coefficients(const std::string &coefFile)
+{
+ std::vector<float> coefs;
+ if (coefFile == "default") {
+ std::copy(default_coefficients.begin(), default_coefficients.end(),
+ std::back_inserter(coefs));
+ }
+ else {
+ std::ifstream coef_fstream(coefFile.c_str());
+ if(!coef_fstream) {
+ fprintf(stderr, "MemlessPoly: file %s could not be opened !\n", coefFile.c_str());
+ throw std::runtime_error("MemlessPoly: Could not open file with coefs! ");
+ }
+ int n_coefs;
+ coef_fstream >> n_coefs;
+
+ if (n_coefs <= 0) {
+ fprintf(stderr, "MemlessPoly: warning: coefs file has invalid format\n");
+ throw std::runtime_error("MemlessPoly: coefs file has invalid format.");
+ }
+
+ if (n_coefs != 8) {
+ throw std::runtime_error( "MemlessPoly: error: coefs file does not have 8 coefs\n");
+ }
+
+ fprintf(stderr, "MemlessPoly: Reading %d coefs...\n", n_coefs);
+
+ coefs.resize(n_coefs);
+
+ int n;
+ for (n = 0; n < n_coefs; n++) {
+ coef_fstream >> coefs[n];
+ PDEBUG("MemlessPoly: coef: %f\n", coefs[n] );
+ if (coef_fstream.eof()) {
+ fprintf(stderr, "MemlessPoly: file %s should contains %d coefs, but EOF reached "\
+ "after %d coefs !\n", coefFile.c_str(), n_coefs, n);
+ throw std::runtime_error("MemlessPoly: coefs file invalid ! ");
+ }
+ }
+ }
+
+ {
+ std::lock_guard<std::mutex> lock(m_coefs_mutex);
+
+ m_coefs = coefs;
+ }
+}
+
+
+int MemlessPoly::internal_process(Buffer* const dataIn, Buffer* dataOut)
+{
+ const float* in = reinterpret_cast<const float*>(dataIn->getData());
+ float* out = reinterpret_cast<float*>(dataOut->getData());
+ size_t sizeIn = dataIn->getLength() / sizeof(float);
+
+ {
+ std::lock_guard<std::mutex> lock(m_coefs_mutex);
+ for (size_t i = 0; i < sizeIn; i += 1) {
+ float mag = std::abs(in[i]);
+ //out[i] = in[i];
+ out[i] = in[i] * (
+ m_coefs[0] +
+ m_coefs[1] * mag +
+ m_coefs[2] * mag*mag +
+ m_coefs[3] * mag*mag*mag +
+ m_coefs[4] * mag*mag*mag*mag +
+ m_coefs[5] * mag*mag*mag*mag*mag +
+ m_coefs[6] * mag*mag*mag*mag*mag*mag +
+ m_coefs[7] * mag*mag*mag*mag*mag*mag*mag
+ );
+ }
+ }
+
+ return dataOut->getLength();
+}
+
+void MemlessPoly::set_parameter(const string& parameter, const string& value)
+{
+ stringstream ss(value);
+ ss.exceptions ( stringstream::failbit | stringstream::badbit );
+
+ if (parameter == "ncoefs") {
+ throw ParameterError("Parameter 'ncoefs' is read-only");
+ }
+ else if (parameter == "coeffile") {
+ try {
+ load_coefficients(value);
+ m_coefs_file = value;
+ }
+ catch (std::runtime_error &e) {
+ throw ParameterError(e.what());
+ }
+ }
+ else {
+ stringstream ss;
+ ss << "Parameter '" << parameter <<
+ "' is not exported by controllable " << get_rc_name();
+ throw ParameterError(ss.str());
+ }
+}
+
+const string MemlessPoly::get_parameter(const string& parameter) const
+{
+ stringstream ss;
+ if (parameter == "ncoefs") {
+ ss << m_coefs.size();
+ }
+ else if (parameter == "coefFile") {
+ ss << m_coefs_file;
+ }
+ else {
+ ss << "Parameter '" << parameter <<
+ "' is not exported by controllable " << get_rc_name();
+ throw ParameterError(ss.str());
+ }
+ return ss.str();
+}
+
diff --git a/src/MemlessPoly.h b/src/MemlessPoly.h
new file mode 100644
index 0000000..210b4b4
--- /dev/null
+++ b/src/MemlessPoly.h
@@ -0,0 +1,78 @@
+/*
+ Copyright (C) 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.
+
+ ODR-DabMod is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ ODR-DabMod is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ODR-DabMod. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+
+#include "RemoteControl.h"
+#include "ModPlugin.h"
+#include "PcDebug.h"
+#include "ThreadsafeQueue.h"
+
+#include <sys/types.h>
+#include <complex>
+#include <thread>
+#include <vector>
+#include <time.h>
+#include <cstdio>
+#include <string>
+#include <memory>
+
+#define MEMLESSPOLY_PIPELINE_DELAY 1
+
+typedef std::complex<float> complexf;
+
+class MemlessPoly : public PipelinedModCodec, public RemoteControllable
+{
+public:
+ MemlessPoly(const std::string& coefs_file);
+
+ virtual const char* name() { return "MemlessPoly"; }
+
+ /******* 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;
+
+//TODO to protected
+ std::vector<float> m_coefs;
+
+
+protected:
+ int internal_process(Buffer* const dataIn, Buffer* dataOut);
+ void load_coefficients(const std::string &coefFile);
+
+ std::string m_coefs_file;
+
+ mutable std::mutex m_coefs_mutex;
+};
+