FFT之蝶形运算

Author🚹:CofCai

Email✉️:cai.dongjun@nexuslink.cn

QQ😙:1664866311

CSDN Page🏠

前言

写这篇博客时,是今天考完电信传输理论后的5个小时,没带计算器。

用C语言实现FFT之时域抽取法的想法是在考数字信号处理前萌发的,在复习时看到书上有编程思想,然后自己又好久都没有敲代码了,手有点痒,然后就按照书上的编程思想进行coding。但是之间有许多错误,哎!用C语言这么久了,还是没有养成编写代码时注意数组越界的习惯。

昨天下午考了数字信号处理,很意外,考点不对,有些考纲上没写,比如考纲上网络结构只写了直接型、级联型以及并联型,没有提到频率采样、线性相位,但是它!它!它!考了,而且考的好偏。另外,我在很久之前就在写数字信号处理的文章/博客,感觉对考试没有帮助,我考前对窗函数设计FIR滤波器很熟悉,感觉很稳,结构它考了频率采样法。

考完了数字信号处理的晚上,继续完成考前未完成的FFT算法,然后经过慢慢思考,发现书上也有错误,关于最内层循环的迭代方式不对,经过理解,然后修改,得出了正确结果(和MATLAB对比,当然我是用的在线octave进行计算的,我电脑打开MTALAB慢)。

今天,此时此刻以及前一个小时,手又有点痒了,于是打开FFT代码,继续修改,优化(其实也没怎么优化),完成了从文件中读取数据,将FFT的结果写入到文件中。

遂作感想如上,记录于此

下面继续写blog,总结。

相关文件

fft.c
fft_rawdata.txt
fft_cookeddata.txt

从fft_rawdata.txt中读取要进行fft的数据,数据格式为:

  1. 第一行一个数字,表示数据点数/长度;
  2. 此后每行有两个数字,表示实部和虚部,用Table分割;

如下:

在这里插入图片描述

fft的结果会保存到fft_cookeddata.txt中去,如下:

在这里插入图片描述

在代码中我还计算了每个fft结果的幅度和相位,但是没有保存到文件中去,而是打印到标准输出,结果如下(结果中我是以%.1f输出的,所以只显示一位小数):

在这里插入图片描述

实操

准备工作

对于复数,直接用一个结构体即可,两个成员,分别表示实部和虚部,后面又定义了一个结构体,其实是一样的,只不过成员名称不一样了,用幅度和角度来表示一个复数。

typedef struct {
	float real;
	float imag;
}complex_t;

// 另外一种复数的表达,幅度加角度/相位
typedef struct {
	float amp;
	float pha;
}complex_ap_t;

时域抽取法/蝶形运算的编程思想参考了数字信号处理书上的第四章。

旋转因子的表示:

刚开始不知道 W N p W_N^p WNp怎么表示,后面一想,它不就是一个复数嘛,用欧拉公式展开就可以得到实部和虚部。
W N p = e − j 2 π N p = cos ⁡ ( 2 π p / N ) − j sin ⁡ ( 2 π p / N ) W_N^p = e^{-j\frac{2\pi}{N}p} = \cos(2{\pi}p/N)-j\sin(2{\pi}p/N) WNp=ejN2πp=cos(2πp/N)jsin(2πp/N)
因为旋转因子用的比较多,比较频繁,所以没有写成函数,而是用了一个宏定义,如下:

#define pi 	(3.1415926535)
// 旋转因子,{}语句块,其结果为最后一条语句的值
#define	WNP(N,P)	({			\
	complex_t temp;				\
	temp.real = cos(2*pi*P/N);	\
	temp.imag = -sin(2*pi*P/N);	\
	temp; })

然后还需要实现复数的基本四则运算,加、减、乘,除(代码中没有实现),还有求一个复数的幅度和相位。

倒序算法

另外,蝶形运算的初始输入的数据需要倒叙,所以需要写一个函数进行输入数据的倒叙,倒序的算法为:对原始数据的序号的二进制进行反转,得到的结果就是其倒叙后的位置(假设总共有N个数据,那么只看 l o g 2 N log_2^N log2N个二进制位)。

比如总共有8个数据,那么就值用看3个二进制位,第3个原始数据,经过倒叙后是第6个数据(从0开始,有第0个数据)。

3 = 011

6 = 110

而C语言不能实现数据的二进制位倒相,所以又要写一个函数(此函数来源于网络)。

//采用移位的方法使一个数的二进制位翻转后返回
unsigned int reverse_bit(unsigned int num, const unsigned int N)
{
	unsigned int ret = 0;
	int bit = 0;
	int i = 0;
	// log(8.0)/log(2.0) = 3.000,but (unsigned int)x = 2,why?
	unsigned int M = (unsigned int)(log(N+0.00001)/log(2.0));
	for (i = 0; i < M; i++)
	{
		ret <<= 1;
		bit = num & 1;
		ret = bit | ret;
		num = num / 2;
	}
	return ret;
}

数据的倒序:

/**
 * 进行FFT前对数据进行倒叙以方便时域抽取法
 * @Author   CofCai
 * @DateTime 2020-12-27T20:48:41+0800
 * @param    c_arr                    数据指针
 * @param    N                        数据长度
 * @return                            成功:1、失败:0
 */
int reverse_order(complex_t* c_arr, const unsigned int N)
{
	complex_t c_temp;
	/** calloc: initial by zeros  */
	char* bitmap = calloc(N, sizeof(char));
	unsigned int i, j;
	for (i = 0; i < N; ++i) {
		if (bitmap[i] == 0) {
			// 先保存第i个数据
			c_temp.real = c_arr[i].real;
			c_temp.imag = c_arr[i].imag;
			// 计算i对应的倒叙,和这个倒叙位置交换数据
			j = reverse_bit(i, N);
			c_arr[i].real = c_arr[j].real;
			c_arr[i].imag = c_arr[j].imag;
			c_arr[j].real = c_temp.real;
			c_arr[j].imag = c_temp.imag;
			// 防止后面j又和i进行倒叙交换
			*(bitmap+i) = 1;
			*(bitmap+j) = 1;
		} else {
			continue;
		}
	}
	return 1;
}

编程思想

蝶形运算要点:
在第L级中:

  1. 每个蝶形的输入数据相隔 B = 2 L − 1 B=2^{L-1} B=2L1个点;

  2. 每级有B个不同的旋转因子;

  3. 相同旋转因子对应的每个蝶形的输出数据相隔 D = 2 L D=2^L D=2L个点;

  4. 同一旋转因子对应着间隔为 D = 2 L D=2^L D=2L点的 2 M − L 2^{M-L} 2ML个蝶形;(此点包含了第3点,但多一个信息)。

也就是说:

  1. 第一层循环控制级数,共 M = l o g N M=logN M=logN级,从 L = 1 , . . . , M L=1,...,M L=1,...,M

  2. 第二层循环控制每层的旋转因子,每层有 B = 2 L − 1 B=2^{L-1} B=2L1个不同的旋转因子;

  3. 第三层循环控制每个旋转因子对应的蝶形运算。

    1. 每个旋转因子会被使用 2 M − L 2^{M-L} 2ML次;

    2. 每个蝶形运算的两个输入数据相隔 B = 2 L − 1 B=2^{L-1} B=2L1个点,输出也是相隔B个点(蝶形:交叉平行);

    3. 而同一旋转因子对应的两个相邻的蝶形运算相隔 D = 2 L D=2^L D=2L个点;

代码实现

/**
 * author:		CofCai
 * datatime:	2020-12-27 21:00:24
 * file description:
 *	 根据数据信号处理书上第4四章FFT提到的时域抽取法编程思想,
 *	 用C语言实现DIT-FFT算法。
 *	 编写过程中遇到问题了:
 *	 	旋转因子该怎么表示?
 *	 	突然又解决了,用旋转因子不就是复数蛮,一样可以用复数表示
 *	 	W_N^k = e^{-j2pik/N} = cos(2pik/N) - j*sin(2pik/N)
 *
 * 	 似乎结果有问题
 * 	 考完了数字信号处理(老师给的考点一点也不靠谱,好多不会,awsl)
 * 	 继续敲代码,做事情前先弄懂原理,刚开始跟着书上的抄,结果不对,
 * 	 后面自己认真看书,理解蝶形运算的过程,然后发现第三层循环,也就
 * 	 是最内循环的迭代方式不对,不应该是++,而应该按照D=2^L的方式迭代
 * 	 
 * 	 蝶形运算要点:
 *	 	在第L级中:
 * 			1. 每个蝶形的输入数据相隔B=2^{L-1}个点;
 * 			2. 每级有B个不同的旋转因子;
 * 			3. 相同旋转因子对应的每个蝶形的输出数据相隔D=2^L个点;
 * 		 	4. 同一旋转因子对应着间隔为D=2^L点的2^{M-L}个蝶形;(此点包含了第3点,但多一个信息)。
 * 		也就是说:
 * 			1. 第一层循环控制级数,共M=logN级,从L=1,...,M;
 * 			2. 第二层循环控制每层的旋转因子,每层有B=2^{L-1}个不同的旋转因子;
 * 			3. 第三层循环控制每个旋转因子对应的蝶形运算
 * 				1. 每个旋转因子会被使用2^{M-L}次;
 * 				2. 每个蝶形运算的两个输入数据相隔B=2^{L-1}个点,输出也是相隔B个点(蝶形:交叉平行);
 * 				3. 而同一旋转因子对应的两个相邻的蝶形运算相隔D=2^L个点;
 *
 *
 * 	该文件的使用:
 * 		fft_rawdata.txt:此文件用于输入原始数据,
 * 						 第一行是数据个数N,此后每一行表示一个数据,分为实部和虚部,
 * 						 中间以table分割,以换行结束,如:
 * 						 3
 * 						 1.2\t2.3\n
 * 						 2.3\t78.2\n
 * 						 7.2\t12.3\n
 * 		fft_cookeddata.txt:此文件用于保存结果
 * 	
 * 	另外需要注意:
 * 		传入fft的是数据指针,因此是在原数据上直接保存结果,所以如果还需要用到原数据,那么请copy一份。
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

typedef struct {
	float real;
	float imag;
}complex_t;

// 另外一种复数的表达,幅度加角度/相位
typedef struct {
	float amp;
	float pha;
}complex_ap_t;


#define DBG_INFO 0
#if DBG_INFO
  	#define DBG_PRINTF(fmt, args...)  \
	do\
	{\
	    printf(fmt, ##args);\
	}while(0)
#else
  #define DBG_PRINTF(fmt, args...)   
#endif

#define pi 	(3.1415926535)
// 旋转因子,{}语句块,其结果为最后一条语句的值
#define	WNP(N,P)	({			\
	complex_t temp;				\
	temp.real = cos(2*pi*P/N);	\
	temp.imag = -sin(2*pi*P/N);	\
	temp; })

#define R2C(R)		({			\
	complex_t c;				\
	c.real = R;					\
	c.imag = 0;					\
	c; })

float cplx_amp(complex_t* c)
{
	return sqrt(pow(c->real, 2) + pow(c->imag, 2));
}

float cplx_angle(complex_t* c)
{
	return atan2(c->imag, c->real);
}

complex_ap_t cplx_amp_angle(complex_t* c)
{
	complex_ap_t c_ap;
	c_ap.amp = cplx_amp(c);
	c_ap.pha = cplx_angle(c);

	return c_ap;
}

// 为了减小开销,还是传入指针吧
complex_t cplx_add(complex_t* c1, complex_t* c2)
{
	complex_t temp;
	temp.real = c1->real + c2->real;
	temp.imag = c1->imag + c2->imag;
	return temp;
}

complex_t cplx_sub(complex_t* c1, complex_t* c2)
{
	complex_t temp;
	temp.real = c1->real - c2->real;
	temp.imag = c1->imag - c2->imag;
	return temp;
}

complex_t cplx_prod(complex_t* c1, complex_t* c2)
{
	complex_t temp;
	// (a1+b1j)(a2+b2j) = (a1a2-b1b2) + (a1b2 + b1a2)
	temp.real = (c1->real * c2->real) - (c1->imag * c2->imag);
	temp.imag = (c1->real * c2->imag) + (c1->imag * c2->real);
	return temp;
}

//采用移位的方法使一个数的二进制位翻转后返回
unsigned int reverse_bit(unsigned int num, const unsigned int N)
{
	unsigned int ret = 0;
	int bit = 0;
	int i = 0;
	// log(8.0)/log(2.0) = 3.000,but (unsigned int)x = 2,why?
	unsigned int M = (unsigned int)(log(N+0.00001)/log(2.0));
	for (i = 0; i < M; i++)
	{
		ret <<= 1;
		bit = num & 1;
		ret = bit | ret;
		num = num / 2;
	}
	return ret;
}

/**
 * 进行FFT前对数据进行倒叙以方便时域抽取法
 * @Author   CofCai
 * @DateTime 2020-12-27T20:48:41+0800
 * @param    c_arr                    数据指针
 * @param    N                        数据长度
 * @return                            成功:1、失败:0
 */
int reverse_order(complex_t* c_arr, const unsigned int N)
{
	complex_t c_temp;
	/** calloc: initial by zeros  */
	char* bitmap = calloc(N, sizeof(char));
	unsigned int i, j;
	for (i = 0; i < N; ++i) {
		if (bitmap[i] == 0) {
			// 先保存第i个数据
			c_temp.real = c_arr[i].real;
			c_temp.imag = c_arr[i].imag;
			// 计算i对应的倒叙,和这个倒叙位置交换数据
			j = reverse_bit(i, N);
			c_arr[i].real = c_arr[j].real;
			c_arr[i].imag = c_arr[j].imag;
			c_arr[j].real = c_temp.real;
			c_arr[j].imag = c_temp.imag;
			// 防止后面j又和i进行倒叙交换
			*(bitmap+i) = 1;
			*(bitmap+j) = 1;
		} else {
			continue;
		}
	}
	return 1;
}

/**
 * FFT算法
 * 在第L级中:
 * 			1. 每个蝶形的输入数据相隔B=2^{L-1}个点;
 * 			2. 每级有B个不同的旋转因子;
 * 			3. 相同旋转因子对应的每个蝶形的输出数据相隔D=2^L个点;
 * 		 	4. 同一旋转因子对应着间隔为D=2^L点的2^{M-L}个蝶形;(此点包含了第3点,但多一个信息)。
 * @Author   CofCai
 * @DateTime 2020-12-28T21:20:27+0800
 * @param    c_arr                    输入序列,为指针
 * @param    N                        序列长度
 * @return                            无
 */
int fft(complex_t* c_arr, const unsigned int N)
{
	//log_a^b = log_c^b / log_c^a:换底公式
	unsigned int M = (unsigned int)(log((float)N+0.00001)/log(2.0));
	DBG_PRINTF("FFT related parameter:");
	DBG_PRINTF("N = %u\tM = %d\n", N, M);
	// L:层数,最大为M
	unsigned int L, k;
	// 蝶形运算中输入的两个数据相距B个点,J用于索引不同的旋转因子
	unsigned int J, B, p;
	// 相同旋转因子的输出结果之间的间隔为:D=2^L
	unsigned int D;
	complex_t c_temp, T, WNp;
	// 倒叙
	reverse_order(c_arr, N);
	// 蝶形运算
	// 第L层
	for (L = 1; L <= M; ++L) {
		B = (unsigned int)pow(2, L-1);
		DBG_PRINTF("第 L = %d层\tB = %d个不同的旋转因子\n", L, B);
		// 第L层有B = 2^(L-1)个旋转因子,每个为W_{2^L}^J,J=0,1,...,B-1。
		// 经过变换,可得旋转因子为:W_N^p,p=Jx2^{M-L}。
		for (J = 0; J <= B-1; ++J) {
			// 第L层第J个旋转因子的指数
			p = (unsigned int)(pow(2, M-L)*J);
			DBG_PRINTF("J = %d对应的旋转因子的指数为P = %d\n", J, p);
			// 前面说过每层共有2^(L-1)个旋转因子,而每个旋转因子在此层会被利用2^{M-L}次
			WNp = WNP(N, p);
			DBG_PRINTF("旋转因子WNp: (%u, %u)-->(%1.f + %1.fj)\n", N, p, WNp.real, WNp.imag);
			// D是同一旋转因子对应的蝶形的输出结果之间的距离,而B是输入碟形数据之间的距离
			D = (unsigned int)pow(2, L);
			// 下面的循环就是计算出WNp对应的蝶形运算的结果
			for (k = J; k <= N-1; k+=D) {
				// A_{L}(J) = A_{L-1}(J) + A_{L-1}(J+B)W_N^p;
				// A_{L}(J+B) = A_{L-1}(J) - A_{L-1}(J+B)W_N^p;
				
				c_temp = cplx_prod(c_arr+k+B, &WNp);
				// DBG_PRINTF("prod:(%1.f+%1.fj) * (%1.f+%1.fj)=(%1.f+%1.fj)\n", 
				// 	(c_arr+k+B)->real, (c_arr+k+B)->imag, WNp.real, WNp.imag, c_temp.real, c_temp.imag);
				
				T = cplx_add(c_arr+k, &c_temp);
				DBG_PRINTF("AL(%u) = A(%u) + A(%u)WNp\n", k, k, k+B);
				// DBG_PRINTF("add:(%1.f+%1.fj) + (%1.f+%1.fj)=(%1.f+%1.fj)\n",
				// 	(c_arr+k)->real, (c_arr+k)->imag, c_temp.real, c_temp.imag, T.real, T.imag);
				
				*(c_arr+k+B) = cplx_sub(c_arr+k, &c_temp);
				DBG_PRINTF("AL(%u) = A(%u) - A(%u)WNp\n", k+B, k, k+B);
				// DBG_PRINTF("sub:(%1.f+%1.fj) - (%1.f+%1.fj)=(%1.f+%1.fj)\n",
				// 	(c_arr+k)->real, (c_arr+k)->imag, c_temp.real, c_temp.imag, (c_arr+k+B)->real, (c_arr+k+B)->imag);
				
				*(c_arr+k) = T;
			}
		}
		putchar('\n');
	}
	DBG_PRINTF("FFT done!!!\n\n");
	return 1;
}

void cplx_amp_angle_batch(complex_t* c, unsigned int N, complex_ap_t* c_ap)
{
	unsigned int i = 0;
	for (i = 0; i < N; ++i) {
		*(c_ap+i) = cplx_amp_angle(c+i);
	}
}

void fft_test()
{
	unsigned int i = 0, j;
	unsigned int N = 8;
	float angle = 0, abs = 0;
	complex_t c_arr[8] = {
		{1, 2},
		{3, 0},
		{5, 0},
		{7, 0},
		{1, 0},
		{3, 0},
		{5, 0},
		{7, 0}
	};
	complex_t* c_cpy = (complex_t*)calloc(N, sizeof(complex_t));
	memcpy(c_cpy, c_arr, N*sizeof(complex_t));

	complex_ap_t* c_ap = (complex_ap_t*)calloc(N, sizeof(complex_ap_t));

	fft(c_cpy, N);
	cplx_amp_angle_batch(c_cpy, N, c_ap);

	printf("i\torigin data\tfft data\tamp\tpha\n");
	printf("-\t-----------\t--------\t---\t---\n");
	for (i = 0; i < N; ++i) {
		printf("%d\t%.2f + %.2fj\t%.2f + %.2fj\t%.2f\t%.2f\n",
			i, c_arr[i].real, c_arr[i].imag, 
			(c_cpy+i)->real, (c_cpy+i)->imag,
			(c_ap+i)->amp, (c_ap+i)->pha );
	}
}


void fft_test_read_data_from_file()
{
	int i, N;
	complex_t c;
	char filename[] = "fft_rawdata.txt";
	FILE* fp = NULL;

	fp = fopen(filename, "r+");
	if (fp == NULL) {
		printf("open %s failure!\n", filename);
		exit(0);
	}
	fscanf(fp, "%d", &N);
	complex_t* c_arr = (complex_t*)calloc(N, sizeof(complex_t));
	complex_ap_t* c_ap_arr = (complex_ap_t*)calloc(N, sizeof(complex_ap_t));

	i = 0;
	while(i < N) {
		fscanf(fp, "%f\t%f", &(c_arr+i)->real, &(c_arr+i)->imag);
		printf("%.1f + %.1fj\n", c_arr[i].real, c_arr[i].imag);
		i++;
	}
	fclose(fp);

	printf("FFT begin!\n");
	fft(c_arr, N);
	printf("FFT done!\n");
	strcpy(filename, "fft_cookeddata.txt");
	fp = fopen(filename, "w+");
	if (fp == NULL) {
		printf("open %s failure!\n", filename);
		fclose(fp);
		exit(0);
	}
	i = 0;
	while(i < N) {
		c_ap_arr[i].amp = cplx_amp(c_arr+i);
		c_ap_arr[i].pha = cplx_angle(c_arr+i);
		printf("%.1f + %.1fj ---> amp=%.1f, pha=%.1f\n", 
			c_arr[i].real, c_arr[i].imag, c_ap_arr[i].amp, c_ap_arr[i].pha);
		fprintf(fp, "%f\t%f\n", c_arr[i].real, c_arr[i].imag);
		i++;
	}
	fclose(fp);
}

int main(int argc, char const *argv[])
{
	fft_test();
	// fft_test_read_data_from_file();

	return 0;
}

fft_test()函数的执行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ldTCAMos-1609315289573)(imgassets/fft_result_with_amp_pha.jpg)]

总结

fft函数传入的是数据指针,因此会修改原数据,如果还需要使用到原始数据,那么请copy一份。

总结个锤子,后天考移动通信,cdj,你还这么浪,赶快滚去复习。

fft_test()函数的执行结果:

在这里插入图片描述

  • 12
    点赞
  • 82
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: 蝶形运算图是一种常见于快速傅里叶变换(Fast Fourier Transform,FFT)算法中的图形表示方法。蝶形运算图可以用来描述FFT算法中的一系列复数的运算过程。 16点FFT蝶形运算图是指在16个输入复数的基础上,进行一系列的乘法和加法运算的过程。具体地说,先将16个输入复数分为两组,分别进行8个蝶形运算。每个蝶形运算包括两个输入信号,每个信号分别与一个复数乘法器相连,然后将它们的乘积相加得到输出信号。这样可以得到8个输出信号。 接着,将这8个输出信号重新分为两组,分别进行4个蝶形运算。同样地,每个蝶形运算有两个输入信号,经过复数乘法器进行乘法运算,然后将乘积相加得到输出信号。这样可以得到4个输出信号。 继续将4个输出信号分为两组,进行2个蝶形运算。每个蝶形运算仍然包括两个输入信号,通过乘法器进行乘法运算,然后将乘积相加得到输出信号。这样可以得到2个输出信号。 最后,将2个输出信号进行1个蝶形运算。同样地,将两个输入信号通过乘法器进行乘法运算,将乘积相加得到输出信号。这样最终得到一个输出信号。 也就是说,16点FFT蝶形运算图涉及了共16个输入信号和15个蝶形运算。通过一系列的乘法和加法运算,可以实现快速计算出FFT的结果。 通过16点FFT蝶形运算图,可以清晰地描述出FFT算法中复杂的运算过程,便于理解和分析算法的实现和优化。同时,蝶形运算图也可以扩展为更大规模的FFT算法,以满足不同的应用需求。 ### 回答2: 16点FFT蝶形运算图是用来进行快速傅里叶变换的计算方法。蝶形运算图是以蝴蝶形状排列的一系列运算单元,用来实现快速傅里叶变换的分步计算。 在16点FFT蝶形运算图中,有16个输入节点和16个输出节点,分别代表输入序列和变换结果序列的各个元素。 首先,将输入序列按照位逆序重新排列,例如将序列0、1、2、3、4、5、6、7、8、9、10、11、12、13、14、15按照逆序排列为0、8、4、12、2、10、6、14、1、9、5、13、3、11、7、15。 接着,将输入序列分为两两一组,依次经过蝶形运算单元进行计算。每个蝶形运算单元包括乘法和加法操作,其具体计算步骤如下: 1. 获取输入序列的两个元素,并用Wn进行乘法运算,其中Wn是旋转因子,通过预先计算得出。 2. 将乘法运算的结果与第二个输入元素进行加法运算。 3. 将加法运算的结果作为本蝶形运算单元的输出,并送入下一个蝶形运算单元。 通过重复上述步骤,不断进行蝶形运算,直到所有的蝶形运算单元都完成计算。最终得到的输出序列即为经过16点FFT变换后的结果。 总结起来,16点FFT蝶形运算图是一种用来进行快速傅里叶变换的计算方法,通过依次进行蝶形运算单元的乘法和加法运算,逐步得到变换结果。这种方法具有高效性和计算速度快的优点,广泛应用于信号处理、图像处理等领域。 ### 回答3: 16点FFT (Fast Fourier Transform) 蝶形运算图是一种用于高效计算离散傅里叶变换的算法结构。 蝶形运算FFT算法中的一种基本计算单元,用于将输入信号按照一定规则进行处理,得到输出信号。16点FFT蝶形运算图包含4个级别的计算,每个级别有4个蝶形运算单元。 在第一级别,输入信号经过4个蝶形运算单元计算后得到4个中间结果。在第二级别,这4个中间结果又分别经过4个蝶形运算单元计算后得到另外4个中间结果。在第三级别,这4个中间结果又分别经过4个蝶形运算单元计算后得到另外4个中间结果。最后,在第四级别,这4个中间结果经过最后的4个蝶形运算单元计算后得到最终的输出结果。 蝶形运算的基本思想是通过对输入信号进行两两配对并进行简单的运算,如加法、减法和乘法,得到输出信号。具体来说,在每个蝶形运算单元中,两个输入信号通过加法和减法得到两个中间结果,然后通过乘法得到最终输出结果。 在16点FFT蝶形运算图中,所有的蝶形运算单元可以并行进行计算,提高了计算效率。同时,由于蝶形运算满足一定的对称性和周期性,可以使用一些优化技巧来减少计算量,进一步提高计算效率。 通过16点FFT蝶形运算图,可以将复杂的离散傅里叶变换问题分解成一系列简单的蝶形运算单元计算,极大地简化了计算过程。这种算法结构在数字信号处理、通信系统等领域中广泛应用,能够高效地处理信号和数据,实现频谱分析、滤波等功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值