Program Listing for File filter.cpp¶
↰ Return to documentation for file (lib/dsp/filter.cpp
)
// ---------------------------------------------------------------------
// This file is part of falcon-core.
//
// Copyright (C) 2015, 2016, 2017 Neuro-Electronics Research Flanders
//
// Falcon-server 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.
//
// Falcon-server 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 falcon-core. If not, see <http://www.gnu.org/licenses/>.
// ---------------------------------------------------------------------
#include "filter.hpp"
using namespace dsp::filter;
IFilter::~IFilter() { unrealize(); }
std::string IFilter::description() const { return description_; }
unsigned int IFilter::nchannels() const {
if (!realized_) {
throw std::runtime_error("Filter has not been realized yet.");
}
return nchannels_;
}
bool IFilter::realized() const { return realized_; }
void IFilter::realize(unsigned int nchannels, double init) {
if (nchannels == 0) {
throw std::runtime_error("Number of channels cannot be 0.");
}
unrealize();
if (!realize_filter(nchannels, init)) {
throw std::runtime_error("Unable to realize filter.");
}
nchannels_ = nchannels;
realized_ = true;
}
void IFilter::unrealize() {
if (realized()) {
unrealize_filter();
realized_ = false;
nchannels_ = 0;
}
}
std::map<std::string, std::string>
dsp::filter::parse_file_header(std::istream &stream) {
std::string expr("#\\s+([\\w\\s]*\\w)\\s*=\\s*([\\-\\w\\s,:\\.]*\\w)\\s*");
std::regex re(expr);
std::smatch match;
// create minimal header
std::map<std::string, std::string> header;
header["filter type"] = "unknown";
header["description"] = "";
header["format"] = "unknown";
bool done = false;
std::string line;
// file has to start with ##
int c;
c = stream.peek();
if (c == EOF || c != '#') {
throw std::runtime_error("No valid header found.");
}
std::getline(stream, line);
if (line.compare(0, 2, "##") != 0) {
throw std::runtime_error(
"No valid header found. Missing starting sequence \"##\".");
}
while (!done) {
std::getline(stream, line);
if (line.compare(0, 1, "#") != 0) {
throw std::runtime_error("Invalid line in header (missing \"#\" "
"character at beginning of line?)");
} else if (line.compare(0, 2, "##") == 0) {
// final line of header
done = true;
} else {
if (std::regex_match(line, match, re)) {
header[match[1].str()] = match[2].str();
}
// skip non matches
// can contain arbitrary comments for documentation purposes
}
}
return header;
}
IFilter *dsp::filter::construct_from_file(std::string file) {
bool binary = false;
std::ifstream stream(file);
if (!stream.good()) {
throw std::runtime_error("Cannot open filter definition file.");
}
auto header = parse_file_header(stream);
if (header["format"] == "binary") {
binary = true;
} else if (header["format"] != "text") {
throw std::runtime_error("Unknown filter file format.");
}
if (header["type"] == "fir") {
return FirFilter::FromStream(stream, header["description"], binary);
} else if (header["type"] == "slope") {
return SlopeFilter::FromStream(stream, header["description"], binary);
} else if (header["type"] == "biquad") {
return BiquadFilter::FromStream(stream, header["description"], binary);
} else {
throw std::runtime_error("Unknown filter type in file.");
}
}
IFilter *dsp::filter::construct_from_yaml(const YAML::Node &node) {
// type: fir OR biquad OR slope
// gain: double (biquad only)
// coefficients: list of doubles (fir) or list of lists of 6 doubles (biquad)
// window size : 1 uint (slope filter only)
// order : 1 uint (slope filter only)
// derivative order: 1 uint (slope filter only)
// description: text
if (node["file"]) {
return construct_from_file(node["file"].as<std::string>());
}
std::string filter_type = node["type"].as<std::string>("unknown");
std::string desc = node["description"].as<std::string>("");
if (filter_type == "fir") {
std::vector<double> coef = node["coefficients"].as<std::vector<double>>();
return new FirFilter(coef, desc);
} else if(filter_type == "slope"){
uint32_t window_size = node["windows size"].as<unsigned int>(SlopeFilter::DEFAULT_WINDOW_SIZE);
uint8_t derivative_order = node["derivative order"].as<unsigned int>(SlopeFilter::DEFAULT_ORDER);
uint8_t order = node["order"].as<unsigned int>(SlopeFilter::DEFAULT_DERIVATIVE_ORDER);
return new SlopeFilter(window_size, order, derivative_order, desc);
} else if (filter_type == "biquad") {
double gain = node["gain"].as<double>();
std::vector<std::array<double, 6>> coef =
node["coefficients"].as<std::vector<std::array<double, 6>>>();
// std::vector<std::vector<double>> data =
// node["coefficients"].as<std::vector<std::vector<double>>>();
return new BiquadFilter(gain, coef, desc);
} else {
throw std::runtime_error("Unknown filter type in YAML.");
}
}
FirFilter::FirFilter(const std::vector<double> &coefficients, std::string description)
: IFilter(description), coefficients_(coefficients) {
ntaps_ = coefficients_.size();
if (ntaps_ < 1) {
throw std::runtime_error("Invalid number of filter taps.");
}
pcoefficients_ = coefficients_.data();
}
IFilter *FirFilter::clone() {
return new FirFilter(coefficients_, description_);
}
FirFilter *FirFilter::FromStream(std::istream &stream, std::string description,
bool binary) {
if (binary) {
throw std::runtime_error("Binary filter files are not yet supported.");
}
std::vector<double> coefficients;
double coef;
while (stream >> coef) {
coefficients.push_back(coef);
}
return new FirFilter(coefficients, description);
}
unsigned int FirFilter::order() const { return ntaps_ - 1; }
std::size_t FirFilter::group_delay() const { return order() / 2; }
double FirFilter::process_channel(double input, unsigned int channel) {
std::rotate(registers_[channel].rbegin(), registers_[channel].rbegin() + 1,
registers_[channel].rend());
registers_[channel][0] = input;
double result = 0;
for (unsigned int k = 0; k < ntaps_; ++k) {
result += coefficients_[k] * registers_[channel][k];
}
return result;
}
void FirFilter::process_sample(std::vector<double> &input,
std::vector<double> &output) {
double result;
for (unsigned int channel = 0; channel < nchannels_; ++channel) {
std::rotate(registers_[channel].rbegin(), registers_[channel].rbegin() + 1,
registers_[channel].rend());
registers_[channel][0] = input[0];
result = 0;
for (unsigned int k = 0; k < ntaps_; ++k) {
result += coefficients_[k] * registers_[channel][k];
}
output[channel] = result;
}
}
void FirFilter::process_sample(std::vector<double>::iterator input,
std::vector<double>::iterator output) {
double result;
for (unsigned int channel = 0; channel < nchannels_; ++channel) {
std::rotate(registers_[channel].rbegin(), registers_[channel].rbegin() + 1,
registers_[channel].rend());
registers_[channel][0] = *input++;
result = 0;
for (unsigned int k = 0; k < ntaps_; ++k) {
result += coefficients_[k] * registers_[channel][k];
}
*output = result;
output++;
}
}
void FirFilter::process_sample(double *input, double *output) {
// skip check if filter has been realized??
double result;
double *coef, *reg;
for (unsigned int channel = 0; channel < nchannels_; ++channel) {
reg = pregisters_[channel];
memmove((void *)(reg + 1), (void *)reg, (ntaps_ - 1) * sizeof(double));
*reg = *input;
coef = pcoefficients_;
result = 0;
for (unsigned int k = 0; k < ntaps_; ++k) {
result += *coef++ * *reg++;
}
*output = result;
input++; // move to next channel
output++; // move to next channel
}
}
void FirFilter::process_channel(std::vector<double> &input,
std::vector<double> &output,
unsigned int channel) {
double result;
uint64_t nsamples = input.size();
// check output.size() == input.size()
for (uint64_t s = 0; s < nsamples; ++s) {
std::rotate(registers_[channel].rbegin(), registers_[channel].rbegin() + 1,
registers_[channel].rend());
registers_[channel][0] = input[s];
result = 0;
for (unsigned int k = 0; k < ntaps_; ++k) {
result += coefficients_[k] * registers_[channel][k];
}
output[s] = result;
}
}
void FirFilter::process_channel(uint64_t nsamples,
std::vector<double>::iterator input,
std::vector<double>::iterator output,
unsigned int channel) {
double result;
for (uint64_t s = 0; s < nsamples; ++s) {
std::rotate(registers_[channel].rbegin(), registers_[channel].rbegin() + 1,
registers_[channel].rend());
registers_[channel][0] = *input++;
result = 0;
for (unsigned int k = 0; k < ntaps_; ++k) {
result += coefficients_[k] * registers_[channel][k];
}
*output = result;
output++;
}
}
void FirFilter::process_channel(uint64_t nsamples, double *input,
double *output, unsigned int channel) {
throw std::runtime_error("Not yet implemented.");
}
void FirFilter::process_by_channel(std::vector<std::vector<double>> &input,
std::vector<std::vector<double>> &output) {
uint64_t nsamples = input.size();
for (uint64_t s = 0; s < nsamples; ++s) {
process_sample(input[s], output[s]);
}
}
void FirFilter::process_by_sample(std::vector<std::vector<double>> &input,
std::vector<std::vector<double>> &output) {
for (unsigned int c = 0; c < nchannels_; ++c) {
process_channel(input[c], output[c]);
}
}
void FirFilter::process_by_channel(uint64_t nsamples, double **input,
double **output) {
for (uint64_t s = 0; s < nsamples; ++s) {
process_sample(input[s], output[s]);
}
}
void FirFilter::process_by_sample(uint64_t nsamples, double **input,
double **output) {
for (unsigned int c = 0; c < nchannels_; ++c) {
process_channel(nsamples, input[c], output[c]);
}
}
void FirFilter::process_by_channel(uint64_t nsamples,
std::vector<double> &input,
std::vector<double> &output) {
assert(nsamples * nchannels_ == input.size() &&
input.size() == output.size());
auto in_it = input.begin();
auto out_it = output.begin();
for (unsigned int s = 0; s < nsamples; ++s) {
process_sample(in_it, out_it);
in_it += nchannels_;
out_it += nchannels_;
}
}
void FirFilter::process_by_sample(uint64_t nsamples, std::vector<double> &input,
std::vector<double> &output) {
assert(nsamples * nchannels_ == input.size() &&
input.size() == output.size());
auto in_it = input.begin();
auto out_it = output.begin();
for (unsigned int c = 0; c < nchannels_; ++c) {
process_channel(nsamples, in_it, out_it, c);
in_it += nsamples;
out_it += nsamples;
}
}
bool FirFilter::realize_filter(unsigned int nchannels, double init) {
// create and initialize register for each channel
for (unsigned int k = 0; k < nchannels; ++k) {
registers_.push_back(std::vector<double>(ntaps_, init));
pregisters_.push_back(registers_.back().data());
}
return true;
}
void FirFilter::unrealize_filter() {
pregisters_.clear();
registers_.clear();
}
SlopeFilter *SlopeFilter::FromStream(std::istream &stream,
std::string description,
bool binary){
if (binary) {
throw std::runtime_error("Binary filter files are not yet supported.");
}
uint32_t window_size, order, derivative_order;
stream >> window_size;
stream >> order;
stream >> derivative_order;
return new SlopeFilter(window_size, order, derivative_order, description);
};
BiquadFilter::BiquadFilter(double gain,
std::vector<std::array<double, 6>> &coefficients,
std::string description)
: IFilter(description), gain_(gain), coefficients_(coefficients) {
nstages_ = coefficients_.size();
if (nstages_ < 1) {
throw std::runtime_error("Invalid number of biquad stages.");
}
}
IFilter *BiquadFilter::clone() {
return new BiquadFilter(gain_, coefficients_, description_);
}
BiquadFilter *BiquadFilter::FromStream(std::istream &stream,
std::string description,
bool binary = false) {
if (binary) {
throw std::runtime_error("Binary filter files are not yet supported.");
}
double gain;
if (!(stream >> gain)) {
throw std::runtime_error("No filter coefficients found.");
}
// read all data
std::vector<double> data;
double value;
while (stream >> value) {
data.push_back(value);
}
// determine number of biquad stages
if (data.size() % 6 != 0) {
throw std::runtime_error(
"Each biquad stage requires exactly 6 coefficients");
}
unsigned int nstages = data.size() / 6;
std::vector<std::array<double, 6>> coefficients(nstages);
auto data_it = data.begin();
for (unsigned int stage = 0; stage < nstages; ++stage) {
for (unsigned int coef = 0; coef < 6; ++coef) {
coefficients[stage][coef] = (*data_it++);
}
}
return new BiquadFilter(gain, coefficients, description);
}
unsigned int BiquadFilter::order() const { return nstages_ * 2; }
double BiquadFilter::process_channel(double x, unsigned int c) {
double u_n, y_n = 0.0;
for (unsigned int s = 0; s < nstages_; ++s) {
u_n = x - coefficients_[s][4] * registers_[c][s][0] -
coefficients_[s][5] * registers_[c][s][1];
y_n = coefficients_[s][0] * u_n +
coefficients_[s][1] * registers_[c][s][0] +
coefficients_[s][2] * registers_[c][s][1];
registers_[c][s][1] = registers_[c][s][0];
registers_[c][s][0] = u_n;
x = y_n;
}
return y_n * gain_;
}
void BiquadFilter::process_sample(std::vector<double>::iterator input,
std::vector<double>::iterator output) {
if (!realized_) {
throw std::runtime_error("Filter has not been realized yet.");
}
for (unsigned int c = 0; c < nchannels_; ++c) {
*output = process_channel((*input++), c);
output++;
}
}
void BiquadFilter::process_sample(double *input, double *output) {
if (!realized_) {
throw std::runtime_error("Filter has not been realized yet.");
}
for (unsigned int c = 0; c < nchannels_; ++c) {
*output = process_channel((*input++), c);
output++;
}
}
void BiquadFilter::process_channel(std::vector<double> &input,
std::vector<double> &output,
unsigned int channel) {
uint64_t nsamples = input.size();
for (uint64_t s = 0; s < nsamples; ++s) {
output[s] = process_channel(input[s], channel);
}
}
void BiquadFilter::process_channel(uint64_t nsamples,
std::vector<double>::iterator input,
std::vector<double>::iterator output,
unsigned int channel) {
for (uint64_t s = 0; s < nsamples; ++s) {
*output = process_channel((*input++), channel);
output++;
}
}
void BiquadFilter::process_channel(uint64_t nsamples, double *input,
double *output, unsigned int channel) {
for (uint64_t s = 0; s < nsamples; ++s) {
output[s] = process_channel(input[s], channel);
}
}
void BiquadFilter::process_by_channel(
std::vector<std::vector<double>> &input,
std::vector<std::vector<double>> &output) {
uint64_t nsamples = input.size();
for (uint64_t s = 0; s < nsamples; ++s) {
process_sample(input[s], output[s]);
}
}
void BiquadFilter::process_by_sample(std::vector<std::vector<double>> &input,
std::vector<std::vector<double>> &output) {
for (unsigned int c = 0; c < nchannels_; ++c) {
process_channel(input[c], output[c]);
}
}
void BiquadFilter::process_by_channel(uint64_t nsamples, double **input,
double **output) {
for (uint64_t s = 0; s < nsamples; ++s) {
process_sample(input[s], output[s]);
}
}
void BiquadFilter::process_by_sample(uint64_t nsamples, double **input,
double **output) {
for (unsigned int c = 0; c < nchannels_; ++c) {
process_channel(nsamples, input[c], output[c], c);
}
}
void BiquadFilter::process_by_channel(uint64_t nsamples,
std::vector<double> &input,
std::vector<double> &output) {
assert(nsamples * nchannels_ == input.size() &&
input.size() == output.size());
auto in_it = input.begin();
auto out_it = output.begin();
for (unsigned int s = 0; s < nsamples; ++s) {
process_sample(in_it, out_it);
in_it += nchannels_;
out_it += nchannels_;
}
}
void BiquadFilter::process_by_sample(uint64_t nsamples,
std::vector<double> &input,
std::vector<double> &output) {
assert(nsamples * nchannels_ == input.size() &&
input.size() == output.size());
auto in_it = input.begin();
auto out_it = output.begin();
for (unsigned int c = 0; c < nchannels_; ++c) {
process_channel(nsamples, in_it, out_it, c);
in_it += nsamples;
out_it += nsamples;
}
}
bool BiquadFilter::realize_filter(unsigned int nchannels, double init) {
for (unsigned int k = 0; k < nchannels; ++k) {
registers_.push_back(std::vector<std::array<double, 2>>(nstages_));
for (auto &it : registers_.back()) {
it.fill(init);
}
}
return true;
}
void BiquadFilter::unrealize_filter() { registers_.clear(); }