LMMC
Lamina Mathematic core
载入中...
搜索中...
未找到
fft.c
浏览该文件的文档.
1
18#include <math.h>
19#include <stdint.h>
20#include <string.h>
21#include "memory_bridge.h"
22#include "internal.h"
23#include "lmmc/config.h"
24#include "lmmc/fft.h"
25#include "lmmc/numeric_scalar.h"
26
30static int fft_is_power_of_two(size_t n) {
31 return (n > 0) && ((n & (n - 1)) == 0);
32}
33
34
38static size_t fft_next_power_of_two(size_t n) {
39 size_t p = 1;
40 if (n == 0) { return 1; }
41 while (p < n) {
42 if (p > SIZE_MAX / 2) { return 0; }
43 p <<= 1;
44 }
45 return p;
46}
47
51static void fft_bit_reverse(lmmc_real_t* real, lmmc_real_t* imag, size_t n) {
52 size_t i, j, bit;
53 lmmc_real_t tmp;
54
55 j = 0;
56 for (i = 1; i < n; ++i) {
57 bit = n >> 1;
58 while (j & bit) {
59 j ^= bit;
60 bit >>= 1;
61 }
62 j ^= bit;
63
64 if (i < j) {
65 tmp = real[i]; real[i] = real[j]; real[j] = tmp;
66 tmp = imag[i]; imag[i] = imag[j]; imag[j] = tmp;
67 }
68 }
69}
70
75static lmmc_status_t fft_radix2(lmmc_real_t* real, lmmc_real_t* imag, size_t n, int inverse) {
76 size_t len, i, j;
77 double angle_sign;
78
79 if (n <= 1) { return LMMC_STATUS_OK; }
80
81 fft_bit_reverse(real, imag, n);
82
83 angle_sign = inverse ? 1.0 : -1.0;
84
85 for (len = 2; len <= n; len <<= 1) {
86 double angle = angle_sign * 2.0 * LMMC_PI / (double)len;
87 double wlen_r = cos(angle);
88 double wlen_i = sin(angle);
89
90 for (i = 0; i < n; i += len) {
91 double w_r = 1.0, w_i = 0.0;
92 size_t half = len >> 1;
93
94 for (j = 0; j < half; ++j) {
95 size_t u_idx = i + j;
96 size_t v_idx = i + j + half;
97
99 double t_r = w_r * real[v_idx] - w_i * imag[v_idx];
100 double t_i = w_r * imag[v_idx] + w_i * real[v_idx];
101
103 real[v_idx] = real[u_idx] - t_r;
104 imag[v_idx] = imag[u_idx] - t_i;
105 real[u_idx] = real[u_idx] + t_r;
106 imag[u_idx] = imag[u_idx] + t_i;
107
109 {
110 double new_w_r = w_r * wlen_r - w_i * wlen_i;
111 double new_w_i = w_r * wlen_i + w_i * wlen_r;
112 w_r = new_w_r;
113 w_i = new_w_i;
114 }
115 }
116 }
117 if (len == n) { break; }
118 }
119
120 if (inverse) {
121 double scale = 1.0 / (double)n;
122 for (i = 0; i < n; ++i) {
123 real[i] *= scale;
124 imag[i] *= scale;
125 }
126 }
127
128 return LMMC_STATUS_OK;
129}
130
131static void fft_chirp_input(
132 const lmmc_real_t* real, const lmmc_real_t* imag,
133 lmmc_real_t* a_r, lmmc_real_t* a_i, size_t n, double sign)
134{
135 size_t k;
136 for (k = 0; k < n; ++k) {
137 const double kd = (double)k;
138 const double phase = sign * LMMC_PI * kd * kd / (double)n;
139 const double c = cos(phase);
140 const double s = sin(phase);
141 a_r[k] = real[k] * c - imag[k] * s;
142 a_i[k] = imag[k] * c + real[k] * s;
143 }
144}
145
146static void fft_chirp_kernel(
147 lmmc_real_t* b_r, lmmc_real_t* b_i,
148 size_t n, size_t m, double sign)
149{
150 size_t k;
151 for (k = 0; k < n; ++k) {
152 const double kd = (double)k;
153 const double phase = -sign * LMMC_PI * kd * kd / (double)n;
154 b_r[k] = cos(phase);
155 b_i[k] = sin(phase);
156 }
157 for (k = 1; k < n; ++k) {
158 b_r[m - k] = b_r[k];
159 b_i[m - k] = b_i[k];
160 }
161}
162
163static void fft_pointwise_product(
164 lmmc_real_t* a_r, lmmc_real_t* a_i,
165 const lmmc_real_t* b_r, const lmmc_real_t* b_i, size_t n)
166{
167 size_t k;
168 for (k = 0; k < n; ++k) {
169 const double tr = a_r[k] * b_r[k] - a_i[k] * b_i[k];
170 const double ti = a_r[k] * b_i[k] + a_i[k] * b_r[k];
171 a_r[k] = tr;
172 a_i[k] = ti;
173 }
174}
175
186static lmmc_status_t fft_bluestein(lmmc_real_t* real, lmmc_real_t* imag, size_t n, int inverse) {
187 size_t m; /* padded power-of-2 length */
188 size_t convolution_length;
189 size_t k;
190 double sign;
191 lmmc_real_t *a_r = NULL, *a_i = NULL;
192 lmmc_real_t *b_r = NULL, *b_i = NULL;
194
195 if (n <= 1) {
196 return LMMC_STATUS_OK;
197 }
198
200 if (!lmmc_safe_mul_size(2, n, &convolution_length)) {
202 }
203 --convolution_length;
204 m = fft_next_power_of_two(convolution_length);
205 if (m == 0) { return LMMC_STATUS_INVALID_ARGUMENT; }
206
212
213 if (a_r == NULL || a_i == NULL || b_r == NULL || b_i == NULL) {
215 goto cleanup;
216 }
217
219 memset(a_r, 0, m * sizeof(lmmc_real_t));
220 memset(a_i, 0, m * sizeof(lmmc_real_t));
221 memset(b_r, 0, m * sizeof(lmmc_real_t));
222 memset(b_i, 0, m * sizeof(lmmc_real_t));
223
227 sign = inverse ? 1.0 : -1.0;
228
229 fft_chirp_input(real, imag, a_r, a_i, n, sign);
230 fft_chirp_kernel(b_r, b_i, n, m, sign);
231
233 st = fft_radix2(a_r, a_i, m, 0);
234 if (st != LMMC_STATUS_OK) { goto cleanup; }
235
236 st = fft_radix2(b_r, b_i, m, 0);
237 if (st != LMMC_STATUS_OK) { goto cleanup; }
238
240 fft_pointwise_product(a_r, a_i, b_r, b_i, m);
241
242 /* Inverse FFT of the product */
243 st = fft_radix2(a_r, a_i, m, 1);
244 if (st != LMMC_STATUS_OK) { goto cleanup; }
245
247 fft_chirp_input(a_r, a_i, real, imag, n, sign);
248
249 /* For inverse FFT, normalize by 1/N */
250 if (inverse) {
251 double scale = 1.0 / (double)n;
252 for (k = 0; k < n; ++k) {
253 real[k] *= scale;
254 imag[k] *= scale;
255 }
256 }
257
258cleanup:
259 lmmc_memory_free(a_r);
260 lmmc_memory_free(a_i);
261 lmmc_memory_free(b_r);
262 lmmc_memory_free(b_i);
263 return st;
264}
265
266lmmc_status_t lmmc_fft(lmmc_real_t* real, lmmc_real_t* imag, size_t n, int inverse) {
267 lmmc_storage_envelope_t real_envelope;
268 lmmc_storage_envelope_t imag_envelope;
269
270 if (real == NULL || imag == NULL || n == 0 ||
271 !lmmc_storage_envelope_checked(
272 real, 1, n, n, sizeof(lmmc_real_t), &real_envelope) ||
273 !lmmc_storage_envelope_checked(
274 imag, 1, n, n, sizeof(lmmc_real_t), &imag_envelope) ||
275 lmmc_storage_envelopes_overlap(&real_envelope, &imag_envelope)) {
277 }
278 if (n == 1) {
279 /* DFT of length 1 is identity */
280 return LMMC_STATUS_OK;
281 }
282
283 /* Dispatch: power-of-4 -> radix-4, power-of-2 -> radix-2, else -> Bluestein */
284 if (lmmc_is_power_of_four(n, NULL)) {
285 return lmmc_fft_radix4(real, imag, n, inverse ? 1 : 0);
286 } else if (fft_is_power_of_two(n)) {
287 return fft_radix2(real, imag, n, inverse ? 1 : 0);
288 } else {
289 return fft_bluestein(real, imag, n, inverse ? 1 : 0);
290 }
291}
292
294 return lmmc_fft(real, imag, n, 0);
295}
296
298 return lmmc_fft(real, imag, n, 1);
299}
300
301static int fft_padding_overlaps(
302 const lmmc_storage_envelope_t* real_in,
303 const lmmc_storage_envelope_t* imag_in,
304 const lmmc_storage_envelope_t* real_out,
305 const lmmc_storage_envelope_t* imag_out,
306 const lmmc_storage_envelope_t* nfft)
307{
308 if (lmmc_storage_envelopes_overlap(real_out, imag_out)) {
309 return 1;
310 }
311 if (lmmc_storage_envelopes_overlap(real_out, real_in) ||
312 lmmc_storage_envelopes_overlap(real_out, imag_in)) {
313 return 1;
314 }
315 if (lmmc_storage_envelopes_overlap(imag_out, real_in) ||
316 lmmc_storage_envelopes_overlap(imag_out, imag_in)) {
317 return 1;
318 }
319 if (lmmc_storage_envelopes_overlap(nfft, real_in) ||
320 lmmc_storage_envelopes_overlap(nfft, imag_in)) {
321 return 1;
322 }
323 return lmmc_storage_envelopes_overlap(nfft, real_out) ||
324 lmmc_storage_envelopes_overlap(nfft, imag_out);
325}
326
328 const lmmc_real_t* real_in, const lmmc_real_t* imag_in, size_t n,
329 lmmc_real_t* real_out, lmmc_real_t* imag_out, size_t* out_nfft)
330{
331 size_t nfft = 0;
332 lmmc_status_t st;
333 lmmc_storage_envelope_t real_in_envelope;
334 lmmc_storage_envelope_t imag_in_envelope;
335 lmmc_storage_envelope_t real_out_envelope;
336 lmmc_storage_envelope_t imag_out_envelope;
337 lmmc_storage_envelope_t nfft_envelope;
338
339 if (real_in == NULL || imag_in == NULL || n == 0) {
341 }
342 if (real_out == NULL || imag_out == NULL || out_nfft == NULL) {
344 }
345
346 st = lmmc_fft_radix4_next_size(n, &nfft);
347 if (st != LMMC_STATUS_OK) {
348 return st;
349 }
350 if (!lmmc_storage_envelope_checked(
351 real_in, 1, n, n, sizeof(lmmc_real_t), &real_in_envelope) ||
352 !lmmc_storage_envelope_checked(
353 imag_in, 1, n, n, sizeof(lmmc_real_t), &imag_in_envelope) ||
354 !lmmc_storage_envelope_checked(
355 real_out, 1, nfft, nfft, sizeof(lmmc_real_t), &real_out_envelope) ||
356 !lmmc_storage_envelope_checked(
357 imag_out, 1, nfft, nfft, sizeof(lmmc_real_t), &imag_out_envelope) ||
358 !lmmc_storage_envelope_checked(
359 out_nfft, 1, 1, 1, sizeof(size_t), &nfft_envelope)) {
361 }
362 if (fft_padding_overlaps(&real_in_envelope, &imag_in_envelope,
363 &real_out_envelope, &imag_out_envelope,
364 &nfft_envelope)) {
366 }
367
368 /* Copy input data */
369 memcpy(real_out, real_in, n * sizeof(lmmc_real_t));
370 memcpy(imag_out, imag_in, n * sizeof(lmmc_real_t));
371
372 /* Zero-pad the remainder */
373 if (nfft > n) {
374 memset(real_out + n, 0, (nfft - n) * sizeof(lmmc_real_t));
375 memset(imag_out + n, 0, (nfft - n) * sizeof(lmmc_real_t));
376 }
377
378 *out_nfft = nfft;
379 return LMMC_STATUS_OK;
380}
LMMC binary real type and arithmetic spelling.
double lmmc_real_t
Public and internal LMMC real type.
lmmc_status_t lmmc_fft_radix4_pad_into(const lmmc_real_t *real_in, const lmmc_real_t *imag_in, size_t n, lmmc_real_t *real_out, lmmc_real_t *imag_out, size_t *out_nfft)
将长度 n 的输入零填充至不小于它的最小 4 的幂。
定义 fft.c:327
lmmc_status_t lmmc_fft_radix4(lmmc_real_t *real, lmmc_real_t *imag, size_t n, int inverse)
对长度为 4 的幂的复数序列执行就地 radix-4 FFT。
lmmc_status_t lmmc_fft_inverse(lmmc_real_t *real, lmmc_real_t *imag, size_t n)
等价于 lmmc_fft(real, imag, n, 1) .
定义 fft.c:297
lmmc_status_t lmmc_fft_forward(lmmc_real_t *real, lmmc_real_t *imag, size_t n)
等价于 lmmc_fft(real, imag, n, 0) .
定义 fft.c:293
lmmc_status_t lmmc_fft_radix4_next_size(size_t n, size_t *out_nfft)
计算不小于 n 的最小 4 的幂,用于 FFT 长度对齐。
lmmc_status_t lmmc_fft(lmmc_real_t *real, lmmc_real_t *imag, size_t n, int inverse)
计算任意长度 N 点 DFT(正变换或逆变换).
定义 fft.c:266
LMMC 内部使用的公共工具(仅源文件可见).
void * lmmc_memory_alloc_array(size_t count, size_t element_size)
void lmmc_memory_free(void *pointer)
Private recoverable allocator for persistent LMMC ownership.
#define LMMC_PI
常量.
lmmc_status_t
LMMC 库统一返回的状态码.
@ LMMC_STATUS_OK
@ LMMC_STATUS_ALLOCATION_FAILED
@ LMMC_STATUS_INVALID_ARGUMENT