dspx 0.1.1-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +185 -0
- package/.vscode/c_cpp_properties.json +17 -0
- package/.vscode/settings.json +68 -0
- package/.vscode/tasks.json +28 -0
- package/DISCLAIMER.md +32 -0
- package/LICENSE +21 -0
- package/README.md +1803 -0
- package/ROADMAP.md +192 -0
- package/TECHNICAL_DEBT.md +165 -0
- package/binding.gyp +65 -0
- package/docs/ADVANCED_LOGGER_FEATURES.md +598 -0
- package/docs/AUTHENTICATION_SECURITY.md +396 -0
- package/docs/BACKEND_IMPROVEMENTS.md +399 -0
- package/docs/CHEBYSHEV_BIQUAD_EQ_IMPLEMENTATION.md +405 -0
- package/docs/FFT_IMPLEMENTATION.md +490 -0
- package/docs/FFT_IMPROVEMENTS_SUMMARY.md +387 -0
- package/docs/FFT_USER_GUIDE.md +494 -0
- package/docs/FILTERS_IMPLEMENTATION.md +260 -0
- package/docs/FILTER_API_GUIDE.md +418 -0
- package/docs/FIR_SIMD_OPTIMIZATION.md +175 -0
- package/docs/LOGGER_API_REFERENCE.md +350 -0
- package/docs/NOTCH_FILTER_QUICK_REF.md +121 -0
- package/docs/PHASE2_TESTS_AND_NOTCH_FILTER.md +341 -0
- package/docs/PHASES_5_7_SUMMARY.md +403 -0
- package/docs/PIPELINE_FILTER_INTEGRATION.md +446 -0
- package/docs/SIMD_OPTIMIZATIONS.md +211 -0
- package/docs/TEST_MIGRATION_SUMMARY.md +173 -0
- package/docs/TIMESERIES_IMPLEMENTATION_SUMMARY.md +322 -0
- package/docs/TIMESERIES_QUICK_REF.md +85 -0
- package/docs/advanced.md +559 -0
- package/docs/time-series-guide.md +617 -0
- package/docs/time-series-migration.md +376 -0
- package/jest.config.js +37 -0
- package/package.json +42 -0
- package/prebuilds/linux-x64/dsp-ts-redis.node +0 -0
- package/prebuilds/win32-x64/dsp-ts-redis.node +0 -0
- package/scripts/test.js +24 -0
- package/src/build/dsp-ts-redis.node +0 -0
- package/src/native/DspPipeline.cc +675 -0
- package/src/native/DspPipeline.h +44 -0
- package/src/native/FftBindings.cc +817 -0
- package/src/native/FilterBindings.cc +1001 -0
- package/src/native/IDspStage.h +53 -0
- package/src/native/adapters/InterpolatorStage.h +201 -0
- package/src/native/adapters/MeanAbsoluteValueStage.h +289 -0
- package/src/native/adapters/MovingAverageStage.h +306 -0
- package/src/native/adapters/RectifyStage.h +88 -0
- package/src/native/adapters/ResamplerStage.h +238 -0
- package/src/native/adapters/RmsStage.h +299 -0
- package/src/native/adapters/SscStage.h +121 -0
- package/src/native/adapters/VarianceStage.h +307 -0
- package/src/native/adapters/WampStage.h +114 -0
- package/src/native/adapters/WaveformLengthStage.h +115 -0
- package/src/native/adapters/ZScoreNormalizeStage.h +326 -0
- package/src/native/core/FftEngine.cc +441 -0
- package/src/native/core/FftEngine.h +224 -0
- package/src/native/core/FirFilter.cc +324 -0
- package/src/native/core/FirFilter.h +149 -0
- package/src/native/core/IirFilter.cc +576 -0
- package/src/native/core/IirFilter.h +210 -0
- package/src/native/core/MovingAbsoluteValueFilter.cc +17 -0
- package/src/native/core/MovingAbsoluteValueFilter.h +135 -0
- package/src/native/core/MovingAverageFilter.cc +18 -0
- package/src/native/core/MovingAverageFilter.h +135 -0
- package/src/native/core/MovingFftFilter.cc +291 -0
- package/src/native/core/MovingFftFilter.h +203 -0
- package/src/native/core/MovingVarianceFilter.cc +194 -0
- package/src/native/core/MovingVarianceFilter.h +114 -0
- package/src/native/core/MovingZScoreFilter.cc +215 -0
- package/src/native/core/MovingZScoreFilter.h +113 -0
- package/src/native/core/Policies.h +352 -0
- package/src/native/core/RmsFilter.cc +18 -0
- package/src/native/core/RmsFilter.h +131 -0
- package/src/native/core/SscFilter.cc +16 -0
- package/src/native/core/SscFilter.h +137 -0
- package/src/native/core/WampFilter.cc +16 -0
- package/src/native/core/WampFilter.h +101 -0
- package/src/native/core/WaveformLengthFilter.cc +17 -0
- package/src/native/core/WaveformLengthFilter.h +98 -0
- package/src/native/utils/CircularBufferArray.cc +336 -0
- package/src/native/utils/CircularBufferArray.h +62 -0
- package/src/native/utils/CircularBufferVector.cc +145 -0
- package/src/native/utils/CircularBufferVector.h +45 -0
- package/src/native/utils/NapiUtils.cc +53 -0
- package/src/native/utils/NapiUtils.h +21 -0
- package/src/native/utils/SimdOps.h +870 -0
- package/src/native/utils/SlidingWindowFilter.cc +239 -0
- package/src/native/utils/SlidingWindowFilter.h +159 -0
- package/src/native/utils/TimeSeriesBuffer.cc +205 -0
- package/src/native/utils/TimeSeriesBuffer.h +140 -0
- package/src/ts/CircularLogBuffer.ts +87 -0
- package/src/ts/DriftDetector.ts +331 -0
- package/src/ts/TopicRouter.ts +428 -0
- package/src/ts/__tests__/AdvancedDsp.test.ts +585 -0
- package/src/ts/__tests__/AuthAndEdgeCases.test.ts +241 -0
- package/src/ts/__tests__/Chaining.test.ts +387 -0
- package/src/ts/__tests__/ChebyshevBiquad.test.ts +229 -0
- package/src/ts/__tests__/CircularLogBuffer.test.ts +158 -0
- package/src/ts/__tests__/DriftDetector.test.ts +389 -0
- package/src/ts/__tests__/Fft.test.ts +484 -0
- package/src/ts/__tests__/ListState.test.ts +153 -0
- package/src/ts/__tests__/Logger.test.ts +208 -0
- package/src/ts/__tests__/LoggerAdvanced.test.ts +319 -0
- package/src/ts/__tests__/LoggerMinor.test.ts +247 -0
- package/src/ts/__tests__/MeanAbsoluteValue.test.ts +398 -0
- package/src/ts/__tests__/MovingAverage.test.ts +322 -0
- package/src/ts/__tests__/RMS.test.ts +315 -0
- package/src/ts/__tests__/Rectify.test.ts +272 -0
- package/src/ts/__tests__/Redis.test.ts +456 -0
- package/src/ts/__tests__/SlopeSignChange.test.ts +166 -0
- package/src/ts/__tests__/Tap.test.ts +164 -0
- package/src/ts/__tests__/TimeBasedExpiration.test.ts +124 -0
- package/src/ts/__tests__/TimeBasedRmsAndMav.test.ts +231 -0
- package/src/ts/__tests__/TimeBasedVarianceAndZScore.test.ts +284 -0
- package/src/ts/__tests__/TimeSeries.test.ts +254 -0
- package/src/ts/__tests__/TopicRouter.test.ts +332 -0
- package/src/ts/__tests__/TopicRouterAdvanced.test.ts +483 -0
- package/src/ts/__tests__/TopicRouterPriority.test.ts +487 -0
- package/src/ts/__tests__/Variance.test.ts +509 -0
- package/src/ts/__tests__/WaveformLength.test.ts +147 -0
- package/src/ts/__tests__/WillisonAmplitude.test.ts +197 -0
- package/src/ts/__tests__/ZScoreNormalize.test.ts +459 -0
- package/src/ts/advanced-dsp.ts +566 -0
- package/src/ts/backends.ts +1137 -0
- package/src/ts/bindings.ts +1225 -0
- package/src/ts/easter-egg.ts +42 -0
- package/src/ts/examples/MeanAbsoluteValue/test-state.ts +99 -0
- package/src/ts/examples/MeanAbsoluteValue/test-streaming.ts +269 -0
- package/src/ts/examples/MovingAverage/test-state.ts +85 -0
- package/src/ts/examples/MovingAverage/test-streaming.ts +188 -0
- package/src/ts/examples/RMS/test-state.ts +97 -0
- package/src/ts/examples/RMS/test-streaming.ts +253 -0
- package/src/ts/examples/Rectify/test-state.ts +107 -0
- package/src/ts/examples/Rectify/test-streaming.ts +242 -0
- package/src/ts/examples/Variance/test-state.ts +195 -0
- package/src/ts/examples/Variance/test-streaming.ts +260 -0
- package/src/ts/examples/ZScoreNormalize/test-state.ts +277 -0
- package/src/ts/examples/ZScoreNormalize/test-streaming.ts +306 -0
- package/src/ts/examples/advanced-dsp-examples.ts +397 -0
- package/src/ts/examples/callbacks/advanced-router-features.ts +326 -0
- package/src/ts/examples/callbacks/benchmark-circular-buffer.ts +109 -0
- package/src/ts/examples/callbacks/monitoring-example.ts +265 -0
- package/src/ts/examples/callbacks/pipeline-callbacks-example.ts +137 -0
- package/src/ts/examples/callbacks/pooled-callbacks-example.ts +274 -0
- package/src/ts/examples/callbacks/priority-routing-example.ts +277 -0
- package/src/ts/examples/callbacks/production-topic-router.ts +214 -0
- package/src/ts/examples/callbacks/topic-based-logging.ts +161 -0
- package/src/ts/examples/chaining/test-chaining-redis.ts +113 -0
- package/src/ts/examples/chaining/test-chaining.ts +52 -0
- package/src/ts/examples/emg-features-example.ts +284 -0
- package/src/ts/examples/fft-example.ts +309 -0
- package/src/ts/examples/fft-examples.ts +349 -0
- package/src/ts/examples/filter-examples.ts +320 -0
- package/src/ts/examples/list-state-example.ts +131 -0
- package/src/ts/examples/logger-example.ts +91 -0
- package/src/ts/examples/notch-filter-examples.ts +243 -0
- package/src/ts/examples/phase5/drift-detection-example.ts +290 -0
- package/src/ts/examples/phase6-7/production-observability.ts +476 -0
- package/src/ts/examples/phase6-7/redis-timeseries-integration.ts +446 -0
- package/src/ts/examples/redis/redis-example.ts +202 -0
- package/src/ts/examples/redis-example.ts +202 -0
- package/src/ts/examples/simd-benchmark.ts +126 -0
- package/src/ts/examples/tap-debugging.ts +230 -0
- package/src/ts/examples/timeseries/comparison-example.ts +290 -0
- package/src/ts/examples/timeseries/iot-sensor-example.ts +143 -0
- package/src/ts/examples/timeseries/redis-streaming-example.ts +233 -0
- package/src/ts/examples/waveform-length-example.ts +139 -0
- package/src/ts/fft.ts +722 -0
- package/src/ts/filters.ts +1078 -0
- package/src/ts/index.ts +120 -0
- package/src/ts/types.ts +589 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#include <cmath>
|
|
3
|
+
#include <algorithm>
|
|
4
|
+
#include <numeric>
|
|
5
|
+
|
|
6
|
+
namespace dsp::core
|
|
7
|
+
{
|
|
8
|
+
/**
|
|
9
|
+
* @brief Policy for calculating a running mean (for Moving Average).
|
|
10
|
+
*
|
|
11
|
+
* Maintains a running sum and computes the mean on demand.
|
|
12
|
+
*/
|
|
13
|
+
template <typename T>
|
|
14
|
+
struct MeanPolicy
|
|
15
|
+
{
|
|
16
|
+
T m_sum = 0;
|
|
17
|
+
|
|
18
|
+
void onAdd(T val) { m_sum += val; }
|
|
19
|
+
void onRemove(T val) { m_sum -= val; }
|
|
20
|
+
void clear() { m_sum = 0; }
|
|
21
|
+
|
|
22
|
+
T getResult(size_t count) const
|
|
23
|
+
{
|
|
24
|
+
if (count == 0)
|
|
25
|
+
return 0;
|
|
26
|
+
return m_sum / static_cast<T>(count);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// For state serialization
|
|
30
|
+
T getState() const { return m_sum; }
|
|
31
|
+
void setState(T sum) { m_sum = sum; }
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @brief Policy for calculating RMS (Root Mean Square).
|
|
36
|
+
*
|
|
37
|
+
* Maintains a running sum of squares and computes RMS on demand.
|
|
38
|
+
*/
|
|
39
|
+
template <typename T>
|
|
40
|
+
struct RmsPolicy
|
|
41
|
+
{
|
|
42
|
+
T m_sum_sq = 0;
|
|
43
|
+
|
|
44
|
+
void onAdd(T val) { m_sum_sq += (val * val); }
|
|
45
|
+
void onRemove(T val) { m_sum_sq -= (val * val); }
|
|
46
|
+
void clear() { m_sum_sq = 0; }
|
|
47
|
+
|
|
48
|
+
T getResult(size_t count) const
|
|
49
|
+
{
|
|
50
|
+
if (count == 0)
|
|
51
|
+
return 0;
|
|
52
|
+
// Clamp to avoid negative values due to floating-point errors
|
|
53
|
+
T mean_sq = std::max(static_cast<T>(0), m_sum_sq / static_cast<T>(count));
|
|
54
|
+
return std::sqrt(mean_sq);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// For state serialization
|
|
58
|
+
T getState() const { return m_sum_sq; }
|
|
59
|
+
void setState(T sumSq) { m_sum_sq = sumSq; }
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @brief Policy for SlidingWindowFilter that maintains a running sum.
|
|
64
|
+
* @tparam T The numeric type (e.g., float, double).
|
|
65
|
+
*/
|
|
66
|
+
template <typename T>
|
|
67
|
+
struct SumPolicy
|
|
68
|
+
{
|
|
69
|
+
double m_sum = 0.0;
|
|
70
|
+
|
|
71
|
+
void onAdd(T value)
|
|
72
|
+
{
|
|
73
|
+
m_sum += static_cast<double>(value);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
void onRemove(T value)
|
|
77
|
+
{
|
|
78
|
+
m_sum -= static_cast<double>(value);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
void clear()
|
|
82
|
+
{
|
|
83
|
+
m_sum = 0.0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
T getResult(size_t count) const
|
|
87
|
+
{
|
|
88
|
+
// The result is just the total sum
|
|
89
|
+
return static_cast<T>(m_sum);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// --- State Serialization ---
|
|
93
|
+
double getState() const
|
|
94
|
+
{
|
|
95
|
+
return m_sum;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
void setState(double state)
|
|
99
|
+
{
|
|
100
|
+
m_sum = state;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Optional: State validation (used in adapter)
|
|
104
|
+
static bool validateState(double state, const std::vector<T> &buffer)
|
|
105
|
+
{
|
|
106
|
+
double actualSum = std::accumulate(buffer.begin(), buffer.end(), 0.0);
|
|
107
|
+
const double tolerance = 0.0001 * std::max(1.0, std::abs(actualSum));
|
|
108
|
+
return (std::abs(state - actualSum) <= tolerance);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @brief Policy for SlidingWindowFilter that counts 'true' entries.
|
|
114
|
+
*/
|
|
115
|
+
struct CounterPolicy
|
|
116
|
+
{
|
|
117
|
+
size_t m_count = 0;
|
|
118
|
+
|
|
119
|
+
void onAdd(bool value)
|
|
120
|
+
{
|
|
121
|
+
if (value)
|
|
122
|
+
{
|
|
123
|
+
m_count++;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
void onRemove(bool value)
|
|
128
|
+
{
|
|
129
|
+
if (value)
|
|
130
|
+
{
|
|
131
|
+
m_count--;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
void clear()
|
|
136
|
+
{
|
|
137
|
+
m_count = 0;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Note: The process buffer is float, so we cast the count
|
|
141
|
+
float getResult(size_t count) const
|
|
142
|
+
{
|
|
143
|
+
return static_cast<float>(m_count);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// --- State Serialization ---
|
|
147
|
+
size_t getState() const
|
|
148
|
+
{
|
|
149
|
+
return m_count;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
void setState(size_t state)
|
|
153
|
+
{
|
|
154
|
+
m_count = state;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Optional: State validation (used in adapter)
|
|
158
|
+
static bool validateState(size_t state, const std::vector<bool> &buffer)
|
|
159
|
+
{
|
|
160
|
+
size_t actualCount = 0;
|
|
161
|
+
for (bool val : buffer)
|
|
162
|
+
{
|
|
163
|
+
if (val)
|
|
164
|
+
actualCount++;
|
|
165
|
+
}
|
|
166
|
+
return state == actualCount;
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* @brief Policy for calculating Mean Absolute Value (MAV).
|
|
172
|
+
*
|
|
173
|
+
* Maintains a running sum of absolute values.
|
|
174
|
+
*/
|
|
175
|
+
template <typename T>
|
|
176
|
+
struct MeanAbsoluteValuePolicy
|
|
177
|
+
{
|
|
178
|
+
T m_sum_abs = 0;
|
|
179
|
+
|
|
180
|
+
void onAdd(T val) { m_sum_abs += std::abs(val); }
|
|
181
|
+
void onRemove(T val) { m_sum_abs -= std::abs(val); }
|
|
182
|
+
void clear() { m_sum_abs = 0; }
|
|
183
|
+
|
|
184
|
+
T getResult(size_t count) const
|
|
185
|
+
{
|
|
186
|
+
if (count == 0)
|
|
187
|
+
return 0;
|
|
188
|
+
return m_sum_abs / static_cast<T>(count);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// For state serialization
|
|
192
|
+
T getState() const { return m_sum_abs; }
|
|
193
|
+
void setState(T sumAbs) { m_sum_abs = sumAbs; }
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* @brief Policy for calculating Variance.
|
|
198
|
+
*
|
|
199
|
+
* Maintains both sum and sum of squares for variance calculation.
|
|
200
|
+
* Uses the computational formula: Var(X) = E[X²] - (E[X])²
|
|
201
|
+
*/
|
|
202
|
+
template <typename T>
|
|
203
|
+
struct VariancePolicy
|
|
204
|
+
{
|
|
205
|
+
T m_sum = 0;
|
|
206
|
+
T m_sum_sq = 0;
|
|
207
|
+
|
|
208
|
+
void onAdd(T val)
|
|
209
|
+
{
|
|
210
|
+
m_sum += val;
|
|
211
|
+
m_sum_sq += (val * val);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
void onRemove(T val)
|
|
215
|
+
{
|
|
216
|
+
m_sum -= val;
|
|
217
|
+
m_sum_sq -= (val * val);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
void clear()
|
|
221
|
+
{
|
|
222
|
+
m_sum = 0;
|
|
223
|
+
m_sum_sq = 0;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
T getResult(size_t count) const
|
|
227
|
+
{
|
|
228
|
+
if (count == 0)
|
|
229
|
+
return 0;
|
|
230
|
+
|
|
231
|
+
T mean = m_sum / static_cast<T>(count);
|
|
232
|
+
T mean_sq = m_sum_sq / static_cast<T>(count);
|
|
233
|
+
|
|
234
|
+
// Clamp to avoid negative variance due to floating-point errors
|
|
235
|
+
return std::max(static_cast<T>(0), mean_sq - (mean * mean));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// For state serialization - returns both values as a pair
|
|
239
|
+
std::pair<T, T> getState() const { return {m_sum, m_sum_sq}; }
|
|
240
|
+
void setState(T sum, T sumSq)
|
|
241
|
+
{
|
|
242
|
+
m_sum = sum;
|
|
243
|
+
m_sum_sq = sumSq;
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* @brief Policy for calculating Z-Score normalization.
|
|
249
|
+
*
|
|
250
|
+
* Maintains sum and sum of squares to compute mean and stddev,
|
|
251
|
+
* then normalizes values to Z-scores.
|
|
252
|
+
*/
|
|
253
|
+
template <typename T>
|
|
254
|
+
struct ZScorePolicy
|
|
255
|
+
{
|
|
256
|
+
T m_sum = 0;
|
|
257
|
+
T m_sum_sq = 0;
|
|
258
|
+
T m_epsilon;
|
|
259
|
+
|
|
260
|
+
explicit ZScorePolicy(T epsilon = static_cast<T>(1e-8))
|
|
261
|
+
: m_epsilon(epsilon) {}
|
|
262
|
+
|
|
263
|
+
void onAdd(T val)
|
|
264
|
+
{
|
|
265
|
+
m_sum += val;
|
|
266
|
+
m_sum_sq += (val * val);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
void onRemove(T val)
|
|
270
|
+
{
|
|
271
|
+
m_sum -= val;
|
|
272
|
+
m_sum_sq -= (val * val);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
void clear()
|
|
276
|
+
{
|
|
277
|
+
m_sum = 0;
|
|
278
|
+
m_sum_sq = 0;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Z-Score needs the current value to normalize
|
|
282
|
+
T getResult(T currentValue, size_t count) const
|
|
283
|
+
{
|
|
284
|
+
if (count == 0)
|
|
285
|
+
return 0;
|
|
286
|
+
|
|
287
|
+
T mean = m_sum / static_cast<T>(count);
|
|
288
|
+
T mean_sq = m_sum_sq / static_cast<T>(count);
|
|
289
|
+
T variance = std::max(static_cast<T>(0), mean_sq - (mean * mean));
|
|
290
|
+
T stddev = std::sqrt(variance);
|
|
291
|
+
|
|
292
|
+
// Avoid division by zero
|
|
293
|
+
if (stddev < m_epsilon)
|
|
294
|
+
{
|
|
295
|
+
return 0;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return (currentValue - mean) / stddev;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// For state serialization
|
|
302
|
+
std::pair<T, T> getState() const { return {m_sum, m_sum_sq}; }
|
|
303
|
+
void setState(T sum, T sumSq)
|
|
304
|
+
{
|
|
305
|
+
m_sum = sum;
|
|
306
|
+
m_sum_sq = sumSq;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
T getEpsilon() const { return m_epsilon; }
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* @brief Policy for FIR filter convolution.
|
|
314
|
+
*
|
|
315
|
+
* Performs FIR filtering using dot product with filter coefficients.
|
|
316
|
+
* The buffer stores recent input samples, and coefficients are the filter taps.
|
|
317
|
+
* Note: SIMD optimization is handled in the FirFilter implementation.
|
|
318
|
+
*/
|
|
319
|
+
template <typename T>
|
|
320
|
+
struct FirConvolutionPolicy
|
|
321
|
+
{
|
|
322
|
+
std::vector<T> m_coefficients;
|
|
323
|
+
|
|
324
|
+
explicit FirConvolutionPolicy(const std::vector<T> &coefficients)
|
|
325
|
+
: m_coefficients(coefficients) {}
|
|
326
|
+
|
|
327
|
+
void onAdd(T val) {} // No incremental state to update
|
|
328
|
+
void onRemove(T val) {} // No incremental state to update
|
|
329
|
+
void clear() {} // No state to clear
|
|
330
|
+
|
|
331
|
+
// Compute convolution (dot product) with the buffer
|
|
332
|
+
// Note: This is a simple scalar version; SIMD is applied in FirFilter
|
|
333
|
+
T getResult(const std::vector<T> &buffer) const
|
|
334
|
+
{
|
|
335
|
+
if (buffer.empty() || m_coefficients.empty())
|
|
336
|
+
return T(0);
|
|
337
|
+
|
|
338
|
+
T result = T(0);
|
|
339
|
+
size_t len = std::min(buffer.size(), m_coefficients.size());
|
|
340
|
+
for (size_t i = 0; i < len; ++i)
|
|
341
|
+
{
|
|
342
|
+
result += buffer[i] * m_coefficients[i];
|
|
343
|
+
}
|
|
344
|
+
return result;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// For state serialization
|
|
348
|
+
const std::vector<T> &getCoefficients() const { return m_coefficients; }
|
|
349
|
+
void setCoefficients(const std::vector<T> &coeffs) { m_coefficients = coeffs; }
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
} // namespace dsp::core
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file RmsFilter.cc
|
|
3
|
+
* @brief Implementation file for RmsFilter (now header-only with policy-based design)
|
|
4
|
+
*
|
|
5
|
+
* This file now only contains explicit template instantiations.
|
|
6
|
+
* The actual implementation is in the header file RmsFilter.h,
|
|
7
|
+
* which delegates to SlidingWindowFilter<T, RmsPolicy<T>>.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#include "RmsFilter.h"
|
|
11
|
+
|
|
12
|
+
// Explicit template instantiation for common types
|
|
13
|
+
namespace dsp::core
|
|
14
|
+
{
|
|
15
|
+
template class RmsFilter<int>;
|
|
16
|
+
template class RmsFilter<float>;
|
|
17
|
+
template class RmsFilter<double>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#include "../utils/SlidingWindowFilter.h"
|
|
3
|
+
#include "Policies.h"
|
|
4
|
+
#include <utility>
|
|
5
|
+
#include <vector>
|
|
6
|
+
#include <stdexcept>
|
|
7
|
+
|
|
8
|
+
namespace dsp::core
|
|
9
|
+
{
|
|
10
|
+
using dsp::utils::SlidingWindowFilter;
|
|
11
|
+
/**
|
|
12
|
+
* @brief Implements an efficient Root Mean Square (RMS) filter.
|
|
13
|
+
*
|
|
14
|
+
* This class is now a thin wrapper around SlidingWindowFilter
|
|
15
|
+
* using the RmsPolicy for statistical computation.
|
|
16
|
+
*
|
|
17
|
+
* @tparam T The numeric type of the samples (e.g., float, double).
|
|
18
|
+
*/
|
|
19
|
+
template <typename T>
|
|
20
|
+
class RmsFilter
|
|
21
|
+
{
|
|
22
|
+
public:
|
|
23
|
+
/**
|
|
24
|
+
* @brief Constructs a new RMS Filter.
|
|
25
|
+
* @param window_size The number of samples to average over (N).
|
|
26
|
+
*/
|
|
27
|
+
explicit RmsFilter(size_t window_size)
|
|
28
|
+
: m_filter(window_size, RmsPolicy<T>{})
|
|
29
|
+
{
|
|
30
|
+
if (window_size == 0)
|
|
31
|
+
{
|
|
32
|
+
throw std::invalid_argument("Window size must be greater than 0");
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @brief Constructs a new time-aware RMS Filter.
|
|
38
|
+
* @param window_size The buffer capacity in samples.
|
|
39
|
+
* @param window_duration_ms The time window duration in milliseconds.
|
|
40
|
+
*/
|
|
41
|
+
explicit RmsFilter(size_t window_size, double window_duration_ms)
|
|
42
|
+
: m_filter(window_size, window_duration_ms, RmsPolicy<T>{})
|
|
43
|
+
{
|
|
44
|
+
if (window_size == 0)
|
|
45
|
+
{
|
|
46
|
+
throw std::invalid_argument("Window size must be greater than 0");
|
|
47
|
+
}
|
|
48
|
+
if (window_duration_ms <= 0.0)
|
|
49
|
+
{
|
|
50
|
+
throw std::invalid_argument("Window duration must be greater than 0");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Delete copy constructor and copy assignment
|
|
55
|
+
RmsFilter(const RmsFilter &) = delete;
|
|
56
|
+
RmsFilter &operator=(const RmsFilter &) = delete;
|
|
57
|
+
|
|
58
|
+
// Enable move semantics
|
|
59
|
+
RmsFilter(RmsFilter &&) noexcept = default;
|
|
60
|
+
RmsFilter &operator=(RmsFilter &&) noexcept = default;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @brief Adds a new sample to the filter.
|
|
64
|
+
* @param newValue The new sample value to add.
|
|
65
|
+
* @return T The new RMS value.
|
|
66
|
+
*/
|
|
67
|
+
T addSample(T newValue) { return m_filter.addSample(newValue); }
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @brief Adds a new sample with timestamp (time-aware mode).
|
|
71
|
+
* @param newValue The new sample value to add.
|
|
72
|
+
* @param timestamp The timestamp in milliseconds.
|
|
73
|
+
* @return T The new RMS value.
|
|
74
|
+
*/
|
|
75
|
+
T addSampleWithTimestamp(T newValue, double timestamp)
|
|
76
|
+
{
|
|
77
|
+
return m_filter.addSampleWithTimestamp(newValue, timestamp);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @brief Gets the current RMS value.
|
|
82
|
+
* @return T The RMS of the samples currently in the buffer.
|
|
83
|
+
*/
|
|
84
|
+
T getRms() const { return m_filter.getPolicy().getResult(m_filter.getCount()); }
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @brief Clears all samples from the filter and resets the sum.
|
|
88
|
+
*/
|
|
89
|
+
void clear() { m_filter.clear(); }
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @brief Checks if the filter's buffer is full (i.e., has N samples).
|
|
93
|
+
* @return true if the buffer is full, false otherwise.
|
|
94
|
+
*/
|
|
95
|
+
bool isFull() const noexcept { return m_filter.isFull(); }
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @brief Checks if the filter is in time-aware mode.
|
|
99
|
+
* @return true if time-aware, false otherwise.
|
|
100
|
+
*/
|
|
101
|
+
bool isTimeAware() const noexcept { return m_filter.isTimeAware(); }
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @brief Exports the filter's internal state.
|
|
105
|
+
*
|
|
106
|
+
* Delegates to SlidingWindowFilter's generic state management.
|
|
107
|
+
*
|
|
108
|
+
* @return A pair containing the buffer contents and running sum of squares.
|
|
109
|
+
*/
|
|
110
|
+
std::pair<std::vector<T>, T> getState() const
|
|
111
|
+
{
|
|
112
|
+
return m_filter.getState();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @brief Restores the filter's internal state.
|
|
117
|
+
*
|
|
118
|
+
* Delegates to SlidingWindowFilter's generic state management.
|
|
119
|
+
*
|
|
120
|
+
* @param bufferData The buffer contents to restore.
|
|
121
|
+
* @param sumOfSquares The running sum of squares to restore.
|
|
122
|
+
*/
|
|
123
|
+
void setState(const std::vector<T> &bufferData, T sumOfSquares)
|
|
124
|
+
{
|
|
125
|
+
m_filter.setState(bufferData, sumOfSquares);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private:
|
|
129
|
+
SlidingWindowFilter<T, RmsPolicy<T>> m_filter;
|
|
130
|
+
};
|
|
131
|
+
} // namespace dsp::core
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file SscFilter.cc
|
|
3
|
+
* @brief Implementation file for SscFilter (now header-only with policy-based design)
|
|
4
|
+
* This file now only contains explicit template instantiations.
|
|
5
|
+
* The actual implementation is in the header file SscFilter.h,
|
|
6
|
+
* which delegates to SlidingWindowFilter<T, CounterPolicy>.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
#include "SscFilter.h"
|
|
10
|
+
|
|
11
|
+
// Explicit template instantiation for common types
|
|
12
|
+
namespace dsp::core
|
|
13
|
+
{
|
|
14
|
+
template class SscFilter<float>;
|
|
15
|
+
template class SscFilter<double>;
|
|
16
|
+
} // namespace dsp::core
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "../utils/SlidingWindowFilter.h"
|
|
4
|
+
#include "Policies.h"
|
|
5
|
+
#include <cmath> // For std::abs
|
|
6
|
+
#include <utility> // For std::pair
|
|
7
|
+
#include <vector>
|
|
8
|
+
|
|
9
|
+
namespace dsp::core
|
|
10
|
+
{
|
|
11
|
+
template <typename T>
|
|
12
|
+
class SscFilter
|
|
13
|
+
{
|
|
14
|
+
public:
|
|
15
|
+
/**
|
|
16
|
+
* @brief Constructs a new Ssc Filter.
|
|
17
|
+
* @param window_size The number of samples to consider in the sliding window.
|
|
18
|
+
* @param threshold The amplitude change threshold to detect.
|
|
19
|
+
*/
|
|
20
|
+
explicit SscFilter(size_t window_size, T threshold)
|
|
21
|
+
: m_filter(window_size),
|
|
22
|
+
m_threshold(threshold),
|
|
23
|
+
m_sample_minus_1(0.0),
|
|
24
|
+
m_sample_minus_2(0.0),
|
|
25
|
+
m_init_count(0) {}
|
|
26
|
+
|
|
27
|
+
// Delete copy constructor and copy assignment
|
|
28
|
+
SscFilter(const SscFilter &) = delete;
|
|
29
|
+
SscFilter &operator=(const SscFilter &) = delete;
|
|
30
|
+
|
|
31
|
+
// Enable move semantics
|
|
32
|
+
SscFilter(SscFilter &&) noexcept = default;
|
|
33
|
+
SscFilter &operator=(SscFilter &&) noexcept = default;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @brief Adds a new sample to the filter.
|
|
37
|
+
* @param sample_n The new sample value to add.
|
|
38
|
+
* @return T The updated SSC count over the window.
|
|
39
|
+
*/
|
|
40
|
+
T addSample(T sample_n) // sample_n is x_{i+1}
|
|
41
|
+
{
|
|
42
|
+
bool did_change = false;
|
|
43
|
+
|
|
44
|
+
if (m_init_count >= 2)
|
|
45
|
+
{
|
|
46
|
+
// We have x_{i-1} (m_sample_minus_2), x_i (m_sample_minus_1), and x_{i+1} (sample_n)
|
|
47
|
+
T diff1 = m_sample_minus_1 - m_sample_minus_2; // (x_i - x_{i-1})
|
|
48
|
+
T diff2 = m_sample_minus_1 - sample_n; // (x_i - x_{i+1})
|
|
49
|
+
|
|
50
|
+
did_change = (diff1 * diff2) > m_threshold;
|
|
51
|
+
}
|
|
52
|
+
else
|
|
53
|
+
{
|
|
54
|
+
if (m_init_count == 0)
|
|
55
|
+
m_sample_minus_2 = sample_n; // First sample
|
|
56
|
+
if (m_init_count == 1)
|
|
57
|
+
m_sample_minus_1 = sample_n; // Second sample
|
|
58
|
+
m_init_count++;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Update state for the *next* iteration
|
|
62
|
+
m_sample_minus_2 = m_sample_minus_1;
|
|
63
|
+
m_sample_minus_1 = sample_n;
|
|
64
|
+
|
|
65
|
+
// Add the boolean to the window, get the count back
|
|
66
|
+
// Cast count (size_t) to T (float/double)
|
|
67
|
+
return static_cast<T>(m_filter.addSample(did_change));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @brief Clears all samples from the filter and resets state.
|
|
72
|
+
*/
|
|
73
|
+
void clear()
|
|
74
|
+
{
|
|
75
|
+
m_filter.clear();
|
|
76
|
+
m_sample_minus_1 = 0.0;
|
|
77
|
+
m_sample_minus_2 = 0.0;
|
|
78
|
+
m_init_count = 0;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// --- State Management ---
|
|
82
|
+
using SscState = std::pair<std::vector<bool>, size_t>;
|
|
83
|
+
|
|
84
|
+
struct SscFilterState
|
|
85
|
+
{
|
|
86
|
+
T sample_minus_1;
|
|
87
|
+
T sample_minus_2;
|
|
88
|
+
int init_count;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @brief Exports the complete filter state (buffer + previous samples).
|
|
93
|
+
* @return A pair containing the buffer contents, running count, and previous samples.
|
|
94
|
+
*/
|
|
95
|
+
|
|
96
|
+
auto getState() const -> std::pair<SscState, SscFilterState>
|
|
97
|
+
{
|
|
98
|
+
// Returns: { {buffer_of_bools, running_count}, {s_m1, s_m2, init_count} }
|
|
99
|
+
return {
|
|
100
|
+
m_filter.getState(),
|
|
101
|
+
{m_sample_minus_1, m_sample_minus_2, m_init_count}};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @brief Restores the complete filter state (buffer + previous samples).
|
|
106
|
+
* @param buffer The buffer contents to restore.
|
|
107
|
+
* @param count The count of 'true' entries in the buffer.
|
|
108
|
+
* @param filterState The previous samples and init count to restore.
|
|
109
|
+
* @return void
|
|
110
|
+
*/
|
|
111
|
+
|
|
112
|
+
void setState(const std::vector<bool> &buffer, size_t count, const SscFilterState &filterState)
|
|
113
|
+
{
|
|
114
|
+
m_filter.setState(buffer, count);
|
|
115
|
+
m_sample_minus_1 = filterState.sample_minus_1;
|
|
116
|
+
m_sample_minus_2 = filterState.sample_minus_2;
|
|
117
|
+
m_init_count = filterState.init_count;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @brief Gets const access to the internal SlidingWindowFilter.
|
|
122
|
+
* @return const SlidingWindowFilter<bool, CounterPolicy>& Reference to the internal filter
|
|
123
|
+
*/
|
|
124
|
+
const dsp::utils::SlidingWindowFilter<bool, CounterPolicy> &getInternalFilter() const
|
|
125
|
+
{
|
|
126
|
+
return m_filter;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private:
|
|
130
|
+
dsp::utils::SlidingWindowFilter<bool, CounterPolicy> m_filter;
|
|
131
|
+
T m_threshold;
|
|
132
|
+
T m_sample_minus_1; // x_i
|
|
133
|
+
T m_sample_minus_2; // x_{i-1}
|
|
134
|
+
int m_init_count; // Needs 2 samples to be fully initialized
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
} // namespace dsp::core
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file WampFilter.cc
|
|
3
|
+
* @brief Implementation file for WampFilter (now header-only with policy-based design)
|
|
4
|
+
* This file now only contains explicit template instantiations.
|
|
5
|
+
* The actual implementation is in the header file WampFilter.h,
|
|
6
|
+
* which delegates to SlidingWindowFilter<T, CounterPolicy>.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
#include "WampFilter.h"
|
|
10
|
+
|
|
11
|
+
// Explicit template instantiation for common types
|
|
12
|
+
namespace dsp::core
|
|
13
|
+
{
|
|
14
|
+
template class WampFilter<float>;
|
|
15
|
+
template class WampFilter<double>;
|
|
16
|
+
}
|