Skip to content

Commit 82fbca5

Browse files
committed
Math: Add cmocka tests for dft3() and fft_multi()
This patch adds tests for the new functions with tests fft_multi and dft3. The reference test vectors data is created with Octave scripts ref_fft_multi.m and ref_dft3.m. Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
1 parent b3b130e commit 82fbca5

17 files changed

Lines changed: 9562 additions & 0 deletions

test/cmocka/src/math/fft/CMakeLists.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,21 @@ cmocka_test(fft
2828
${PROJECT_SOURCE_DIR}/src/audio/component.c
2929
${PROJECT_SOURCE_DIR}/src/math/numbers.c
3030
)
31+
32+
cmocka_test(dft3
33+
dft3.c
34+
${PROJECT_SOURCE_DIR}/src/math/fft/fft_multi.c
35+
${PROJECT_SOURCE_DIR}/src/math/fft/fft_32.c
36+
${PROJECT_SOURCE_DIR}/src/math/fft/fft_common.c
37+
${PROJECT_SOURCE_DIR}/test/cmocka/src/notifier_mocks.c
38+
${PROJECT_SOURCE_DIR}/test/cmocka/src/common_mocks.c
39+
)
40+
41+
cmocka_test(fft_multi
42+
fft_multi.c
43+
${PROJECT_SOURCE_DIR}/src/math/fft/fft_multi.c
44+
${PROJECT_SOURCE_DIR}/src/math/fft/fft_common.c
45+
${PROJECT_SOURCE_DIR}/src/math/fft/fft_32.c
46+
${PROJECT_SOURCE_DIR}/test/cmocka/src/notifier_mocks.c
47+
${PROJECT_SOURCE_DIR}/test/cmocka/src/common_mocks.c
48+
)

test/cmocka/src/math/fft/dft3.c

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
//
3+
// Copyright(c) 2025 Intel Corporation. All rights reserved.
4+
//
5+
// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
6+
7+
#include <sof/audio/format.h>
8+
#include <sof/math/fft.h>
9+
#include "ref_dft3_32.h"
10+
11+
#include <errno.h>
12+
#include <stdlib.h>
13+
#include <stdio.h>
14+
#include <stdint.h>
15+
#include <stdarg.h>
16+
#include <stddef.h>
17+
#include <setjmp.h>
18+
#include <string.h>
19+
#include <cmocka.h>
20+
#include <math.h>
21+
22+
#define SOFM_DFT3_MAX_ERROR_ABS 3.1
23+
#define SOFM_DFT3_MAX_ERROR_RMS 1.1
24+
#define DFT_SIZE 3
25+
26+
static void dft3_32_test(const int32_t *in_real, const int32_t *in_imag,
27+
const int32_t *ref_real, const int32_t *ref_imag, int num_tests)
28+
29+
{
30+
struct icomplex32 x[DFT_SIZE];
31+
struct icomplex32 y[DFT_SIZE];
32+
double delta;
33+
double error_rms;
34+
double delta_max = 0;
35+
double sum_squares = 0;
36+
const int32_t *p_in_real = in_real;
37+
const int32_t *p_in_imag = in_imag;
38+
const int32_t *p_ref_real = ref_real;
39+
const int32_t *p_ref_imag = ref_imag;
40+
int i, j;
41+
42+
for (i = 0; i < num_tests; i++) {
43+
for (j = 0; j < DFT_SIZE; j++) {
44+
x[j].real = *p_in_real++;
45+
x[j].imag = *p_in_imag++;
46+
}
47+
48+
dft3_32(x, y);
49+
50+
for (j = 0; j < DFT_SIZE; j++) {
51+
delta = (double)*p_ref_real - (double)y[j].real;
52+
sum_squares += delta * delta;
53+
if (delta > delta_max)
54+
delta_max = delta;
55+
else if (-delta > delta_max)
56+
delta_max = -delta;
57+
58+
delta = (double)*p_ref_imag - (double)y[j].imag;
59+
sum_squares += delta * delta;
60+
if (delta > delta_max)
61+
delta_max = delta;
62+
else if (-delta > delta_max)
63+
delta_max = -delta;
64+
65+
p_ref_real++;
66+
p_ref_imag++;
67+
}
68+
}
69+
70+
error_rms = sqrt(sum_squares / (double)(2 * DFT_SIZE * num_tests));
71+
printf("Max absolute error = %5.2f (max %5.2f), error RMS = %5.2f (max %5.2f)\n",
72+
delta_max, SOFM_DFT3_MAX_ERROR_ABS, error_rms, SOFM_DFT3_MAX_ERROR_RMS);
73+
74+
assert_true(error_rms < SOFM_DFT3_MAX_ERROR_RMS);
75+
assert_true(delta_max < SOFM_DFT3_MAX_ERROR_ABS);
76+
}
77+
78+
static void dft3_32_test_1(void **state)
79+
{
80+
(void)state;
81+
82+
dft3_32_test(input_data_real_q31, input_data_imag_q31,
83+
ref_data_real_q31, ref_data_imag_q31,
84+
REF_SOFM_DFT3_NUM_TESTS);
85+
}
86+
87+
int main(void)
88+
{
89+
const struct CMUnitTest tests[] = {
90+
cmocka_unit_test(dft3_32_test_1),
91+
};
92+
93+
cmocka_set_message_output(CM_OUTPUT_TAP);
94+
95+
return cmocka_run_group_tests(tests, NULL, NULL);
96+
}
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
//
3+
// Copyright(c) 2025 Intel Corporation. All rights reserved.
4+
//
5+
// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
6+
7+
#include <sof/audio/format.h>
8+
#include <sof/math/fft.h>
9+
#include "ref_fft_multi_96_32.h"
10+
#include "ref_fft_multi_512_32.h"
11+
#include "ref_fft_multi_768_32.h"
12+
#include "ref_fft_multi_1024_32.h"
13+
#include "ref_fft_multi_1536_32.h"
14+
#include "ref_fft_multi_3072_32.h"
15+
16+
#include "ref_ifft_multi_24_32.h"
17+
#include "ref_ifft_multi_256_32.h"
18+
#include "ref_ifft_multi_1024_32.h"
19+
#include "ref_ifft_multi_1536_32.h"
20+
#include "ref_ifft_multi_3072_32.h"
21+
22+
#include <errno.h>
23+
#include <stdlib.h>
24+
#include <stdio.h>
25+
#include <stdint.h>
26+
#include <stdarg.h>
27+
#include <stddef.h>
28+
#include <setjmp.h>
29+
#include <string.h>
30+
#include <cmocka.h>
31+
#include <math.h>
32+
33+
#define FFT_MAX_ERROR_ABS 1050.0 /* about -126 dB */
34+
#define FFT_MAX_ERROR_RMS 35.0 /* about -156 dB */
35+
#define IFFT_MAX_ERROR_ABS 2400000.0 /* about -59 dB */
36+
#define IFFT_MAX_ERROR_RMS 44000.0 /* about -94 dB */
37+
38+
struct processing_module dummy;
39+
40+
static void fft_multi_32_test(const int32_t *in_real, const int32_t *in_imag,
41+
const int32_t *ref_real, const int32_t *ref_imag,
42+
int num_bins, int num_tests, double max_error_abs,
43+
double max_error_rms, bool do_ifft)
44+
{
45+
struct icomplex32 *x;
46+
struct icomplex32 *y;
47+
struct fft_multi_plan *plan;
48+
double delta;
49+
double error_rms;
50+
double delta_max = 0;
51+
double sum_squares = 0;
52+
const int32_t *p_in_real = in_real;
53+
const int32_t *p_in_imag = in_imag;
54+
const int32_t *p_ref_real = ref_real;
55+
const int32_t *p_ref_imag = ref_imag;
56+
int i, j;
57+
FILE *fh1, *fh2;
58+
59+
x = malloc(num_bins * sizeof(struct icomplex32));
60+
if (!x) {
61+
fprintf(stderr, "Failed to allocate input data buffer.\n");
62+
assert_true(false);
63+
}
64+
65+
y = malloc(num_bins * sizeof(struct icomplex32));
66+
if (!y) {
67+
fprintf(stderr, "Failed to allocate output data buffer.\n");
68+
assert_true(false);
69+
}
70+
71+
plan = mod_fft_multi_plan_new(&dummy, x, y, num_bins, 32);
72+
if (!plan) {
73+
fprintf(stderr, "Failed to allocate FFT plan.\n");
74+
assert_true(false);
75+
}
76+
77+
fh1 = fopen("debug_fft_multi_in.txt", "w");
78+
fh2 = fopen("debug_fft_multi_out.txt", "w");
79+
80+
for (i = 0; i < num_tests; i++) {
81+
for (j = 0; j < num_bins; j++) {
82+
x[j].real = *p_in_real++;
83+
x[j].imag = *p_in_imag++;
84+
fprintf(fh1, "%d %d\n", x[j].real, x[j].imag);
85+
}
86+
87+
fft_multi_execute_32(plan, do_ifft);
88+
89+
for (j = 0; j < num_bins; j++) {
90+
fprintf(fh2, "%d %d %d %d\n",
91+
y[j].real, y[j].imag, *p_ref_real, *p_ref_imag);
92+
delta = (double)*p_ref_real - (double)y[j].real;
93+
sum_squares += delta * delta;
94+
if (delta > delta_max)
95+
delta_max = delta;
96+
else if (-delta > delta_max)
97+
delta_max = -delta;
98+
99+
delta = (double)*p_ref_imag - (double)y[j].imag;
100+
sum_squares += delta * delta;
101+
if (delta > delta_max)
102+
delta_max = delta;
103+
else if (-delta > delta_max)
104+
delta_max = -delta;
105+
106+
p_ref_real++;
107+
p_ref_imag++;
108+
}
109+
110+
}
111+
112+
mod_fft_multi_plan_free(&dummy, plan);
113+
free(y);
114+
free(x);
115+
fclose(fh1); fclose(fh2);
116+
117+
error_rms = sqrt(sum_squares / (double)(2 * num_bins * num_tests));
118+
printf("Max absolute error = %5.2f (limit %5.2f), error RMS = %5.2f (limit %5.2f)\n",
119+
delta_max, max_error_abs, error_rms, max_error_rms);
120+
121+
assert_true(error_rms < max_error_rms);
122+
assert_true(delta_max < max_error_abs);
123+
}
124+
125+
static void fft_multi_32_test_1(void **state)
126+
{
127+
(void)state;
128+
129+
/* Test FFT */
130+
fft_multi_32_test(fft_in_real_96_q31, fft_in_imag_96_q31,
131+
fft_ref_real_96_q31, fft_ref_imag_96_q31,
132+
96, REF_SOFM_FFT_MULTI_96_NUM_TESTS,
133+
FFT_MAX_ERROR_ABS, FFT_MAX_ERROR_RMS, false);
134+
fft_multi_32_test(fft_in_real_512_q31, fft_in_imag_512_q31,
135+
fft_ref_real_512_q31, fft_ref_imag_512_q31,
136+
512, REF_SOFM_FFT_MULTI_512_NUM_TESTS,
137+
FFT_MAX_ERROR_ABS, FFT_MAX_ERROR_RMS, false);
138+
fft_multi_32_test(fft_in_real_768_q31, fft_in_imag_768_q31,
139+
fft_ref_real_768_q31, fft_ref_imag_768_q31,
140+
768, REF_SOFM_FFT_MULTI_768_NUM_TESTS,
141+
FFT_MAX_ERROR_ABS, FFT_MAX_ERROR_RMS, false);
142+
fft_multi_32_test(fft_in_real_1024_q31, fft_in_imag_1024_q31,
143+
fft_ref_real_1024_q31, fft_ref_imag_1024_q31,
144+
1024, REF_SOFM_FFT_MULTI_1024_NUM_TESTS,
145+
FFT_MAX_ERROR_ABS, FFT_MAX_ERROR_RMS, false);
146+
fft_multi_32_test(fft_in_real_1536_q31, fft_in_imag_1536_q31,
147+
fft_ref_real_1536_q31, fft_ref_imag_1536_q31,
148+
1536, REF_SOFM_FFT_MULTI_1536_NUM_TESTS,
149+
FFT_MAX_ERROR_ABS, FFT_MAX_ERROR_RMS, false);
150+
fft_multi_32_test(fft_in_real_3072_q31, fft_in_imag_3072_q31,
151+
fft_ref_real_3072_q31, fft_ref_imag_3072_q31,
152+
3072, REF_SOFM_FFT_MULTI_3072_NUM_TESTS,
153+
FFT_MAX_ERROR_ABS, FFT_MAX_ERROR_RMS, false);
154+
fft_multi_32_test(fft_in_real_3072_q31, fft_in_imag_3072_q31,
155+
fft_ref_real_3072_q31, fft_ref_imag_3072_q31,
156+
3072, REF_SOFM_FFT_MULTI_3072_NUM_TESTS,
157+
FFT_MAX_ERROR_ABS, FFT_MAX_ERROR_RMS, false);
158+
159+
/* Test IFFT */
160+
fft_multi_32_test(ifft_in_real_24_q31, ifft_in_imag_24_q31,
161+
ifft_ref_real_24_q31, ifft_ref_imag_24_q31,
162+
24, REF_SOFM_IFFT_MULTI_24_NUM_TESTS,
163+
IFFT_MAX_ERROR_ABS, IFFT_MAX_ERROR_RMS, true);
164+
fft_multi_32_test(ifft_in_real_256_q31, ifft_in_imag_256_q31,
165+
ifft_ref_real_256_q31, ifft_ref_imag_256_q31,
166+
256, REF_SOFM_IFFT_MULTI_256_NUM_TESTS,
167+
IFFT_MAX_ERROR_ABS, IFFT_MAX_ERROR_RMS, true);
168+
fft_multi_32_test(ifft_in_real_1024_q31, ifft_in_imag_1024_q31,
169+
ifft_ref_real_1024_q31, ifft_ref_imag_1024_q31,
170+
1024, REF_SOFM_IFFT_MULTI_1024_NUM_TESTS,
171+
IFFT_MAX_ERROR_ABS, IFFT_MAX_ERROR_RMS, true);
172+
fft_multi_32_test(ifft_in_real_1536_q31, ifft_in_imag_1536_q31,
173+
ifft_ref_real_1536_q31, ifft_ref_imag_1536_q31,
174+
1536, REF_SOFM_IFFT_MULTI_1536_NUM_TESTS,
175+
IFFT_MAX_ERROR_ABS, IFFT_MAX_ERROR_RMS, true);
176+
fft_multi_32_test(ifft_in_real_3072_q31, ifft_in_imag_3072_q31,
177+
ifft_ref_real_3072_q31, ifft_ref_imag_3072_q31,
178+
3072, REF_SOFM_IFFT_MULTI_3072_NUM_TESTS,
179+
IFFT_MAX_ERROR_ABS, IFFT_MAX_ERROR_RMS, true);
180+
}
181+
182+
int main(void)
183+
{
184+
const struct CMUnitTest tests[] = {
185+
cmocka_unit_test(fft_multi_32_test_1),
186+
};
187+
188+
cmocka_set_message_output(CM_OUTPUT_TAP);
189+
190+
return cmocka_run_group_tests(tests, NULL, NULL);
191+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
% ref_dft3 - Generate C header files for DFT3 function unit tests
2+
3+
% SPDX-License-Identifier: BSD-3-Clause
4+
%
5+
% Copyright(c) 2025 Intel Corporation. All rights reserved.
6+
7+
function ref_dft3()
8+
9+
path(path(), '../../../m');
10+
opt.describe = export_get_git_describe();
11+
12+
N = 3;
13+
num_tests = 100;
14+
scale_q31 = 2^31;
15+
opt.bits = 32;
16+
min_int32 = int32(-2^31);
17+
max_int32 = int32(2^31 - 1);
18+
19+
% Random values
20+
input_data_real_q31 = int32((2 * rand(N, num_tests) - 1) * scale_q31);
21+
input_data_imag_q31 = int32((2 * rand(N, num_tests) - 1) * scale_q31);
22+
23+
% Apply max and min values to first two tests
24+
input_data_real_q31(:,1) = [max_int32 max_int32 max_int32];
25+
input_data_imag_q31(:,1) = [max_int32 max_int32 max_int32];
26+
input_data_real_q31(:,2) = [min_int32 min_int32 min_int32];
27+
input_data_imag_q31(:,2) = [min_int32 min_int32 min_int32];
28+
29+
% Convert to float for reference DFT
30+
input_data_real_f = double(input_data_real_q31) / scale_q31;
31+
input_data_imag_f = double(input_data_imag_q31) / scale_q31;
32+
input_data_f = complex(input_data_real_f, input_data_imag_f);
33+
34+
ref_data_f = zeros(N, num_tests);
35+
for i = 1:num_tests
36+
ref_data_f(:,i) = fft(1/N * input_data_f(:,i));
37+
end
38+
39+
input_data_vec_f = reshape(input_data_f, N * num_tests, 1);
40+
input_data_real_q31 = int32(real(input_data_vec_f) * scale_q31);
41+
input_data_imag_q31 = int32(imag(input_data_vec_f) * scale_q31);
42+
43+
ref_data_vec_f = reshape(ref_data_f, N * num_tests, 1);
44+
ref_data_real_q31 = int32(real(ref_data_vec_f) * scale_q31);
45+
ref_data_imag_q31 = int32(imag(ref_data_vec_f) * scale_q31);
46+
47+
header_fn = sprintf('ref_dft3_32.h');
48+
fh = export_headerfile_open(header_fn);
49+
comment = sprintf('Created %s with script ref_dft3.m %s', ...
50+
datestr(now, 0), opt.describe);
51+
export_comment(fh, comment);
52+
export_ndefine(fh, 'REF_SOFM_DFT3_NUM_TESTS', num_tests);
53+
export_vector(fh, opt.bits, 'input_data_real_q31', input_data_real_q31);
54+
export_vector(fh, opt.bits, 'input_data_imag_q31', input_data_imag_q31);
55+
export_vector(fh, opt.bits, 'ref_data_real_q31', ref_data_real_q31);
56+
export_vector(fh, opt.bits, 'ref_data_imag_q31', ref_data_imag_q31);
57+
fclose(fh);
58+
fprintf(1, 'Exported %s.\n', header_fn);
59+
end

0 commit comments

Comments
 (0)