利用C语言实现FFT、IFFT运算

一、FFT算法理论

FFT、IFFT
上述分别为FFT、IFFT公式。下面首先讨论FFT的算法实现。
本文采用输入逆序、输出顺序的FFT计算方法。实质上就是在时域对x(n)进行“奇偶分类”、在频域上对X(k)进行“前后分类”。 值得说明的是,这里的“奇”和“偶”是相对的概念,并不完全是通常我们所理解的“奇”和“偶”。下面将给出一个例子进行说明:
FFT运算示例
·图中,总共可以分为 “三级” ,每一级包含若干 “蝶形计算单元”。每一个蝶形单元的结构如下:蝶形运算单元
因此,进行FFT运算只需考虑三件事情。一、将输入序列倒序排列,也就是进行奇偶分类。二、实现最基本的蝶形计算单元。三、根据规律,划分出FFT运算的级数,并且确定出每一级中参与蝶形运算的数据分组。

二、FFT运算的代码实现

  1. 输入倒序排列(奇偶分类)。事实上,将输入倒序排列就是进行了奇偶分类。如果FFT的点数为8,那么这8个数的二进制表示分别为000,001,010,011,100,101,110,111。进行二进制层面倒序排列就变为000,100,010,110,001,101,011,111;重新转变为十进制就是0,4,2,6,1,5,3,7。 上面的计算方式对人类来说是很方便的,但是对于电脑来说,效果并不是太好,因此需要另外发现一种规律。
  2. 输入奇偶分类(倒序排列)。上面已经提到,倒序排列实际上就是在进行奇偶分类,而进行奇偶分类对电脑来说会简单很多。总结1中提到的规律,三位二进制数可以理解为FFT运算的级数。我们可以发现,在倒序排列完成后,“偶数”总是排在“奇数”前面。 这里需要提出“偶中的偶”,“奇中的奇”的概念。即每次进行“余2”运算,结果是0的都排在前面。现在看来,电脑实现算法的方式就是每次进行“余2”运算,进行奇偶分类;然后进行“除2”运算,再进行“余2”运算,进行奇偶分类……直到最后排序完成。 实现代码如下:
void reverse()		//输入倒序
{ 
	complex temp;
	char* flag = (char*)malloc(N * sizeof(char));	//用于标记是否被换过,若为1,则已经换过
	int i, j, current_n, target_n;
	for (i = 0;i < N;i++) {
		flag[i] = 0;
	}
	for (i = 0;i < N;i++) {
		current_n = i;
		target_n = 0;
		//获取两个互为倒序的标号,换种思路
		for (j = 0;j < bits;j++) {
			target_n = target_n + (int)((current_n % 2) * pow(2, bits - j - 1));
			current_n /= 2;
		}
		current_n = i;
		//对应标号值互换
		if (current_n != target_n && flag[current_n] != 1) {
			temp = x[current_n];
			x[current_n] = x[target_n];
			x[target_n] = temp;
			flag[target_n] = 1;
		}
	}
	free(flag);
}

说明:complex为自己定义的复数数据结构。再次强调,奇偶分类就是倒序排序。二者是一样的。

  1. 蝶形运算单元的实现。分析蝶形运算单元,两输入、两输出,因此蝶形运算单元需要注意的就是输入、输出采用同一片内存存储即可,也就是让输出数据覆盖输入数据,这样可以有效节约存储资源。
void butterfly(int x1_point, int x2_point, complex wn)	//蝶形计算单元
{
	complex result1, result2, T;
	T = multiplication(x[x2_point], wn);
	result1 = add(x[x1_point], T);
	result2 = sub(x[x1_point], T);

	x[x1_point] = result1;
	x[x2_point] = result2;
}

说明:上面multiplication、add、sub方法均为自己定义,wn为旋转因子。

  1. 进行单级FFT运算。需要注意的就是总结规律。总结出两对偶结点的“结点跨距”、“分组间隔”、“分组数”……有了这些才能够编写将参与蝶形运算的数据分类配对。
void single_fft(int m)			//进行单级的fft运算
{
	//首先进行分组
	int point_distance = (int)pow(2, m - 1);	//结点跨距
	int group_distance = 2 * point_distance;	//分组间隔
	int group_count = N / group_distance;		//分组数

	int group_header = 0;

	int x1, x2;								//参与计算两结点的标号
	complex w;

	int i, j;								//循环控制符
	//分组
	for (i = 0;i < group_count;i++) {
		//获取一组的标号范围
		group_header = i * group_distance;
		//将每组分成两半,前一半均为x1,后一半均为x2
		for (j = 0;j < group_distance / 2;j++) {
			w = w_builder(m, j);
			x1 = j + group_header;
			x2 = x1 + point_distance;
			butterfly(x1, x2, w);
		}
	}
}
  1. 有了单级FFT运算以后,将每一级组合就可以实现FFT运算了。
complex* fft(int fft_point, int xn_length, complex* xn)		//fft最终封装
{
	int i;
	//初始化
	fft_init(fft_point, xn_length, xn);
	//输入倒序
	reverse();
	//fft运算
	for (i = 1;i <= bits;i++) {
		single_fft(i);
	}
	return x;
}

三、IFFT算法理论

IFFT算法理论
从上面的等式可以看出,括号里面的内容实际上就是FFT运算。也就是说只需要对输入、输出序列进行一定的处理,就可以利用刚刚已经做好的FFT算法实现IFFT的运算!

四、IFFT运算的代码实现

complex* ifft(int xk_length, complex* xk)		//ifft最终封装
{
	int i;
	//对输入序列进行处理
	ifft_init(xk_length, xk);
	//套用fft算法
	reverse();
	for (i = 1;i <= bits;i++) {
		single_fft(i);
	}
	//对输出序列进行处理
	for (i = 0;i < N;i++) {
		x[i] = conjugate(x[i]);		//求共轭
		x[i].real /= N;
		x[i].imaginary /= N;
	}
	return x;
}

五、总结

本文也程序也考虑到了FFT自动补零问题的处理,大家可以参考完整代码(FFT、IFFT均验证正确)。FFT.zip

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

萌哒哒虎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值