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,131 @@
1
+ import { createDspPipeline } from "../index.js";
2
+
3
+ console.log("Pipeline State Listing Example\n");
4
+
5
+ // Example 1: Simple pipeline
6
+ console.log("1. Simple Pipeline");
7
+ {
8
+ const pipeline = createDspPipeline()
9
+ .MovingAverage({ mode: "moving", windowSize: 100 })
10
+ .Rectify({ mode: "full" })
11
+ .Rms({ mode: "moving", windowSize: 50 });
12
+
13
+ const summary = pipeline.listState();
14
+ console.log(" Pipeline summary:", JSON.stringify(summary, null, 2));
15
+ }
16
+
17
+ // Example 2: After processing (with state)
18
+ console.log("\n2. Pipeline After Processing (With State)");
19
+ {
20
+ const pipeline = createDspPipeline()
21
+ .MovingAverage({ mode: "moving", windowSize: 10 })
22
+ .Rms({ mode: "moving", windowSize: 5 });
23
+
24
+ // Process some data to populate state
25
+ const input = new Float32Array(50).map((_, i) => Math.sin(i * 0.1));
26
+ await pipeline.process(input, { sampleRate: 1000, channels: 1 });
27
+
28
+ const summary = pipeline.listState();
29
+ console.log(" Pipeline summary after processing:");
30
+ console.log(` Total stages: ${summary.stageCount}`);
31
+ summary.stages.forEach((stage) => {
32
+ console.log(` Stage ${stage.index}: ${stage.type}`);
33
+ if (stage.windowSize)
34
+ console.log(` - Window size: ${stage.windowSize}`);
35
+ if (stage.numChannels)
36
+ console.log(` - Channels: ${stage.numChannels}`);
37
+ if (stage.mode) console.log(` - Mode: ${stage.mode}`);
38
+ if (stage.bufferSize)
39
+ console.log(` - Buffer size: ${stage.bufferSize}`);
40
+ if (stage.channelCount)
41
+ console.log(` - Channel count: ${stage.channelCount}`);
42
+ });
43
+ }
44
+
45
+ // Example 3: Multi-channel pipeline
46
+ console.log("\n3. Multi-Channel Pipeline");
47
+ {
48
+ const pipeline = createDspPipeline()
49
+ .MovingAverage({ mode: "moving", windowSize: 20 })
50
+ .Rectify()
51
+ .Rms({ mode: "moving", windowSize: 10 });
52
+
53
+ // Process 4-channel data
54
+ const input = new Float32Array(400).map((_, i) => Math.sin(i * 0.1));
55
+ await pipeline.process(input, { sampleRate: 2000, channels: 4 });
56
+
57
+ const summary = pipeline.listState();
58
+ console.log(" Multi-channel pipeline:");
59
+ summary.stages.forEach((stage) => {
60
+ console.log(` ${stage.type}: ${stage.channelCount} channels`);
61
+ });
62
+ }
63
+
64
+ // Example 4: Monitoring/Debugging
65
+ console.log("\n4. Pipeline Monitoring/Debugging");
66
+ {
67
+ const pipeline = createDspPipeline()
68
+ .MovingAverage({ mode: "moving", windowSize: 100 })
69
+ .Rectify({ mode: "half" })
70
+ .Rms({ mode: "moving", windowSize: 50 });
71
+
72
+ // This could be used in a monitoring endpoint
73
+ const summary = pipeline.listState();
74
+
75
+ console.log(" Pipeline health check:");
76
+ console.log(` - Pipeline has ${summary.stageCount} stages`);
77
+ console.log(
78
+ ` - Configuration is valid: ${summary.stages.every((s) => s.type)}`
79
+ );
80
+ console.log(
81
+ ` - Timestamp: ${new Date(summary.timestamp * 1000).toISOString()}`
82
+ );
83
+
84
+ // Check for expected configuration
85
+ const hasMovingAverage = summary.stages.some(
86
+ (s) => s.type === "movingAverage"
87
+ );
88
+ const hasRms = summary.stages.some((s) => s.type === "rms");
89
+ console.log(` - Has MovingAverage: ${hasMovingAverage}`);
90
+ console.log(` - Has RMS: ${hasRms}`);
91
+ }
92
+
93
+ // Example 5: Comparison with saveState()
94
+ console.log("\n5. listState() vs saveState() Comparison");
95
+ {
96
+ const pipeline = createDspPipeline().MovingAverage({ mode: "moving", windowSize: 5 });
97
+
98
+ // Process some data
99
+ const input = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
100
+ await pipeline.process(input, { sampleRate: 1000, channels: 1 });
101
+
102
+ // Compare sizes
103
+ const summary = pipeline.listState();
104
+ const fullState = await pipeline.saveState();
105
+
106
+ console.log(" Size comparison:");
107
+ console.log(
108
+ ` - listState() JSON: ${JSON.stringify(summary).length} bytes`
109
+ );
110
+ console.log(` - saveState() JSON: ${fullState.length} bytes`);
111
+ console.log(
112
+ ` - Reduction: ${(
113
+ (1 - JSON.stringify(summary).length / fullState.length) *
114
+ 100
115
+ ).toFixed(1)}%`
116
+ );
117
+
118
+ console.log("\n listState() returns:");
119
+ console.log(` ${JSON.stringify(summary, null, 2)}`);
120
+
121
+ console.log("\n saveState() includes:");
122
+ console.log(` - Full circular buffer contents (all samples)`);
123
+ console.log(` - Running sums/squares for each channel`);
124
+ console.log(` - Complete state for restoration`);
125
+ }
126
+
127
+ console.log("\nUse Cases:");
128
+ console.log(" - listState(): Monitoring dashboards, health checks, debugging");
129
+ console.log(
130
+ " - saveState(): Redis persistence, crash recovery, state migration"
131
+ );
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Example: Production-Ready Unified Logger
3
+ *
4
+ * This example demonstrates the new Logger class with multiple backend handlers,
5
+ * error isolation, and structured logging capabilities.
6
+ */
7
+
8
+ import {
9
+ Logger,
10
+ createConsoleHandler,
11
+ createLokiHandler,
12
+ createPrometheusHandler,
13
+ createPagerDutyHandler,
14
+ } from "../index.js";
15
+
16
+ // Example 1: Basic logger with console output
17
+ const basicLogger = new Logger([createConsoleHandler()]);
18
+
19
+ await basicLogger.info("Pipeline initialized");
20
+ await basicLogger.warn("High latency detected", "performance", {
21
+ latency: 450,
22
+ });
23
+ await basicLogger.error("Connection failed", "redis.connection", {
24
+ host: "localhost",
25
+ port: 6379,
26
+ });
27
+
28
+ // Example 2: Production logger with multiple backends
29
+ const productionLogger = new Logger([
30
+ // Console for local debugging
31
+ createConsoleHandler(),
32
+
33
+ // Loki for centralized log aggregation
34
+ createLokiHandler({
35
+ endpoint: "https://loki.example.com",
36
+ apiKey: "your-loki-api-key",
37
+ batchSize: 100,
38
+ flushInterval: 5000,
39
+ }),
40
+
41
+ // Prometheus for metrics
42
+ createPrometheusHandler({
43
+ endpoint: "https://prometheus-pushgateway.example.com",
44
+ }),
45
+
46
+ // PagerDuty for critical alerts
47
+ createPagerDutyHandler({
48
+ endpoint: "https://events.pagerduty.com/v2/enqueue",
49
+ apiKey: "your-routing-key",
50
+ }),
51
+ ]);
52
+
53
+ // Example 3: Child logger with topic prefix
54
+ const pipelineLogger = productionLogger.child("dsp.pipeline");
55
+ const filterLogger = pipelineLogger.child("filter");
56
+
57
+ await filterLogger.info("MovingAverage initialized", "init", {
58
+ windowSize: 10,
59
+ sampleRate: 1000,
60
+ });
61
+
62
+ // Example 4: Error isolation demonstration
63
+ const resilientLogger = new Logger([
64
+ createConsoleHandler(), // This will work
65
+
66
+ // Simulate a failing handler
67
+ async () => {
68
+ throw new Error("Network timeout");
69
+ },
70
+
71
+ createConsoleHandler(), // This will still work!
72
+ ]);
73
+
74
+ // All handlers are isolated - one failure doesn't break others
75
+ await resilientLogger.info("Resilient logging works!");
76
+
77
+ // Example 5: Structured logging with rich context
78
+ await productionLogger.info("DSP processing complete", "dsp.result", {
79
+ channels: 8,
80
+ samples: 10000,
81
+ duration_ms: 142,
82
+ filters: ["MovingAverage", "RMS", "ZScoreNormalize"],
83
+ metrics: {
84
+ throughput: 70422, // samples/sec
85
+ memory_mb: 3.2,
86
+ cpu_percent: 12.5,
87
+ },
88
+ tags: ["production", "high-performance"],
89
+ });
90
+
91
+ console.log("\n✅ All examples completed successfully!");
@@ -0,0 +1,243 @@
1
+ /**
2
+ * Notch Filter Examples
3
+ *
4
+ * Demonstrates how to create and use notch filters for removing specific frequency bands.
5
+ * Common use cases: 50/60 Hz power line noise removal, narrow-band interference rejection
6
+ *
7
+ * IMPORTANT: Notch filters are created using band-stop filters
8
+ * API: FirFilter.createBandStop({ lowCutoffFrequency, highCutoffFrequency, sampleRate, order })
9
+ */
10
+
11
+ import { FirFilter } from "../filters.js";
12
+
13
+ // Main async function to handle Promise-based filter processing
14
+ async function main() {
15
+ console.log("=== Notch Filter Examples ===\n");
16
+
17
+ // =============================================================================
18
+ // Example 1: FIR Notch Filter for 60 Hz Power Line Noise
19
+ // =============================================================================
20
+ console.log("Example 1: FIR Notch Filter (60 Hz power line noise)");
21
+
22
+ const firNotch60Hz = FirFilter.createBandStop({
23
+ lowCutoffFrequency: 58, // Lower edge of notch
24
+ highCutoffFrequency: 62, // Upper edge of notch
25
+ sampleRate: 1000,
26
+ order: 101, // Higher order = sharper notch
27
+ windowType: "hamming",
28
+ });
29
+
30
+ // Test signal with 60 Hz interference
31
+ const sampleRate = 1000;
32
+ const duration = 1; // 1 second
33
+ const numSamples = sampleRate * duration;
34
+ const signal = new Float32Array(numSamples);
35
+
36
+ // Create signal: 10 Hz sine wave + 60 Hz noise
37
+ for (let i = 0; i < numSamples; i++) {
38
+ const t = i / sampleRate;
39
+ signal[i] =
40
+ Math.sin(2 * Math.PI * 10 * t) + // 10 Hz signal
41
+ 0.5 * Math.sin(2 * Math.PI * 60 * t); // 60 Hz noise
42
+ }
43
+
44
+ const filtered = await firNotch60Hz.process(signal);
45
+
46
+ console.log(
47
+ ` Input samples (first 10): ${Array.from(signal.slice(0, 10))
48
+ .map((v: number) => v.toFixed(3))
49
+ .join(", ")}`
50
+ );
51
+ console.log(
52
+ ` Filtered samples (first 10): ${Array.from(filtered.slice(0, 10))
53
+ .map((v: number) => v.toFixed(3))
54
+ .join(", ")}`
55
+ );
56
+ console.log(` 60 Hz component should be significantly reduced\n`);
57
+
58
+ // =============================================================================
59
+ // Example 2: Cascaded Notch Filters (Remove Multiple Harmonics)
60
+ // =============================================================================
61
+ console.log("Example 2: Cascaded Notch Filters (60 Hz + harmonics)");
62
+
63
+ // Remove 60 Hz fundamental and 2nd harmonic (120 Hz)
64
+ const notch60 = FirFilter.createBandStop({
65
+ lowCutoffFrequency: 58,
66
+ highCutoffFrequency: 62,
67
+ sampleRate: 1000,
68
+ order: 51,
69
+ });
70
+
71
+ const notch120 = FirFilter.createBandStop({
72
+ lowCutoffFrequency: 118,
73
+ highCutoffFrequency: 122,
74
+ sampleRate: 1000,
75
+ order: 51,
76
+ });
77
+
78
+ // Signal with multiple harmonics
79
+ const harmonicSignal = new Float32Array(1000);
80
+ for (let i = 0; i < 1000; i++) {
81
+ const t = i / 1000;
82
+ harmonicSignal[i] =
83
+ Math.sin(2 * Math.PI * 10 * t) + // Desired 10 Hz
84
+ 0.3 * Math.sin(2 * Math.PI * 60 * t) + // 60 Hz noise
85
+ 0.2 * Math.sin(2 * Math.PI * 120 * t); // 120 Hz harmonic
86
+ }
87
+
88
+ // Apply filters in series (must await each step)
89
+ const step1 = await notch60.process(harmonicSignal);
90
+ const step2 = await notch120.process(step1);
91
+
92
+ console.log(" Original signal: 10 Hz + 60 Hz + 120 Hz interference");
93
+ console.log(" After 60 Hz notch: Reduced 60 Hz component");
94
+ console.log(" After 120 Hz notch: Reduced both harmonics");
95
+ console.log(
96
+ ` Final output (first 10): ${Array.from(step2.slice(0, 10))
97
+ .map((v: number) => v.toFixed(3))
98
+ .join(", ")}\n`
99
+ );
100
+
101
+ // =============================================================================
102
+ // Example 3: Real-time Notch Filtering (Stateful Processing)
103
+ // =============================================================================
104
+ console.log("Example 3: Real-time Notch Filtering (Streaming)");
105
+
106
+ const realtimeNotch = FirFilter.createBandStop({
107
+ lowCutoffFrequency: 49,
108
+ highCutoffFrequency: 51, // 50 Hz notch (EU power line)
109
+ sampleRate: 1000,
110
+ order: 51,
111
+ });
112
+
113
+ console.log(" Processing 5 frames of 100 samples each...");
114
+
115
+ for (let frame = 0; frame < 5; frame++) {
116
+ const frameData = new Float32Array(100);
117
+
118
+ // Generate frame with 50 Hz noise
119
+ for (let i = 0; i < 100; i++) {
120
+ const sampleIndex = frame * 100 + i;
121
+ const t = sampleIndex / 1000;
122
+ frameData[i] =
123
+ Math.sin(2 * Math.PI * 5 * t) + // 5 Hz signal
124
+ 0.4 * Math.sin(2 * Math.PI * 50 * t); // 50 Hz noise
125
+ }
126
+
127
+ const filteredFrame = await realtimeNotch.process(frameData);
128
+
129
+ const inputPower =
130
+ frameData.reduce((sum: number, x: number) => sum + x * x, 0) /
131
+ frameData.length;
132
+ const outputPower =
133
+ filteredFrame.reduce((sum: number, x: number) => sum + x * x, 0) /
134
+ filteredFrame.length;
135
+
136
+ console.log(
137
+ ` Frame ${frame + 1}: Input power=${inputPower.toFixed(
138
+ 4
139
+ )}, Output power=${outputPower.toFixed(4)}`
140
+ );
141
+ }
142
+
143
+ console.log(" Filters maintain state between process() calls by default\n");
144
+
145
+ // =============================================================================
146
+ // Example 4: Notch Filter Design Trade-offs
147
+ // =============================================================================
148
+ console.log("Example 4: Notch Filter Design Trade-offs");
149
+
150
+ console.log(" Narrow Notch (1 Hz bandwidth, order 201):");
151
+ console.log(" + Minimal passband distortion");
152
+ console.log(" + Precise frequency rejection");
153
+ console.log(" - Higher computational cost");
154
+ console.log(" - Longer group delay");
155
+ console.log();
156
+ console.log(" Wide Notch (10 Hz bandwidth, order 51):");
157
+ console.log(" + Lower computational cost");
158
+ console.log(" + Shorter group delay");
159
+ console.log(" - More passband distortion near notch");
160
+ console.log(" - Less precise frequency rejection\n");
161
+
162
+ // =============================================================================
163
+ // Example 5: Verifying Notch Filter Performance
164
+ // =============================================================================
165
+ console.log("Example 5: Verifying Notch Filter Performance");
166
+
167
+ const testNotch = FirFilter.createBandStop({
168
+ lowCutoffFrequency: 95,
169
+ highCutoffFrequency: 105, // 100 Hz notch
170
+ sampleRate: 1000,
171
+ order: 101,
172
+ });
173
+
174
+ // Test with pure 100 Hz tone (should be completely removed)
175
+ const testSignal100Hz = new Float32Array(1000);
176
+ for (let i = 0; i < 1000; i++) {
177
+ testSignal100Hz[i] = Math.sin((2 * Math.PI * 100 * i) / 1000);
178
+ }
179
+
180
+ const filtered100Hz = await testNotch.process(testSignal100Hz);
181
+
182
+ // Calculate attenuation
183
+ const inputPower =
184
+ testSignal100Hz.reduce((sum: number, x: number) => sum + x * x, 0) /
185
+ testSignal100Hz.length;
186
+ const outputPower =
187
+ filtered100Hz.reduce((sum: number, x: number) => sum + x * x, 0) /
188
+ filtered100Hz.length;
189
+ const attenuationDb = 10 * Math.log10(outputPower / inputPower);
190
+
191
+ console.log(` Input: Pure 100 Hz sine wave`);
192
+ console.log(` Input RMS power: ${inputPower.toFixed(6)}`);
193
+ console.log(` Output RMS power: ${outputPower.toFixed(6)}`);
194
+ console.log(` Attenuation: ${attenuationDb.toFixed(2)} dB`);
195
+ console.log(` (More negative = better rejection)\n`);
196
+
197
+ // =============================================================================
198
+ // Important Notes
199
+ // =============================================================================
200
+ console.log("=== Important Notes ===");
201
+ console.log();
202
+ console.log("1. CORRECT API Usage:");
203
+ console.log(" const notchFilter = FirFilter.createBandStop({");
204
+ console.log(" lowCutoffFrequency: 58,");
205
+ console.log(" highCutoffFrequency: 62,");
206
+ console.log(" sampleRate: 1000,");
207
+ console.log(" order: 51");
208
+ console.log(" });");
209
+ console.log();
210
+ console.log("2. Notch filters are implemented as band-stop filters");
211
+ console.log(' Mode: "bandstop" or "notch" are equivalent');
212
+ console.log(' Note: "notch" is a MODE, not a TYPE');
213
+ console.log();
214
+ console.log("3. Filter Type Selection:");
215
+ console.log(" - FIR: Linear phase, always stable, higher order needed");
216
+ console.log(
217
+ " - IIR: Sharper rolloff, lower order, may have phase distortion"
218
+ );
219
+ console.log();
220
+ console.log("4. Parameters:");
221
+ console.log(" - lowCutoffFrequency: Lower edge of rejection band");
222
+ console.log(" - highCutoffFrequency: Upper edge of rejection band");
223
+ console.log(" - Bandwidth = highCutoff - lowCutoff");
224
+ console.log();
225
+ console.log("5. Design Guidelines:");
226
+ console.log(" - Narrow notch: Bandwidth < 5 Hz (needs high order)");
227
+ console.log(" - Wide notch: Bandwidth > 10 Hz (lower order acceptable)");
228
+ console.log(" - Power line rejection: 2-4 Hz bandwidth typical");
229
+ console.log();
230
+ console.log("6. Common Applications:");
231
+ console.log(" - 50/60 Hz power line noise removal");
232
+ console.log(" - RF interference rejection");
233
+ console.log(" - Mechanical vibration artifact removal");
234
+ console.log(" - Narrow-band EMI filtering");
235
+ console.log();
236
+ console.log("7. Pipeline Integration:");
237
+ console.log(" - Filter stages in DSP pipeline not yet implemented");
238
+ console.log(" - Use standalone filters and manual chaining for now");
239
+ console.log(" - See examples above for correct usage patterns");
240
+ }
241
+
242
+ // Run the examples
243
+ main().catch(console.error);