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,239 @@
|
|
|
1
|
+
#include "SlidingWindowFilter.h"
|
|
2
|
+
#include "../core/Policies.h"
|
|
3
|
+
|
|
4
|
+
using namespace dsp::utils;
|
|
5
|
+
|
|
6
|
+
// -----------------------------------------------------------------------------
|
|
7
|
+
// Constructor
|
|
8
|
+
// Constructs a new sliding window filter with the specified window size
|
|
9
|
+
// @ param window_size - The number of samples in the sliding window
|
|
10
|
+
// @ param policy - An instance of the policy (default-constructed if not provided)
|
|
11
|
+
// @ return void
|
|
12
|
+
// -----------------------------------------------------------------------------
|
|
13
|
+
template <typename T, typename Policy>
|
|
14
|
+
SlidingWindowFilter<T, Policy>::SlidingWindowFilter(size_t window_size, Policy policy)
|
|
15
|
+
: m_buffer(window_size), m_policy(std::move(policy))
|
|
16
|
+
{
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// -----------------------------------------------------------------------------
|
|
20
|
+
// Constructor (Time-Aware)
|
|
21
|
+
// Constructs a new time-aware sliding window filter
|
|
22
|
+
// @ param window_size - The number of samples in the sliding window
|
|
23
|
+
// @ param window_duration_ms - The maximum age of samples in milliseconds
|
|
24
|
+
// @ param policy - An instance of the policy (default-constructed if not provided)
|
|
25
|
+
// @ return void
|
|
26
|
+
// -----------------------------------------------------------------------------
|
|
27
|
+
template <typename T, typename Policy>
|
|
28
|
+
SlidingWindowFilter<T, Policy>::SlidingWindowFilter(size_t window_size, double window_duration_ms, Policy policy)
|
|
29
|
+
: m_buffer(window_size, window_duration_ms), m_policy(std::move(policy))
|
|
30
|
+
{
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// -----------------------------------------------------------------------------
|
|
34
|
+
// addSample
|
|
35
|
+
// Adds a new sample to the sliding window
|
|
36
|
+
// If buffer is full, removes oldest sample (delegates to policy.onRemove)
|
|
37
|
+
// Then adds new sample (delegates to policy.onAdd)
|
|
38
|
+
// @ param newValue - The new sample to add
|
|
39
|
+
// @ return T - The computed result from the policy
|
|
40
|
+
// -----------------------------------------------------------------------------
|
|
41
|
+
template <typename T, typename Policy>
|
|
42
|
+
T SlidingWindowFilter<T, Policy>::addSample(T newValue)
|
|
43
|
+
{
|
|
44
|
+
if (m_buffer.isFull())
|
|
45
|
+
{
|
|
46
|
+
T oldestValue = m_buffer.peek();
|
|
47
|
+
m_policy.onRemove(oldestValue);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
m_buffer.pushOverwrite(newValue);
|
|
51
|
+
m_policy.onAdd(newValue);
|
|
52
|
+
|
|
53
|
+
return m_policy.getResult(m_buffer.getCount());
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// -----------------------------------------------------------------------------
|
|
57
|
+
// addSampleWithTimestamp
|
|
58
|
+
// Adds a new sample with timestamp (time-aware mode)
|
|
59
|
+
// Expires old samples first, then adds the new sample
|
|
60
|
+
// @ param newValue - The new sample to add
|
|
61
|
+
// @ param timestamp - The timestamp in milliseconds
|
|
62
|
+
// @ return T - The computed result from the policy
|
|
63
|
+
// -----------------------------------------------------------------------------
|
|
64
|
+
template <typename T, typename Policy>
|
|
65
|
+
T SlidingWindowFilter<T, Policy>::addSampleWithTimestamp(T newValue, double timestamp)
|
|
66
|
+
{
|
|
67
|
+
if (!m_buffer.isTimeAware())
|
|
68
|
+
{
|
|
69
|
+
throw std::runtime_error("addSampleWithTimestamp requires time-aware mode");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// First, expire old samples and update policy
|
|
73
|
+
while (m_buffer.getCount() > 0)
|
|
74
|
+
{
|
|
75
|
+
size_t initial_count = m_buffer.getCount();
|
|
76
|
+
m_buffer.expireOld(timestamp);
|
|
77
|
+
size_t expired_count = initial_count - m_buffer.getCount();
|
|
78
|
+
|
|
79
|
+
// We need to reconstruct the policy state from remaining samples
|
|
80
|
+
// This is inefficient but correct. Better approach: track what was removed
|
|
81
|
+
if (expired_count > 0)
|
|
82
|
+
{
|
|
83
|
+
// Rebuild policy from scratch using remaining samples
|
|
84
|
+
m_policy.clear();
|
|
85
|
+
auto remaining = m_buffer.toVector();
|
|
86
|
+
for (const auto &value : remaining)
|
|
87
|
+
{
|
|
88
|
+
m_policy.onAdd(value);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
break; // expireOld already removed all expired samples
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Check if buffer is full (by sample count)
|
|
95
|
+
if (m_buffer.isFull())
|
|
96
|
+
{
|
|
97
|
+
T oldestValue = m_buffer.peek();
|
|
98
|
+
m_policy.onRemove(oldestValue);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Add the new sample
|
|
102
|
+
m_buffer.pushOverwriteWithTimestamp(newValue, timestamp);
|
|
103
|
+
m_policy.onAdd(newValue);
|
|
104
|
+
|
|
105
|
+
return m_policy.getResult(m_buffer.getCount());
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// -----------------------------------------------------------------------------
|
|
109
|
+
// clear
|
|
110
|
+
// Clears all samples from the filter
|
|
111
|
+
// Resets both the circular buffer and the policy state
|
|
112
|
+
// @ return void
|
|
113
|
+
// -----------------------------------------------------------------------------
|
|
114
|
+
template <typename T, typename Policy>
|
|
115
|
+
void SlidingWindowFilter<T, Policy>::clear()
|
|
116
|
+
{
|
|
117
|
+
m_buffer.clear();
|
|
118
|
+
m_policy.clear();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// -----------------------------------------------------------------------------
|
|
122
|
+
// isFull
|
|
123
|
+
// Checks if the buffer is full
|
|
124
|
+
// @ return bool - true if buffer contains window_size samples
|
|
125
|
+
// -----------------------------------------------------------------------------
|
|
126
|
+
template <typename T, typename Policy>
|
|
127
|
+
bool SlidingWindowFilter<T, Policy>::isFull() const noexcept
|
|
128
|
+
{
|
|
129
|
+
return m_buffer.isFull();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// -----------------------------------------------------------------------------
|
|
133
|
+
// getCount
|
|
134
|
+
// Gets the current number of samples in the buffer
|
|
135
|
+
// @ return size_t - The number of samples
|
|
136
|
+
// -----------------------------------------------------------------------------
|
|
137
|
+
template <typename T, typename Policy>
|
|
138
|
+
size_t SlidingWindowFilter<T, Policy>::getCount() const noexcept
|
|
139
|
+
{
|
|
140
|
+
return m_buffer.getCount();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// -----------------------------------------------------------------------------
|
|
144
|
+
// getWindowSize
|
|
145
|
+
// Gets the window size
|
|
146
|
+
// @ return size_t - The maximum number of samples in the window
|
|
147
|
+
// -----------------------------------------------------------------------------
|
|
148
|
+
template <typename T, typename Policy>
|
|
149
|
+
size_t SlidingWindowFilter<T, Policy>::getWindowSize() const noexcept
|
|
150
|
+
{
|
|
151
|
+
return m_buffer.getCapacity();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// -----------------------------------------------------------------------------
|
|
155
|
+
// getBufferContents
|
|
156
|
+
// Exports the buffer contents
|
|
157
|
+
// @ return std::vector<T> - A vector containing all samples in the buffer
|
|
158
|
+
// -----------------------------------------------------------------------------
|
|
159
|
+
template <typename T, typename Policy>
|
|
160
|
+
std::vector<T> SlidingWindowFilter<T, Policy>::getBufferContents() const
|
|
161
|
+
{
|
|
162
|
+
return m_buffer.toVector();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// -----------------------------------------------------------------------------
|
|
166
|
+
// setBufferContents
|
|
167
|
+
// Restores the buffer contents
|
|
168
|
+
// @ param bufferData - The samples to restore to the buffer
|
|
169
|
+
// @ return void
|
|
170
|
+
// -----------------------------------------------------------------------------
|
|
171
|
+
template <typename T, typename Policy>
|
|
172
|
+
void SlidingWindowFilter<T, Policy>::setBufferContents(const std::vector<T> &bufferData)
|
|
173
|
+
{
|
|
174
|
+
m_buffer.fromVector(bufferData);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// -----------------------------------------------------------------------------
|
|
178
|
+
// getPolicy
|
|
179
|
+
// Access to the policy for state serialization
|
|
180
|
+
// @ return Policy& - Reference to the internal policy object
|
|
181
|
+
// -----------------------------------------------------------------------------
|
|
182
|
+
template <typename T, typename Policy>
|
|
183
|
+
Policy &SlidingWindowFilter<T, Policy>::getPolicy()
|
|
184
|
+
{
|
|
185
|
+
return m_policy;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// -----------------------------------------------------------------------------
|
|
189
|
+
// getPolicy (const)
|
|
190
|
+
// Const access to the policy
|
|
191
|
+
// @ return const Policy& - Const reference to the internal policy object
|
|
192
|
+
// -----------------------------------------------------------------------------
|
|
193
|
+
template <typename T, typename Policy>
|
|
194
|
+
const Policy &SlidingWindowFilter<T, Policy>::getPolicy() const
|
|
195
|
+
{
|
|
196
|
+
return m_policy;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Note: getState() and setState() are template methods defined in the header
|
|
200
|
+
// They use decltype and are auto-deduced, so they must remain in the header
|
|
201
|
+
|
|
202
|
+
// -----------------------------------------------------------------------------
|
|
203
|
+
// Explicit template instantiations
|
|
204
|
+
// Instantiate all Policy combinations we use
|
|
205
|
+
// -----------------------------------------------------------------------------
|
|
206
|
+
namespace dsp::utils
|
|
207
|
+
{
|
|
208
|
+
using namespace dsp::core;
|
|
209
|
+
|
|
210
|
+
// MeanPolicy instantiations
|
|
211
|
+
template class SlidingWindowFilter<int, MeanPolicy<int>>;
|
|
212
|
+
template class SlidingWindowFilter<float, MeanPolicy<float>>;
|
|
213
|
+
template class SlidingWindowFilter<double, MeanPolicy<double>>;
|
|
214
|
+
|
|
215
|
+
// RmsPolicy instantiations
|
|
216
|
+
template class SlidingWindowFilter<int, RmsPolicy<int>>;
|
|
217
|
+
template class SlidingWindowFilter<float, RmsPolicy<float>>;
|
|
218
|
+
template class SlidingWindowFilter<double, RmsPolicy<double>>;
|
|
219
|
+
|
|
220
|
+
// MeanAbsoluteValuePolicy instantiations
|
|
221
|
+
template class SlidingWindowFilter<float, MeanAbsoluteValuePolicy<float>>;
|
|
222
|
+
template class SlidingWindowFilter<double, MeanAbsoluteValuePolicy<double>>;
|
|
223
|
+
|
|
224
|
+
// VariancePolicy instantiations
|
|
225
|
+
template class SlidingWindowFilter<int, VariancePolicy<int>>;
|
|
226
|
+
template class SlidingWindowFilter<float, VariancePolicy<float>>;
|
|
227
|
+
template class SlidingWindowFilter<double, VariancePolicy<double>>;
|
|
228
|
+
|
|
229
|
+
// Note: ZScorePolicy is NOT instantiated here because it has a different interface
|
|
230
|
+
// (getResult takes 2 parameters: currentValue and count, not just count)
|
|
231
|
+
// MovingZScoreFilter doesn't use SlidingWindowFilter template
|
|
232
|
+
|
|
233
|
+
// WaveformLengthFilter instantiations
|
|
234
|
+
template class dsp::utils::SlidingWindowFilter<float, SumPolicy<float>>;
|
|
235
|
+
template class dsp::utils::SlidingWindowFilter<double, SumPolicy<double>>;
|
|
236
|
+
|
|
237
|
+
// WAMP,SSC instantiations
|
|
238
|
+
template class dsp::utils::SlidingWindowFilter<bool, CounterPolicy>;
|
|
239
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#include "CircularBufferArray.h"
|
|
3
|
+
#include <utility>
|
|
4
|
+
#include <vector>
|
|
5
|
+
|
|
6
|
+
namespace dsp::utils
|
|
7
|
+
{
|
|
8
|
+
/**
|
|
9
|
+
* @brief A generic, policy-based sliding window filter engine.
|
|
10
|
+
*
|
|
11
|
+
* This class implements the sliding window logic (circular buffer management)
|
|
12
|
+
* while delegating the statistical computation to a Policy class.
|
|
13
|
+
*
|
|
14
|
+
* This is a zero-cost abstraction: the compiler inlines all policy methods,
|
|
15
|
+
* resulting in performance identical to hand-written specialized filters.
|
|
16
|
+
*
|
|
17
|
+
* @tparam T The numeric type (e.g., float, double).
|
|
18
|
+
* @tparam Policy The struct that defines the state and math operations.
|
|
19
|
+
* Must implement: onAdd(T), onRemove(T), clear(), getResult(size_t)
|
|
20
|
+
*/
|
|
21
|
+
template <typename T, typename Policy>
|
|
22
|
+
class SlidingWindowFilter
|
|
23
|
+
{
|
|
24
|
+
public:
|
|
25
|
+
/**
|
|
26
|
+
* @brief Constructs a new sliding window filter.
|
|
27
|
+
* @param window_size The number of samples in the sliding window.
|
|
28
|
+
* @param policy An instance of the policy (default-constructed if not provided).
|
|
29
|
+
*/
|
|
30
|
+
explicit SlidingWindowFilter(size_t window_size, Policy policy = Policy());
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @brief Constructs a new time-aware sliding window filter.
|
|
34
|
+
* @param window_size The number of samples in the sliding window.
|
|
35
|
+
* @param window_duration_ms The maximum age of samples in milliseconds.
|
|
36
|
+
* @param policy An instance of the policy (default-constructed if not provided).
|
|
37
|
+
*/
|
|
38
|
+
explicit SlidingWindowFilter(size_t window_size, double window_duration_ms, Policy policy = Policy());
|
|
39
|
+
|
|
40
|
+
// Delete copy constructor and copy assignment
|
|
41
|
+
SlidingWindowFilter(const SlidingWindowFilter &) = delete;
|
|
42
|
+
SlidingWindowFilter &operator=(const SlidingWindowFilter &) = delete;
|
|
43
|
+
|
|
44
|
+
// Enable move semantics
|
|
45
|
+
SlidingWindowFilter(SlidingWindowFilter &&) noexcept = default;
|
|
46
|
+
SlidingWindowFilter &operator=(SlidingWindowFilter &&) noexcept = default;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @brief Adds a new sample to the sliding window.
|
|
50
|
+
*
|
|
51
|
+
* If the buffer is full, removes the oldest sample (delegates to policy.onRemove),
|
|
52
|
+
* then adds the new sample (delegates to policy.onAdd).
|
|
53
|
+
*
|
|
54
|
+
* @param newValue The new sample to add.
|
|
55
|
+
* @return T The computed result from the policy.
|
|
56
|
+
*/
|
|
57
|
+
T addSample(T newValue);
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @brief Adds a new sample with timestamp (time-aware mode).
|
|
61
|
+
*
|
|
62
|
+
* Expires old samples first, then adds the new sample.
|
|
63
|
+
*
|
|
64
|
+
* @param newValue The new sample to add.
|
|
65
|
+
* @param timestamp The timestamp in milliseconds.
|
|
66
|
+
* @return T The computed result from the policy.
|
|
67
|
+
*/
|
|
68
|
+
T addSampleWithTimestamp(T newValue, double timestamp);
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @brief Clears all samples from the filter.
|
|
72
|
+
*
|
|
73
|
+
* Resets both the circular buffer and the policy state.
|
|
74
|
+
*/
|
|
75
|
+
void clear();
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @brief Checks if the buffer is full.
|
|
79
|
+
* @return true if the buffer contains window_size samples.
|
|
80
|
+
*/
|
|
81
|
+
bool isFull() const noexcept;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @brief Gets the current number of samples in the buffer.
|
|
85
|
+
* @return size_t The number of samples.
|
|
86
|
+
*/
|
|
87
|
+
size_t getCount() const noexcept;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @brief Gets the window size.
|
|
91
|
+
* @return size_t The maximum number of samples in the window.
|
|
92
|
+
*/
|
|
93
|
+
size_t getWindowSize() const noexcept;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @brief Checks if this is a time-aware filter.
|
|
97
|
+
* @return bool True if window duration is set.
|
|
98
|
+
*/
|
|
99
|
+
bool isTimeAware() const noexcept { return m_buffer.isTimeAware(); }
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @brief Exports the buffer contents.
|
|
103
|
+
* @return std::vector<T> A vector containing all samples in the buffer.
|
|
104
|
+
*/
|
|
105
|
+
std::vector<T> getBufferContents() const;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @brief Restores the buffer contents.
|
|
109
|
+
* @param bufferData The samples to restore to the buffer.
|
|
110
|
+
*/
|
|
111
|
+
void setBufferContents(const std::vector<T> &bufferData);
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @brief Access to the policy for state serialization.
|
|
115
|
+
* @return Policy& Reference to the internal policy object.
|
|
116
|
+
*/
|
|
117
|
+
Policy &getPolicy();
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @brief Const access to the policy.
|
|
121
|
+
* @return const Policy& Const reference to the internal policy object.
|
|
122
|
+
*/
|
|
123
|
+
const Policy &getPolicy() const;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* @brief Exports the complete filter state (buffer + policy state).
|
|
127
|
+
*
|
|
128
|
+
* This is a generic state serialization method that works with any policy
|
|
129
|
+
* that implements getState(). Returns a pair of buffer contents and policy state.
|
|
130
|
+
*
|
|
131
|
+
* @return std::pair<std::vector<T>, PolicyState> Buffer contents and policy state.
|
|
132
|
+
*/
|
|
133
|
+
auto getState() const -> std::pair<std::vector<T>, decltype(std::declval<Policy>().getState())>
|
|
134
|
+
{
|
|
135
|
+
return {m_buffer.toVector(), m_policy.getState()};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* @brief Restores the complete filter state (buffer + policy state).
|
|
140
|
+
*
|
|
141
|
+
* This is a generic state deserialization method that works with any policy
|
|
142
|
+
* that implements setState(). Restores both buffer contents and policy state.
|
|
143
|
+
*
|
|
144
|
+
* @param bufferData The buffer contents to restore.
|
|
145
|
+
* @param policyState The policy state to restore (type depends on policy).
|
|
146
|
+
*/
|
|
147
|
+
template <typename PolicyState>
|
|
148
|
+
void setState(const std::vector<T> &bufferData, const PolicyState &policyState)
|
|
149
|
+
{
|
|
150
|
+
m_buffer.fromVector(bufferData);
|
|
151
|
+
m_policy.setState(policyState);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private:
|
|
155
|
+
CircularBufferArray<T> m_buffer;
|
|
156
|
+
Policy m_policy;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
} // namespace dsp::utils
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
#include "TimeSeriesBuffer.h"
|
|
2
|
+
|
|
3
|
+
namespace dsp::utils
|
|
4
|
+
{
|
|
5
|
+
// -----------------------------------------------------------------------------
|
|
6
|
+
// Constructor
|
|
7
|
+
// -----------------------------------------------------------------------------
|
|
8
|
+
template <typename T>
|
|
9
|
+
TimeSeriesBuffer<T>::TimeSeriesBuffer(size_t max_samples, uint64_t window_duration_ms)
|
|
10
|
+
: m_max_samples(max_samples), m_window_duration_ms(window_duration_ms)
|
|
11
|
+
{
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// -----------------------------------------------------------------------------
|
|
15
|
+
// push - Adds a new timestamped sample
|
|
16
|
+
// Automatically enforces window constraints after insertion
|
|
17
|
+
// -----------------------------------------------------------------------------
|
|
18
|
+
template <typename T>
|
|
19
|
+
void TimeSeriesBuffer<T>::push(TimestampType timestamp, T value)
|
|
20
|
+
{
|
|
21
|
+
m_samples.emplace_back(timestamp, value);
|
|
22
|
+
enforceWindowConstraints();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// -----------------------------------------------------------------------------
|
|
26
|
+
// removeOlderThan - Removes samples older than cutoff timestamp
|
|
27
|
+
// -----------------------------------------------------------------------------
|
|
28
|
+
template <typename T>
|
|
29
|
+
size_t TimeSeriesBuffer<T>::removeOlderThan(TimestampType cutoff_timestamp)
|
|
30
|
+
{
|
|
31
|
+
size_t removed = 0;
|
|
32
|
+
while (!m_samples.empty() && m_samples.front().first < cutoff_timestamp)
|
|
33
|
+
{
|
|
34
|
+
m_samples.pop_front();
|
|
35
|
+
++removed;
|
|
36
|
+
}
|
|
37
|
+
return removed;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// -----------------------------------------------------------------------------
|
|
41
|
+
// front - Gets the oldest sample (does not remove)
|
|
42
|
+
// -----------------------------------------------------------------------------
|
|
43
|
+
template <typename T>
|
|
44
|
+
const typename TimeSeriesBuffer<T>::Sample &TimeSeriesBuffer<T>::front() const
|
|
45
|
+
{
|
|
46
|
+
if (m_samples.empty())
|
|
47
|
+
{
|
|
48
|
+
throw std::out_of_range("TimeSeriesBuffer::front() called on empty buffer");
|
|
49
|
+
}
|
|
50
|
+
return m_samples.front();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// -----------------------------------------------------------------------------
|
|
54
|
+
// back - Gets the newest sample (does not remove)
|
|
55
|
+
// -----------------------------------------------------------------------------
|
|
56
|
+
template <typename T>
|
|
57
|
+
const typename TimeSeriesBuffer<T>::Sample &TimeSeriesBuffer<T>::back() const
|
|
58
|
+
{
|
|
59
|
+
if (m_samples.empty())
|
|
60
|
+
{
|
|
61
|
+
throw std::out_of_range("TimeSeriesBuffer::back() called on empty buffer");
|
|
62
|
+
}
|
|
63
|
+
return m_samples.back();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// -----------------------------------------------------------------------------
|
|
67
|
+
// popFront - Removes the oldest sample
|
|
68
|
+
// -----------------------------------------------------------------------------
|
|
69
|
+
template <typename T>
|
|
70
|
+
void TimeSeriesBuffer<T>::popFront()
|
|
71
|
+
{
|
|
72
|
+
if (m_samples.empty())
|
|
73
|
+
{
|
|
74
|
+
throw std::out_of_range("TimeSeriesBuffer::popFront() called on empty buffer");
|
|
75
|
+
}
|
|
76
|
+
m_samples.pop_front();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// -----------------------------------------------------------------------------
|
|
80
|
+
// size - Returns the current number of samples
|
|
81
|
+
// -----------------------------------------------------------------------------
|
|
82
|
+
template <typename T>
|
|
83
|
+
size_t TimeSeriesBuffer<T>::size() const noexcept
|
|
84
|
+
{
|
|
85
|
+
return m_samples.size();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// -----------------------------------------------------------------------------
|
|
89
|
+
// empty - Checks if buffer is empty
|
|
90
|
+
// -----------------------------------------------------------------------------
|
|
91
|
+
template <typename T>
|
|
92
|
+
bool TimeSeriesBuffer<T>::empty() const noexcept
|
|
93
|
+
{
|
|
94
|
+
return m_samples.empty();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// -----------------------------------------------------------------------------
|
|
98
|
+
// clear - Removes all samples
|
|
99
|
+
// -----------------------------------------------------------------------------
|
|
100
|
+
template <typename T>
|
|
101
|
+
void TimeSeriesBuffer<T>::clear() noexcept
|
|
102
|
+
{
|
|
103
|
+
m_samples.clear();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// -----------------------------------------------------------------------------
|
|
107
|
+
// toVector - Exports all samples as a vector
|
|
108
|
+
// -----------------------------------------------------------------------------
|
|
109
|
+
template <typename T>
|
|
110
|
+
std::vector<typename TimeSeriesBuffer<T>::Sample> TimeSeriesBuffer<T>::toVector() const
|
|
111
|
+
{
|
|
112
|
+
return std::vector<Sample>(m_samples.begin(), m_samples.end());
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// -----------------------------------------------------------------------------
|
|
116
|
+
// fromVector - Restores buffer from vector
|
|
117
|
+
// -----------------------------------------------------------------------------
|
|
118
|
+
template <typename T>
|
|
119
|
+
void TimeSeriesBuffer<T>::fromVector(const std::vector<Sample> &samples)
|
|
120
|
+
{
|
|
121
|
+
m_samples.clear();
|
|
122
|
+
for (const auto &sample : samples)
|
|
123
|
+
{
|
|
124
|
+
m_samples.push_back(sample);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// -----------------------------------------------------------------------------
|
|
129
|
+
// getTimeSpan - Returns time difference between newest and oldest sample
|
|
130
|
+
// -----------------------------------------------------------------------------
|
|
131
|
+
template <typename T>
|
|
132
|
+
uint64_t TimeSeriesBuffer<T>::getTimeSpan() const noexcept
|
|
133
|
+
{
|
|
134
|
+
if (m_samples.size() < 2)
|
|
135
|
+
{
|
|
136
|
+
return 0;
|
|
137
|
+
}
|
|
138
|
+
return m_samples.back().first - m_samples.front().first;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// -----------------------------------------------------------------------------
|
|
142
|
+
// begin/end - Iterator access
|
|
143
|
+
// -----------------------------------------------------------------------------
|
|
144
|
+
template <typename T>
|
|
145
|
+
typename std::deque<typename TimeSeriesBuffer<T>::Sample>::const_iterator
|
|
146
|
+
TimeSeriesBuffer<T>::begin() const noexcept
|
|
147
|
+
{
|
|
148
|
+
return m_samples.begin();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
template <typename T>
|
|
152
|
+
typename std::deque<typename TimeSeriesBuffer<T>::Sample>::const_iterator
|
|
153
|
+
TimeSeriesBuffer<T>::end() const noexcept
|
|
154
|
+
{
|
|
155
|
+
return m_samples.end();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// -----------------------------------------------------------------------------
|
|
159
|
+
// getMaxSamples - Returns the max sample count constraint
|
|
160
|
+
// -----------------------------------------------------------------------------
|
|
161
|
+
template <typename T>
|
|
162
|
+
size_t TimeSeriesBuffer<T>::getMaxSamples() const noexcept
|
|
163
|
+
{
|
|
164
|
+
return m_max_samples;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// -----------------------------------------------------------------------------
|
|
168
|
+
// getWindowDuration - Returns the time window constraint
|
|
169
|
+
// -----------------------------------------------------------------------------
|
|
170
|
+
template <typename T>
|
|
171
|
+
uint64_t TimeSeriesBuffer<T>::getWindowDuration() const noexcept
|
|
172
|
+
{
|
|
173
|
+
return m_window_duration_ms;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// -----------------------------------------------------------------------------
|
|
177
|
+
// enforceWindowConstraints - Private helper to maintain window limits
|
|
178
|
+
// Called automatically after each push()
|
|
179
|
+
// -----------------------------------------------------------------------------
|
|
180
|
+
template <typename T>
|
|
181
|
+
void TimeSeriesBuffer<T>::enforceWindowConstraints()
|
|
182
|
+
{
|
|
183
|
+
// Enforce time-based window (if enabled)
|
|
184
|
+
if (m_window_duration_ms > 0 && m_samples.size() > 1)
|
|
185
|
+
{
|
|
186
|
+
TimestampType newest_timestamp = m_samples.back().first;
|
|
187
|
+
TimestampType cutoff = newest_timestamp - m_window_duration_ms;
|
|
188
|
+
removeOlderThan(cutoff);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Enforce sample-count window (if enabled)
|
|
192
|
+
while (m_max_samples > 0 && m_samples.size() > m_max_samples)
|
|
193
|
+
{
|
|
194
|
+
m_samples.pop_front();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// -----------------------------------------------------------------------------
|
|
199
|
+
// Explicit template instantiations
|
|
200
|
+
// -----------------------------------------------------------------------------
|
|
201
|
+
template class TimeSeriesBuffer<int>;
|
|
202
|
+
template class TimeSeriesBuffer<float>;
|
|
203
|
+
template class TimeSeriesBuffer<double>;
|
|
204
|
+
|
|
205
|
+
} // namespace dsp::utils
|