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,101 @@
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 WampFilter
13
+ {
14
+ public:
15
+ /**
16
+ * @brief Constructs a new Wamp 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 WampFilter(size_t window_size, T threshold)
21
+ : m_filter(window_size), m_threshold(threshold), m_previous_sample(0.0), m_is_initialized(false) {}
22
+
23
+ // Delete copy constructor and copy assignment
24
+ WampFilter(const WampFilter &) = delete;
25
+ WampFilter &operator=(const WampFilter &) = delete;
26
+
27
+ // Enable move semantics
28
+ WampFilter(WampFilter &&) noexcept = default;
29
+ WampFilter &operator=(WampFilter &&) noexcept = default;
30
+
31
+ /**
32
+ * @brief Adds a new sample to the filter.
33
+ * @param newValue The new sample value to add.
34
+ * @return T The updated WAMP count over the window.
35
+ */
36
+ T addSample(T newValue)
37
+ {
38
+ bool did_exceed = false;
39
+ if (m_is_initialized)
40
+ {
41
+ did_exceed = std::abs(newValue - m_previous_sample) > m_threshold;
42
+ }
43
+
44
+ m_previous_sample = newValue;
45
+ m_is_initialized = true;
46
+
47
+ // Add the boolean to the window, get the count back
48
+ // Cast count (size_t) to T (float/double)
49
+ return static_cast<T>(m_filter.addSample(did_exceed));
50
+ }
51
+
52
+ /**
53
+ * @brief Clears all samples from the filter and resets state.
54
+ */
55
+ void clear()
56
+ {
57
+ m_filter.clear();
58
+ m_previous_sample = 0.0;
59
+ m_is_initialized = false;
60
+ }
61
+
62
+ /**
63
+ * @brief Exports the complete filter state (buffer + previous sample).
64
+ * @return A pair containing the buffer contents and previous sample.
65
+ */
66
+ auto getState() const
67
+ {
68
+ return std::make_pair(m_filter.getState(), m_previous_sample);
69
+ }
70
+
71
+ /**
72
+ * @brief Restores the complete filter state (buffer + previous sample).
73
+ * @param buffer The buffer contents to restore.
74
+ * @param count The count of 'true' entries in the buffer.
75
+ * @param prevSample The previous sample value to restore.
76
+ * @return void
77
+ */
78
+ void setState(const std::vector<bool> &buffer, size_t count, T prevSample)
79
+ {
80
+ m_filter.setState(buffer, count);
81
+ m_previous_sample = prevSample;
82
+ m_is_initialized = true;
83
+ }
84
+
85
+ /**
86
+ * @brief Gets const access to the internal SlidingWindowFilter.
87
+ * @return const SlidingWindowFilter<bool, CounterPolicy>& Reference to the internal filter
88
+ */
89
+ const dsp::utils::SlidingWindowFilter<bool, CounterPolicy> &getInternalFilter() const
90
+ {
91
+ return m_filter;
92
+ }
93
+
94
+ private:
95
+ dsp::utils::SlidingWindowFilter<bool, CounterPolicy> m_filter;
96
+ T m_threshold;
97
+ T m_previous_sample;
98
+ bool m_is_initialized;
99
+ };
100
+
101
+ } // namespace dsp::core
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @file WaveformLengthFilter.cc
3
+ * @brief Implementation file for WaveformLengthFilter (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 WaveformLengthFilter.h,
6
+ * which delegates to SlidingWindowFilter<T, SumPolicy<T>>.
7
+ */
8
+
9
+ #include "WaveformLengthFilter.h"
10
+
11
+ // Explicit template instantiation for float type
12
+ namespace dsp::core
13
+ {
14
+ template class WaveformLengthFilter<float>;
15
+ template class WaveformLengthFilter<double>;
16
+
17
+ }
@@ -0,0 +1,98 @@
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 WaveformLengthFilter
13
+ {
14
+ public:
15
+ /**
16
+ * @brief Constructs a new Waveform Length Filter.
17
+ * @param window_size The number of samples to consider in the sliding window.
18
+ */
19
+ explicit WaveformLengthFilter(size_t window_size)
20
+ : m_filter(window_size), m_previous_sample(0.0f), m_is_initialized(false) {}
21
+
22
+ // Delete copy constructor and copy assignment
23
+ WaveformLengthFilter(const WaveformLengthFilter &) = delete;
24
+ WaveformLengthFilter &operator=(const WaveformLengthFilter &) = delete;
25
+
26
+ // Enable move semantics
27
+ WaveformLengthFilter(WaveformLengthFilter &&) noexcept = default;
28
+ WaveformLengthFilter &operator=(WaveformLengthFilter &&) noexcept = default;
29
+
30
+ /**
31
+ * @brief Adds a new sample to the filter.
32
+ * @param newValue The new sample value to add.
33
+ * @return T The updated waveform length over the window.
34
+ */
35
+ T addSample(float newValue)
36
+ {
37
+ T diff = 0.0;
38
+ if (m_is_initialized)
39
+ {
40
+ diff = std::abs(newValue - m_previous_sample);
41
+ }
42
+
43
+ m_previous_sample = newValue;
44
+ m_is_initialized = true;
45
+
46
+ // Add the difference to the window, get the sum of diffs back
47
+ return m_filter.addSample(diff);
48
+ }
49
+
50
+ /**
51
+ * @brief Clears all samples from the filter and resets state.
52
+ */
53
+ void clear()
54
+ {
55
+ m_filter.clear();
56
+ m_previous_sample = 0.0;
57
+ m_is_initialized = false;
58
+ }
59
+
60
+ /**
61
+ * @brief Exports the complete filter state (buffer + previous sample).
62
+ * @return A pair containing the buffer contents, running sum, and previous sample.
63
+ */
64
+ std::pair<std::pair<std::vector<T>, double>, T> getState() const
65
+ {
66
+ // Returns: { {buffer_of_diffs, running_sum}, previous_sample }
67
+ return std::make_pair(m_filter.getState(), m_previous_sample);
68
+ }
69
+
70
+ /**
71
+ * @brief Restores the complete filter state (buffer + previous sample).
72
+ * @param buffer The buffer of differences to restore.
73
+ * @param runningSum The running sum of differences to restore.
74
+ * @param prevSample The previous sample value to restore.
75
+ * @return void
76
+ */
77
+ void setState(const std::vector<T> &buffer, double runningSum, T prevSample)
78
+ {
79
+ m_filter.setState(buffer, runningSum);
80
+ m_previous_sample = prevSample;
81
+ m_is_initialized = true; // Assume if state is set, we are init'd
82
+ }
83
+
84
+ /**
85
+ * @brief Gets const access to the internal SlidingWindowFilter.
86
+ * @return const SlidingWindowFilter<float, SumPolicy<float>>& Reference to the internal filter
87
+ */
88
+ const dsp::utils::SlidingWindowFilter<T, SumPolicy<T>> &getInternalFilter() const
89
+ {
90
+ return m_filter;
91
+ }
92
+
93
+ private:
94
+ dsp::utils::SlidingWindowFilter<T, SumPolicy<T>> m_filter;
95
+ T m_previous_sample;
96
+ bool m_is_initialized;
97
+ };
98
+ }
@@ -0,0 +1,336 @@
1
+ #include "CircularBufferArray.h"
2
+ #include <algorithm>
3
+ #include <stdexcept>
4
+ #include <memory>
5
+
6
+ using namespace dsp::utils;
7
+
8
+ // -----------------------------------------------------------------------------
9
+ // Constructor
10
+ // Initializes the circular buffer with a specified size using std::make_unique
11
+ // @ param size - The size of the circular buffer
12
+ // @ param windowDuration_ms - Optional window duration for time-based expiration (0 = disabled)
13
+ // @ return void
14
+ // -----------------------------------------------------------------------------
15
+ template <typename T>
16
+ CircularBufferArray<T>::CircularBufferArray(size_t size, double windowDuration_ms)
17
+ : buffer(std::make_unique<T[]>(std::max(size, static_cast<size_t>(1)))),
18
+ timestamps(windowDuration_ms > 0.0 ? std::make_unique<double[]>(std::max(size, static_cast<size_t>(1))) : nullptr),
19
+ head(0),
20
+ tail(0),
21
+ capacity(std::max(size, static_cast<size_t>(1))),
22
+ count(0),
23
+ windowDuration_ms(windowDuration_ms)
24
+ {
25
+ // Buffers are automatically initialized by make_unique
26
+ }
27
+
28
+ // Note: Move constructor and move assignment operator are now defaulted in the header
29
+ // std::unique_ptr handles move semantics correctly by default
30
+
31
+ // -----------------------------------------------------------------------------
32
+ // Method: push
33
+ // Adds an item to the circular buffer
34
+ // @ param item - The item to add
35
+ // @ return bool - True if the item was added, false if the buffer is full
36
+ // -----------------------------------------------------------------------------
37
+ template <typename T>
38
+ bool CircularBufferArray<T>::push(const T &item)
39
+ {
40
+ if (isFull())
41
+ {
42
+ return false; // Buffer is full
43
+ }
44
+
45
+ this->buffer[this->head] = item;
46
+ this->head = (this->head + 1) % this->capacity;
47
+ this->count++;
48
+ return true;
49
+ }
50
+
51
+ // -----------------------------------------------------------------------------
52
+ // Method: pop
53
+ // Removes an item from the circular buffer
54
+ // @ param item - The item to remove
55
+ // @ return bool - True if the item was removed, false if the buffer is empty
56
+ // -----------------------------------------------------------------------------
57
+ template <typename T>
58
+ bool CircularBufferArray<T>::pop(T &item) noexcept
59
+ {
60
+ if (isEmpty())
61
+ {
62
+ return false; // Buffer is empty
63
+ }
64
+
65
+ item = this->buffer[this->tail];
66
+ this->tail = (this->tail + 1) % this->capacity;
67
+ this->count--;
68
+ return true;
69
+ }
70
+
71
+ // -----------------------------------------------------------------------------
72
+
73
+ // Method: clear
74
+ // Clears the circular buffer
75
+ // @ param void
76
+ // @ return void
77
+ // -----------------------------------------------------------------------------
78
+ template <typename T>
79
+ void CircularBufferArray<T>::clear() noexcept
80
+ {
81
+ this->head = 0;
82
+ this->tail = 0;
83
+ this->count = 0;
84
+ }
85
+
86
+ // -----------------------------------------------------------------------------
87
+ // Method: pushOverwrite
88
+ // Adds an item to the circular buffer, overwriting the oldest item if full
89
+ // @ param item - The item to add
90
+ // @ return bool - Always returns true
91
+ template <typename T>
92
+ void CircularBufferArray<T>::pushOverwrite(const T &item)
93
+ {
94
+ if (isFull())
95
+ this->tail = (this->tail + 1) % this->capacity;
96
+ this->buffer[this->head] = item;
97
+ this->head = (this->head + 1) % this->capacity;
98
+ if (this->count < this->capacity)
99
+ ++this->count;
100
+ }
101
+
102
+ // -----------------------------------------------------------------------------
103
+ // Getter: getCapacity
104
+ // @ param void
105
+ // @ return size_t - The capacity of the circular buffer
106
+ // -----------------------------------------------------------------------------
107
+ template <typename T>
108
+ size_t CircularBufferArray<T>::getCapacity() const noexcept
109
+ {
110
+ return this->capacity;
111
+ }
112
+
113
+ // -----------------------------------------------------------------------------
114
+ // Getter: getCount
115
+ // @ param void
116
+ // @ return size_t - The number of elements in the circular buffer
117
+ // -----------------------------------------------------------------------------
118
+ template <typename T>
119
+ size_t CircularBufferArray<T>::getCount() const noexcept
120
+ {
121
+ return this->count;
122
+ }
123
+
124
+ // -----------------------------------------------------------------------------
125
+ // Getter: isEmpty
126
+ // @ param void
127
+ // @ return bool - True if the buffer is empty, false otherwise
128
+ // -----------------------------------------------------------------------------
129
+ template <typename T>
130
+ bool CircularBufferArray<T>::isEmpty() const noexcept
131
+ {
132
+ return this->count == 0;
133
+ }
134
+
135
+ // -----------------------------------------------------------------------------
136
+ // Getter: isFull
137
+ // @ param void
138
+ // @ return bool - True if the buffer is full, false otherwise
139
+ // -----------------------------------------------------------------------------
140
+ template <typename T>
141
+ bool CircularBufferArray<T>::isFull() const noexcept
142
+ {
143
+ return this->count == this->capacity;
144
+ }
145
+
146
+ // -----------------------------------------------------------------------------
147
+ // Method: peek
148
+ // Returns the item at the head of the circular buffer without removing it
149
+ // @ param void
150
+ // @ return T - The item at the head of the buffer
151
+ template <typename T>
152
+ T CircularBufferArray<T>::peek() const
153
+ {
154
+ if (isEmpty())
155
+ {
156
+ throw std::runtime_error("Buffer is empty");
157
+ }
158
+
159
+ return this->buffer[this->tail];
160
+ }
161
+
162
+ // -----------------------------------------------------------------------------
163
+ // Method: toVector
164
+ // Exports the buffer contents in order (oldest to newest) as a vector
165
+ // @ return std::vector<T> - The buffer contents in order
166
+ // -----------------------------------------------------------------------------
167
+ template <typename T>
168
+ std::vector<T> CircularBufferArray<T>::toVector() const
169
+ {
170
+ std::vector<T> result;
171
+ result.reserve(this->count);
172
+
173
+ for (size_t i = 0; i < this->count; ++i)
174
+ {
175
+ size_t index = (this->tail + i) % this->capacity;
176
+ result.push_back(this->buffer[index]);
177
+ }
178
+
179
+ return result;
180
+ }
181
+
182
+ // -----------------------------------------------------------------------------
183
+ // Method: fromVector
184
+ // Imports buffer contents from a vector, maintaining order
185
+ // @ param data - The vector containing the data to import
186
+ // -----------------------------------------------------------------------------
187
+ template <typename T>
188
+ void CircularBufferArray<T>::fromVector(const std::vector<T> &data)
189
+ {
190
+ clear();
191
+
192
+ for (const auto &item : data)
193
+ {
194
+ pushOverwrite(item);
195
+ }
196
+ }
197
+
198
+ // -----------------------------------------------------------------------------
199
+ // Method: pushWithTimestamp
200
+ // Adds an item with a timestamp to the buffer (requires time-aware mode)
201
+ // @ param item - The item to add
202
+ // @ param timestamp - The timestamp in milliseconds
203
+ // -----------------------------------------------------------------------------
204
+ template <typename T>
205
+ void CircularBufferArray<T>::pushWithTimestamp(const T &item, double timestamp)
206
+ {
207
+ if (!isTimeAware())
208
+ {
209
+ throw std::runtime_error("pushWithTimestamp requires time-aware mode (windowDuration > 0)");
210
+ }
211
+
212
+ if (isFull())
213
+ {
214
+ return; // Buffer is full
215
+ }
216
+
217
+ buffer[head] = item;
218
+ timestamps[head] = timestamp;
219
+ head = (head + 1) % capacity;
220
+ count++;
221
+ }
222
+
223
+ // -----------------------------------------------------------------------------
224
+ // Method: pushOverwriteWithTimestamp
225
+ // Adds an item with timestamp, overwriting oldest if full (requires time-aware mode)
226
+ // @ param item - The item to add
227
+ // @ param timestamp - The timestamp in milliseconds
228
+ // -----------------------------------------------------------------------------
229
+ template <typename T>
230
+ void CircularBufferArray<T>::pushOverwriteWithTimestamp(const T &item, double timestamp)
231
+ {
232
+ if (!isTimeAware())
233
+ {
234
+ throw std::runtime_error("pushOverwriteWithTimestamp requires time-aware mode (windowDuration > 0)");
235
+ }
236
+
237
+ if (isFull())
238
+ {
239
+ tail = (tail + 1) % capacity;
240
+ }
241
+
242
+ buffer[head] = item;
243
+ timestamps[head] = timestamp;
244
+ head = (head + 1) % capacity;
245
+
246
+ if (count < capacity)
247
+ {
248
+ ++count;
249
+ }
250
+ }
251
+
252
+ // -----------------------------------------------------------------------------
253
+ // Method: expireOld
254
+ // Removes samples older than windowDuration from the current timestamp
255
+ // @ param currentTimestamp - The current timestamp in milliseconds
256
+ // @ return size_t - Number of samples expired
257
+ // -----------------------------------------------------------------------------
258
+ template <typename T>
259
+ size_t CircularBufferArray<T>::expireOld(double currentTimestamp)
260
+ {
261
+ if (!isTimeAware() || isEmpty())
262
+ {
263
+ return 0;
264
+ }
265
+
266
+ size_t expired_count = 0;
267
+ double cutoff_time = currentTimestamp - windowDuration_ms;
268
+
269
+ // Remove samples from tail while they're older than cutoff
270
+ while (count > 0 && timestamps[tail] < cutoff_time)
271
+ {
272
+ tail = (tail + 1) % capacity;
273
+ --count;
274
+ ++expired_count;
275
+ }
276
+
277
+ return expired_count;
278
+ }
279
+
280
+ // -----------------------------------------------------------------------------
281
+ // Method: toVectorWithTimestamps
282
+ // Exports the buffer contents with timestamps (requires time-aware mode)
283
+ // @ return std::vector<std::pair<double, T>> - The buffer contents with timestamps
284
+ // -----------------------------------------------------------------------------
285
+ template <typename T>
286
+ std::vector<std::pair<double, T>> CircularBufferArray<T>::toVectorWithTimestamps() const
287
+ {
288
+ if (!isTimeAware())
289
+ {
290
+ throw std::runtime_error("toVectorWithTimestamps requires time-aware mode");
291
+ }
292
+
293
+ std::vector<std::pair<double, T>> result;
294
+ result.reserve(count);
295
+
296
+ for (size_t i = 0; i < count; ++i)
297
+ {
298
+ size_t index = (tail + i) % capacity;
299
+ result.push_back({timestamps[index], buffer[index]});
300
+ }
301
+
302
+ return result;
303
+ }
304
+
305
+ // -----------------------------------------------------------------------------
306
+ // Method: fromVectorWithTimestamps
307
+ // Imports buffer contents with timestamps (requires time-aware mode)
308
+ // @ param data - The vector containing (timestamp, value) pairs
309
+ // -----------------------------------------------------------------------------
310
+ template <typename T>
311
+ void CircularBufferArray<T>::fromVectorWithTimestamps(const std::vector<std::pair<double, T>> &data)
312
+ {
313
+ if (!isTimeAware())
314
+ {
315
+ throw std::runtime_error("fromVectorWithTimestamps requires time-aware mode");
316
+ }
317
+
318
+ clear();
319
+
320
+ for (const auto &[timestamp, value] : data)
321
+ {
322
+ pushOverwriteWithTimestamp(value, timestamp);
323
+ }
324
+ }
325
+
326
+ // Note: Destructor is now defaulted in the header
327
+ // std::unique_ptr automatically cleans up the buffers
328
+
329
+ // Explicit template instantiation for common types
330
+ namespace dsp::utils
331
+ {
332
+ template class CircularBufferArray<int>;
333
+ template class CircularBufferArray<float>;
334
+ template class CircularBufferArray<double>;
335
+ template class CircularBufferArray<bool>;
336
+ }
@@ -0,0 +1,62 @@
1
+ #pragma once
2
+
3
+ #include <vector>
4
+ #include <stdexcept>
5
+ #include <memory>
6
+
7
+ namespace dsp::utils
8
+ {
9
+
10
+ template <typename T>
11
+
12
+ class CircularBufferArray
13
+ {
14
+ public:
15
+ // constructors
16
+ explicit CircularBufferArray(size_t size, double windowDuration_ms = 0.0);
17
+ CircularBufferArray(const CircularBufferArray &other) = delete; // disable copy to avoid shallow copy
18
+ CircularBufferArray &operator=(const CircularBufferArray &other) = delete; // disable copy assignment to avoid shallow copy
19
+
20
+ // move semantics (defaulted - compiler generated is now safe with unique_ptr)
21
+ CircularBufferArray(CircularBufferArray &&other) noexcept = default;
22
+ CircularBufferArray &operator=(CircularBufferArray &&other) noexcept = default;
23
+
24
+ // methods
25
+ bool push(const T &item);
26
+ bool pop(T &item) noexcept;
27
+ void clear() noexcept;
28
+ void pushOverwrite(const T &item);
29
+
30
+ // Time-aware methods (require timestamps to be enabled)
31
+ void pushWithTimestamp(const T &item, double timestamp);
32
+ void pushOverwriteWithTimestamp(const T &item, double timestamp);
33
+ size_t expireOld(double currentTimestamp);
34
+
35
+ // getters
36
+ size_t getCapacity() const noexcept;
37
+ size_t getCount() const noexcept;
38
+ bool isEmpty() const noexcept;
39
+ bool isFull() const noexcept;
40
+ T peek() const;
41
+ bool isTimeAware() const noexcept { return windowDuration_ms > 0.0; }
42
+ double getWindowDuration() const noexcept { return windowDuration_ms; }
43
+
44
+ // state management
45
+ std::vector<T> toVector() const;
46
+ void fromVector(const std::vector<T> &data);
47
+ std::vector<std::pair<double, T>> toVectorWithTimestamps() const;
48
+ void fromVectorWithTimestamps(const std::vector<std::pair<double, T>> &data);
49
+
50
+ // destructor (defaulted - unique_ptr handles cleanup automatically)
51
+ ~CircularBufferArray() = default;
52
+
53
+ private:
54
+ std::unique_ptr<T[]> buffer;
55
+ std::unique_ptr<double[]> timestamps; // Optional timestamp array (nullptr if not time-aware)
56
+ size_t head;
57
+ size_t tail;
58
+ size_t capacity;
59
+ size_t count;
60
+ double windowDuration_ms; // Maximum age of samples (0 = disabled)
61
+ };
62
+ } // namespace dsp::utils