summaryrefslogtreecommitdiffstats
path: root/src/MemlessPoly.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/MemlessPoly.cpp')
-rw-r--r--src/MemlessPoly.cpp202
1 files changed, 202 insertions, 0 deletions
diff --git a/src/MemlessPoly.cpp b/src/MemlessPoly.cpp
new file mode 100644
index 0000000..a34dde1
--- /dev/null
+++ b/src/MemlessPoly.cpp
@@ -0,0 +1,202 @@
+/*
+ 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 block implements a FIR filter. The real filter taps are given
+ as floats, and the block can take advantage of SSE.
+ For better performance, filtering 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>
+
+#ifdef __SSE__
+# include <xmmintrin.h>
+#endif
+
+using namespace std;
+
+/* This is the FIR Filter calculated with the doc/fir-filter/generate-filter.py script
+ * with settings
+ * gain = 1
+ * sampling_freq = 2.048e6
+ * cutoff = 810e3
+ * transition_width = 250e3
+ *
+ * It is a good default filter for the common scenarios.
+ */
+
+ //0.8, -0.2, 0.2, 0.25,
+static const std::array<float, 8> default_coefficients({
+ 0.1, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 0.0
+ });
+
+
+MemlessPoly::MemlessPoly(const std::string& taps_file) :
+ PipelinedModCodec(),
+ RemoteControllable("memlesspoly"),
+ m_taps_file(taps_file)
+{
+ PDEBUG("MemlessPoly::MemlessPoly(%s) @ %p\n",
+ taps_file.c_str(), this);
+
+ RC_ADD_PARAMETER(ntaps, "(Read-only) number of filter taps.");
+ RC_ADD_PARAMETER(tapsfile, "Filename containing filter taps. When written to, the new file gets automatically loaded.");
+
+ load_filter_taps(m_taps_file);
+
+ start_pipeline_thread();
+}
+
+void MemlessPoly::load_filter_taps(const std::string &tapsFile)
+{
+ std::vector<float> filter_taps;
+ if (tapsFile == "default") {
+ std::copy(default_coefficients.begin(), default_coefficients.end(),
+ std::back_inserter(filter_taps));
+ }
+ else {
+ std::ifstream taps_fstream(tapsFile.c_str());
+ if(!taps_fstream) {
+ fprintf(stderr, "MemlessPoly: file %s could not be opened !\n", tapsFile.c_str());
+ throw std::runtime_error("MemlessPoly: Could not open file with taps! ");
+ }
+ int n_taps;
+ taps_fstream >> n_taps;
+
+ if (n_taps <= 0) {
+ fprintf(stderr, "MemlessPoly: warning: taps file has invalid format\n");
+ throw std::runtime_error("MemlessPoly: taps file has invalid format.");
+ }
+
+ if (n_taps > 100) {
+ fprintf(stderr, "MemlessPoly: warning: taps file has more than 100 taps\n");
+ }
+
+ fprintf(stderr, "MemlessPoly: Reading %d taps...\n", n_taps);
+
+ filter_taps.resize(n_taps);
+
+ int n;
+ for (n = 0; n < n_taps; n++) {
+ taps_fstream >> filter_taps[n];
+ PDEBUG("MemlessPoly: tap: %f\n", filter_taps[n] );
+ if (taps_fstream.eof()) {
+ fprintf(stderr, "MemlessPoly: file %s should contains %d taps, but EOF reached "\
+ "after %d taps !\n", tapsFile.c_str(), n_taps, n);
+ throw std::runtime_error("MemlessPoly: filtertaps file invalid ! ");
+ }
+ }
+ }
+
+ {
+ std::lock_guard<std::mutex> lock(m_taps_mutex);
+
+ m_taps = filter_taps;
+ }
+}
+
+
+int MemlessPoly::internal_process(Buffer* const dataIn, Buffer* dataOut)
+{
+ size_t i;
+
+ 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_taps_mutex);
+ for (i = 0; i < sizeIn; i += 1) {
+ float mag = std::abs(in[i]);
+ //out[i] = in[i];
+ out[i] = in[i] * (
+ default_coefficients[0] +
+ default_coefficients[1] * mag +
+ default_coefficients[2] * mag*mag +
+ default_coefficients[3] * mag*mag*mag +
+ default_coefficients[4] * mag*mag*mag*mag +
+ default_coefficients[5] * mag*mag*mag*mag*mag +
+ default_coefficients[6] * mag*mag*mag*mag*mag*mag +
+ default_coefficients[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 == "ntaps") {
+ throw ParameterError("Parameter 'ntaps' is read-only");
+ }
+ else if (parameter == "tapsfile") {
+ try {
+ load_filter_taps(value);
+ m_taps_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 == "ntaps") {
+ ss << m_taps.size();
+ }
+ else if (parameter == "tapsfile") {
+ ss << m_taps_file;
+ }
+ else {
+ ss << "Parameter '" << parameter <<
+ "' is not exported by controllable " << get_rc_name();
+ throw ParameterError(ss.str());
+ }
+ return ss.str();
+}
+