/*
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 .
*/
#include "MemlessPoly.h"
#include "PcDebug.h"
#include "Utils.h"
#include
#include
#include
#include
#include
#include
#ifdef __SSE__
# include
#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 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(coeffile, "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 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 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(dataIn->getData());
float* out = reinterpret_cast(dataOut->getData());
size_t sizeIn = dataIn->getLength() / sizeof(float);
{
std::lock_guard 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] * (
m_taps[0] +
m_taps[1] * mag +
m_taps[2] * mag*mag +
m_taps[3] * mag*mag*mag +
m_taps[4] * mag*mag*mag*mag +
m_taps[5] * mag*mag*mag*mag*mag +
m_taps[6] * mag*mag*mag*mag*mag*mag +
m_taps[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 == "coeffile") {
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();
}