Falcon
Toggle main menu visibility
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
31
namespace
dsp {
32
namespace
algorithms {
33
34
enum class
Slope : std::uint8_t { UP, DOWN };
35
36
class
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
57
class
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
112
class
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
127
class
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
160
class
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
177
enum class
SpikeDetectionMode : std::uint8_t { PEAK = 0, THRESHOLD };
178
enum class
SpikeDetectionSign : std::int8_t { UP = 1, DOWN = -1 };
179
180
class
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
dsp::algorithms::SpikeDetector::is_spike
bool is_spike(const uint64_t timestamp, const ForwardIterator sample)
Definition
algorithms.hpp:219
extensions
falcon-fklab-extensions
lib
dsp
algorithms.hpp
Generated by
1.18.0