synapse-app-sdk
C++ SDK for Synapse Apps
Loading...
Searching...
No Matches
threshold_detector.hpp
1#pragma once
2
3#include "synapse-app-sdk/dsp/spike/base_spike_detector.hpp"
4#include <deque>
5#include <memory>
6
7namespace synapse {
8
16class ThresholdDetector {
17public:
18 struct Config {
19 float threshold; // Voltage threshold for spike detection (absolute value)
20 uint32_t waveform_size; // Total number of samples in the waveform
21 // (threshold in middle)
22 uint64_t
23 refractory_period; // Minimum time between spikes (in timestamp units)
24 float sample_rate_hz; // Sample rate used for timestamp calculations
25 };
26
27 explicit ThresholdDetector(const Config &config)
28 : config_(config), buffer_(config.waveform_size), last_spike_time_(0),
29 collecting_spike_(false), samples_after_threshold_(0),
30 pending_spike_(nullptr),
31 sample_period_ns_(static_cast<uint64_t>(1e9 / config.sample_rate_hz)),
32 sample_count_(0) {}
33
34 void setup(const Config &config) {
35 config_ = config;
36 buffer_.resize(config.waveform_size);
37 sample_period_ns_ = static_cast<uint64_t>(1e9 / config.sample_rate_hz);
38 reset();
39 }
40
41 SpikeEvent *detect(const float sample, const uint64_t frame_timestamp,
42 const uint32_t channel_id) {
43 // Calculate a timestamp for this specific sample based on sample count and
44 // rate This ensures precise timing even within frames
45 uint64_t sample_timestamp =
46 frame_timestamp + (sample_count_ * sample_period_ns_);
47 sample_count_++;
48
49 // Store sample and timestamp in circular buffer
50 buffer_.push_back({sample, sample_timestamp});
51
52 // Check if we're collecting post-threshold samples for a spike
53 if (collecting_spike_) {
54 samples_after_threshold_++;
55
56 // If we've collected enough post-threshold samples, return the spike
57 const uint32_t samples_after_needed = config_.waveform_size / 2;
58 if (samples_after_threshold_ >= samples_after_needed) {
59 collecting_spike_ = false;
60
61 // Fill the spike waveform from the buffer
62 pending_spike_->waveform.reserve(buffer_.size());
63 for (const auto &sample_data : buffer_) {
64 pending_spike_->waveform.push_back(sample_data.value);
65 }
66
67 SpikeEvent *event = pending_spike_;
68 pending_spike_ = nullptr;
69 return event;
70 }
71
72 return nullptr;
73 }
74
75 // Check for threshold crossing (negative polarity only)
76 const bool threshold_crossed = sample <= -std::abs(config_.threshold);
77
78 // Check if we've crossed threshold and are outside refractory period
79 if (threshold_crossed &&
80 (sample_timestamp - last_spike_time_ >= config_.refractory_period)) {
81 last_spike_time_ = sample_timestamp;
82 collecting_spike_ = true;
83 samples_after_threshold_ = 0;
84
85 // Create new spike event
86 pending_spike_ = new SpikeEvent();
87
88 // The timestamp is the current sample's threshold crossing time
89 pending_spike_->timestamp = sample_timestamp;
90 pending_spike_->channel_id = channel_id;
91 pending_spike_->waveform.reserve(buffer_.size());
92
93 return nullptr;
94 }
95
96 return nullptr;
97 }
98
99 void reset() {
100 buffer_.clear();
101 for (size_t i = 0; i < buffer_.size(); i++) {
102 buffer_.push_back({0.0f, 0}); // Initialize with zeros
103 }
104
105 collecting_spike_ = false;
106 samples_after_threshold_ = 0;
107 last_spike_time_ = 0;
108 sample_count_ = 0;
109
110 if (pending_spike_) {
111 delete pending_spike_;
112 pending_spike_ = nullptr;
113 }
114 }
115
116private:
117 // Structure to store sample value and its timestamp
118 struct SampleData {
119 float value;
120 uint64_t timestamp;
121 };
122
123 Config config_;
124 std::deque<SampleData> buffer_; // Circular buffer for sample storage
125 uint64_t last_spike_time_; // Timestamp of last detected spike
126 bool collecting_spike_; // Flag indicating if we're collecting a spike
127 uint32_t
128 samples_after_threshold_; // Number of samples collected after threshold
129 SpikeEvent *pending_spike_; // Spike currently being collected
130 uint64_t sample_period_ns_; // Period between samples in ns
131 uint64_t
132 sample_count_; // Count of samples processed for timestamp calculation
133};
134
146std::unique_ptr<BaseSpikeDetector> create_threshold_detector(
147 const float threshold, const uint32_t waveform_size = 50,
148 const uint64_t refractory_us = 1000, // 1ms default refractory period
149 const float sample_rate_hz = 30000.0);
150
151} // namespace synapse
Definition threshold_detector.hpp:18