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.
Files changed (172) hide show
  1. package/.github/workflows/ci.yml +185 -0
  2. package/.vscode/c_cpp_properties.json +17 -0
  3. package/.vscode/settings.json +68 -0
  4. package/.vscode/tasks.json +28 -0
  5. package/DISCLAIMER.md +32 -0
  6. package/LICENSE +21 -0
  7. package/README.md +1803 -0
  8. package/ROADMAP.md +192 -0
  9. package/TECHNICAL_DEBT.md +165 -0
  10. package/binding.gyp +65 -0
  11. package/docs/ADVANCED_LOGGER_FEATURES.md +598 -0
  12. package/docs/AUTHENTICATION_SECURITY.md +396 -0
  13. package/docs/BACKEND_IMPROVEMENTS.md +399 -0
  14. package/docs/CHEBYSHEV_BIQUAD_EQ_IMPLEMENTATION.md +405 -0
  15. package/docs/FFT_IMPLEMENTATION.md +490 -0
  16. package/docs/FFT_IMPROVEMENTS_SUMMARY.md +387 -0
  17. package/docs/FFT_USER_GUIDE.md +494 -0
  18. package/docs/FILTERS_IMPLEMENTATION.md +260 -0
  19. package/docs/FILTER_API_GUIDE.md +418 -0
  20. package/docs/FIR_SIMD_OPTIMIZATION.md +175 -0
  21. package/docs/LOGGER_API_REFERENCE.md +350 -0
  22. package/docs/NOTCH_FILTER_QUICK_REF.md +121 -0
  23. package/docs/PHASE2_TESTS_AND_NOTCH_FILTER.md +341 -0
  24. package/docs/PHASES_5_7_SUMMARY.md +403 -0
  25. package/docs/PIPELINE_FILTER_INTEGRATION.md +446 -0
  26. package/docs/SIMD_OPTIMIZATIONS.md +211 -0
  27. package/docs/TEST_MIGRATION_SUMMARY.md +173 -0
  28. package/docs/TIMESERIES_IMPLEMENTATION_SUMMARY.md +322 -0
  29. package/docs/TIMESERIES_QUICK_REF.md +85 -0
  30. package/docs/advanced.md +559 -0
  31. package/docs/time-series-guide.md +617 -0
  32. package/docs/time-series-migration.md +376 -0
  33. package/jest.config.js +37 -0
  34. package/package.json +42 -0
  35. package/prebuilds/linux-x64/dsp-ts-redis.node +0 -0
  36. package/prebuilds/win32-x64/dsp-ts-redis.node +0 -0
  37. package/scripts/test.js +24 -0
  38. package/src/build/dsp-ts-redis.node +0 -0
  39. package/src/native/DspPipeline.cc +675 -0
  40. package/src/native/DspPipeline.h +44 -0
  41. package/src/native/FftBindings.cc +817 -0
  42. package/src/native/FilterBindings.cc +1001 -0
  43. package/src/native/IDspStage.h +53 -0
  44. package/src/native/adapters/InterpolatorStage.h +201 -0
  45. package/src/native/adapters/MeanAbsoluteValueStage.h +289 -0
  46. package/src/native/adapters/MovingAverageStage.h +306 -0
  47. package/src/native/adapters/RectifyStage.h +88 -0
  48. package/src/native/adapters/ResamplerStage.h +238 -0
  49. package/src/native/adapters/RmsStage.h +299 -0
  50. package/src/native/adapters/SscStage.h +121 -0
  51. package/src/native/adapters/VarianceStage.h +307 -0
  52. package/src/native/adapters/WampStage.h +114 -0
  53. package/src/native/adapters/WaveformLengthStage.h +115 -0
  54. package/src/native/adapters/ZScoreNormalizeStage.h +326 -0
  55. package/src/native/core/FftEngine.cc +441 -0
  56. package/src/native/core/FftEngine.h +224 -0
  57. package/src/native/core/FirFilter.cc +324 -0
  58. package/src/native/core/FirFilter.h +149 -0
  59. package/src/native/core/IirFilter.cc +576 -0
  60. package/src/native/core/IirFilter.h +210 -0
  61. package/src/native/core/MovingAbsoluteValueFilter.cc +17 -0
  62. package/src/native/core/MovingAbsoluteValueFilter.h +135 -0
  63. package/src/native/core/MovingAverageFilter.cc +18 -0
  64. package/src/native/core/MovingAverageFilter.h +135 -0
  65. package/src/native/core/MovingFftFilter.cc +291 -0
  66. package/src/native/core/MovingFftFilter.h +203 -0
  67. package/src/native/core/MovingVarianceFilter.cc +194 -0
  68. package/src/native/core/MovingVarianceFilter.h +114 -0
  69. package/src/native/core/MovingZScoreFilter.cc +215 -0
  70. package/src/native/core/MovingZScoreFilter.h +113 -0
  71. package/src/native/core/Policies.h +352 -0
  72. package/src/native/core/RmsFilter.cc +18 -0
  73. package/src/native/core/RmsFilter.h +131 -0
  74. package/src/native/core/SscFilter.cc +16 -0
  75. package/src/native/core/SscFilter.h +137 -0
  76. package/src/native/core/WampFilter.cc +16 -0
  77. package/src/native/core/WampFilter.h +101 -0
  78. package/src/native/core/WaveformLengthFilter.cc +17 -0
  79. package/src/native/core/WaveformLengthFilter.h +98 -0
  80. package/src/native/utils/CircularBufferArray.cc +336 -0
  81. package/src/native/utils/CircularBufferArray.h +62 -0
  82. package/src/native/utils/CircularBufferVector.cc +145 -0
  83. package/src/native/utils/CircularBufferVector.h +45 -0
  84. package/src/native/utils/NapiUtils.cc +53 -0
  85. package/src/native/utils/NapiUtils.h +21 -0
  86. package/src/native/utils/SimdOps.h +870 -0
  87. package/src/native/utils/SlidingWindowFilter.cc +239 -0
  88. package/src/native/utils/SlidingWindowFilter.h +159 -0
  89. package/src/native/utils/TimeSeriesBuffer.cc +205 -0
  90. package/src/native/utils/TimeSeriesBuffer.h +140 -0
  91. package/src/ts/CircularLogBuffer.ts +87 -0
  92. package/src/ts/DriftDetector.ts +331 -0
  93. package/src/ts/TopicRouter.ts +428 -0
  94. package/src/ts/__tests__/AdvancedDsp.test.ts +585 -0
  95. package/src/ts/__tests__/AuthAndEdgeCases.test.ts +241 -0
  96. package/src/ts/__tests__/Chaining.test.ts +387 -0
  97. package/src/ts/__tests__/ChebyshevBiquad.test.ts +229 -0
  98. package/src/ts/__tests__/CircularLogBuffer.test.ts +158 -0
  99. package/src/ts/__tests__/DriftDetector.test.ts +389 -0
  100. package/src/ts/__tests__/Fft.test.ts +484 -0
  101. package/src/ts/__tests__/ListState.test.ts +153 -0
  102. package/src/ts/__tests__/Logger.test.ts +208 -0
  103. package/src/ts/__tests__/LoggerAdvanced.test.ts +319 -0
  104. package/src/ts/__tests__/LoggerMinor.test.ts +247 -0
  105. package/src/ts/__tests__/MeanAbsoluteValue.test.ts +398 -0
  106. package/src/ts/__tests__/MovingAverage.test.ts +322 -0
  107. package/src/ts/__tests__/RMS.test.ts +315 -0
  108. package/src/ts/__tests__/Rectify.test.ts +272 -0
  109. package/src/ts/__tests__/Redis.test.ts +456 -0
  110. package/src/ts/__tests__/SlopeSignChange.test.ts +166 -0
  111. package/src/ts/__tests__/Tap.test.ts +164 -0
  112. package/src/ts/__tests__/TimeBasedExpiration.test.ts +124 -0
  113. package/src/ts/__tests__/TimeBasedRmsAndMav.test.ts +231 -0
  114. package/src/ts/__tests__/TimeBasedVarianceAndZScore.test.ts +284 -0
  115. package/src/ts/__tests__/TimeSeries.test.ts +254 -0
  116. package/src/ts/__tests__/TopicRouter.test.ts +332 -0
  117. package/src/ts/__tests__/TopicRouterAdvanced.test.ts +483 -0
  118. package/src/ts/__tests__/TopicRouterPriority.test.ts +487 -0
  119. package/src/ts/__tests__/Variance.test.ts +509 -0
  120. package/src/ts/__tests__/WaveformLength.test.ts +147 -0
  121. package/src/ts/__tests__/WillisonAmplitude.test.ts +197 -0
  122. package/src/ts/__tests__/ZScoreNormalize.test.ts +459 -0
  123. package/src/ts/advanced-dsp.ts +566 -0
  124. package/src/ts/backends.ts +1137 -0
  125. package/src/ts/bindings.ts +1225 -0
  126. package/src/ts/easter-egg.ts +42 -0
  127. package/src/ts/examples/MeanAbsoluteValue/test-state.ts +99 -0
  128. package/src/ts/examples/MeanAbsoluteValue/test-streaming.ts +269 -0
  129. package/src/ts/examples/MovingAverage/test-state.ts +85 -0
  130. package/src/ts/examples/MovingAverage/test-streaming.ts +188 -0
  131. package/src/ts/examples/RMS/test-state.ts +97 -0
  132. package/src/ts/examples/RMS/test-streaming.ts +253 -0
  133. package/src/ts/examples/Rectify/test-state.ts +107 -0
  134. package/src/ts/examples/Rectify/test-streaming.ts +242 -0
  135. package/src/ts/examples/Variance/test-state.ts +195 -0
  136. package/src/ts/examples/Variance/test-streaming.ts +260 -0
  137. package/src/ts/examples/ZScoreNormalize/test-state.ts +277 -0
  138. package/src/ts/examples/ZScoreNormalize/test-streaming.ts +306 -0
  139. package/src/ts/examples/advanced-dsp-examples.ts +397 -0
  140. package/src/ts/examples/callbacks/advanced-router-features.ts +326 -0
  141. package/src/ts/examples/callbacks/benchmark-circular-buffer.ts +109 -0
  142. package/src/ts/examples/callbacks/monitoring-example.ts +265 -0
  143. package/src/ts/examples/callbacks/pipeline-callbacks-example.ts +137 -0
  144. package/src/ts/examples/callbacks/pooled-callbacks-example.ts +274 -0
  145. package/src/ts/examples/callbacks/priority-routing-example.ts +277 -0
  146. package/src/ts/examples/callbacks/production-topic-router.ts +214 -0
  147. package/src/ts/examples/callbacks/topic-based-logging.ts +161 -0
  148. package/src/ts/examples/chaining/test-chaining-redis.ts +113 -0
  149. package/src/ts/examples/chaining/test-chaining.ts +52 -0
  150. package/src/ts/examples/emg-features-example.ts +284 -0
  151. package/src/ts/examples/fft-example.ts +309 -0
  152. package/src/ts/examples/fft-examples.ts +349 -0
  153. package/src/ts/examples/filter-examples.ts +320 -0
  154. package/src/ts/examples/list-state-example.ts +131 -0
  155. package/src/ts/examples/logger-example.ts +91 -0
  156. package/src/ts/examples/notch-filter-examples.ts +243 -0
  157. package/src/ts/examples/phase5/drift-detection-example.ts +290 -0
  158. package/src/ts/examples/phase6-7/production-observability.ts +476 -0
  159. package/src/ts/examples/phase6-7/redis-timeseries-integration.ts +446 -0
  160. package/src/ts/examples/redis/redis-example.ts +202 -0
  161. package/src/ts/examples/redis-example.ts +202 -0
  162. package/src/ts/examples/simd-benchmark.ts +126 -0
  163. package/src/ts/examples/tap-debugging.ts +230 -0
  164. package/src/ts/examples/timeseries/comparison-example.ts +290 -0
  165. package/src/ts/examples/timeseries/iot-sensor-example.ts +143 -0
  166. package/src/ts/examples/timeseries/redis-streaming-example.ts +233 -0
  167. package/src/ts/examples/waveform-length-example.ts +139 -0
  168. package/src/ts/fft.ts +722 -0
  169. package/src/ts/filters.ts +1078 -0
  170. package/src/ts/index.ts +120 -0
  171. package/src/ts/types.ts +589 -0
  172. package/tsconfig.json +15 -0
@@ -0,0 +1,114 @@
1
+ #pragma once
2
+
3
+ #include "../utils/CircularBufferArray.h"
4
+ #include <utility>
5
+ #include <vector>
6
+ #include <cmath>
7
+ #include <stdexcept> // for noexcept
8
+ #include <algorithm> // for std::max
9
+
10
+ namespace dsp::core
11
+ {
12
+ /**
13
+ * @brief Implements an efficient Moving Variance filter.
14
+ *
15
+ * This class uses a circular buffer to store the last 'N' samples
16
+ * and maintains a running sum AND a running sum-of-squares
17
+ * for O(1) variance calculation using the formula:
18
+ * Variance = (Mean of Squares) - (Mean)^2
19
+ *
20
+ * @tparam T The numeric type of the samples (e.g., float, double).
21
+ */
22
+ template <typename T>
23
+ class MovingVarianceFilter
24
+ {
25
+ public:
26
+ /**
27
+ * @brief Constructs a new Moving Variance Filter.
28
+ * @param window_size The number of samples to average over (N).
29
+ */
30
+ explicit MovingVarianceFilter(size_t window_size);
31
+
32
+ /**
33
+ * @brief Constructs a new time-aware Moving Variance Filter.
34
+ * @param window_size The buffer capacity in samples.
35
+ * @param window_duration_ms The time window duration in milliseconds.
36
+ */
37
+ explicit MovingVarianceFilter(size_t window_size, double window_duration_ms);
38
+
39
+ // Delete copy constructor and copy assignment
40
+ MovingVarianceFilter(const MovingVarianceFilter &) = delete;
41
+ MovingVarianceFilter &operator=(const MovingVarianceFilter &) = delete;
42
+
43
+ // Enable move semantics
44
+ MovingVarianceFilter(MovingVarianceFilter &&) noexcept = default;
45
+ MovingVarianceFilter &operator=(MovingVarianceFilter &&) noexcept = default;
46
+
47
+ /**
48
+ * @brief Adds a new sample to the filter.
49
+ *
50
+ * This updates the running sums and the internal circular buffer.
51
+ * The new variance value is calculated and returned.
52
+ *
53
+ * @param newValue The new sample value to add.
54
+ * @return T The new moving variance.
55
+ */
56
+ T addSample(T newValue);
57
+
58
+ /**
59
+ * @brief Adds a new sample with timestamp (time-aware mode).
60
+ *
61
+ * Expires old samples, rebuilds statistics, then adds new sample.
62
+ *
63
+ * @param newValue The new sample value to add.
64
+ * @param timestamp The timestamp in milliseconds.
65
+ * @return T The new moving variance.
66
+ */
67
+ T addSampleWithTimestamp(T newValue, double timestamp);
68
+
69
+ /**
70
+ * @brief Gets the current moving variance.
71
+ * @return T The variance of the samples currently in the buffer.
72
+ */
73
+ T getVariance() const;
74
+
75
+ /**
76
+ * @brief Clears all samples from the filter and resets the sums.
77
+ */
78
+ void clear();
79
+
80
+ /**
81
+ * @brief Checks if the filter's buffer is full (i.e., has N samples).
82
+ * @return true if the buffer is full, false otherwise.
83
+ */
84
+ bool isFull() const noexcept;
85
+
86
+ /**
87
+ * @brief Checks if the filter is in time-aware mode.
88
+ * @return true if time-aware, false otherwise.
89
+ */
90
+ bool isTimeAware() const noexcept;
91
+
92
+ /**
93
+ * @brief Exports the filter's internal state.
94
+ * @return A pair containing:
95
+ * 1. The buffer contents (std::vector<T>)
96
+ * 2. A pair of sums (running_sum, running_sum_of_squares)
97
+ */
98
+ std::pair<std::vector<T>, std::pair<T, T>> getState() const;
99
+
100
+ /**
101
+ * @brief Restores the filter's internal state.
102
+ * @param bufferData The buffer contents to restore.
103
+ * @param sum The running sum to restore.
104
+ * @param sumOfSquares The running sum of squares to restore.
105
+ */
106
+ void setState(const std::vector<T> &bufferData, T sum, T sumOfSquares);
107
+
108
+ private:
109
+ dsp::utils::CircularBufferArray<T> buffer;
110
+ T running_sum;
111
+ T running_sum_of_squares;
112
+ size_t window_size;
113
+ };
114
+ } // namespace dsp::core
@@ -0,0 +1,215 @@
1
+ #include "MovingZScoreFilter.h"
2
+
3
+ using namespace dsp::core;
4
+
5
+ // -----------------------------------------------------------------------------
6
+ // Constructor
7
+ // -----------------------------------------------------------------------------
8
+ template <typename T>
9
+ MovingZScoreFilter<T>::MovingZScoreFilter(size_t window_size, T epsilon)
10
+ : buffer(window_size), // Initialize the circular buffer
11
+ running_sum(0),
12
+ running_sum_of_squares(0),
13
+ window_size(window_size),
14
+ m_epsilon(epsilon)
15
+ {
16
+ if (window_size == 0)
17
+ {
18
+ throw std::invalid_argument("Window size must be greater than 0");
19
+ }
20
+ }
21
+
22
+ // -----------------------------------------------------------------------------
23
+ // Time-aware Constructor
24
+ // -----------------------------------------------------------------------------
25
+ template <typename T>
26
+ MovingZScoreFilter<T>::MovingZScoreFilter(size_t window_size, double window_duration_ms, T epsilon)
27
+ : buffer(window_size, window_duration_ms), // Initialize time-aware circular buffer
28
+ running_sum(0),
29
+ running_sum_of_squares(0),
30
+ window_size(window_size),
31
+ m_epsilon(epsilon)
32
+ {
33
+ if (window_size == 0)
34
+ {
35
+ throw std::invalid_argument("Window size must be greater than 0");
36
+ }
37
+ if (window_duration_ms <= 0.0)
38
+ {
39
+ throw std::invalid_argument("Window duration must be greater than 0");
40
+ }
41
+ }
42
+
43
+ // -----------------------------------------------------------------------------
44
+ // Method: addSample
45
+ // -----------------------------------------------------------------------------
46
+ template <typename T>
47
+ T MovingZScoreFilter<T>::addSample(T newValue)
48
+ {
49
+ T oldestValue = 0;
50
+
51
+ // If the buffer is full, get the oldest value
52
+ if (buffer.isFull())
53
+ {
54
+ oldestValue = buffer.peek();
55
+ }
56
+
57
+ // Add the new value to the buffer
58
+ buffer.pushOverwrite(newValue);
59
+
60
+ // Update running sums
61
+ T oldestValueSquared = oldestValue * oldestValue;
62
+ T newValueSquared = newValue * newValue;
63
+
64
+ running_sum = running_sum - oldestValue + newValue;
65
+ running_sum_of_squares = running_sum_of_squares - oldestValueSquared + newValueSquared;
66
+
67
+ // --- Calculate new stats ---
68
+ size_t count = buffer.getCount();
69
+ T countT = static_cast<T>(count);
70
+
71
+ // Calculate mean
72
+ T mean = running_sum / countT;
73
+
74
+ // Calculate mean of squares
75
+ T mean_of_squares = running_sum_of_squares / countT;
76
+
77
+ // Variance = E[X^2] - (E[X])^2
78
+ T variance = std::max(static_cast<T>(0), mean_of_squares - (mean * mean));
79
+
80
+ // Standard Deviation
81
+ T stddev = std::sqrt(variance);
82
+
83
+ // --- Calculate Z-Score ---
84
+ // Use epsilon to prevent division by zero
85
+ if (stddev < m_epsilon)
86
+ {
87
+ return 0; // If stddev is ~0, all values are the mean, so z-score is 0
88
+ }
89
+ else
90
+ {
91
+ return (newValue - mean) / stddev;
92
+ }
93
+ }
94
+
95
+ // -----------------------------------------------------------------------------
96
+ // Method: addSampleWithTimestamp
97
+ // -----------------------------------------------------------------------------
98
+ template <typename T>
99
+ T MovingZScoreFilter<T>::addSampleWithTimestamp(T newValue, double timestamp)
100
+ {
101
+ // Expire old samples
102
+ size_t expired_count = buffer.expireOld(timestamp);
103
+
104
+ // Rebuild running statistics if samples were expired
105
+ if (expired_count > 0)
106
+ {
107
+ running_sum = 0;
108
+ running_sum_of_squares = 0;
109
+
110
+ auto remaining = buffer.toVector();
111
+ for (const auto &value : remaining)
112
+ {
113
+ running_sum += value;
114
+ running_sum_of_squares += value * value;
115
+ }
116
+ }
117
+
118
+ // Add new sample
119
+ T oldestValue = 0;
120
+ if (buffer.isFull())
121
+ {
122
+ oldestValue = buffer.peek();
123
+ }
124
+
125
+ buffer.pushOverwriteWithTimestamp(newValue, timestamp);
126
+
127
+ // Update running sums
128
+ T oldestValueSquared = oldestValue * oldestValue;
129
+ T newValueSquared = newValue * newValue;
130
+
131
+ running_sum = running_sum - oldestValue + newValue;
132
+ running_sum_of_squares = running_sum_of_squares - oldestValueSquared + newValueSquared;
133
+
134
+ // --- Calculate new stats ---
135
+ size_t count = buffer.getCount();
136
+ T countT = static_cast<T>(count);
137
+
138
+ // Calculate mean
139
+ T mean = running_sum / countT;
140
+
141
+ // Calculate mean of squares
142
+ T mean_of_squares = running_sum_of_squares / countT;
143
+
144
+ // Variance = E[X^2] - (E[X])^2
145
+ T variance = std::max(static_cast<T>(0), mean_of_squares - (mean * mean));
146
+
147
+ // Standard Deviation
148
+ T stddev = std::sqrt(variance);
149
+
150
+ // --- Calculate Z-Score ---
151
+ if (stddev < m_epsilon)
152
+ {
153
+ return 0; // If stddev is ~0, all values are the mean, so z-score is 0
154
+ }
155
+ else
156
+ {
157
+ return (newValue - mean) / stddev;
158
+ }
159
+ }
160
+
161
+ // -----------------------------------------------------------------------------
162
+ // Method: clear
163
+ // -----------------------------------------------------------------------------
164
+ template <typename T>
165
+ void MovingZScoreFilter<T>::clear()
166
+ {
167
+ buffer.clear();
168
+ running_sum = 0;
169
+ running_sum_of_squares = 0;
170
+ }
171
+
172
+ // -----------------------------------------------------------------------------
173
+ // Method: isFull
174
+ // -----------------------------------------------------------------------------
175
+ template <typename T>
176
+ bool MovingZScoreFilter<T>::isFull() const noexcept
177
+ {
178
+ return buffer.isFull();
179
+ }
180
+
181
+ // -----------------------------------------------------------------------------
182
+ // Method: isTimeAware
183
+ // -----------------------------------------------------------------------------
184
+ template <typename T>
185
+ bool MovingZScoreFilter<T>::isTimeAware() const noexcept
186
+ {
187
+ return buffer.isTimeAware();
188
+ }
189
+
190
+ // -----------------------------------------------------------------------------
191
+ // Method: getState
192
+ // -----------------------------------------------------------------------------
193
+ template <typename T>
194
+ std::pair<std::vector<T>, std::pair<T, T>> MovingZScoreFilter<T>::getState() const
195
+ {
196
+ return {buffer.toVector(), {running_sum, running_sum_of_squares}};
197
+ }
198
+
199
+ // -----------------------------------------------------------------------------
200
+ // Method: setState
201
+ // -----------------------------------------------------------------------------
202
+ template <typename T>
203
+ void MovingZScoreFilter<T>::setState(const std::vector<T> &bufferData, T sum, T sumOfSquares)
204
+ {
205
+ buffer.fromVector(bufferData);
206
+ running_sum = sum;
207
+ running_sum_of_squares = sumOfSquares;
208
+ }
209
+
210
+ // Explicit template instantiation for common types
211
+ namespace dsp::core
212
+ {
213
+ template class MovingZScoreFilter<float>;
214
+ template class MovingZScoreFilter<double>;
215
+ }
@@ -0,0 +1,113 @@
1
+ #pragma once
2
+
3
+ #include "../utils/CircularBufferArray.h"
4
+ #include <utility>
5
+ #include <vector>
6
+ #include <cmath>
7
+ #include <stdexcept>
8
+ #include <algorithm> // for std::max
9
+
10
+ namespace dsp::core
11
+ {
12
+ /**
13
+ * @brief Implements an efficient Moving Z-Score normalization filter.
14
+ *
15
+ * This class uses a circular buffer to store the last 'N' samples
16
+ * and maintains a running sum AND a running sum-of-squares
17
+ * for O(1) mean and standard deviation calculation.
18
+ *
19
+ * Z-Score = (Value - Mean) / StandardDeviation
20
+ *
21
+ * @tparam T The numeric type of the samples (e.g., float, double).
22
+ */
23
+ template <typename T>
24
+ class MovingZScoreFilter
25
+ {
26
+ public:
27
+ /**
28
+ * @brief Constructs a new Moving Z-Score Filter.
29
+ * @param window_size The number of samples to average over (N).
30
+ * @param epsilon A small value to prevent division by zero (default 1e-6).
31
+ */
32
+ explicit MovingZScoreFilter(size_t window_size, T epsilon = 1e-6);
33
+
34
+ /**
35
+ * @brief Constructs a new time-aware Moving Z-Score Filter.
36
+ * @param window_size The buffer capacity in samples.
37
+ * @param window_duration_ms The time window duration in milliseconds.
38
+ * @param epsilon A small value to prevent division by zero (default 1e-6).
39
+ */
40
+ explicit MovingZScoreFilter(size_t window_size, double window_duration_ms, T epsilon = 1e-6);
41
+
42
+ // Delete copy constructor and copy assignment
43
+ MovingZScoreFilter(const MovingZScoreFilter &) = delete;
44
+ MovingZScoreFilter &operator=(const MovingZScoreFilter &) = delete;
45
+
46
+ // Enable move semantics
47
+ MovingZScoreFilter(MovingZScoreFilter &&) noexcept = default;
48
+ MovingZScoreFilter &operator=(MovingZScoreFilter &&) noexcept = default;
49
+
50
+ /**
51
+ * @brief Adds a new sample to the filter and returns its Z-Score.
52
+ *
53
+ * This updates the running sums and the internal circular buffer.
54
+ * The Z-Score of the new sample is calculated based on the
55
+ * *new* window statistics (including the sample just added).
56
+ *
57
+ * @param newValue The new sample value to add.
58
+ * @return T The Z-Score of the new sample.
59
+ */
60
+ T addSample(T newValue);
61
+
62
+ /**
63
+ * @brief Adds a new sample with timestamp and returns its Z-Score.
64
+ *
65
+ * Expires old samples, rebuilds statistics, then adds new sample.
66
+ *
67
+ * @param newValue The new sample value to add.
68
+ * @param timestamp The timestamp in milliseconds.
69
+ * @return T The Z-Score of the new sample.
70
+ */
71
+ T addSampleWithTimestamp(T newValue, double timestamp);
72
+
73
+ /**
74
+ * @brief Clears all samples from the filter and resets the sums.
75
+ */
76
+ void clear();
77
+
78
+ /**
79
+ * @brief Checks if the filter's buffer is full (i.e., has N samples).
80
+ * @return true if the buffer is full, false otherwise.
81
+ */
82
+ bool isFull() const noexcept;
83
+
84
+ /**
85
+ * @brief Checks if the filter is in time-aware mode.
86
+ * @return true if time-aware, false otherwise.
87
+ */
88
+ bool isTimeAware() const noexcept;
89
+
90
+ /**
91
+ * @brief Exports the filter's internal state.
92
+ * @return A pair containing:
93
+ * 1. The buffer contents (std::vector<T>)
94
+ * 2. A pair of sums (running_sum, running_sum_of_squares)
95
+ */
96
+ std::pair<std::vector<T>, std::pair<T, T>> getState() const;
97
+
98
+ /**
99
+ * @brief Restores the filter's internal state.
100
+ * @param bufferData The buffer contents to restore.
101
+ * @param sum The running sum to restore.
102
+ * @param sumOfSquares The running sum of squares to restore.
103
+ */
104
+ void setState(const std::vector<T> &bufferData, T sum, T sumOfSquares);
105
+
106
+ private:
107
+ dsp::utils::CircularBufferArray<T> buffer;
108
+ T running_sum;
109
+ T running_sum_of_squares;
110
+ size_t window_size;
111
+ T m_epsilon;
112
+ };
113
+ } // namespace dsp::core