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,441 @@
1
+ /**
2
+ * FFT/DFT Engine Implementation with SIMD Optimizations
3
+ */
4
+
5
+ #define _USE_MATH_DEFINES
6
+ #include <cmath>
7
+ #include "FftEngine.h"
8
+ #include "../utils/SimdOps.h"
9
+ #include <algorithm>
10
+ #include <stdexcept>
11
+
12
+ #ifndef M_PI
13
+ #define M_PI 3.14159265358979323846
14
+ #endif
15
+
16
+ namespace dsp
17
+ {
18
+ namespace core
19
+ {
20
+
21
+ template <typename T>
22
+ FftEngine<T>::FftEngine(size_t size)
23
+ : m_size(size), m_isPowerOfTwo(checkPowerOfTwo(size))
24
+ {
25
+ if (size == 0)
26
+ {
27
+ throw std::invalid_argument("FFT size must be > 0");
28
+ }
29
+
30
+ // Initialize twiddle factors and bit-reversal for FFT
31
+ if (m_isPowerOfTwo)
32
+ {
33
+ initTwiddleFactors();
34
+ initBitReversal();
35
+ }
36
+
37
+ // Allocate working buffer
38
+ m_workBuffer.resize(m_size);
39
+ }
40
+
41
+ // ========== Complex Transforms ==========
42
+
43
+ template <typename T>
44
+ void FftEngine<T>::fft(const Complex *input, Complex *output)
45
+ {
46
+ if (!m_isPowerOfTwo)
47
+ {
48
+ throw std::runtime_error("FFT requires power-of-2 size. Use DFT for arbitrary sizes.");
49
+ }
50
+
51
+ // Copy input to output for in-place operation
52
+ std::copy(input, input + m_size, output);
53
+
54
+ // Perform Cooley-Tukey FFT
55
+ cooleyTukeyFFT(output, false);
56
+ }
57
+
58
+ template <typename T>
59
+ void FftEngine<T>::ifft(const Complex *input, Complex *output)
60
+ {
61
+ if (!m_isPowerOfTwo)
62
+ {
63
+ throw std::runtime_error("IFFT requires power-of-2 size. Use IDFT for arbitrary sizes.");
64
+ }
65
+
66
+ // Copy input to output
67
+ std::copy(input, input + m_size, output);
68
+
69
+ // Perform inverse FFT
70
+ cooleyTukeyFFT(output, true);
71
+
72
+ // Scale by 1/N
73
+ T scale = T(1) / static_cast<T>(m_size);
74
+ for (size_t i = 0; i < m_size; ++i)
75
+ {
76
+ output[i] *= scale;
77
+ }
78
+ }
79
+
80
+ template <typename T>
81
+ void FftEngine<T>::dft(const Complex *input, Complex *output)
82
+ {
83
+ // Direct DFT computation: X[k] = Σ x[n] * e^(-j2πkn/N)
84
+ const T two_pi = static_cast<T>(2.0 * M_PI);
85
+
86
+ for (size_t k = 0; k < m_size; ++k)
87
+ {
88
+ Complex sum(0, 0);
89
+
90
+ for (size_t n = 0; n < m_size; ++n)
91
+ {
92
+ T angle = -two_pi * static_cast<T>(k * n) / static_cast<T>(m_size);
93
+ Complex twiddle(std::cos(angle), std::sin(angle));
94
+ sum += input[n] * twiddle;
95
+ }
96
+
97
+ output[k] = sum;
98
+ }
99
+ }
100
+
101
+ template <typename T>
102
+ void FftEngine<T>::idft(const Complex *input, Complex *output)
103
+ {
104
+ // Inverse DFT: x[n] = (1/N) * Σ X[k] * e^(j2πkn/N)
105
+ const T two_pi = static_cast<T>(2.0 * M_PI);
106
+ const T scale = T(1) / static_cast<T>(m_size);
107
+
108
+ for (size_t n = 0; n < m_size; ++n)
109
+ {
110
+ Complex sum(0, 0);
111
+
112
+ for (size_t k = 0; k < m_size; ++k)
113
+ {
114
+ T angle = two_pi * static_cast<T>(k * n) / static_cast<T>(m_size);
115
+ Complex twiddle(std::cos(angle), std::sin(angle));
116
+ sum += input[k] * twiddle;
117
+ }
118
+
119
+ output[n] = sum * scale;
120
+ }
121
+ }
122
+
123
+ // ========== Real-Input Transforms ==========
124
+
125
+ template <typename T>
126
+ void FftEngine<T>::rfft(const T *input, Complex *output)
127
+ {
128
+ if (!m_isPowerOfTwo)
129
+ {
130
+ throw std::runtime_error("RFFT requires power-of-2 size. Use RDFT for arbitrary sizes.");
131
+ }
132
+
133
+ // Pack real input into complex array (imaginary part = 0)
134
+ for (size_t i = 0; i < m_size; ++i)
135
+ {
136
+ m_workBuffer[i] = Complex(input[i], 0);
137
+ }
138
+
139
+ // Perform standard FFT
140
+ cooleyTukeyFFT(m_workBuffer.data(), false);
141
+
142
+ // Copy half spectrum (exploit Hermitian symmetry)
143
+ size_t halfSize = getHalfSize();
144
+ for (size_t i = 0; i < halfSize; ++i)
145
+ {
146
+ output[i] = m_workBuffer[i];
147
+ }
148
+ }
149
+
150
+ template <typename T>
151
+ void FftEngine<T>::irfft(const Complex *input, T *output)
152
+ {
153
+ if (!m_isPowerOfTwo)
154
+ {
155
+ throw std::runtime_error("IRFFT requires power-of-2 size. Use IRDFT for arbitrary sizes.");
156
+ }
157
+
158
+ size_t halfSize = getHalfSize();
159
+
160
+ // Reconstruct full spectrum using Hermitian symmetry: X[N-k] = X*[k]
161
+ m_workBuffer[0] = input[0]; // DC component
162
+
163
+ for (size_t i = 1; i < halfSize - 1; ++i)
164
+ {
165
+ m_workBuffer[i] = input[i];
166
+ m_workBuffer[m_size - i] = std::conj(input[i]);
167
+ }
168
+
169
+ // Nyquist frequency (if N is even)
170
+ if (m_size % 2 == 0)
171
+ {
172
+ m_workBuffer[m_size / 2] = input[halfSize - 1];
173
+ }
174
+
175
+ // Perform inverse FFT
176
+ cooleyTukeyFFT(m_workBuffer.data(), true);
177
+
178
+ // Extract real part and scale by 1/N
179
+ T scale = T(1) / static_cast<T>(m_size);
180
+ for (size_t i = 0; i < m_size; ++i)
181
+ {
182
+ output[i] = m_workBuffer[i].real() * scale;
183
+ }
184
+ }
185
+
186
+ template <typename T>
187
+ void FftEngine<T>::rdft(const T *input, Complex *output)
188
+ {
189
+ // Direct RDFT: compute only half spectrum
190
+ const T two_pi = static_cast<T>(2.0 * M_PI);
191
+ size_t halfSize = getHalfSize();
192
+
193
+ for (size_t k = 0; k < halfSize; ++k)
194
+ {
195
+ Complex sum(0, 0);
196
+
197
+ for (size_t n = 0; n < m_size; ++n)
198
+ {
199
+ T angle = -two_pi * static_cast<T>(k * n) / static_cast<T>(m_size);
200
+ Complex twiddle(std::cos(angle), std::sin(angle));
201
+ sum += Complex(input[n], 0) * twiddle;
202
+ }
203
+
204
+ output[k] = sum;
205
+ }
206
+ }
207
+
208
+ template <typename T>
209
+ void FftEngine<T>::irdft(const Complex *input, T *output)
210
+ {
211
+ // Inverse RDFT: reconstruct real signal from half spectrum
212
+ const T two_pi = static_cast<T>(2.0 * M_PI);
213
+ const T scale = T(1) / static_cast<T>(m_size);
214
+ size_t halfSize = getHalfSize();
215
+
216
+ for (size_t n = 0; n < m_size; ++n)
217
+ {
218
+ Complex sum(0, 0);
219
+
220
+ // DC component
221
+ sum += input[0];
222
+
223
+ // Positive frequencies
224
+ for (size_t k = 1; k < halfSize - 1; ++k)
225
+ {
226
+ T angle = two_pi * static_cast<T>(k * n) / static_cast<T>(m_size);
227
+ Complex twiddle(std::cos(angle), std::sin(angle));
228
+
229
+ // Add X[k] * e^(j2πkn/N) + X*[k] * e^(-j2πkn/N)
230
+ sum += input[k] * twiddle;
231
+ sum += std::conj(input[k]) * std::conj(twiddle);
232
+ }
233
+
234
+ // Nyquist frequency (if N is even)
235
+ if (m_size % 2 == 0 && halfSize > 1)
236
+ {
237
+ T angle = two_pi * static_cast<T>((halfSize - 1) * n) / static_cast<T>(m_size);
238
+ Complex twiddle(std::cos(angle), std::sin(angle));
239
+ sum += input[halfSize - 1] * twiddle;
240
+ }
241
+
242
+ output[n] = sum.real() * scale;
243
+ }
244
+ }
245
+
246
+ // ========== Utility Methods ==========
247
+
248
+ template <typename T>
249
+ void FftEngine<T>::getMagnitude(const Complex *spectrum, T *magnitudes, size_t length)
250
+ {
251
+ // MEDIUM WIN: SIMD-optimized magnitude calculation
252
+ if constexpr (std::is_same_v<T, float>)
253
+ {
254
+ // Extract real and imaginary parts into separate arrays for SIMD
255
+ std::vector<float> real(length);
256
+ std::vector<float> imag(length);
257
+
258
+ for (size_t i = 0; i < length; ++i)
259
+ {
260
+ real[i] = spectrum[i].real();
261
+ imag[i] = spectrum[i].imag();
262
+ }
263
+
264
+ dsp::simd::complex_magnitude(real.data(), imag.data(), magnitudes, length);
265
+ }
266
+ else
267
+ {
268
+ // Fallback for double precision
269
+ for (size_t i = 0; i < length; ++i)
270
+ {
271
+ magnitudes[i] = std::abs(spectrum[i]);
272
+ }
273
+ }
274
+ }
275
+
276
+ template <typename T>
277
+ void FftEngine<T>::getPhase(const Complex *spectrum, T *phases, size_t length)
278
+ {
279
+ // Phase calculation (atan2 doesn't benefit much from SIMD)
280
+ for (size_t i = 0; i < length; ++i)
281
+ {
282
+ phases[i] = std::arg(spectrum[i]);
283
+ }
284
+ }
285
+
286
+ template <typename T>
287
+ void FftEngine<T>::getPower(const Complex *spectrum, T *power, size_t length)
288
+ {
289
+ // MEDIUM WIN: SIMD-optimized power calculation
290
+ if constexpr (std::is_same_v<T, float>)
291
+ {
292
+ // Extract real and imaginary parts into separate arrays for SIMD
293
+ std::vector<float> real(length);
294
+ std::vector<float> imag(length);
295
+
296
+ for (size_t i = 0; i < length; ++i)
297
+ {
298
+ real[i] = spectrum[i].real();
299
+ imag[i] = spectrum[i].imag();
300
+ }
301
+
302
+ dsp::simd::complex_power(real.data(), imag.data(), power, length);
303
+ }
304
+ else
305
+ {
306
+ // Fallback for double precision
307
+ for (size_t i = 0; i < length; ++i)
308
+ {
309
+ T mag = std::abs(spectrum[i]);
310
+ power[i] = mag * mag;
311
+ }
312
+ }
313
+ }
314
+
315
+ // ========== Private Methods ==========
316
+
317
+ template <typename T>
318
+ void FftEngine<T>::initTwiddleFactors()
319
+ {
320
+ m_twiddleFactors.resize(m_size / 2);
321
+
322
+ const T two_pi = static_cast<T>(2.0 * M_PI);
323
+
324
+ for (size_t k = 0; k < m_size / 2; ++k)
325
+ {
326
+ T angle = -two_pi * static_cast<T>(k) / static_cast<T>(m_size);
327
+ m_twiddleFactors[k] = Complex(std::cos(angle), std::sin(angle));
328
+ }
329
+ }
330
+
331
+ template <typename T>
332
+ void FftEngine<T>::initBitReversal()
333
+ {
334
+ m_bitReversalIndices.resize(m_size);
335
+
336
+ size_t bits = 0;
337
+ size_t temp = m_size;
338
+ while (temp > 1)
339
+ {
340
+ temp >>= 1;
341
+ ++bits;
342
+ }
343
+
344
+ for (size_t i = 0; i < m_size; ++i)
345
+ {
346
+ m_bitReversalIndices[i] = reverseBits(i, bits);
347
+ }
348
+ }
349
+
350
+ template <typename T>
351
+ void FftEngine<T>::bitReverse(Complex *data)
352
+ {
353
+ for (size_t i = 0; i < m_size; ++i)
354
+ {
355
+ size_t j = m_bitReversalIndices[i];
356
+ if (i < j)
357
+ {
358
+ std::swap(data[i], data[j]);
359
+ }
360
+ }
361
+ }
362
+
363
+ template <typename T>
364
+ void FftEngine<T>::cooleyTukeyFFT(Complex *data, bool inverse)
365
+ {
366
+ // Bit-reversal permutation
367
+ bitReverse(data);
368
+
369
+ // Cooley-Tukey decimation-in-time
370
+ for (size_t len = 2; len <= m_size; len *= 2)
371
+ {
372
+ size_t halfLen = len / 2;
373
+ size_t step = m_size / len;
374
+
375
+ for (size_t i = 0; i < m_size; i += len)
376
+ {
377
+ for (size_t j = 0; j < halfLen; ++j)
378
+ {
379
+ size_t twiddleIdx = j * step;
380
+ Complex twiddle = m_twiddleFactors[twiddleIdx];
381
+
382
+ if (inverse)
383
+ {
384
+ twiddle = std::conj(twiddle);
385
+ }
386
+
387
+ butterfly(data[i + j], data[i + j + halfLen], twiddle);
388
+ }
389
+ }
390
+ }
391
+ }
392
+
393
+ template <typename T>
394
+ inline void FftEngine<T>::butterfly(Complex &a, Complex &b, const Complex &twiddle)
395
+ {
396
+ Complex temp = b * twiddle;
397
+ b = a - temp;
398
+ a = a + temp;
399
+ }
400
+
401
+ template <typename T>
402
+ bool FftEngine<T>::checkPowerOfTwo(size_t n)
403
+ {
404
+ return n > 0 && (n & (n - 1)) == 0;
405
+ }
406
+
407
+ template <typename T>
408
+ size_t FftEngine<T>::nextPowerOfTwo(size_t n)
409
+ {
410
+ if (n == 0)
411
+ return 1;
412
+
413
+ --n;
414
+ n |= n >> 1;
415
+ n |= n >> 2;
416
+ n |= n >> 4;
417
+ n |= n >> 8;
418
+ n |= n >> 16;
419
+ n |= n >> 32;
420
+
421
+ return n + 1;
422
+ }
423
+
424
+ template <typename T>
425
+ size_t FftEngine<T>::reverseBits(size_t x, size_t bits)
426
+ {
427
+ size_t result = 0;
428
+ for (size_t i = 0; i < bits; ++i)
429
+ {
430
+ result = (result << 1) | (x & 1);
431
+ x >>= 1;
432
+ }
433
+ return result;
434
+ }
435
+
436
+ // Explicit template instantiations
437
+ template class FftEngine<float>;
438
+ template class FftEngine<double>;
439
+
440
+ } // namespace core
441
+ } // namespace dsp
@@ -0,0 +1,224 @@
1
+ /**
2
+ * FFT/DFT Engine - High-performance Fourier transforms
3
+ *
4
+ * Implements all 8 standard transforms:
5
+ * - DFT/IDFT: Direct Fourier Transform (O(N²))
6
+ * - FFT/IFFT: Fast Fourier Transform (O(N log N))
7
+ * - RDFT/IRDFT: Real-input DFT (outputs N/2+1 bins)
8
+ * - RFFT/IRFFT: Real-input FFT (fast version)
9
+ *
10
+ * Features:
11
+ * - Cooley-Tukey radix-2 FFT algorithm
12
+ * - In-place computation for memory efficiency
13
+ * - Bit-reversal permutation
14
+ * - Twiddle factor caching
15
+ * - SIMD optimization where possible
16
+ * - Hermitian symmetry exploitation for real inputs
17
+ */
18
+
19
+ #ifndef DSP_CORE_FFT_ENGINE_H
20
+ #define DSP_CORE_FFT_ENGINE_H
21
+
22
+ #include <complex>
23
+ #include <vector>
24
+ #include <cmath>
25
+ #include <memory>
26
+
27
+ namespace dsp
28
+ {
29
+ namespace core
30
+ {
31
+
32
+ template <typename T = float>
33
+ class FftEngine
34
+ {
35
+ public:
36
+ using Complex = std::complex<T>;
37
+
38
+ /**
39
+ * Constructor
40
+ * @param size FFT size (must be power of 2 for FFT, any size for DFT)
41
+ */
42
+ explicit FftEngine(size_t size);
43
+
44
+ ~FftEngine() = default;
45
+
46
+ // ========== Complex Transforms ==========
47
+
48
+ /**
49
+ * Forward FFT (complex -> complex)
50
+ * X[k] = Σ x[n] * e^(-j2πkn/N)
51
+ *
52
+ * @param input Complex input signal (size N)
53
+ * @param output Complex frequency spectrum (size N)
54
+ */
55
+ void fft(const Complex *input, Complex *output);
56
+
57
+ /**
58
+ * Inverse FFT (complex -> complex)
59
+ * x[n] = (1/N) * Σ X[k] * e^(j2πkn/N)
60
+ *
61
+ * @param input Complex frequency spectrum (size N)
62
+ * @param output Complex time-domain signal (size N)
63
+ */
64
+ void ifft(const Complex *input, Complex *output);
65
+
66
+ /**
67
+ * Forward DFT (complex -> complex)
68
+ * Direct computation, slower but works for any size
69
+ *
70
+ * @param input Complex input signal
71
+ * @param output Complex frequency spectrum
72
+ */
73
+ void dft(const Complex *input, Complex *output);
74
+
75
+ /**
76
+ * Inverse DFT (complex -> complex)
77
+ *
78
+ * @param input Complex frequency spectrum
79
+ * @param output Complex time-domain signal
80
+ */
81
+ void idft(const Complex *input, Complex *output);
82
+
83
+ // ========== Real-Input Transforms ==========
84
+
85
+ /**
86
+ * Forward RFFT (real -> complex, half spectrum)
87
+ * Exploits Hermitian symmetry: X[k] = X*[N-k]
88
+ *
89
+ * @param input Real input signal (size N)
90
+ * @param output Complex half-spectrum (size N/2+1)
91
+ */
92
+ void rfft(const T *input, Complex *output);
93
+
94
+ /**
95
+ * Inverse RFFT (complex half-spectrum -> real)
96
+ * Reconstructs real signal from half spectrum
97
+ *
98
+ * @param input Complex half-spectrum (size N/2+1)
99
+ * @param output Real time-domain signal (size N)
100
+ */
101
+ void irfft(const Complex *input, T *output);
102
+
103
+ /**
104
+ * Forward RDFT (real -> complex, half spectrum)
105
+ * Direct computation version of RFFT
106
+ *
107
+ * @param input Real input signal (size N)
108
+ * @param output Complex half-spectrum (size N/2+1)
109
+ */
110
+ void rdft(const T *input, Complex *output);
111
+
112
+ /**
113
+ * Inverse RDFT (complex half-spectrum -> real)
114
+ * Direct computation version of IRFFT
115
+ *
116
+ * @param input Complex half-spectrum (size N/2+1)
117
+ * @param output Real time-domain signal (size N)
118
+ */
119
+ void irdft(const Complex *input, T *output);
120
+
121
+ // ========== Utility Methods ==========
122
+
123
+ /**
124
+ * Get FFT size
125
+ */
126
+ size_t getSize() const { return m_size; }
127
+
128
+ /**
129
+ * Get half-spectrum size (for real transforms)
130
+ */
131
+ size_t getHalfSize() const { return m_size / 2 + 1; }
132
+
133
+ /**
134
+ * Check if size is power of 2
135
+ */
136
+ bool isPowerOfTwo() const { return m_isPowerOfTwo; }
137
+
138
+ /**
139
+ * Get magnitude spectrum from complex spectrum
140
+ * |X[k]| = sqrt(Re²(X[k]) + Im²(X[k]))
141
+ *
142
+ * @param spectrum Complex spectrum
143
+ * @param magnitudes Output magnitude array (same size)
144
+ */
145
+ void getMagnitude(const Complex *spectrum, T *magnitudes, size_t length);
146
+
147
+ /**
148
+ * Get phase spectrum from complex spectrum
149
+ * ∠X[k] = atan2(Im(X[k]), Re(X[k]))
150
+ *
151
+ * @param spectrum Complex spectrum
152
+ * @param phases Output phase array (same size)
153
+ */
154
+ void getPhase(const Complex *spectrum, T *phases, size_t length);
155
+
156
+ /**
157
+ * Get power spectrum (magnitude squared)
158
+ * P[k] = |X[k]|²
159
+ *
160
+ * @param spectrum Complex spectrum
161
+ * @param power Output power array (same size)
162
+ */
163
+ void getPower(const Complex *spectrum, T *power, size_t length);
164
+
165
+ private:
166
+ size_t m_size; // FFT size
167
+ bool m_isPowerOfTwo; // Whether size is power of 2
168
+
169
+ // Cached twiddle factors for FFT
170
+ std::vector<Complex> m_twiddleFactors;
171
+
172
+ // Bit-reversal permutation indices
173
+ std::vector<size_t> m_bitReversalIndices;
174
+
175
+ // Working buffer for in-place operations
176
+ std::vector<Complex> m_workBuffer;
177
+
178
+ /**
179
+ * Initialize twiddle factors: W_N^k = e^(-j2πk/N)
180
+ */
181
+ void initTwiddleFactors();
182
+
183
+ /**
184
+ * Initialize bit-reversal permutation table
185
+ */
186
+ void initBitReversal();
187
+
188
+ /**
189
+ * Perform bit-reversal permutation
190
+ */
191
+ void bitReverse(Complex *data);
192
+
193
+ /**
194
+ * Core Cooley-Tukey FFT algorithm (in-place)
195
+ * @param data Complex array (size must be power of 2)
196
+ * @param inverse If true, performs inverse transform
197
+ */
198
+ void cooleyTukeyFFT(Complex *data, bool inverse);
199
+
200
+ /**
201
+ * Butterfly operation for FFT
202
+ */
203
+ inline void butterfly(Complex &a, Complex &b, const Complex &twiddle);
204
+
205
+ /**
206
+ * Check if number is power of 2
207
+ */
208
+ static bool checkPowerOfTwo(size_t n);
209
+
210
+ /**
211
+ * Compute next power of 2
212
+ */
213
+ static size_t nextPowerOfTwo(size_t n);
214
+
215
+ /**
216
+ * Reverse bits of integer
217
+ */
218
+ static size_t reverseBits(size_t x, size_t bits);
219
+ };
220
+
221
+ } // namespace core
222
+ } // namespace dsp
223
+
224
+ #endif // DSP_CORE_FFT_ENGINE_H