Program Listing for File algorithms.cpp¶
↰ Return to documentation for file (lib/dsp/algorithms.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 "algorithms.hpp"
using namespace dsp::algorithms;
double ThresholdCrosser::threshold() const { return threshold_; }
void ThresholdCrosser::set_threshold(double value) { threshold_ = value; }
Slope ThresholdCrosser::slope() const { return slope_; }
void ThresholdCrosser::set_slope(Slope value) { slope_ = value; }
bool ThresholdCrosser::has_crossed(double sample) {
if (slope_ == Slope::UP) {
return has_crossed_up(sample);
} else {
return has_crossed_down(sample);
}
}
bool ThresholdCrosser::has_crossed_up(double sample) {
bool ret = prev_sample_ <= threshold_ && sample > threshold_;
prev_sample_ = sample;
return ret;
}
bool ThresholdCrosser::has_crossed_down(double sample) {
bool ret = prev_sample_ >= threshold_ && sample < threshold_;
prev_sample_ = sample;
return ret;
}
RunningStatistics::RunningStatistics(double alpha, uint64_t burn_in,
bool outlier_protection,
double outlier_zscore,
double outlier_half_life, double center,
double dispersion)
: outlier_protection_(outlier_protection) {
set_alpha(alpha);
burn_in_counter_ = burn_in;
set_burn_in(burn_in);
set_outlier_zscore(outlier_zscore);
set_outlier_half_life(outlier_half_life);
set_center(center);
set_dispersion(dispersion);
}
double RunningStatistics::alpha() const { return alpha_; }
uint64_t RunningStatistics::burn_in() const { return burn_in_; }
double RunningStatistics::center() const { return center_; }
double RunningStatistics::dispersion() const { return dispersion_; }
bool RunningStatistics::outlier_protection() const {
return outlier_protection_;
}
double RunningStatistics::outlier_zscore() const { return outlier_zscore_; }
double RunningStatistics::outlier_half_life() const {
return outlier_half_life_;
}
bool RunningStatistics::is_burning_in() const { return burn_in_counter_ > 0; }
double RunningStatistics::zscore(double value) const {
return (value - center_) / dispersion_;
}
void RunningStatistics::set_outlier_protection(bool value) {
outlier_protection_ = value;
}
void RunningStatistics::set_outlier_zscore(double value) {
if (value <= 0) {
throw std::out_of_range("Outlier zscore should be larger than zero.");
}
outlier_zscore_ = value;
}
void RunningStatistics::set_outlier_half_life(double value) {
if (value <= 0) {
throw std::out_of_range("Outlier half life should be larger than zero.");
}
outlier_half_life_ = value;
}
void RunningStatistics::set_center(double value) { center_ = value; }
void RunningStatistics::set_dispersion(double value) { if (value < 0) {
throw std::out_of_range("Dispersion should be equal to or larger than 0.");
}
dispersion_ = value;
}
void RunningStatistics::set_alpha(double value) {
if (value < 0 || value > 1) {
throw std::out_of_range("Alpha should be in range 0-1.");
}
alpha_ = value;
}
void RunningStatistics::set_burn_in(uint64_t value) {
burn_in_ = value;
if (burn_in_counter_ > burn_in_) {
burn_in_counter_ = burn_in_;
}
}
void RunningStatistics::add_sample(double sample) {
double alpha = alpha_;
// adjust alpha during burn-in or for outliers
if (burn_in_counter_ > 0) {
--burn_in_counter_;
alpha = alpha + (1.0 - alpha) / (burn_in_ - burn_in_counter_);
} else if (outlier_protection_) {
double z = std::abs(zscore(sample));
if (z > outlier_zscore_) {
alpha = alpha * std::pow(2, (outlier_zscore_ - z) / outlier_half_life_);
}
}
update_statistics(sample, alpha);
}
void RunningStatistics::add_samples(std::vector<double> samples) {
for (auto &it : samples) {
add_sample(it);
}
}
double RunningMeanMAD::mean() const { return center_; }
double RunningMeanMAD::mad() const { return dispersion_; }
void RunningMeanMAD::update_statistics(double sample, double alpha) {
center_ = (1 - alpha) * center_ + alpha * sample;
dispersion_ = (1 - alpha) * dispersion_ + alpha * std::abs(sample - center_);
}
void PeakDetector::reset(uint64_t init_timestamp, double init_value) {
previous_value_ = init_value;
previous_timestamp_ = init_timestamp;
last_slope_is_up_ = false;
npeaks_found_ = 0;
last_peak_timestamp_ = 0;
last_peak_amplitude_ = 0.0;
}
bool PeakDetector::is_peak(const uint64_t ×tamp, const double &sample) {
double diff = sample - previous_value_;
bool peak = diff < 0 && last_slope_is_up_;
if (peak) {
++npeaks_found_;
last_peak_amplitude_ = previous_value_;
last_peak_timestamp_ = previous_timestamp_;
}
previous_value_ = sample;
previous_timestamp_ = timestamp;
if (diff != 0) {
last_slope_is_up_ = diff > 0;
}
return peak;
}
double PeakDetector::last_peak_amplitude() const {
return last_peak_amplitude_;
}
uint64_t PeakDetector::last_peak_timestamp() const {
return last_peak_timestamp_;
}
bool PeakDetector::upslope() const { return last_slope_is_up_; }
uint64_t PeakDetector::npeaks() const { return npeaks_found_; }
ExponentialSmoother::ExponentialSmoother(double alpha, double init_value)
: value_(init_value) {
set_alpha(alpha);
}
void ExponentialSmoother::set_alpha(double value) {
if (value < 0 || value > 1) {
throw std::out_of_range("Alpha should be in range 0-1.");
}
alpha_ = value;
}
double ExponentialSmoother::smooth(double value) {
value_ = alpha_ * value + (1 - alpha_) * value_;
return value_;
}
double ExponentialSmoother::alpha() const { return alpha_; }
double ExponentialSmoother::value() const { return value_; }
void ExponentialSmoother::set_value(double value) { value_ = value; }
SpikeDetector::SpikeDetector(unsigned int nchannels, double threshold,
unsigned int peak_life_time)
: nchannels_(nchannels), threshold_(threshold),
peak_life_time_(peak_life_time) {
reset();
}
bool SpikeDetector::is_spike(const uint64_t timestamp,
const std::vector<double> &sample) {
return is_spike(timestamp, sample.begin());
}
unsigned int SpikeDetector::nchannels() const { return nchannels_; }
double SpikeDetector::threshold() const { return threshold_; }
void SpikeDetector::set_threshold(double value) { threshold_ = value; }
unsigned int SpikeDetector::peak_life_time() const { return peak_life_time_; }
void SpikeDetector::set_peak_life_time(unsigned int value) {
peak_life_time_ = value;
}
uint64_t SpikeDetector::timestamp_detected_spike() const {
return spike_timestamp_;
}
const std::vector<double> &SpikeDetector::amplitudes_detected_spike() const {
return peak_amplitudes_;
}
uint64_t SpikeDetector::nspikes() const { return nspikes_found_; }
void SpikeDetector::reset() {
previous_sample_.assign(nchannels_, 0);
peak_countdown_ = 0;
slope_.assign(nchannels_, 0.0);
spike_timestamp_ = 0;
nspikes_found_ = 0;
peak_found_.assign(nchannels_, false);
peak_amplitudes_.assign(nchannels_, 0.0);
npeaks_found_ = 0;
detection_mode_ = SpikeDetectionMode::THRESHOLD;
}