Falcon
Loading...
Searching...
No Matches
algorithms.hpp
1// ---------------------------------------------------------------------
2// This file is part of falcon-core.
3//
4// Copyright (C) 2015, 2016, 2017 Neuro-Electronics Research Flanders
5//
6// Falcon-server is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10//
11// Falcon-server is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15//
16// You should have received a copy of the GNU General Public License
17// along with falcon-core. If not, see <http://www.gnu.org/licenses/>.
18// ---------------------------------------------------------------------
19
20#pragma once
21
22#include <algorithm>
23#include <cassert>
24#include <cmath>
25#include <cstdint>
26#include <iostream>
27#include <limits>
28#include <stdexcept>
29#include <vector>
30
31namespace dsp {
32namespace algorithms {
33
34enum class Slope : std::uint8_t { UP, DOWN };
35
36class ThresholdCrosser {
37 public:
38 ThresholdCrosser(double threshold, Slope slope = Slope::UP)
39 : threshold_(threshold), slope_(slope) {}
40
41 double threshold() const;
42 void set_threshold(double value);
43
44 Slope slope() const;
45 void set_slope(Slope value);
46
47 bool has_crossed(double sample);
48 bool has_crossed_up(double sample);
49 bool has_crossed_down(double sample);
50
51 private:
52 double threshold_;
53 Slope slope_;
54 double prev_sample_ = 0.0;
55};
56
57class RunningStatistics {
58 public:
59 RunningStatistics(double alpha, uint64_t burn_in = 0, bool outlier_protection = false,
60 double outlier_zscore = 3, double outlier_half_life = 1, double center = 0.0,
61 double dispersion = 0.0);
62
63 double alpha() const;
64 uint64_t burn_in() const;
65 double center() const;
66 double dispersion() const;
67
68 bool outlier_protection() const;
69 double outlier_zscore() const;
70 double outlier_half_life() const;
71
72 bool is_burning_in() const;
73
74 double zscore(double value) const;
75
76 void set_center(double value);
77 void set_dispersion(double value);
78 void set_alpha(double value);
79 void set_burn_in(uint64_t value);
80
81 void set_outlier_protection(bool value);
82 void set_outlier_zscore(double value);
83 void set_outlier_half_life(double value);
84
85 void add_sample(double sample);
86 void add_samples(std::vector<double> samples);
87
88 template <typename Iter>
89 void add_samples(Iter begin, Iter end) {
90 for (; begin != end; ++begin) {
91 add_sample(*begin);
92 }
93 }
94
95 protected:
96 double center_;
97 double dispersion_;
98
99 protected:
100 virtual void update_statistics(double sample, double alpha) = 0;
101
102 private:
103 double alpha_;
104 uint64_t burn_in_;
105 uint64_t burn_in_counter_;
106
107 bool outlier_protection_;
108 double outlier_zscore_;
109 double outlier_half_life_;
110};
111
112class RunningMeanMAD : public RunningStatistics {
113 public:
114 RunningMeanMAD(double alpha = 1.0, unsigned int burn_in = 0, bool outlier_protection = false,
115 double outlier_zscore = 3, double outlier_half_life = 1, double mean = 0.0,
116 double mad = 0.0)
117 : RunningStatistics(alpha, burn_in, outlier_protection, outlier_zscore, outlier_half_life,
118 mean, mad) {}
119
120 double mean() const;
121 double mad() const;
122
123 protected:
124 virtual void update_statistics(double sample, double alpha);
125};
126
127class PeakDetector {
128 public:
129 PeakDetector(uint64_t init_timestamp = 0, double init_value = 0.0)
130 : last_slope_is_up_(false),
131 previous_value_(init_value),
132 previous_timestamp_(init_timestamp),
133 npeaks_found_(0),
134 last_peak_amplitude_(0),
135 last_peak_timestamp_(0) {}
136
137 void reset(uint64_t init_timestamp = 0, double init_value = 0.0);
138
139 double last_peak_amplitude() const;
140 uint64_t last_peak_timestamp() const;
141
142 bool is_peak(const uint64_t& timestamp, const double& sample);
143
144 bool upslope() const;
145
146 uint64_t npeaks() const;
147
148 private:
149 bool last_slope_is_up_;
150
151 double previous_value_;
152 uint64_t previous_timestamp_;
153
154 uint64_t npeaks_found_;
155
156 double last_peak_amplitude_;
157 uint64_t last_peak_timestamp_;
158};
159
160class ExponentialSmoother {
161 public:
162 ExponentialSmoother(double alpha, double init_value = 0.0);
163
164 double smooth(double value);
165
166 double alpha() const;
167 void set_alpha(double value);
168
169 double value() const;
170 void set_value(double value);
171
172 private:
173 double alpha_;
174 double value_;
175};
176
177enum class SpikeDetectionMode : std::uint8_t { PEAK = 0, THRESHOLD };
178enum class SpikeDetectionSign : std::int8_t { UP = 1, DOWN = -1 };
179
180class SpikeDetector {
181 public:
182 SpikeDetector(unsigned int nchannels, double threshold, unsigned int peak_life_time,
183 SpikeDetectionSign sign = SpikeDetectionSign::UP);
184
185 ~SpikeDetector() {}
186
187 void reset();
188
189 unsigned int nchannels() const;
190
191 double threshold() const;
192 void set_threshold(double value);
193
194 unsigned int peak_life_time() const;
195 void set_peak_life_time(unsigned int value);
196
197 uint64_t timestamp_detected_spike() const;
198 const std::vector<double>& amplitudes_detected_spike() const;
199
218 template <typename ForwardIterator>
219 bool is_spike(const uint64_t timestamp, const ForwardIterator sample) {
220 unsigned int c;
221 auto spike_found = false;
222 auto it = sample;
223 double current_sample;
224 double previous_sample;
225
226 if (detection_mode_ == SpikeDetectionMode::THRESHOLD) {
227 // is threshold crossed on any of the channels?
228 for (c = 0; c < nchannels_; ++c) {
229 current_sample = int(sign_) * (*it);
230 previous_sample = int(sign_) * previous_sample_[c];
231
232 if (previous_sample <= threshold_ && current_sample > threshold_) {
233 detection_mode_ = SpikeDetectionMode::PEAK;
234 prepare_peak_detection(timestamp, sample);
235 break;
236 }
237 ++it;
238 }
239 } else if (detection_mode_ == SpikeDetectionMode::PEAK) {
240 // look for peaks
241 for (c = 0; c < nchannels_; ++c) {
242 current_sample = int(sign_) * (*it);
243 previous_sample = int(sign_) * previous_sample_[c];
244
245 if (!peak_found_[c]) {
246 if (int(sign_) * slope_[c] > 0 && current_sample < previous_sample) {
247 peak_found_[c] = true;
248 ++npeaks_found_;
249 peak_amplitudes_[c] = previous_sample_[c];
250 }
251 }
252 ++it;
253 }
254
255 --peak_countdown_;
256
257 if (peak_countdown_ == 0 || npeaks_found_ == nchannels_) {
258 // QUESTION: should we wait until peak_life_time window is over,
259 // or restart threshold detection as soon as a peak was found on
260 // all channels
261
262 // set spike amplitude of channels without peak
263 // QUESTION: should we only accepts spikes with peaks in all
264 // channels? QUESTION: if not, what amplitude should we assign
265 // to the channels without peak (e.g. some invalid value, or
266 // current sample)
267 if (npeaks_found_ > 0) { // spike found!!
268 ++nspikes_found_;
269 spike_found = true;
270 }
271 detection_mode_ = SpikeDetectionMode::THRESHOLD;
272 } else {
273 update_slope(sample);
274 }
275 }
276
277 std::copy_n(sample, nchannels_, previous_sample_.begin());
278
279 return spike_found;
280 }
281
282 bool is_spike(const uint64_t timestamp, const std::vector<double>& sample);
283
284 uint64_t nspikes() const;
285
286 private:
287 template <typename ForwardIterator>
288 void update_slope(ForwardIterator sample) {
289 for (decltype(nchannels_) channel = 0; channel < nchannels_; ++channel) {
290 if (previous_sample_[channel] != *sample) { // deal with plateaus
291 slope_[channel] = *sample - previous_sample_[channel];
292 }
293 ++sample;
294 }
295 }
296
297 template <typename ForwardIterator>
298 void prepare_peak_detection(const uint64_t timestamp, const ForwardIterator sample) {
299 spike_timestamp_ = timestamp;
300 peak_countdown_ = peak_life_time_;
301 npeaks_found_ = 0;
302 peak_found_.assign(nchannels_, false);
303 peak_amplitudes_ = previous_sample_;
304 update_slope(sample);
305 }
306
307 private:
308 unsigned int nchannels_;
309 double threshold_;
310 unsigned int peak_life_time_;
311 uint64_t nspikes_found_;
312
313 private:
314 SpikeDetectionMode detection_mode_;
315 std::vector<double> previous_sample_;
316 uint64_t spike_timestamp_;
317 std::vector<double> slope_;
318 unsigned int peak_countdown_;
319 std::vector<bool> peak_found_;
320 unsigned int npeaks_found_;
321 std::vector<double> peak_amplitudes_;
322 SpikeDetectionSign sign_;
323};
324
325} // namespace algorithms
326} // namespace dsp
bool is_spike(const uint64_t timestamp, const ForwardIterator sample)
Definition algorithms.hpp:219