C语言实现的FFT与IFFT源代码,不依赖特定平台

目录

  • 源码
    • FFT.c
    • FFT.h
  • 使用方法
    • 初始化
    • 输入数据
    • FFT 快速傅里叶变换
    • 解算FFT结果
      • 使用python绘制FFT波形
    • IFFT 快速傅里叶逆变换
    • 解算IFFT结果

Windows 10 20H2
Visual Studio 2015
Python 3.8.12 (default, Oct 12 2021, 03:01:40) [MSC v.1916 64 bit (AMD64)] :: Anaconda, Inc. on win32


小改自用于ARM上的FFT与IFFT源代码(C语言,不依赖特定平台)—— syrchina V6.55版,其V6.6版改用了动态内存分配,可能不适用于部分单片机平台。

要使用窗函数处理,见常见窗函数的C语言实现及其形状,适用于单片机、DSP作FFT运算

经过仔细阅读和测试,syrchina大佬的版本(2011.05.22)应该是优化自吉帅虎大佬的版本(2010.2.20),主要是优化了查表和蝶形算法的部分,在VS2015中Debug模式下计算一轮1024点的FFT足足快了几乎一倍,且它们的输出结果是一样的。syrchina大佬的版本附有IFFT的算法,故这里稍作修改,做个记录,供以后使用。
吉帅虎版
在这里插入图片描述
syrchina版
在这里插入图片描述

源码

FFT.c

#include "FFT.h"Complex data_of_N_FFT[FFT_N];			//定义存储单元,原始数据与复数结果均使用之 
double SIN_TABLE_of_N_FFT[FFT_N / 4 + 1];//创建正弦函数表 初始化FFT程序 
void Init_FFT(void)
{int i;for (i = 0; i <= FFT_N / 4; i++){SIN_TABLE_of_N_FFT[i] = sin(2 * i * PI / FFT_N);}
}double Sin_find(double x)
{int i = (int)(FFT_N * x);	//注意:i已经转化为0~N之间的整数了! i = i >> 1;					// i = i / 2;if (i > FFT_N / 4){	//根据FFT相关公式,sin()参数不会超过PI, 即i不会超过N/2 i = FFT_N / 2 - i;//i = i - 2*(i-Npart4);}return SIN_TABLE_of_N_FFT[i];
}double Cos_find(double x)
{int i = (int)(FFT_N*x);//注意:i已经转化为0~N之间的整数了! i = i >> 1;if (i < FFT_N / 4){ //不会超过N/2 return SIN_TABLE_of_N_FFT[FFT_N / 4 - i];}else //i > Npart4 && i < N/2 {return -SIN_TABLE_of_N_FFT[i - FFT_N / 4];}
}//变址 
void ChangeSeat(Complex *DataInput)
{int nextValue, nextM, i, k, j = 0;Complex temp;nextValue = FFT_N / 2;					//变址运算,即把自然顺序变成倒位序,采用雷德算法nextM = FFT_N - 1;for (i = 0; i < nextM; i++){if (i < j)							//如果i<j,即进行变址{temp = DataInput[j];DataInput[j] = DataInput[i];DataInput[i] = temp;}k = nextValue;						//求j的下一个倒位序while (k <= j)						//如果k<=j,表示j的最高位为1{j = j - k;						//把最高位变成0k = k / 2;						//k/2,比较次高位,依次类推,逐个比较,直到某个位为0}j = j + k;							//把0改为1}
}//FFT运算函数 
void FFT(void)
{int L = FFT_N, B, J, K, M_of_N_FFT;int step, KB;double angle;Complex W, Temp_XX;ChangeSeat(data_of_N_FFT);				//变址 //CREATE_SIN_TABLE();for (M_of_N_FFT = 1; (L = L >> 1) != 1; ++M_of_N_FFT);	//计算蝶形级数for (L = 1; L <= M_of_N_FFT; L++){step = 1 << L;						//2^LB = step >> 1;						//B=2^(L-1)for (J = 0; J < B; J++){//P = (1<<(M-L))*J;//P=2^(M-L) *J angle = (double)J / B;			//这里还可以优化 W.real = Cos_find(angle); 		//使用C++时该函数可声明为inline W.real =  cos(angle*PI);W.imag = -Sin_find(angle);		//使用C++时该函数可声明为inline W.imag = -sin(angle*PI);for (K = J; K < FFT_N; K = K + step){KB = K + B;//Temp_XX = XX_complex(data[KB],W); 用下面两行直接计算复数乘法,省去函数调用开销 Temp_XX.real = data_of_N_FFT[KB].real * W.real - data_of_N_FFT[KB].imag*W.imag;Temp_XX.imag = W.imag*data_of_N_FFT[KB].real + data_of_N_FFT[KB].imag*W.real;data_of_N_FFT[KB].real = data_of_N_FFT[K].real - Temp_XX.real;data_of_N_FFT[KB].imag = data_of_N_FFT[K].imag - Temp_XX.imag;data_of_N_FFT[K].real = data_of_N_FFT[K].real + Temp_XX.real;data_of_N_FFT[K].imag = data_of_N_FFT[K].imag + Temp_XX.imag;}}}
}//IFFT运算函数 
void IFFT(void)
{int L = FFT_N, B, J, K, M_of_N_FFT;int step, KB;double angle;Complex W, Temp_XX;ChangeSeat(data_of_N_FFT);//变址 for (M_of_N_FFT = 1; (L = L >> 1) != 1; ++M_of_N_FFT);	//计算蝶形级数for (L = 1; L <= M_of_N_FFT; L++){step = 1 << L;						//2^LB = step >> 1;						//B=2^(L-1)for (J = 0; J<B; J++){angle = (double)J / B;			//这里还可以优化  W.real = Cos_find(angle);		//使用C++时该函数可声明为inline W.real = cos(angle*PI);W.imag = Sin_find(angle);		//使用C++时该函数可声明为inline W.imag = sin(angle*PI);for (K = J; K < FFT_N; K = K + step){KB = K + B;//用下面两行直接计算复数乘法,省去函数调用开销 Temp_XX.real = data_of_N_FFT[KB].real * W.real - data_of_N_FFT[KB].imag*W.imag;Temp_XX.imag = W.imag*data_of_N_FFT[KB].real + data_of_N_FFT[KB].imag*W.real;data_of_N_FFT[KB].real = data_of_N_FFT[K].real - Temp_XX.real;data_of_N_FFT[KB].imag = data_of_N_FFT[K].imag - Temp_XX.imag;data_of_N_FFT[K].real = data_of_N_FFT[K].real + Temp_XX.real;data_of_N_FFT[K].imag = data_of_N_FFT[K].imag + Temp_XX.imag;}}}
}/*****************************************************************
函数原型:void Refresh_Data(int id, double wave_data)
函数功能:更新数据
输入参数:id: 标号; wave_data: 一个点的值
输出参数:无
*****************************************************************/
void Refresh_Data(int id, double wave_data)
{data_of_N_FFT[id].real = wave_data;data_of_N_FFT[id].imag = 0;
}

FFT.h

#ifndef FFT_H_
#define FFT_H_#include <math.h>#define PI				3.14159265358979323846264338327950288419716939937510	//圆周率#define FFT_N			1024		//傅里叶变换的点数 #define FFT_RESULT(x) 	(sqrt(data_of_N_FFT[x].real*data_of_N_FFT[x].real+data_of_N_FFT[x].imag*data_of_N_FFT[x].imag)/ (FFT_N >> (x != 0)))
#define FFT_Hz(x, Sample_Frequency)		((double)(x * Sample_Frequency) / FFT_N)#define IFFT_RESULT(x)	(data_of_N_FFT[x].real / FFT_N)typedef struct						//定义复数结构体 
{double real, imag;
}Complex;extern Complex data_of_N_FFT[];	
extern double SIN_TABLE_of_N_FFT[];void Init_FFT(void);
void FFT(void);
void IFFT(void);
void Refresh_Data(int id, double wave_data);#endif // !FFT_H_

使用方法

初始化

在FFT.h中修改FFT的点数

由于FFT变换归一化后,除了0Hz直流分量外,整个结果表是对称的,即若点数为1024,则我们只看前512个点即可,所以这个傅里叶变换的点数可根据你的屏幕长方向上的像素数来决定,如128x64的屏幕可以考虑使用256点的FFT。

#define FFT_N			1024		//傅里叶变换的点数 

初始化一遍,生成正弦表

Init_FFT();			//①初始化各项参数,此函数只需执行一次 ; 修改FFT的点数去头文件的宏定义处修改 

输入数据

这里设采样频率为100Hz,产生一个0~5V,10Hz方波

void Refresh_Data(int id, double wave_data);

	#define Sample_Frequency 100for (i = 0; i < FFT_N; i++)//制造输入序列 {if (sin(2 * PI * 10 * i / Sample_Frequency) > 0)Refresh_Data(i, 5);else if (sin(2 * PI * 10 * i / Sample_Frequency) < 0)Refresh_Data(i, 0);elseRefresh_Data(i, 2.5);}

FFT 快速傅里叶变换

FFT();				//③进行 FFT计算 

解算FFT结果

使用 FFT_Hz (i, Sample_Frequency) 获取该点频率
使用 FFT_RESULT (i) 获取该点模值

由于FFT结果是对称的,故只需看前FFT_N / 2个点

我这里为了看波形用C++跑的,将生成的坐标保存进out.txt中

#include <iostream>
#include <fstream>
using namespace std;
	ofstream out;out.open("out.txt");for (int i = 0; i < FFT_N / 2; ++i){out << FFT_Hz(i, Sample_Frequency) << " " << FFT_RESULT(i) << endl;}out.close();

使用python绘制FFT波形

import matplotlib.pyplot as pltx = []
y = []with open(r'D:\out.txt的路径\out.txt') as TXT_File:for line in TXT_File:tmp = line.split()x.append(float(tmp[0]))y.append(float(tmp[1]))fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.plot(x, y)
plt.show()

在这里插入图片描述

IFFT 快速傅里叶逆变换

	IFFT();//进行 IFFT计算

解算IFFT结果

使用 IFFT_RESULT (i) 读取每个点的波形

我这里同样为了看波形将生成的坐标保存进out1.txt中

	ofstream out1;out1.open("out1.txt");for (int i = 0; i < FFT_N; ++i){out1 << i << " " << IFFT_RESULT(i) << endl;}out1.close();

可以看到,又转变回0~5V的方波了,故也许可以FFT后直接对特定频率的信号处理再逆变换回去,实现滤波。
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/400495.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

产品经理能力产品经理工作积累(3)

每日一贴,今天的内容关键字为产品经理能力 按职业司理的层次模型产品司理又可分工匠型,元帅型和老师型. (1). 工匠型 工匠型产品司理主要的代价在于:在某个专业领域里其技能的娴熟水平. (2). 元帅型 元帅型产品司理,可以在一个领域中带领一帮人来完成一个特定的项目.他的能力体…

垂直和水平居中方法小结

水平居中 行内元素 text-align:center; 块元素 1.定宽块元素水平居中 margin:0 auto; 2.不定宽块元素水平居中 方法一&#xff1a;利用浮动的包裹性和百分比相对定位 <div classouter><div classinner></div> </div> 我们想要使inner(不定宽)水平居中于…

MySQL命令行导出数据库

MySQL命令行导出数据库&#xff1a; 1&#xff0c;进入MySQL目录下的bin文件夹&#xff1a;cd MySQL中到bin文件夹的目录 如我输入的命令行&#xff1a;cd C:\Program Files\MySQL\MySQL Server 4.1\bin (或者直接将windows的环境变量path中添加该目录) 2&#xff0c;导出数据库…

在51单片机上使用递归的注意事项

目录问题应对措施原理普中51-单核-A2 STC89C52 Keil uVision V5.29.0.0 PK51 Prof.Developers Kit Version:9.60.0.0 问题 在Keil C51中直接使用递归会报如下警告&#xff1a; recursive call to non-reentrant function 为了提高运行效率&#xff0c;C51采用静态分配局部变量…

ASP.Net 获取服务器信息

1: Response.Write("服务器机器名&#xff1a;" Server.MachineName); 2: Response.Write("<br/>");3: Response.Write("服务器IP地址&#xff1a;" Request.ServerVariables["LOCAL_ADDR"]);4: Response.Write("<br/…

POJ 2456 - Aggressive cows(二分)

Description Farmer John has built a new long barn, with N (2 < N < 100,000) stalls. The stalls are located along a straight line at positions x1,…,xN (0 < xi < 1,000,000,000). His C (2 < C < N) cows don’t like this barn layout and becom…

〖Android〗存在多个Android设备时,使用Shell脚本选择一个Android设备

Shell脚本&#xff1a; #!/bin/bash devices( $(adb devices|grep device$|awk {print $1}|xargs echo) )case ${#devices[]} in0 )echo "cant found a android device!";;1 )serial$devices;;* )select serial in ${devices[]}; dobreak;done;; esacif [[ -z $seria…

C盘瘦身:QQ文件的清理及Group2文件夹

目录问题解决方法Windows 10 20H2 TIM 问题 最近C盘被撑爆了 使用SpaceSniffer一扫发现QQ的文件中有个Group2文件夹占了我17G 但使用QQ自带的个人文件夹清理却扫不到&#xff0c;据说直接删除会丢失近期所有群聊的聊天图片 解决方法 在这个地方找到了大神fsz1987给出的解…

分享20个Android游戏源代码。以后看看。

分享20个Android游戏源码&#xff0c;希望大家喜欢哈&#xff01;http://www.apkbus.com/android-21834-1-1.htmlAndroid 疯狂足球游戏源码http://www.apkbus.com/android-20986-1-1.htmlandroid源码捏苍蝇游戏源码http://www.apkbus.com/android-20987-1-1.htmlAndroid游戏源码…

【.Net】C# 将Access中时间段条件查询的数据添加到ListView中

一、让ListView控件显示表头的方法 在窗体中添加ListView 空间&#xff0c;其属性中设置&#xff1a;View属性设置为&#xff1a;Detail&#xff0c;Columns集合中添加表头中的文字。 二、利用代码给ListView添加Item。 首先&#xff0c;ListView的Item属性包括Items和SubItems…

获取ArcGIS安装路径

在要素类进行符号化时&#xff0c;使用axSymbologyControl需要安装路径下的Style文件路径&#xff0c;在AE9.3VS2008中是这样的&#xff1a; Microsoft.Win32.RegistryKey regKey Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE\\ESRI\\CoreRuntime",…

【51单片机快速入门指南】3.2:定时器/计数器

目录快速使用硬知识传统51单片机 CPU 时序的有关知识&#xff08;12T&#xff09;51 单片机定时器原理51 单片机定时/计数器结构定时器/计数器0/1定时器/计数器0和1的相关寄存器控制寄存器工作模式寄存器工作模式模式0(13位定时器/计数器)模式1(16位定时器/计数器)模式2(8位自动…

EBS并发管理器请求汇总(按照并发消耗时间,等待时间,平均等待事件等汇总)...

此数据集用于确定正在使用中并发管理器&#xff0c;并可与实际的在启动时分配的并发管理器。而且考虑完成状态为 正常/警告 的请求。 select q.concurrent_queue_name,count(*) cnt,sum(r.actual_completion_date - r.actual_start_date) * 24 elapsed,avg(r.actual_completion…

Linux内核RCU(Read Copy Update)锁简析

在非常早曾经&#xff0c;大概是2009年的时候。写过一篇关于Linux RCU锁的文章《RCU锁在linux内核的演变》&#xff0c;如今我承认。那个时候我尽管懂了RCU锁&#xff0c;可是我没有能力用一种非常easy的描写叙述把Linux的实现给展示出来&#xff0c;有道是你能给别人用你自己的…

sublime_text 3 注册序列号

为什么80%的码农都做不了架构师&#xff1f;>>> ----- BEGIN LICENSE ---- Andrew Weber Single User License EA7E-855605 813A03DD 5E4AD9E6 6C0EEB94 BC99798F 942194A6 02396E98 E62C9979 4BB979FE 91424C9D A45400BF F6747D88 2FB88078 90F5CC94 1CDC92DC 845…

【51单片机快速入门指南】3.2.1:PWM、呼吸灯与舵机

目录硬知识PWM&#xff08;脉冲宽度调制&#xff09;基本原理脉宽调制分类上机实战呼吸灯main.c中断服务函数修改TIM.c中的中断服务函数效果开发板电路分析舵机控制舵机控制方法main.c中断服务函数修改中断服务函数舵机测试程序main.c效果普中51-单核-A2 STC89C52 Keil uVisio…

Oracle常用查看表结构命令

转载自:http://blog.520591.com/1301 获取表&#xff1a; select table_name from user_tables; //当前用户的表 select table_name from all_tables; //所有用户的表 select table_name from dba_tables; //包括系统表 select table_name from dba_tables where owner’用户名…

Centos7 安装oracle数据库

参考的内容&#xff1a; http://docs.oracle.com/cd/E11882_01/install.112/e24325/toc.htm#CHDCBCJF http://www.cnblogs.com/yingsong/p/6031452.html http://www.cnblogs.com/yingsong/p/6031235.html 步骤主要是有&#xff1a; 1、安装依赖的 软件包 2、创建用户和目录&…

ABAP常见面试问题

ABAP常见面试问题 1. What is the typical structure of an ABAP program? 2. What are field symbols and field groups.? Have you used "component idx of structure" clause with field groups? 3. What should be the approach for writing a BDC program? …

车牌识别之车牌定位(方案总结)

尊敬原作者&#xff0c;转自:http://blog.csdn.net/hqw7286/article/details/5810353 一直研究车牌识别算法&#xff0c;主要关注车牌定位和字符识别。我想分享一下我对车牌定位的看法。 从根本上讲&#xff0c;车牌定位的算法分为三类&#xff0c;一类是基于边缘的&#xff0c…