C#实现FFT(递归法)的示例代码

 更新时间:2022年07月11日 14:05:34   作者:Mokera  
FFT是数字信号处理中的重要算法。这篇文章将为大家详细介绍一下如何利用C#语言实现FFT(递归法),文中的示例代码讲解详细,感兴趣的可以了解一下
(福利推荐:【腾讯云】服务器最新限时优惠活动,云服务器1核2G仅99元/年、2核4G仅768元/3年,立即抢购>>>:9i0i.cn/qcloud

(福利推荐:你还在原价购买阿里云服务器?现在阿里云0.8折限时抢购活动来啦!4核8G企业云服务器仅2998元/3年,立即抢购>>>:9i0i.cn/aliyun

1. C#实现复数类

我们在进行信号分析的时候,难免会使用到复数。但是遗憾的是,C#没有自带的复数类,以下提供了一种复数类的构建方法。

复数相比于实数,可以理解为一个二维数,构建复数类,我们需要实现以下这些内容:

  • 复数实部与虚部的属性
  • 复数与复数的加减乘除运算
  • 复数与实数的加减乘除运算
  • 复数取模
  • 复数取相位角
  • 欧拉公式(即eix+y

C#实现的代码如下:

 public class Complex
    {
        double real;
        double imag;
        public Complex(double x, double y)   //构造函数
        {
            this.real = x;
            this.imag = y;
        }
        //通过属性实现对复数实部与虚部的单独查看和设置
        public double Real
        {
            set { this.real = value; }
            get { return this.real; }
        }
        public double Imag
        {
            set { this.imag = value; }
            get { return this.imag; }
        }
        //重载加法
        public static Complex operator +(Complex c1, Complex c2)
        {
            return new Complex(c1.real + c2.real, c1.imag + c2.imag);
        }
        public static Complex operator +(double c1, Complex c2)
        {
            return new Complex(c1 + c2.real, c2.imag);
        }
        public static Complex operator +(Complex c1, double c2)
        {
            return new Complex(c1.Real + c2, c1.imag);
        }
        //重载减法
        public static Complex operator -(Complex c1, Complex c2)
        {
            return new Complex(c1.real - c2.real, c1.imag - c2.imag);
        }
        public static Complex operator -(double c1, Complex c2)
        {
            return new Complex(c1 - c2.real, -c2.imag);
        }
        public static Complex operator -(Complex c1, double c2)
        {
            return new Complex(c1.real - c2, c1.imag);
        }
        //重载乘法
        public static Complex operator *(Complex c1, Complex c2)
        {
            double cr = c1.real * c2.real - c1.imag * c2.imag;
            double ci = c1.imag * c2.real + c2.imag * c1.real;
            return new Complex(Math.Round(cr, 4), Math.Round(ci, 4));
        }
        public static Complex operator *(double c1, Complex c2)
        {
            double cr = c1 * c2.real;
            double ci = c1 * c2.imag;
            return new Complex(Math.Round(cr, 4), Math.Round(ci, 4));
        }
        public static Complex operator *(Complex c1, double c2)
        {
            double cr = c1.Real * c2;
            double ci = c1.Imag * c2;
            return new Complex(Math.Round(cr, 4), Math.Round(ci, 4));
        }

        //重载除法
        public static Complex operator /(Complex c1, Complex c2)
        {
            if (c2.real == 0 && c2.imag == 0)
            {
                return new Complex(double.NaN, double.NaN);
            }
            else
            {
                double cr = (c1.imag * c2.imag + c2.real * c1.real) / (c2.imag * c2.imag + c2.real * c2.real);
                double ci = (c1.imag * c2.real - c2.imag * c1.real) / (c2.imag * c2.imag + c2.real * c2.real);
                return new Complex(Math.Round(cr, 4), Math.Round(ci, 4));           //保留四位小数后输出
            }
        }
      
        public static Complex operator /(double c1, Complex c2)
        {
            if (c2.real == 0 && c2.imag == 0)
            {
                return new Complex(double.NaN, double.NaN);
            }
            else
            {
                double cr = c1 * c2.Real / (c2.imag * c2.imag + c2.real * c2.real);
                double ci = -c1 * c2.imag / (c2.imag * c2.imag + c2.real * c2.real);
                return new Complex(Math.Round(cr, 4), Math.Round(ci, 4));           //保留四位小数后输出
            }
        }
      
        public static Complex operator /(Complex c1, double c2)
        {
            if (c2 == 0)
            {
                return new Complex(double.NaN, double.NaN);
            }
            else
            {
                double cr = c1.Real / c2;
                double ci = c1.imag / c2;
                return new Complex(Math.Round(cr, 4), Math.Round(ci, 4));           //保留四位小数后输出
            }
        }
        //创建一个取模的方法
        public static double Abs(Complex c)
        {
            return Math.Sqrt(c.imag * c.imag + c.real * c.real);
        }
        //创建一个取相位角的方法
        public static double Angle(Complex c)
        {
            return Math.Round(Math.Atan2(c.real, c.imag), 6);//保留6位小数输出
        }
        //重载字符串转换方法,便于显示复数
        public override string ToString()
        {
            if (imag >= 0)
                return string.Format("{0}+i{1}", real, imag);
            else
                return string.Format("{0}-i{1}", real, -imag);
        }
        //欧拉公式
        public static Complex Exp(Complex c)
        {
            double amplitude = Math.Exp(c.real);
            double cr = amplitude * Math.Cos(c.imag);
            double ci = amplitude * Math.Sin(c.imag);
            return new Complex(Math.Round(cr, 4), Math.Round(ci, 4));//保留四位小数输出
        }
    }

2. 递归法实现FFT

以下的递归法是基于奇偶分解实现的。

奇偶分解的原理推导如下:

x(2r)和x(2r+1)都是长度为N/2−1的数据序列,不妨令

则原来的DFT就变成了:

于是,将原来的N点傅里叶变换变成了两个N/2点傅里叶变换的线性组合。

但是,N/2点傅里叶变换只能确定N/2个频域数据,另外N/2个数据怎么确定呢?

因为X1(k)和X2(k)周期都是N/2,所以有

从而得到:

综上,我们就可以得到递归法实现FFT的流程:

1.对于每组数据,按奇偶分解成两组数据

2.两组数据分别进行傅里叶变换,得到X1(k)和X2(k)

3.总体数据的X(k)由下式确定:

4.对上述过程进行递归

具体代码实现如下:

public Complex[] FFTre(Complex[] c)
{
    int n = c.Length;
    Complex[] cout = new Complex[n];
    if (n == 1)
    {
        cout[0] = c[0];
        return cout;
    }
    else
    {
        double n_2_f = n / 2;
        int n_2 = (int)Math.Floor(n_2_f);
        Complex[] c1 = new Complex[n / 2];
        Complex[] c2 = new Complex[n / 2];
        for (int i = 0; i < n_2; i++)
        {
            c1[i] = c[2 * i];
            c2[i] = c[2 * i + 1];
        }
        Complex[] c1out = FFTre(c1);
        Complex[] c2out = FFTre(c2);
        Complex[] c3 = new Complex[n / 2];
        for (int i = 0; i < n / 2; i++)
        {
            c3[i] = new Complex(0, -2 * Math.PI * i / n);
        }
        for (int i = 0; i < n / 2; i++)
        {
            c2out[i] = c2out[i] * Complex.Exp(c3[i]);
        }

        for (int i = 0; i < n / 2; i++)
        {
            cout[i] = c1out[i] + c2out[i];
            cout[i + n / 2] = c1out[i] - c2out[i];
        }
        return cout;
    }
}

3. 补充:窗函数

顺便提供几个常用的窗函数:

  • Rectangle
  • Bartlett
  • Hamming
  • Hanning
  • Blackman
    public class WDSLib
    {
        //以下窗函数均为periodic
        public double[] Rectangle(int len)
        {
            double[] win = new double[len];
            for (int i = 0; i < len; i++)
            {
                win[i] = 1;
            }
            return win;
        }

        public double[] Bartlett(int len)
        {
            double length = (double)len - 1;
            double[] win = new double[len];
            for (int i = 0; i < len; i++)
            {
                if (i < len / 2) { win[i] = 2 * i / length; }
                else { win[i] = 2 - 2 * i / length; }
            }
            return win;
        }

        public double[] Hamming(int len)
        {
            double[] win = new double[len];
            for (int i = 0; i < len; i++)
            {
                win[i] = 0.54 - 0.46 * Math.Cos(Math.PI * 2 * i / len);
            }
            return win;
        }

        public double[] Hanning(int len)
        {
            double[] win = new double[len];
            for (int i = 0; i < len; i++)
            {
                win[i] = 0.5 * (1 - Math.Cos(2 * Math.PI * i / len));
            }
            return win;
        }

        public double[] Blackman(int len)
        {
            double[] win = new double[len];
            for (int i = 0; i < len; i++)
            {
                win[i] = 0.42 - 0.5 * Math.Cos(Math.PI * 2 * (double)i / len) + 0.08 * Math.Cos(Math.PI * 4 * (double)i / len);
            }
            return win;
        }
    }

以上就是C#实现FFT(递归法)的示例代码的详细内容,更多关于C# FFT递归法的资料请关注程序员之家其它相关文章!

相关文章

  • C#自定义类型强制转换实例分析

    C#自定义类型强制转换实例分析

    这篇文章主要介绍了C#自定义类型强制转换的方法,实例分析了C#类型转换的相关技巧,需要的朋友可以参考下
    2015-05-05
  • c#使用netmail方式发送邮件示例

    c#使用netmail方式发送邮件示例

    这篇文章主要介绍了c#使用netmail方式发送邮件的示例,大家参考使用吧
    2014-01-01
  • c#调用c++的DLL的实现方法

    c#调用c++的DLL的实现方法

    本文主要介绍了c#调用c++的DLL的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-05-05
  • C#将HashTable中键列表或值列表复制到一维数组的方法

    C#将HashTable中键列表或值列表复制到一维数组的方法

    这篇文章主要介绍了C#将HashTable中键列表或值列表复制到一维数组中方法,涉及C#操作HashTable的相关技巧,需要的朋友可以参考下
    2015-04-04
  • C#判断一个String是否为数字类型

    C#判断一个String是否为数字类型

    本文主要介绍C#判断一个String是否为数字类型几种的方法,需要的朋友可以参考下。
    2016-06-06
  • c#调整图片分辨率的实现示例

    c#调整图片分辨率的实现示例

    本文主要介绍了c#调整图片分辨率的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-10-10
  • C#自定义事件模拟风吹草摇摆效果

    C#自定义事件模拟风吹草摇摆效果

    这篇文章主要介绍了C#自定义事件模拟风吹草摇摆效果,草地上每一颗草都监听HoverTreeWindEvent事件,根据风向(WindDdirection)调整姿态。需要的朋友可以参考下
    2017-08-08
  • C#导入和导出CSV文件

    C#导入和导出CSV文件

    这篇文章介绍了C#导入和导出CSV文件的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-05-05
  • C# 最齐全的上传图片方法

    C# 最齐全的上传图片方法

    本文主要介绍了C# 最齐全的上传图片方法,方法里包括了图片大小限制、图片尺寸、文件内容等等的判断。具有很好的参考价值,下面跟着小编一起来看下吧
    2017-01-01
  • Unity UGUI的ScrollRect滚动视图组件使用详解

    Unity UGUI的ScrollRect滚动视图组件使用详解

    这篇文章主要为大家介绍了Unity UGUI的ScrollRect滚动视图组件使用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-07-07

最新评论

?


http://www.vxiaotou.com