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,一经查实,立即删除!

相关文章

在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采用静态分配局部变量…

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

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

获取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位自动…

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…

Proteus仿真单片机:51单片机的仿真

目录新建工程调试在Proteus中编写程序导入Keil生成的Hex程序Windows 10 20H2 Proteus 8 Frofessional v8.9 SP2 Keil uVision V5.29.0.0 PK51 Prof.Developers Kit Version:9.60.0.0 新建工程 设置名称和路径 下一步 下一步 选择系列、控制器和编译器 双击MCU设置主频 …

Linux 命令行输入

这几天刚刚接触到Linux&#xff0c;在windows上安装的VMWare虚拟机&#xff0c;Centos7。安装什么都是贾爷和办公室的同事帮忙搞定的。 在虚拟机界面&#xff0c;按快捷键CtrlAltEnter&#xff0c;可以全屏显示Linux界面&#xff0c;再按一次则退出全屏。 如何在Linux里输入命令…

【51单片机快速入门指南】2.5:并行I/O扩展与8255A

目录硬知识单片机I/O扩展基础知识I/O接口电路的功能速度协调输出数据锁存数据总线隔离数据转换增强驱动能力单片机并行扩展总线并行扩展总线的组成80C51单片机并行扩展总线I/O编址技术可编程并行接口芯片82558255硬件逻辑结构口电路总线接口电路A组和B组控制电路中断控制电路82…

win 下 apache2.4 +tomcat7 集群

为什么80%的码农都做不了架构师&#xff1f;>>> 反正每次来做一个不熟悉的东西&#xff0c;就是各种的search ,前一次去做过一个apache的东西&#xff0c;各种蛋疼&#xff0c;各种不能用。好多的东西也是比较旧了的咯。 这次结合前辈的各种东借西拿&#xff0c;总…

Proteus仿真单片机:PIC18单片机的仿真

目录新建工程ProteusMPLAB X IDE调试Windows 10 20H2 Proteus 8 Frofessional v8.9 SP2 MPLAB X IDE v5.45 新建工程 Proteus 下一步 下一步 选择芯片、编译器 搭建实验电路 MPLAB X IDE MPLAB X IDE 新建工程 选择独有项目 选择芯片 选择编译器 配置工程名称、路…

Realm学习总结

参考博客: http://www.jianshu.com/p/096bec929f2a http://www.cnblogs.com/ilyy/p/5648051.html 参考的博客介绍很详细,我就不写了..写了一个简单的学习的demo. GitHub地址: https://github.com/PengSiSi/RealmDemo 代码如下: // // ViewController.m // RealmDemo // // C…

with(nolock)的用法

with(nolock)的介绍 大家在写查询时,为了性能,往往会在表后面加一个nolock,或者是with(nolock),其目的就是查询是不锁定表,从而达到提高查询速度的目的。 当同一时间有多个用户访问同一资源,并发用户中如果有用户对资源做了修改&#xff0c;此时就会对其它用户产生某些不利的影…

【PIC18单片机学习笔记】一、程序的烧录

目录编程器烧录软件烧录准备程序main.cpic18.h烧录效果Windows 10 20H2 PICkit 3 v3.10 MPLAB X IDE v5.45 PIC18F46J50 编程器 所用编程器为PICkit 3.5 按图示连接好编程器和开发板 烧录软件 所用烧录软件为PICkit 3 v3.10 初次使用需要给编程器更新固件&#xff0c…

ASIHttpRequest:创建队列、下载请求、断点续传、解压缩

ps&#xff1a;本文转载自网络&#xff1a;http://ryan.easymorse.com/?p12 感谢作者 工程完整代码下载地址&#xff1a;RequestTestDownload1 可完成&#xff1a; 下载指定链接的zip压缩文件存放在Documents目录下支持断点续传显示下载进度解压到指定目录—————————…

熊仔科技Steamduino PIC18F46J50主控板 部分原理图

目录连接情况原理图实物图Proteus 8 Frofessional v8.9 SP2 从学长那嫖的&#xff0c;本来貌似是要用来做写字机器人的&#xff0c;后面学长换成Arduino UNO了。 网上找不到资料&#xff0c;用万用表简单测了测没有丝印部分的连接情况 连接情况 RD2/PMD2/RP19 USR_LED…

【51单片机快速入门指南】3.3:USART 串口通信

目录快速使用硬知识串行口相关寄存器串行口控制寄存器SCON和PCON串行口数据缓冲寄存器SBUF从机地址控制寄存器SADEN和SADDR与串行口中断相关的寄存器IE和IPH、IP串行口工作模式串行口工作模式0&#xff1a;同步移位寄存器串行口工作模式1&#xff1a;8位UART&#xff0c;波特率…

Spring JTA应用JOTM Atomikos II JOTM

上节建立了一个简单的Java Application以及所需要的数据库和数据表&#xff0c;本节将介绍JOTM在Spring中的配置。 JOTM(Java Open Transaction Manager)是ObjectWeb的一个开源JTA实现&#xff0c;本身也是开源应用程序服务器JOnAS(Java Open Application Server)的一部分&…

从包中构建瓦片服务器

SWITCH2OSM 切换到OPENSTREETMAP 丰富的数据 OpenStreetMap数据丰富而详细&#xff0c;包含与实地人相关的大量数据 - 收集的数据。 功能包括&#xff1a; 道路&#xff0c;铁路&#xff0c;水路等餐厅&#xff0c;商店&#xff0c;车站&#xff0c;自动取款机等。步行和自行车…

【51单片机快速入门指南】4: 软件 I2C

目录硬知识I2C 介绍I2C 物理层I2C 协议层数据有效性规定起始和停止信号应答响应总线的寻址方式数据传输示例程序Soft_I2C.cSoft_I2C.h普中51-单核-A2 STC89C52 Keil uVision V5.29.0.0 PK51 Prof.Developers Kit Version:9.60.0.0 硬知识 摘自《普中 51 单片机开发攻略》 I2…