Skip to content

Commit 4a3a3fb

Browse files
committed
Test: ztest: Add test sof.unit.math.complex
The new ztest cases test_icomplex32_to_polar and ipolar32_to_complex test the conversion functions with a pre-calculated -1,+1 box saturated (re, im) spiral shape with 1000 points. Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
1 parent 1a2b848 commit 4a3a3fb

File tree

6 files changed

+946
-0
lines changed

6 files changed

+946
-0
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
cmake_minimum_required(VERSION 3.20.0)
2+
3+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
4+
project(test_math_complex)
5+
6+
set(SOF_ROOT "${PROJECT_SOURCE_DIR}/../../../../../..")
7+
8+
target_include_directories(app PRIVATE
9+
${SOF_ROOT}/zephyr/include
10+
${SOF_ROOT}/src/include
11+
)
12+
13+
# Define SOF-specific configurations for unit testing
14+
target_compile_definitions(app PRIVATE
15+
-DCONFIG_ZEPHYR_POSIX=1
16+
-DCONFIG_LIBRARY=1
17+
-DUNIT_TEST=1
18+
)
19+
20+
target_sources(app PRIVATE
21+
test_complex_polar.c
22+
${SOF_ROOT}/src/math/complex.c
23+
${SOF_ROOT}/src/math/sqrt_int32.c
24+
${SOF_ROOT}/src/math/trig.c
25+
)
26+
27+
# Link math library for standard math functions
28+
target_link_libraries(app PRIVATE m)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
CONFIG_ZTEST=y
2+
CONFIG_SOF_FULL_ZEPHYR_APPLICATION=n
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
//
3+
// Copyright(c) 2026 Intel Corporation.
4+
5+
#include <math.h>
6+
#include <sof/common.h>
7+
#include <sof/math/icomplex32.h>
8+
#include <zephyr/logging/log.h>
9+
#include <zephyr/ztest.h>
10+
11+
/* Test data tables from Octave generated reference */
12+
#include "test_complex_polar_tables.h"
13+
14+
LOG_MODULE_REGISTER(test_complex_polar, LOG_LEVEL_INF);
15+
16+
#define COMPLEX_ABS_TOL 1.2e-8
17+
#define MAGNITUDE_ABS_TOL 7.1e-8
18+
#define ANGLE_ABS_TOL 4.4e-5
19+
20+
/**
21+
* @brief Test complex to polar conversion function
22+
*
23+
* This test validates the icomplex32_to_polar(() function against
24+
* Octave-generated reference values. The test includes 1000
25+
*
26+
* Complex number values are Q1.31 -1.0 to +1.0
27+
* Polar magnitude values are Q2.30 0 to +2.0
28+
* Polar angle values are Q3.29 from -pi to +pi
29+
*/
30+
ZTEST(math_complex, test_icomplex32_to_polar)
31+
{
32+
struct icomplex32 complex, complex_mag_max, complex_ang_max;
33+
struct ipolar32 polar;
34+
double ref_magnitude, ref_angle;
35+
double magnitude, angle;
36+
double delta_mag, delta_ang;
37+
double magnitude_scale_q30 = 1.0 / 1073741824.0;
38+
double angle_scale_q29 = 1.0 / 536870912.0;
39+
double delta_mag_max = 0;
40+
double delta_ang_max = 0;
41+
int i;
42+
43+
for (i = 0; i < TEST_COMPLEX_POLAR_NUM_POINTS; i++) {
44+
complex.real = test_real_values[i];
45+
complex.imag = test_imag_values[i];
46+
ref_magnitude = magnitude_scale_q30 * test_magnitude_values[i];
47+
ref_angle = angle_scale_q29 * test_angle_values[i];
48+
sofm_icomplex32_to_polar(&complex, &polar);
49+
50+
magnitude = magnitude_scale_q30 * polar.magnitude;
51+
delta_mag = fabs(ref_magnitude - magnitude);
52+
if (delta_mag > delta_mag_max) {
53+
delta_mag_max = delta_mag;
54+
complex_mag_max = complex;
55+
}
56+
57+
angle = angle_scale_q29 * polar.angle;
58+
delta_ang = fabs(ref_angle - angle);
59+
if (delta_ang > delta_ang_max) {
60+
delta_ang_max = delta_ang;
61+
complex_ang_max = complex;
62+
}
63+
64+
zassert_true(delta_mag <= MAGNITUDE_ABS_TOL, "Magnitude calc error at (%d, %d)",
65+
complex.real, complex.imag);
66+
zassert_true(delta_ang <= ANGLE_ABS_TOL, "Angle calc error at (%d, %d)",
67+
complex.real, complex.imag);
68+
}
69+
70+
/* Re-run worst cases to print info */
71+
sofm_icomplex32_to_polar(&complex_mag_max, &polar);
72+
printf("delta_mag_max = %g at (%d, %d) -> (%d, %d)\n", delta_mag_max, complex_mag_max.real,
73+
complex_mag_max.imag, polar.magnitude, polar.angle);
74+
75+
sofm_icomplex32_to_polar(&complex_ang_max, &polar);
76+
printf("delta_ang_max = %g at (%d, %d) -> (%d, %d)\n", delta_ang_max, complex_ang_max.real,
77+
complex_ang_max.imag, polar.magnitude, polar.angle);
78+
}
79+
80+
ZTEST(math_complex, test_ipolar32_to_complex)
81+
{
82+
struct icomplex32 complex;
83+
struct ipolar32 polar, polar_real_max, polar_imag_max;
84+
double ref_real, ref_imag;
85+
double real, imag;
86+
double delta_real, delta_imag;
87+
double scale_q31 = 1.0 / 2147483648.0;
88+
double delta_real_max = 0;
89+
double delta_imag_max = 0;
90+
int i;
91+
92+
for (i = 0; i < TEST_COMPLEX_POLAR_NUM_POINTS; i++) {
93+
polar.magnitude = test_magnitude_values[i];
94+
polar.angle = test_angle_values[i];
95+
ref_real = scale_q31 * test_real_values[i];
96+
ref_imag = scale_q31 * test_imag_values[i];
97+
sofm_ipolar32_to_complex(&polar, &complex);
98+
99+
real = scale_q31 * complex.real;
100+
delta_real = fabs(ref_real - real);
101+
if (delta_real > delta_real_max) {
102+
delta_real_max = delta_real;
103+
polar_real_max = polar;
104+
}
105+
106+
imag = scale_q31 * complex.imag;
107+
delta_imag = fabs(ref_imag - imag);
108+
if (delta_imag > delta_imag_max) {
109+
delta_imag_max = delta_imag;
110+
polar_imag_max = polar;
111+
}
112+
113+
zassert_true(delta_real <= COMPLEX_ABS_TOL, "Real calc error at (%d, %d)",
114+
polar.magnitude, polar.angle);
115+
zassert_true(delta_imag <= COMPLEX_ABS_TOL, "Imag calc error at (%d, %d)",
116+
polar.magnitude, polar.angle);
117+
}
118+
119+
/* Re-run worst cases to print info */
120+
sofm_ipolar32_to_complex(&polar_real_max, &complex);
121+
printf("delta_real_max = %g at (%d, %d) -> (%d, %d)\n", delta_real_max,
122+
polar_real_max.magnitude, polar_real_max.angle, complex.real, complex.imag);
123+
124+
sofm_ipolar32_to_complex(&polar_imag_max, &complex);
125+
printf("delta_iamg_max = %g at (%d, %d) -> (%d, %d)\n", delta_imag_max,
126+
polar_imag_max.magnitude, polar_imag_max.angle, complex.real, complex.imag);
127+
}
128+
129+
ZTEST_SUITE(math_complex, NULL, NULL, NULL, NULL, NULL);
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
function test_complex_polar_reference()
2+
3+
% Make a box flattened spriral of (re,im) values to exercise the
4+
% Q1.31 complex numbers range
5+
q31_scale = 2^31;
6+
num_points = 1000;
7+
magnitude0 = linspace(0, sqrt(2), num_points);
8+
angle0 = pi/4 + linspace(0, 10*2*pi, num_points);
9+
re = max(min(magnitude0 .* cos(angle0), 1), -1);
10+
im = max(min(magnitude0 .* sin(angle0), 1), -1);
11+
ref_re = int32(re * q31_scale);
12+
ref_im = int32(im * q31_scale);
13+
re = double(ref_re)/q31_scale;
14+
im = double(ref_im)/q31_scale;
15+
figure(1)
16+
plot(re, im)
17+
grid on
18+
19+
% In polar format magnitude is Q2.30 and angle Q3.29
20+
q29_scale = 2^29;
21+
q30_scale = 2^30;
22+
magnitude = sqrt(re.^2 + im.^2);
23+
phase = angle(complex(re, im));
24+
ref_magnitude = int32(magnitude * q30_scale);
25+
ref_angle = int32(phase * q29_scale);
26+
magnitude = double(ref_magnitude)/q30_scale;
27+
phase = double(ref_angle)/q29_scale;
28+
29+
figure(2)
30+
subplot(2,1,1);
31+
plot(magnitude);
32+
grid on
33+
subplot(2,1,2);
34+
plot(phase);
35+
grid on
36+
37+
fh = export_headerfile('test_complex_polar_tables.h');
38+
dn = 'TEST_COMPLEX_POLAR_NUM_POINTS';
39+
vl = 6;
40+
export_define(fh, dn, num_points);
41+
export_array(fh, 'test_real_values', dn, vl, ref_re);
42+
export_array(fh, 'test_imag_values', dn, vl, ref_im);
43+
export_array(fh, 'test_magnitude_values', dn, vl, ref_magnitude);
44+
export_array(fh, 'test_angle_values', dn, vl, ref_angle);
45+
fclose(fh);
46+
47+
end
48+
49+
function fh = export_headerfile(headerfn)
50+
fh = fopen(headerfn, 'w');
51+
fprintf(fh, '/* SPDX-License-Identifier: BSD-3-Clause\n');
52+
fprintf(fh, ' *\n');
53+
fprintf(fh, ' * Copyright(c) %s Intel Corporation.\n', ...
54+
datestr(now, 'yyyy'));
55+
fprintf(fh, ' */\n\n');
56+
end
57+
58+
function export_define(fh, dn, val)
59+
fprintf(fh, '#define %s %d\n', dn, val);
60+
end
61+
62+
function export_array(fh, vn, size_str, vl, data)
63+
fprintf(fh, '\n');
64+
fprintf(fh, 'static const int32_t %s[%s] = {\n', vn, size_str);
65+
66+
n = length(data)
67+
k = 0;
68+
for i = 1:vl:n
69+
fprintf(fh, '\t');
70+
for j = 1:min(vl, n-k)
71+
k = k + 1;
72+
fprintf(fh, '%12d,', data(k));
73+
end
74+
fprintf(fh, '\n');
75+
end
76+
77+
k
78+
79+
fprintf(fh, '};\n');
80+
end

0 commit comments

Comments
 (0)