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,272 @@
1
+ import { describe, test, beforeEach } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { createDspPipeline, DspProcessor } from "../bindings.js";
4
+
5
+ const DEFAULT_OPTIONS = { channels: 1, sampleRate: 44100 };
6
+
7
+ function assertCloseTo(actual: number, expected: number, precision = 5) {
8
+ const tolerance = Math.pow(10, -precision);
9
+ assert.ok(
10
+ Math.abs(actual - expected) < tolerance,
11
+ `Expected ${actual} to be close to ${expected} (tolerance: ${tolerance})`
12
+ );
13
+ }
14
+
15
+ describe("Rectify Filter", () => {
16
+ let processor: DspProcessor;
17
+
18
+ beforeEach(() => {
19
+ processor = createDspPipeline();
20
+ });
21
+
22
+ describe("Full-Wave Rectification", () => {
23
+ test("should convert all values to absolute (full-wave rectification)", async () => {
24
+ processor.Rectify({ mode: "full" });
25
+
26
+ const input = new Float32Array([1, -2, 3, -4, 5, -6]);
27
+ const output = await processor.process(input, DEFAULT_OPTIONS);
28
+
29
+ assert.equal(output.length, 6);
30
+ assertCloseTo(output[0], 1);
31
+ assertCloseTo(output[1], 2); // |-2| = 2
32
+ assertCloseTo(output[2], 3);
33
+ assertCloseTo(output[3], 4); // |-4| = 4
34
+ assertCloseTo(output[4], 5);
35
+ assertCloseTo(output[5], 6); // |-6| = 6
36
+ });
37
+
38
+ test("should default to full-wave when no mode specified", async () => {
39
+ processor.Rectify(); // No params
40
+
41
+ const input = new Float32Array([-5, 10, -15]);
42
+ const output = await processor.process(input, DEFAULT_OPTIONS);
43
+
44
+ assertCloseTo(output[0], 5);
45
+ assertCloseTo(output[1], 10);
46
+ assertCloseTo(output[2], 15);
47
+ });
48
+
49
+ test("should handle already positive values", async () => {
50
+ processor.Rectify({ mode: "full" });
51
+
52
+ const input = new Float32Array([1, 2, 3, 4, 5]);
53
+ const output = await processor.process(input, DEFAULT_OPTIONS);
54
+
55
+ assert.deepEqual(Array.from(output), [1, 2, 3, 4, 5]);
56
+ });
57
+
58
+ test("should handle zero values", async () => {
59
+ processor.Rectify({ mode: "full" });
60
+
61
+ const input = new Float32Array([0, -0, 0]);
62
+ const output = await processor.process(input, DEFAULT_OPTIONS);
63
+
64
+ assert.deepEqual(Array.from(output), [0, 0, 0]);
65
+ });
66
+ });
67
+
68
+ describe("Half-Wave Rectification", () => {
69
+ test("should keep positive values and zero negative values", async () => {
70
+ processor.Rectify({ mode: "half" });
71
+
72
+ const input = new Float32Array([1, -2, 3, -4, 5, -6]);
73
+ const output = await processor.process(input, DEFAULT_OPTIONS);
74
+
75
+ assert.equal(output.length, 6);
76
+ assertCloseTo(output[0], 1);
77
+ assertCloseTo(output[1], 0); // -2 → 0
78
+ assertCloseTo(output[2], 3);
79
+ assertCloseTo(output[3], 0); // -4 → 0
80
+ assertCloseTo(output[4], 5);
81
+ assertCloseTo(output[5], 0); // -6 → 0
82
+ });
83
+
84
+ test("should not affect positive values", async () => {
85
+ processor.Rectify({ mode: "half" });
86
+
87
+ const input = new Float32Array([10, 20, 30]);
88
+ const output = await processor.process(input, DEFAULT_OPTIONS);
89
+
90
+ assert.deepEqual(Array.from(output), [10, 20, 30]);
91
+ });
92
+
93
+ test("should handle mixed positive/negative pattern", async () => {
94
+ processor.Rectify({ mode: "half" });
95
+
96
+ const input = new Float32Array([5, -5, 10, -10, 15, -15]);
97
+ const output = await processor.process(input, DEFAULT_OPTIONS);
98
+
99
+ assertCloseTo(output[0], 5);
100
+ assertCloseTo(output[1], 0);
101
+ assertCloseTo(output[2], 10);
102
+ assertCloseTo(output[3], 0);
103
+ assertCloseTo(output[4], 15);
104
+ assertCloseTo(output[5], 0);
105
+ });
106
+ });
107
+
108
+ describe("State Management", () => {
109
+ test("should serialize and deserialize full-wave mode correctly", async () => {
110
+ processor.Rectify({ mode: "full" });
111
+
112
+ await processor.process(new Float32Array([1, -2, 3]), DEFAULT_OPTIONS);
113
+
114
+ const stateJson = await processor.saveState();
115
+ const state = JSON.parse(stateJson);
116
+
117
+ assert.ok(state);
118
+ assert.equal(state.stages.length, 1);
119
+ assert.equal(state.stages[0].type, "rectify");
120
+ assert.equal(state.stages[0].state.mode, "full");
121
+
122
+ // Load into new processor
123
+ const processor2 = createDspPipeline();
124
+ processor2.Rectify({ mode: "full" });
125
+ await processor2.loadState(stateJson);
126
+
127
+ const output = await processor2.process(
128
+ new Float32Array([-5, 10]),
129
+ DEFAULT_OPTIONS
130
+ );
131
+ assertCloseTo(output[0], 5);
132
+ assertCloseTo(output[1], 10);
133
+ });
134
+
135
+ test("should serialize and deserialize half-wave mode correctly", async () => {
136
+ processor.Rectify({ mode: "half" });
137
+
138
+ await processor.process(new Float32Array([1, -2, 3]), DEFAULT_OPTIONS);
139
+
140
+ const stateJson = await processor.saveState();
141
+ const state = JSON.parse(stateJson);
142
+
143
+ assert.equal(state.stages[0].state.mode, "half");
144
+
145
+ // Load into new processor
146
+ const processor2 = createDspPipeline();
147
+ processor2.Rectify({ mode: "half" });
148
+ await processor2.loadState(stateJson);
149
+
150
+ const output = await processor2.process(
151
+ new Float32Array([-5, 10]),
152
+ DEFAULT_OPTIONS
153
+ );
154
+ assertCloseTo(output[0], 0);
155
+ assertCloseTo(output[1], 10);
156
+ });
157
+
158
+ test("should preserve mode across state save/load", async () => {
159
+ processor.Rectify({ mode: "half" });
160
+
161
+ const stateJson = await processor.saveState();
162
+
163
+ // Create new processor with different mode
164
+ const processor2 = createDspPipeline();
165
+ processor2.Rectify({ mode: "half" }); // Must match for deserialization
166
+
167
+ await processor2.loadState(stateJson);
168
+
169
+ // Verify mode is preserved
170
+ const output = await processor2.process(
171
+ new Float32Array([-10, 10]),
172
+ DEFAULT_OPTIONS
173
+ );
174
+ assertCloseTo(output[0], 0); // Half-wave behavior
175
+ assertCloseTo(output[1], 10);
176
+ });
177
+
178
+ test("should handle reset (no-op for stateless filter)", async () => {
179
+ processor.Rectify({ mode: "full" });
180
+
181
+ await processor.process(new Float32Array([1, -2]), DEFAULT_OPTIONS);
182
+ processor.clearState();
183
+
184
+ const output = await processor.process(
185
+ new Float32Array([-5]),
186
+ DEFAULT_OPTIONS
187
+ );
188
+ assertCloseTo(output[0], 5); // Still works after reset
189
+ });
190
+ });
191
+
192
+ describe("Edge Cases", () => {
193
+ test("should handle empty input array", async () => {
194
+ processor.Rectify({ mode: "full" });
195
+
196
+ const output = await processor.process(
197
+ new Float32Array([]),
198
+ DEFAULT_OPTIONS
199
+ );
200
+ assert.equal(output.length, 0);
201
+ });
202
+
203
+ test("should handle single sample", async () => {
204
+ processor.Rectify({ mode: "half" });
205
+
206
+ const output1 = await processor.process(
207
+ new Float32Array([-10]),
208
+ DEFAULT_OPTIONS
209
+ );
210
+ assertCloseTo(output1[0], 0);
211
+
212
+ const output2 = await processor.process(
213
+ new Float32Array([10]),
214
+ DEFAULT_OPTIONS
215
+ );
216
+ assertCloseTo(output2[0], 10);
217
+ });
218
+
219
+ test("should handle very small negative values (half-wave)", async () => {
220
+ processor.Rectify({ mode: "half" });
221
+
222
+ const input = new Float32Array([-0.0001, -0.00001, -0.000001]);
223
+ const output = await processor.process(input, DEFAULT_OPTIONS);
224
+
225
+ assert.deepEqual(Array.from(output), [0, 0, 0]);
226
+ });
227
+
228
+ test("should handle very large values", async () => {
229
+ processor.Rectify({ mode: "full" });
230
+
231
+ const input = new Float32Array([-1e6, 1e6, -1e9]);
232
+ const output = await processor.process(input, DEFAULT_OPTIONS);
233
+
234
+ assertCloseTo(output[0], 1e6);
235
+ assertCloseTo(output[1], 1e6);
236
+ assertCloseTo(output[2], 1e9);
237
+ });
238
+ });
239
+
240
+ describe("Multi-channel Processing", () => {
241
+ test("should rectify multiple channels independently", async () => {
242
+ processor.Rectify({ mode: "full" });
243
+
244
+ const input = new Float32Array([-1, -2, -3, -4, -5, -6]);
245
+ const output = await processor.process(input, DEFAULT_OPTIONS);
246
+
247
+ // All should be positive
248
+ assert.ok(Array.from(output).every((v) => v >= 0));
249
+ assertCloseTo(output[0], 1);
250
+ assertCloseTo(output[1], 2);
251
+ assertCloseTo(output[2], 3);
252
+ });
253
+
254
+ test("should handle multiple process calls with state continuity", async () => {
255
+ processor.Rectify({ mode: "half" });
256
+
257
+ const output1 = await processor.process(
258
+ new Float32Array([5, -5]),
259
+ DEFAULT_OPTIONS
260
+ );
261
+ assertCloseTo(output1[0], 5);
262
+ assertCloseTo(output1[1], 0);
263
+
264
+ const output2 = await processor.process(
265
+ new Float32Array([10, -10]),
266
+ DEFAULT_OPTIONS
267
+ );
268
+ assertCloseTo(output2[0], 10);
269
+ assertCloseTo(output2[1], 0);
270
+ });
271
+ });
272
+ });