Skip to content

Commit 4e438f3

Browse files
committed
Math: Add functions to/from polar format for complex numbers
This patch adds functions sofm_icomplex32_to_polar() and sofm_ipolar32_to_complex(). In polar format the Q1.31 (real, imag) numbers pair is converted to (magnitude, angle). The magnitude is Q2.30 format and angle in -pi to +pi radians in Q3.29 format. The conversion to polar and back loses some quality so there currently is no support for icomplex16. Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
1 parent 122b08b commit 4e438f3

File tree

4 files changed

+100
-0
lines changed

4 files changed

+100
-0
lines changed

src/include/sof/math/icomplex32.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ struct icomplex32 {
2525
int32_t imag;
2626
};
2727

28+
/**
29+
* struct ipolar32 - Storage for complex number in polar format.
30+
* @param magnitude The length of vector in Q2.30 format.
31+
* @param angle The phase angle of the vector -pi to +pi in Q3.29 format.
32+
*/
33+
struct ipolar32 {
34+
int32_t magnitude;
35+
int32_t angle;
36+
};
37+
2838
/*
2939
* These helpers are optimized for FFT calculation only.
3040
* e.g. _add/sub() assume the output won't be saturate so no check needed,
@@ -79,4 +89,25 @@ static inline void icomplex32_shift(const struct icomplex32 *input, int32_t n,
7989
}
8090
}
8191

92+
/**
93+
* sofm_icomplex32_to_polar() - Convert (re, im) complex number to polar.
94+
* @param complex Pointer to input complex number in Q1.31 format.
95+
* @param polar Pointer to output complex number in Q2.30 format for
96+
* magnitude and Q3.29 for phase angle.
97+
*
98+
* The function can be used to convert data in-place with same address for
99+
* input and output. It can be useful to save scratch memory.
100+
*/
101+
void sofm_icomplex32_to_polar(struct icomplex32 *complex, struct ipolar32 *polar);
102+
103+
/**
104+
* sofm_ipolar32_to_complex() - Convert complex number from polar to normal (re, im) format.
105+
* @param polar Pointer to input complex number in polar format.
106+
* @param complex Pointer to output complex number in normal format in Q1.31.
107+
*
108+
* This function can be used to convert data in-place with same address for input
109+
* and output. It can be useful to save scratch memory.
110+
*/
111+
void sofm_ipolar32_to_complex(struct ipolar32 *polar, struct icomplex32 *complex);
112+
82113
#endif /* __SOF_ICOMPLEX32_H__ */

src/math/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ if(CONFIG_MATH_MU_LAW_CODEC)
9191
list(APPEND base_files mu_law.c)
9292
endif()
9393

94+
if(CONFIG_MATH_COMPLEX)
95+
list(APPEND base_files complex.c)
96+
endif()
97+
9498
is_zephyr(zephyr)
9599
if(zephyr) ### Zephyr ###
96100

src/math/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,17 @@ config MATH_DECIBELS
9595
Select this to enable db2lin_fixed() and exp_fixed()
9696
functions.
9797

98+
config MATH_COMPLEX
99+
bool "Operations for complex numbers"
100+
default n
101+
help
102+
Select this to enable functions for complex numbers
103+
arithmetic such as conversions to/from polar format.
104+
98105
config MATH_FFT
99106
bool "FFT library"
100107
default n
108+
select MATH_COMPLEX
101109
help
102110
Enable Fast Fourier Transform library, this should not be selected
103111
directly, please select it from other audio components where need it.

src/math/complex.c

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
//
3+
// Copyright(c) 2026 Intel Corporation.
4+
//
5+
// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
6+
//
7+
8+
#include <rtos/symbol.h>
9+
#include <sof/audio/format.h>
10+
#include <sof/math/icomplex32.h>
11+
#include <sof/math/sqrt.h>
12+
#include <stdint.h>
13+
14+
/* sofm_icomplex32_to_polar() - Convert (re, im) complex number to polar. */
15+
void sofm_icomplex32_to_polar(struct icomplex32 *complex, struct ipolar32 *polar)
16+
{
17+
struct icomplex32 c = *complex;
18+
int64_t squares_sum;
19+
int32_t sqrt_arg;
20+
int32_t acos_arg;
21+
int32_t acos_val;
22+
23+
/* Calculate square of magnitudes Q1.31, result is Q2.62 */
24+
squares_sum = (int64_t)c.real * c.real + (int64_t)c.imag * c.imag;
25+
26+
/* Square root */
27+
sqrt_arg = Q_SHIFT_RND(squares_sum, 62, 30);
28+
polar->magnitude = sofm_sqrt_int32(sqrt_arg); /* Q2.30 */
29+
30+
/* Avoid divide by zero and ambiguous angle for a zero vector. */
31+
if (polar->magnitude == 0) {
32+
polar->angle = 0;
33+
return;
34+
}
35+
36+
/* Calculate phase angle with acos( complex->real / polar->magnitude) */
37+
acos_arg = sat_int32((((int64_t)c.real) << 29) / polar->magnitude); /* Q2.30 */
38+
acos_val = acos_fixed_32b(acos_arg); /* Q3.29 */
39+
polar->angle = (c.imag < 0) ? -acos_val : acos_val;
40+
}
41+
EXPORT_SYMBOL(sofm_icomplex32_to_polar);
42+
43+
/* sofm_ipolar32_to_complex() - Convert complex number from polar to normal (re, im) format. */
44+
void sofm_ipolar32_to_complex(struct ipolar32 *polar, struct icomplex32 *complex)
45+
{
46+
struct cordic_cmpx cexp;
47+
int32_t phase;
48+
int32_t magnitude;
49+
50+
/* The conversion can happen in-place, so load copies of the values first */
51+
magnitude = polar->magnitude;
52+
phase = Q_SHIFT_RND(polar->angle, 29, 28); /* Q3.29 to Q2.28 */
53+
cmpx_exp_32b(phase, &cexp); /* Q2.30 */
54+
complex->real = sat_int32(Q_MULTSR_32X32((int64_t)magnitude, cexp.re, 30, 30, 31));
55+
complex->imag = sat_int32(Q_MULTSR_32X32((int64_t)magnitude, cexp.im, 30, 30, 31));
56+
}
57+
EXPORT_SYMBOL(sofm_ipolar32_to_complex);

0 commit comments

Comments
 (0)