第19章 DSP复数运算-共轭,点乘和求模
本期教程主要讲解复数运算中的共轭,点乘和模的求解。
19.1 初学者重要提示
19.2 DSP基础运算指令
19.3 复数共轭运算(ComplexConj)
19.4 复数点乘(ComplexDotProduct)
19.5 复数求模(ComplexMag)
19.6 实验例程说明(MDK)
19.7 实验例程说明(IAR)
19.8 总结
19.1 初学者重要提示
- 复数运算比较重要,后面FFT章节要用到,如果印象不深的话,需要温习下高数知识了。
19.2 DSP基础运算指令
本章用到的DSP指令在前面章节都已经讲解过。
19.3 复数共轭运算(ComplexConj)
这部分函数用于复数共轭运算,公式描述如下:
for(n=0; n<numSamples; n++)
{
pDst[(2*n)+0)] = pSrc[(2*n)+0]; // 实部
pDst[(2*n)+1)] = -pSrc[(2*n)+1]; // 虚部
}
用代数式来表示a+bi的共轭就是a-bi。
19.3.1 函数arm_cmplx_conj_f32
函数原型:
void arm_cmplx_conj_f32(
const float32_t * pSrc,
float32_t * pDst,
uint32_t numSamples)
函数描述:
这个函数用于浮点数的复位共轭求解。
函数参数:
- 第1个参数是源数据地址。
- 第2个参数是求共轭后的数据地址。
- 第3个参数是转换的数据个数。
注意事项:
参数pSrc中存储的数据格式是(实部,虚部,实部,虚部……………),一定要按照这个顺序存储数据,比如数据1-j,j,2+3j这个三个数在数组中的存储格式就是:pSrc[6] = {1, -1, 0, 1, 2, 3}。(注意第三个数据是0)。函数的输出结果pDst也是按照这个顺序存储的。
19.3.2 函数arm_cmplx_conj_q31
函数原型:
void arm_cmplx_conj_q31(
const q31_t * pSrc,
q31_t * pDst,
uint32_t numSamples)
函数描述:
这个函数用于定点数Q31的复数共轭求解。
函数参数:
- 第1个参数是源数据地址。
- 第2个参数是求共轭后的数据地址。
- 第3个参数是转换的数据个数。
注意事项:
- 数组pSrc中存储的数据格式是(实部,虚部,实部,虚部……………),一定要按照这个顺序存储数据,比如数据1-j,j,2+3j这个三个数在数组中的存储格式就是:pSrc[6] = {1, -1, 0, 1, 2, 3}。(注意第三个数据是0)。函数的输出结果pDst也是按照这个顺序存储的。
- 这个函数使用了饱和运算。数值0x80000000由于饱和运算(源码中的__QSUB(0, in))将变成0x7FFFFFFF。
19.3.3 函数arm_cmplx_conj_q15
函数原型:
void arm_cmplx_conj_q15(
const q15_t * pSrc,
q15_t * pDst,
uint32_t numSamples)
函数描述:
这个函数用于定点数Q15的复数共轭求解。
函数参数:
- 第1个参数是源数据地址。
- 第2个参数是求共轭后的数据地址。
- 第3个参数是转换的数据个数。
注意事项:
- 数组pSrc中存储的数据格式是(实部,虚部,实部,虚部……………),一定要按照这个顺序存储数据,比如数据1-j,j,2+3j这个三个数在数组中的存储格式就是:pSrc[6] = {1, -1, 0, 1, 2, 3}。(注意第三个数据是0)。函数的输出结果pDst也是按照这个顺序存储的。
- 这个函数使用了饱和运算。数值0x8000由于饱和运算(源码中的__QSAX(0, in1))将变成0x7FFFF。
19.3.4 使用举例
程序设计:
实验现象:
19.4 复数点乘(ComplexDotProduct)
这部分函数用于复数共轭运算,公式描述如下:
realResult = 0;
imagResult = 0;
for (n = 0; n < numSamples; n++) {
realResult += pSrcA[(2*n)+0] * pSrcB[(2*n)+0] - pSrcA[(2*n)+1] * pSrcB[(2*n)+1]; //实部
imagResult += pSrcA[(2*n)+0] * pSrcB[(2*n)+1] + pSrcA[(2*n)+1] * pSrcB[(2*n)+0]; //虚部
}
用代数式来表示复数乘法就是:
(a+bi)(c+di)=(ac-bd)+(ad+bc)i。
19.4.1 函数arm_cmplx_dot_prod_f32
函数原型:
void arm_cmplx_dot_prod_f32(
const float32_t * pSrcA,
const float32_t * pSrcB,
uint32_t numSamples,
float32_t * realResult,
float32_t * imagResult)
函数描述:
这个函数用于浮点数的复数点乘。
函数参数:
- 第1个参数是源数据A地址。
- 第2个参数是源数据B地址。
- 第3个参数是点乘的数据个数。
- 第4个参数是点乘后的实数地址。
- 第5个参数是点乘后的虚数地址。
注意事项:
数组pSrcA和pSrcB中存储的数据格式是(实部,虚部,实部,虚部……………),一定要按照这个顺序存储数据,比如数据1-j,j,2+3j这个三个数在数组中的存储格式就是:pSrcA[6] = {1, -1, 0, 1, 2, 3}。(注意第三个数据是0)。而输出结果的实部和虚部是分开存储的。
19.4.2 函数arm_cmplx_dot_prod_q31
函数原型:
void arm_cmplx_dot_prod_q31(
const q31_t * pSrcA,
const q31_t * pSrcB,
uint32_t numSamples,
q63_t * realResult,
q63_t * imagResult)
函数描述:
这个函数用于定点数Q31的复数点乘。
函数参数:
- 第1个参数是源数据A地址。
- 第2个参数是源数据B地址。
- 第3个参数是点乘的数据个数。
- 第4个参数是点乘后的实数地址。
- 第5个参数是点乘后的虚数地址。
注意事项:
- 数组pSrcA和pSrcB中存储的数据格式是(实部,虚部,实部,虚部……………),一定要按照这个顺序存储数据,比如数据1-j,j,2+3j这个三个数在数组中的存储格式就是:pSrcA[6] = {1, -1, 0, 1, 2, 3}。(注意第三个数据是0)。而输出结果的实部和虚部是分开存储的。
- 这个函数的内部使用了64累加器,1.31格式数据乘以1.31格式数据结果就是2.62格式,这里我们将所得结果右移14位,那么数据就是16.48格式。由于加数是不支持饱和运算,所以只要numSamples的个数小于32768就不会有溢出的危险。
19.4.3 函数arm_cmplx_dot_prod_q15
函数原型:
void arm_cmplx_dot_prod_q15(
const q15_t * pSrcA,
const q15_t * pSrcB,
uint32_t numSamples,
q31_t * realResult,
q31_t * imagResult)
函数描述:
这个函数用于定点数Q15的复数点乘。
函数参数:
- 第1个参数是源数据A地址。
- 第2个参数是源数据B地址。
- 第3个参数是点乘的数据个数。
- 第4个参数是点乘后的实数地址。
- 第5个参数是点乘后的虚数地址。
注意事项:
- 数组pSrcA和pSrcB中存储的数据格式是(实部,虚部,实部,虚部……………),一定要按照这个顺序存储数据,比如数据1-j,j,2+3j这个三个数在数组中的存储格式就是:pSrcA[6] = {1, -1, 0, 1, 2, 3}。(注意第三个数据是0)。而输出结果的实部和虚部是分开存储的。
- 这个函数的内部使用了64累加器,1.15格式数据乘以1.15格式数据结果就是2.30格式,对应到64bit就是34.30,然后将最终的计算结果转换为8.24。
19.4.4 使用举例
程序设计:
实验现象:
19.5 复数求模 ComplexMag
这部分函数用于复数求模,公式描述如下:
for (n = 0; n < numSamples; n++) {
pDst[n] = sqrt(pSrc[(2*n)+0]^2 + pSrc[(2*n)+1]^2);
}
用代数式来表示复数乘法就是:
19.5.1 函数arm_cmplx_mag_f32
函数原型:
void arm_cmplx_mag_f32(
const float32_t * pSrc,
float32_t * pDst,
uint32_t numSamples)
函数描述:
这个函数用于浮点数类型的复数求模。
函数参数:
- 第1个参数是源数据地址。
- 第2个参数是求模后的数据地址。
- 第3个参数是要求解的复数个数。
注意事项:
数组pSrcA中存储的数据格式是(实部,虚部,实部,虚部……………),一定要按照这个顺序存储数据,比如数据1-j,j,2+3j这个三个数在数组中的存储格式就是:pSrcA[6] = {1, -1, 0, 1, 2, 3}。(注意第三个数据是0)。而模值的结果存到到pDst里面。
19.5.2 函数arm_cmplx_mag_q31
函数原型:
void arm_cmplx_mag_q31(
const q31_t * pSrc,
q31_t * pDst,
uint32_t numSamples)
函数描述:
这个函数用于定点数Q31类型的复数求模。
函数参数:
- 第1个参数是源数据地址。
- 第2个参数是求模后的数据地址。
- 第3个参数是要求解的复数个数。
注意事项:
- 数组pSrcA中存储的数据格式是(实部,虚部,实部,虚部……………),一定要按照这个顺序存储数据,比如数据1-j,j,2+3j这个三个数在数组中的存储格式就是:pSrcA[6] = {1, -1, 0, 1, 2, 3}。(注意第三个数据是0)。而模值的结果存到到pDst里面。
- 1.31格式的数据乘1.31格式的数据,并经过移位处理后结果是2.30格式。
19.5.3 函数arm_cmplx_mag_q15
函数原型:
void arm_cmplx_mag_q15(
const q15_t * pSrc,
q15_t * pDst,
uint32_t numSamples)
函数描述:
这个函数用于定点数Q15类型的复数求模。
函数参数:
- 第1个参数是源数据地址。
- 第2个参数是求模后的数据地址。
- 第3个参数是要求解的复数个数
注意事项:
- 数组pSrcA中存储的数据格式是(实部,虚部,实部,虚部……………),一定要按照这个顺序存储数据,比如数据1-j,j,2+3j这个三个数在数组中的存储格式就是:pSrcA[6] = {1, -1, 0, 1, 2, 3}。(注意第三个数据是0)。而模值的结果存到到pDst里面。
- 1.15格式的数据乘1.15格式的数据,并经过移位处理后结果是2.14格式。
19.5.4 使用举例
程序设计:
实验现象:
19.6 实验例程说明(MDK)
配套例子:
V6-214_DSP复数运算(共轭,点乘和求模)
实验目的:
- 学习DSP复数运算(共轭,点乘和求模)
实验内容:
- 启动一个自动重装软件定时器,每100ms翻转一次LED2。
- 按下按键K1,串口打函数DSP_CONJ的输出数据。
- 按下按键K2,串口打函数DSP_CmplxDotProduct的输出数据。
- 按下按键K3,串口打函数DSP_CmplxMag的输出数据。
使用AC6注意事项
特别注意附件章节C的问题
上电后串口打印的信息:
波特率 115200,数据位 8,奇偶校验位无,停止位 1。
详见本章的3.4 4.4,5.4小节。
程序设计:
系统栈大小分配:
硬件外设初始化
硬件外设的初始化是在 bsp.c 文件实现:
主功能:
主程序实现如下操作:
- 启动一个自动重装软件定时器,每100ms翻转一次LED2。
- 按下按键K1,串口打函数DSP_CONJ的输出数据。
- 按下按键K2,串口打函数DSP_CmplxDotProduct的输出数据。
- 按下按键K3,串口打函数DSP_CmplxMag的输出数据。
19.7 实验例程说明(IAR)
配套例子:
V6-214_DSP复数运算(共轭,点乘和求模)
实验目的:
- 学习DSP复数运算(共轭,点乘和求模)
实验内容:
- 启动一个自动重装软件定时器,每100ms翻转一次LED2。
- 按下按键K1,串口打函数DSP_CONJ的输出数据。
- 按下按键K2,串口打函数DSP_CmplxDotProduct的输出数据。
- 按下按键K3,串口打函数DSP_CmplxMag的输出数据。
上电后串口打印的信息:
波特率 115200,数据位 8,奇偶校验位无,停止位 1。
详见本章的3.4 4.4,5.4小节。
程序设计:
系统栈大小分配:
硬件外设初始化
硬件外设的初始化是在 bsp.c 文件实现:
主功能:
主程序实现如下操作:
- 启动一个自动重装软件定时器,每100ms翻转一次LED2。
- 按下按键K1,串口打函数DSP_CONJ的输出数据。
- 按下按键K2,串口打函数DSP_CmplxDotProduct的输出数据。
- 按下按键K3,串口打函数DSP_CmplxMag的输出数据。
19.8 总结
本期教程就跟大家讲这么多,有兴趣的可以深入研究下算法的具体实现。
微信公众号:armfly_com