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,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test script for RMS state management (save/load/clear)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { createDspPipeline } from "../../bindings.js";
|
|
6
|
+
|
|
7
|
+
async function testRmsStateManagement() {
|
|
8
|
+
console.log("=== Testing RMS Pipeline State Management ===\n");
|
|
9
|
+
|
|
10
|
+
// 1. Create pipeline and add RMS stage
|
|
11
|
+
const pipeline = createDspPipeline();
|
|
12
|
+
pipeline.Rms({ mode: "moving", windowSize: 3 });
|
|
13
|
+
|
|
14
|
+
console.log("Pipeline created with RMS filter (window=3)");
|
|
15
|
+
|
|
16
|
+
// 2. Process some data (sine wave samples)
|
|
17
|
+
const input1 = new Float32Array([1, -2, 3, -4, 5]);
|
|
18
|
+
console.log("\nProcessing first batch:", Array.from(input1));
|
|
19
|
+
|
|
20
|
+
const output1 = await pipeline.processCopy(input1, {
|
|
21
|
+
sampleRate: 1000,
|
|
22
|
+
channels: 1,
|
|
23
|
+
});
|
|
24
|
+
console.log(
|
|
25
|
+
"Output (RMS values):",
|
|
26
|
+
Array.from(output1).map((v) => v.toFixed(4))
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
// 3. Save state to JSON
|
|
30
|
+
console.log("\n--- Saving State ---");
|
|
31
|
+
const stateJson = await pipeline.saveState();
|
|
32
|
+
console.log("State saved:");
|
|
33
|
+
console.log(JSON.stringify(JSON.parse(stateJson), null, 2));
|
|
34
|
+
|
|
35
|
+
// 4. Continue processing
|
|
36
|
+
const input2 = new Float32Array([6, -7, 8]);
|
|
37
|
+
console.log("\nProcessing second batch:", Array.from(input2));
|
|
38
|
+
|
|
39
|
+
const output2 = await pipeline.processCopy(input2, {
|
|
40
|
+
sampleRate: 1000,
|
|
41
|
+
channels: 1,
|
|
42
|
+
});
|
|
43
|
+
console.log(
|
|
44
|
+
"Output (RMS values):",
|
|
45
|
+
Array.from(output2).map((v) => v.toFixed(4))
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
// 5. Save state again
|
|
49
|
+
const stateJson2 = await pipeline.saveState();
|
|
50
|
+
console.log("\nState after second batch:");
|
|
51
|
+
console.log(JSON.stringify(JSON.parse(stateJson2), null, 2));
|
|
52
|
+
|
|
53
|
+
// 6. Create new pipeline and load state
|
|
54
|
+
console.log("\n--- Creating New Pipeline ---");
|
|
55
|
+
const pipeline2 = createDspPipeline();
|
|
56
|
+
pipeline2.Rms({ mode: "moving", windowSize: 3 });
|
|
57
|
+
|
|
58
|
+
console.log("Loading previous state...");
|
|
59
|
+
const loaded = await pipeline2.loadState(stateJson2);
|
|
60
|
+
console.log("Load successful:", loaded);
|
|
61
|
+
|
|
62
|
+
// 7. Process with restored state
|
|
63
|
+
const input3 = new Float32Array([9, -10]);
|
|
64
|
+
console.log("\nProcessing with restored pipeline:", Array.from(input3));
|
|
65
|
+
|
|
66
|
+
const output3 = await pipeline2.processCopy(input3, {
|
|
67
|
+
sampleRate: 1000,
|
|
68
|
+
channels: 1,
|
|
69
|
+
});
|
|
70
|
+
console.log(
|
|
71
|
+
"Output (RMS values):",
|
|
72
|
+
Array.from(output3).map((v) => v.toFixed(4))
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
// 8. Clear state
|
|
76
|
+
console.log("\n--- Clearing State ---");
|
|
77
|
+
pipeline2.clearState();
|
|
78
|
+
console.log("State cleared");
|
|
79
|
+
|
|
80
|
+
// 9. Process after clear (should start fresh)
|
|
81
|
+
const input4 = new Float32Array([1, -2, 3]);
|
|
82
|
+
console.log("\nProcessing after clear:", Array.from(input4));
|
|
83
|
+
|
|
84
|
+
const output4 = await pipeline2.processCopy(input4, {
|
|
85
|
+
sampleRate: 1000,
|
|
86
|
+
channels: 1,
|
|
87
|
+
});
|
|
88
|
+
console.log(
|
|
89
|
+
"Output (fresh start):",
|
|
90
|
+
Array.from(output4).map((v) => v.toFixed(4))
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
console.log("\nRMS state management test complete!");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Run test
|
|
97
|
+
testRmsStateManagement().catch(console.error);
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test streaming data processing with RMS and state continuity
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { createDspPipeline } from "../../bindings.js";
|
|
6
|
+
|
|
7
|
+
// Simulate streaming chunks of data (e.g., EMG signals, audio)
|
|
8
|
+
async function* simulateDataStream(
|
|
9
|
+
totalSamples: number,
|
|
10
|
+
chunkSize: number
|
|
11
|
+
): AsyncGenerator<Float32Array> {
|
|
12
|
+
for (let i = 0; i < totalSamples; i += chunkSize) {
|
|
13
|
+
// Simulate data arriving over time (e.g., from sensor, audio device)
|
|
14
|
+
await new Promise((resolve) => setTimeout(resolve, 100)); // 100ms delay
|
|
15
|
+
|
|
16
|
+
const remainingSamples = Math.min(chunkSize, totalSamples - i);
|
|
17
|
+
const chunk = new Float32Array(remainingSamples);
|
|
18
|
+
|
|
19
|
+
// Generate sample data (e.g., sine wave with varying amplitude + noise)
|
|
20
|
+
for (let j = 0; j < remainingSamples; j++) {
|
|
21
|
+
const t = (i + j) / 100;
|
|
22
|
+
const amplitude = 1 + 0.5 * Math.sin(2 * Math.PI * 0.5 * t); // Slow amplitude modulation
|
|
23
|
+
chunk[j] =
|
|
24
|
+
amplitude * Math.sin(2 * Math.PI * 10 * t) + Math.random() * 0.1; // 10Hz carrier + noise
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
yield chunk;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function testStreamingRmsProcessing() {
|
|
32
|
+
console.log("=== Testing Streaming RMS Processing ===\n");
|
|
33
|
+
|
|
34
|
+
const pipeline = createDspPipeline();
|
|
35
|
+
pipeline.Rms({ mode: "moving", windowSize: 20 }); // Calculate RMS over 20-sample window
|
|
36
|
+
|
|
37
|
+
console.log("Pipeline created with 20-sample RMS window");
|
|
38
|
+
console.log(
|
|
39
|
+
"Simulating real-time data stream (100 samples, 20 samples/chunk)\n"
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
let chunkNumber = 0;
|
|
43
|
+
let totalProcessed = 0;
|
|
44
|
+
const allOutputs: number[] = [];
|
|
45
|
+
|
|
46
|
+
// Process streaming data
|
|
47
|
+
for await (const chunk of simulateDataStream(100, 20)) {
|
|
48
|
+
chunkNumber++;
|
|
49
|
+
console.log(
|
|
50
|
+
`Chunk ${chunkNumber}: Received ${
|
|
51
|
+
chunk.length
|
|
52
|
+
} samples [${chunk[0].toFixed(3)}, ${chunk[1].toFixed(3)}, ...]`
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
// Process chunk - pipeline remembers state from previous chunks
|
|
56
|
+
const output = await pipeline.process(chunk, {
|
|
57
|
+
sampleRate: 100,
|
|
58
|
+
channels: 1,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
console.log(
|
|
62
|
+
` RMS output → [${output[0].toFixed(3)}, ${output[1].toFixed(
|
|
63
|
+
3
|
|
64
|
+
)}, ...]\n`
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
allOutputs.push(...Array.from(output));
|
|
68
|
+
totalProcessed += chunk.length;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
console.log(`Stream complete: ${totalProcessed} samples processed`);
|
|
72
|
+
console.log(
|
|
73
|
+
` First 10 RMS values: [${allOutputs
|
|
74
|
+
.slice(0, 10)
|
|
75
|
+
.map((v) => v.toFixed(3))
|
|
76
|
+
.join(", ")}]`
|
|
77
|
+
);
|
|
78
|
+
console.log(
|
|
79
|
+
` Last 10 RMS values: [${allOutputs
|
|
80
|
+
.slice(-10)
|
|
81
|
+
.map((v) => v.toFixed(3))
|
|
82
|
+
.join(", ")}]`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Test with state persistence across stream interruption
|
|
87
|
+
async function testStreamInterruption() {
|
|
88
|
+
console.log("\n\n=== Testing RMS Stream Interruption & Recovery ===\n");
|
|
89
|
+
|
|
90
|
+
const pipeline = createDspPipeline();
|
|
91
|
+
pipeline.Rms({ mode: "moving", windowSize: 5 });
|
|
92
|
+
|
|
93
|
+
console.log("Processing first part of stream...");
|
|
94
|
+
let chunk1 = new Float32Array([1, -2, 3, -4, 5]);
|
|
95
|
+
let output1 = await pipeline.process(chunk1, {
|
|
96
|
+
sampleRate: 100,
|
|
97
|
+
channels: 1,
|
|
98
|
+
});
|
|
99
|
+
console.log(
|
|
100
|
+
`Chunk 1: [${Array.from(chunk1)}] → [${Array.from(output1).map((v) =>
|
|
101
|
+
v.toFixed(3)
|
|
102
|
+
)}]`
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
let chunk2 = new Float32Array([6, -7, 8]);
|
|
106
|
+
let output2 = await pipeline.process(chunk2, {
|
|
107
|
+
sampleRate: 100,
|
|
108
|
+
channels: 1,
|
|
109
|
+
});
|
|
110
|
+
console.log(
|
|
111
|
+
`Chunk 2: [${Array.from(chunk2)}] → [${Array.from(output2).map((v) =>
|
|
112
|
+
v.toFixed(3)
|
|
113
|
+
)}]`
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
// Save state (simulate crash/restart)
|
|
117
|
+
console.log("\nSaving state (simulating service restart)...");
|
|
118
|
+
const savedState = await pipeline.saveState();
|
|
119
|
+
const parsedState = JSON.parse(savedState);
|
|
120
|
+
console.log("State saved:");
|
|
121
|
+
console.log(" - Window size:", parsedState.stages[0].state.windowSize);
|
|
122
|
+
console.log(" - Channels:", parsedState.stages[0].state.numChannels);
|
|
123
|
+
console.log(
|
|
124
|
+
" - Buffer:",
|
|
125
|
+
parsedState.stages[0].state.channels[0].buffer.map((v: number) =>
|
|
126
|
+
v.toFixed(3)
|
|
127
|
+
)
|
|
128
|
+
);
|
|
129
|
+
console.log(
|
|
130
|
+
" - Running sum of squares:",
|
|
131
|
+
parsedState.stages[0].state.channels[0].runningSumOfSquares.toFixed(3)
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
// Create new pipeline and restore
|
|
135
|
+
console.log("\nCreating new pipeline and restoring state...");
|
|
136
|
+
const pipeline2 = createDspPipeline();
|
|
137
|
+
pipeline2.Rms({ mode: "moving", windowSize: 5 });
|
|
138
|
+
await pipeline2.loadState(savedState);
|
|
139
|
+
console.log("State restored!");
|
|
140
|
+
|
|
141
|
+
// Continue processing
|
|
142
|
+
console.log("\nContinuing stream processing...");
|
|
143
|
+
let chunk3 = new Float32Array([9, -10, 11]);
|
|
144
|
+
let output3 = await pipeline2.process(chunk3, {
|
|
145
|
+
sampleRate: 100,
|
|
146
|
+
channels: 1,
|
|
147
|
+
});
|
|
148
|
+
console.log(
|
|
149
|
+
`Chunk 3: [${Array.from(chunk3)}] → [${Array.from(output3).map((v) =>
|
|
150
|
+
v.toFixed(3)
|
|
151
|
+
)}]`
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
console.log("\nRMS stream recovered seamlessly!");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Test multi-channel streaming (e.g., stereo audio, multi-sensor EMG)
|
|
158
|
+
async function testMultiChannelRmsStreaming() {
|
|
159
|
+
console.log("\n\n=== Testing Multi-Channel RMS Streaming ===\n");
|
|
160
|
+
|
|
161
|
+
const pipeline = createDspPipeline();
|
|
162
|
+
pipeline.Rms({ mode: "moving", windowSize: 3 });
|
|
163
|
+
|
|
164
|
+
console.log("Processing 2-channel interleaved data stream (RMS per channel)");
|
|
165
|
+
console.log("Format: [ch1_s1, ch2_s1, ch1_s2, ch2_s2, ...]\n");
|
|
166
|
+
|
|
167
|
+
// Simulate 3 chunks of 2-channel data (4 samples per chunk = 2 samples per channel)
|
|
168
|
+
// Channel 1: positive values, Channel 2: negative values
|
|
169
|
+
const chunks = [
|
|
170
|
+
new Float32Array([1, -10, 2, -20, 3, -30, 4, -40]), // 4 samples × 2 channels
|
|
171
|
+
new Float32Array([5, -50, 6, -60, 7, -70, 8, -80]),
|
|
172
|
+
new Float32Array([9, -90, 10, -100, 11, -110, 12, -120]),
|
|
173
|
+
];
|
|
174
|
+
|
|
175
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
176
|
+
console.log(`Chunk ${i + 1} input: [${Array.from(chunks[i])}]`);
|
|
177
|
+
const output = await pipeline.process(chunks[i], {
|
|
178
|
+
sampleRate: 100,
|
|
179
|
+
channels: 2,
|
|
180
|
+
});
|
|
181
|
+
console.log(
|
|
182
|
+
`Chunk ${i + 1} RMS output: [${Array.from(output).map((v) =>
|
|
183
|
+
v.toFixed(2)
|
|
184
|
+
)}]\n`
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
console.log("Each channel maintains independent RMS filter state!");
|
|
189
|
+
console.log(
|
|
190
|
+
"Note: RMS converts negative values to positive (magnitude only)"
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Test RMS with actual use case: envelope detection
|
|
195
|
+
async function testEnvelopeDetection() {
|
|
196
|
+
console.log("\n\n=== Testing RMS for Envelope Detection ===\n");
|
|
197
|
+
|
|
198
|
+
const pipeline = createDspPipeline();
|
|
199
|
+
pipeline.Rms({ mode: "moving", windowSize: 10 }); // 10-sample RMS window for envelope
|
|
200
|
+
|
|
201
|
+
console.log("Use case: Detecting signal envelope/amplitude over time");
|
|
202
|
+
console.log("Input: Amplitude-modulated sine wave\n");
|
|
203
|
+
|
|
204
|
+
// Generate amplitude-modulated signal: carrier × envelope
|
|
205
|
+
const samples = 50;
|
|
206
|
+
const carrierFreq = 20; // Hz
|
|
207
|
+
const envelopeFreq = 2; // Hz (slow amplitude change)
|
|
208
|
+
|
|
209
|
+
const input = new Float32Array(samples);
|
|
210
|
+
for (let i = 0; i < samples; i++) {
|
|
211
|
+
const t = i / 100;
|
|
212
|
+
const envelope = 0.5 + 0.5 * Math.sin(2 * Math.PI * envelopeFreq * t);
|
|
213
|
+
const carrier = Math.sin(2 * Math.PI * carrierFreq * t);
|
|
214
|
+
input[i] = envelope * carrier;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
console.log(
|
|
218
|
+
"First 10 input samples:",
|
|
219
|
+
Array.from(input.slice(0, 10)).map((v) => v.toFixed(3))
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
const output = await pipeline.process(input, {
|
|
223
|
+
sampleRate: 100,
|
|
224
|
+
channels: 1,
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
console.log(
|
|
228
|
+
"First 10 RMS values (envelope):",
|
|
229
|
+
Array.from(output.slice(0, 10)).map((v) => v.toFixed(3))
|
|
230
|
+
);
|
|
231
|
+
console.log(
|
|
232
|
+
"Last 10 RMS values (envelope):",
|
|
233
|
+
Array.from(output.slice(-10)).map((v) => v.toFixed(3))
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
console.log("\nRMS successfully tracks signal envelope!");
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Run all tests
|
|
240
|
+
async function runAllTests() {
|
|
241
|
+
try {
|
|
242
|
+
await testStreamingRmsProcessing();
|
|
243
|
+
await testStreamInterruption();
|
|
244
|
+
await testMultiChannelRmsStreaming();
|
|
245
|
+
await testEnvelopeDetection();
|
|
246
|
+
console.log("\nAll RMS streaming tests passed!");
|
|
247
|
+
} catch (error) {
|
|
248
|
+
console.error("Test failed:", error);
|
|
249
|
+
process.exit(1);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
runAllTests();
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test script for Rectify state management (save/load/clear)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { createDspPipeline } from "../../bindings.js";
|
|
6
|
+
|
|
7
|
+
async function testRectifyStateManagement() {
|
|
8
|
+
console.log("=== Testing Rectify Pipeline State Management ===\n");
|
|
9
|
+
|
|
10
|
+
// Test 1: Full-wave rectification (default)
|
|
11
|
+
console.log("--- Test 1: Full-Wave Rectification (Default) ---");
|
|
12
|
+
const pipelineFull = createDspPipeline();
|
|
13
|
+
pipelineFull.Rectify(); // Default is full-wave
|
|
14
|
+
|
|
15
|
+
const inputFull = new Float32Array([1, -2, 3, -4, 5, -6]);
|
|
16
|
+
console.log("Input:", Array.from(inputFull));
|
|
17
|
+
|
|
18
|
+
const outputFull = await pipelineFull.processCopy(inputFull, {
|
|
19
|
+
sampleRate: 1000,
|
|
20
|
+
channels: 1,
|
|
21
|
+
});
|
|
22
|
+
console.log(
|
|
23
|
+
"Output (full-wave):",
|
|
24
|
+
Array.from(outputFull).map((v) => v.toFixed(1))
|
|
25
|
+
);
|
|
26
|
+
console.log(
|
|
27
|
+
"Expected: [1.0, 2.0, 3.0, 4.0, 5.0, 6.0] (all positive, absolute values)\n"
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
// Test 2: Half-wave rectification
|
|
31
|
+
console.log("--- Test 2: Half-Wave Rectification ---");
|
|
32
|
+
const pipelineHalf = createDspPipeline();
|
|
33
|
+
pipelineHalf.Rectify({ mode: "half" });
|
|
34
|
+
|
|
35
|
+
const inputHalf = new Float32Array([1, -2, 3, -4, 5, -6]);
|
|
36
|
+
console.log("Input:", Array.from(inputHalf));
|
|
37
|
+
|
|
38
|
+
const outputHalf = await pipelineHalf.processCopy(inputHalf, {
|
|
39
|
+
sampleRate: 1000,
|
|
40
|
+
channels: 1,
|
|
41
|
+
});
|
|
42
|
+
console.log(
|
|
43
|
+
"Output (half-wave):",
|
|
44
|
+
Array.from(outputHalf).map((v) => v.toFixed(1))
|
|
45
|
+
);
|
|
46
|
+
console.log(
|
|
47
|
+
"Expected: [1.0, 0.0, 3.0, 0.0, 5.0, 0.0] (negatives become zero)\n"
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
// Test 3: State serialization (full-wave)
|
|
51
|
+
console.log("--- Test 3: State Serialization (Full-Wave) ---");
|
|
52
|
+
const stateFull = await pipelineFull.saveState();
|
|
53
|
+
const parsedStateFull = JSON.parse(stateFull);
|
|
54
|
+
console.log("Saved state:");
|
|
55
|
+
console.log(JSON.stringify(parsedStateFull, null, 2));
|
|
56
|
+
console.log(`Mode: ${parsedStateFull.stages[0].state.mode}\n`);
|
|
57
|
+
|
|
58
|
+
// Test 4: State serialization (half-wave)
|
|
59
|
+
console.log("--- Test 4: State Serialization (Half-Wave) ---");
|
|
60
|
+
const stateHalf = await pipelineHalf.saveState();
|
|
61
|
+
const parsedStateHalf = JSON.parse(stateHalf);
|
|
62
|
+
console.log("Saved state:");
|
|
63
|
+
console.log(JSON.stringify(parsedStateHalf, null, 2));
|
|
64
|
+
console.log(`Mode: ${parsedStateHalf.stages[0].state.mode}\n`);
|
|
65
|
+
|
|
66
|
+
// Test 5: Load state and verify mode preservation
|
|
67
|
+
console.log("--- Test 5: Load State & Verify Mode ---");
|
|
68
|
+
const pipelineNew = createDspPipeline();
|
|
69
|
+
pipelineNew.Rectify(); // Start with full-wave
|
|
70
|
+
|
|
71
|
+
console.log("Loading half-wave state into new pipeline...");
|
|
72
|
+
await pipelineNew.loadState(stateHalf);
|
|
73
|
+
|
|
74
|
+
const testInput = new Float32Array([2, -3, 4, -5]);
|
|
75
|
+
const testOutput = await pipelineNew.processCopy(testInput, {
|
|
76
|
+
sampleRate: 1000,
|
|
77
|
+
channels: 1,
|
|
78
|
+
});
|
|
79
|
+
console.log("Input:", Array.from(testInput));
|
|
80
|
+
console.log(
|
|
81
|
+
"Output:",
|
|
82
|
+
Array.from(testOutput).map((v) => v.toFixed(1))
|
|
83
|
+
);
|
|
84
|
+
console.log("Expected: [2.0, 0.0, 4.0, 0.0] (half-wave mode preserved)\n");
|
|
85
|
+
|
|
86
|
+
// Test 6: Clear state (no effect on Rectify, but tests the interface)
|
|
87
|
+
console.log("--- Test 6: Clear State ---");
|
|
88
|
+
pipelineNew.clearState();
|
|
89
|
+
console.log("State cleared (Rectify has no internal buffers)\n");
|
|
90
|
+
|
|
91
|
+
// Process after clear - should still work with same mode
|
|
92
|
+
const afterClear = await pipelineNew.processCopy(new Float32Array([1, -2]), {
|
|
93
|
+
sampleRate: 1000,
|
|
94
|
+
channels: 1,
|
|
95
|
+
});
|
|
96
|
+
console.log("After clear, input: [1, -2]");
|
|
97
|
+
console.log(
|
|
98
|
+
"Output:",
|
|
99
|
+
Array.from(afterClear).map((v) => v.toFixed(1))
|
|
100
|
+
);
|
|
101
|
+
console.log("Mode still preserved after clear\n");
|
|
102
|
+
|
|
103
|
+
console.log("Rectify state management test complete!");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Run test
|
|
107
|
+
testRectifyStateManagement().catch(console.error);
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test streaming data processing with Rectify
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { createDspPipeline } from "../../bindings.js";
|
|
6
|
+
|
|
7
|
+
// Simulate streaming chunks of data (e.g., AC audio signal, EMG with negative artifacts)
|
|
8
|
+
async function* simulateDataStream(
|
|
9
|
+
totalSamples: number,
|
|
10
|
+
chunkSize: number
|
|
11
|
+
): AsyncGenerator<Float32Array> {
|
|
12
|
+
for (let i = 0; i < totalSamples; i += chunkSize) {
|
|
13
|
+
// Simulate data arriving over time
|
|
14
|
+
await new Promise((resolve) => setTimeout(resolve, 100)); // 100ms delay
|
|
15
|
+
|
|
16
|
+
const remainingSamples = Math.min(chunkSize, totalSamples - i);
|
|
17
|
+
const chunk = new Float32Array(remainingSamples);
|
|
18
|
+
|
|
19
|
+
// Generate bipolar signal (positive and negative values)
|
|
20
|
+
for (let j = 0; j < remainingSamples; j++) {
|
|
21
|
+
const t = (i + j) / 100;
|
|
22
|
+
chunk[j] = Math.sin(2 * Math.PI * 5 * t); // 5Hz sine wave (bipolar: -1 to +1)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
yield chunk;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function testStreamingFullWaveRectification() {
|
|
30
|
+
console.log("=== Testing Streaming Full-Wave Rectification ===\n");
|
|
31
|
+
|
|
32
|
+
const pipeline = createDspPipeline();
|
|
33
|
+
pipeline.Rectify({ mode: "full" }); // Full-wave: absolute value
|
|
34
|
+
|
|
35
|
+
console.log("Pipeline created with full-wave rectifier");
|
|
36
|
+
console.log(
|
|
37
|
+
"Simulating real-time bipolar signal stream (100 samples, 20 samples/chunk)\n"
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
let chunkNumber = 0;
|
|
41
|
+
let totalProcessed = 0;
|
|
42
|
+
const allOutputs: number[] = [];
|
|
43
|
+
|
|
44
|
+
// Process streaming data
|
|
45
|
+
for await (const chunk of simulateDataStream(100, 20)) {
|
|
46
|
+
chunkNumber++;
|
|
47
|
+
console.log(
|
|
48
|
+
`Chunk ${chunkNumber}: Received ${
|
|
49
|
+
chunk.length
|
|
50
|
+
} samples [${chunk[0].toFixed(3)}, ${chunk[1].toFixed(3)}, ...]`
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// Process chunk - rectify converts all to positive
|
|
54
|
+
const output = await pipeline.process(chunk, {
|
|
55
|
+
sampleRate: 100,
|
|
56
|
+
channels: 1,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
console.log(
|
|
60
|
+
` Rectified → [${output[0].toFixed(3)}, ${output[1].toFixed(
|
|
61
|
+
3
|
|
62
|
+
)}, ...] (all positive)\n`
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
allOutputs.push(...Array.from(output));
|
|
66
|
+
totalProcessed += chunk.length;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
console.log(`Stream complete: ${totalProcessed} samples processed`);
|
|
70
|
+
console.log(
|
|
71
|
+
` First 10 outputs: [${allOutputs
|
|
72
|
+
.slice(0, 10)
|
|
73
|
+
.map((v) => v.toFixed(3))
|
|
74
|
+
.join(", ")}]`
|
|
75
|
+
);
|
|
76
|
+
console.log(
|
|
77
|
+
` All values ≥ 0: ${allOutputs.every((v) => v >= 0) ? "Yes" : "No"}`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function testStreamingHalfWaveRectification() {
|
|
82
|
+
console.log("\n\n=== Testing Streaming Half-Wave Rectification ===\n");
|
|
83
|
+
|
|
84
|
+
const pipeline = createDspPipeline();
|
|
85
|
+
pipeline.Rectify({ mode: "half" }); // Half-wave: negatives → 0
|
|
86
|
+
|
|
87
|
+
console.log("Pipeline created with half-wave rectifier");
|
|
88
|
+
console.log("Simulating bipolar signal stream (zeros out negative half)\n");
|
|
89
|
+
|
|
90
|
+
let chunkNumber = 0;
|
|
91
|
+
const allOutputs: number[] = [];
|
|
92
|
+
|
|
93
|
+
for await (const chunk of simulateDataStream(100, 20)) {
|
|
94
|
+
chunkNumber++;
|
|
95
|
+
const output = await pipeline.process(chunk, {
|
|
96
|
+
sampleRate: 100,
|
|
97
|
+
channels: 1,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
console.log(
|
|
101
|
+
`Chunk ${chunkNumber}: [${output[0].toFixed(3)}, ${output[1].toFixed(
|
|
102
|
+
3
|
|
103
|
+
)}, ...] (negatives → 0)`
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
allOutputs.push(...Array.from(output));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
console.log(`\nStream complete`);
|
|
110
|
+
const zeroCount = allOutputs.filter((v) => v === 0).length;
|
|
111
|
+
const positiveCount = allOutputs.filter((v) => v > 0).length;
|
|
112
|
+
console.log(` Zero values: ${zeroCount}`);
|
|
113
|
+
console.log(` Positive values: ${positiveCount}`);
|
|
114
|
+
console.log(
|
|
115
|
+
` All values ≥ 0: ${allOutputs.every((v) => v >= 0) ? "Yes" : "No"}`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Test state persistence across stream interruption
|
|
120
|
+
async function testStreamInterruption() {
|
|
121
|
+
console.log("\n\n=== Testing Rectify Stream Interruption & Recovery ===\n");
|
|
122
|
+
|
|
123
|
+
const pipeline = createDspPipeline();
|
|
124
|
+
pipeline.Rectify({ mode: "half" });
|
|
125
|
+
|
|
126
|
+
console.log("Processing first part of stream (half-wave)...");
|
|
127
|
+
let chunk1 = new Float32Array([1, -2, 3, -4, 5]);
|
|
128
|
+
let output1 = await pipeline.process(chunk1, {
|
|
129
|
+
sampleRate: 100,
|
|
130
|
+
channels: 1,
|
|
131
|
+
});
|
|
132
|
+
console.log(`Chunk 1: [${Array.from(chunk1)}] → [${Array.from(output1)}]`);
|
|
133
|
+
|
|
134
|
+
// Save state (simulate crash/restart)
|
|
135
|
+
console.log("\nSaving state (simulating service restart)...");
|
|
136
|
+
const savedState = await pipeline.saveState();
|
|
137
|
+
const parsedState = JSON.parse(savedState);
|
|
138
|
+
console.log("State saved:");
|
|
139
|
+
console.log(` - Type: ${parsedState.stages[0].type}`);
|
|
140
|
+
console.log(` - Mode: ${parsedState.stages[0].state.mode}`);
|
|
141
|
+
|
|
142
|
+
// Create new pipeline and restore
|
|
143
|
+
console.log("\nCreating new pipeline and restoring state...");
|
|
144
|
+
const pipeline2 = createDspPipeline();
|
|
145
|
+
pipeline2.Rectify({ mode: "full" }); // Start with different mode
|
|
146
|
+
await pipeline2.loadState(savedState); // Should restore to half-wave
|
|
147
|
+
console.log("State restored (mode should be 'half')!");
|
|
148
|
+
|
|
149
|
+
// Continue processing
|
|
150
|
+
console.log("\nContinuing stream processing...");
|
|
151
|
+
let chunk2 = new Float32Array([6, -7, 8, -9]);
|
|
152
|
+
let output2 = await pipeline2.process(chunk2, {
|
|
153
|
+
sampleRate: 100,
|
|
154
|
+
channels: 1,
|
|
155
|
+
});
|
|
156
|
+
console.log(`Chunk 2: [${Array.from(chunk2)}] → [${Array.from(output2)}]`);
|
|
157
|
+
console.log(`Expected: [6, 0, 8, 0] (half-wave mode restored)`);
|
|
158
|
+
|
|
159
|
+
console.log("\nRectify stream recovered seamlessly!");
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Test multi-channel rectification
|
|
163
|
+
async function testMultiChannelRectify() {
|
|
164
|
+
console.log("\n\n=== Testing Multi-Channel Rectification ===\n");
|
|
165
|
+
|
|
166
|
+
const pipeline = createDspPipeline();
|
|
167
|
+
pipeline.Rectify({ mode: "full" });
|
|
168
|
+
|
|
169
|
+
console.log("Processing 2-channel interleaved data (full-wave)");
|
|
170
|
+
console.log("Format: [ch1_s1, ch2_s1, ch1_s2, ch2_s2, ...]\n");
|
|
171
|
+
|
|
172
|
+
// Channel 1: positive bias, Channel 2: negative bias
|
|
173
|
+
const chunks = [
|
|
174
|
+
new Float32Array([1, -10, 2, -20, -3, 30, -4, 40]),
|
|
175
|
+
new Float32Array([5, -50, -6, 60, 7, -70, -8, 80]),
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
179
|
+
console.log(`Chunk ${i + 1} input: [${Array.from(chunks[i])}]`);
|
|
180
|
+
const output = await pipeline.process(chunks[i], {
|
|
181
|
+
sampleRate: 100,
|
|
182
|
+
channels: 2,
|
|
183
|
+
});
|
|
184
|
+
console.log(`Chunk ${i + 1} output: [${Array.from(output)}]`);
|
|
185
|
+
console.log(
|
|
186
|
+
` All positive: ${
|
|
187
|
+
Array.from(output).every((v) => v >= 0) ? "Yes" : "No"
|
|
188
|
+
}\n`
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
console.log("Multi-channel rectification works independently per sample!");
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Test practical use case: EMG signal pre-processing
|
|
196
|
+
async function testEmgPreProcessing() {
|
|
197
|
+
console.log("\n\n=== Testing EMG Signal Pre-Processing ===\n");
|
|
198
|
+
|
|
199
|
+
console.log("Use case: Prepare EMG for envelope detection");
|
|
200
|
+
console.log("Pipeline: Full-wave Rectify (converts to magnitude)\n");
|
|
201
|
+
|
|
202
|
+
const pipeline = createDspPipeline();
|
|
203
|
+
pipeline.Rectify({ mode: "full" });
|
|
204
|
+
|
|
205
|
+
// Simulate raw EMG with positive and negative spikes
|
|
206
|
+
const rawEmg = new Float32Array([
|
|
207
|
+
0.1, -0.3, 0.5, -0.8, 1.2, -1.5, 0.9, -0.6, 0.3, -0.1,
|
|
208
|
+
]);
|
|
209
|
+
|
|
210
|
+
console.log(
|
|
211
|
+
"Raw EMG (bipolar):",
|
|
212
|
+
Array.from(rawEmg).map((v) => v.toFixed(2))
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
const rectified = await pipeline.process(rawEmg, {
|
|
216
|
+
sampleRate: 2000,
|
|
217
|
+
channels: 1,
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
console.log(
|
|
221
|
+
"Rectified EMG (magnitude):",
|
|
222
|
+
Array.from(rectified).map((v) => v.toFixed(2))
|
|
223
|
+
);
|
|
224
|
+
console.log("\nRectified signal is ready for RMS/envelope detection!");
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Run all tests
|
|
228
|
+
async function runAllTests() {
|
|
229
|
+
try {
|
|
230
|
+
await testStreamingFullWaveRectification();
|
|
231
|
+
await testStreamingHalfWaveRectification();
|
|
232
|
+
await testStreamInterruption();
|
|
233
|
+
await testMultiChannelRectify();
|
|
234
|
+
await testEmgPreProcessing();
|
|
235
|
+
console.log("\nAll Rectify streaming tests passed!");
|
|
236
|
+
} catch (error) {
|
|
237
|
+
console.error("Test failed:", error);
|
|
238
|
+
process.exit(1);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
runAllTests();
|