C#中实现FFT的两种方法

5 篇文章 0 订阅

最近工作中有个需求,在C#环境中实现FFT算法,在网上找了些资料,最后实现了下面的两种方式,实际应用任选其一就好。

第一种方法:

不依赖C#中的Complex,需要实现计算过程的每一步详细步骤。

输入序列长度为2的N次幂,使用前需先定义序列长度:

FFT filter = new FFT(256);

filter.fft(x,y) 其中x为实部y为虚部,计算后x为FFT后的实部,y为FFT后的虚部。

 class FFT
    {
        int n, m;
        // Lookup tables. Only need to recompute when size of FFT changes.
        double[] cos;
        double[] sin;

        public FFT(int n)
        {
            this.n = n;
            this.m = (int)(Math.Log(n) / Math.Log(2));

            // Make sure n is a power of 2
            if (n != (1 << m))
                Console.Out.WriteLine("FFT length must be power of 2");

            // precompute tables
            cos = new double[n / 2];
            sin = new double[n / 2];

            for (int i = 0; i < n / 2; i++)
            {
                cos[i] = Math.Cos(-2 * Math.PI * i / n);
                sin[i] = Math.Sin(-2 * Math.PI * i / n);
            }

        }
///x为实部y为虚部///
        private void fft(double[] x, double[] y)
        {
            int i, j, k, n1, n2, a;
            double c, s, t1, t2;

            // Bit-reverse
            j = 0;
            n2 = n / 2;
            for (i = 1; i < n - 1; i++)
            {
                n1 = n2;
                while (j >= n1)
                {
                    j = j - n1;
                    n1 = n1 / 2;
                }
                j = j + n1;

                if (i < j)
                {
                    t1 = x[i];
                    x[i] = x[j];
                    x[j] = t1;
                    t1 = y[i];
                    y[i] = y[j];
                    y[j] = t1;
                }
            }

            // FFT
            n1 = 0;
            n2 = 1;

            for (i = 0; i < m; i++)
            {
                n1 = n2;
                n2 = n2 + n2;
                a = 0;

                for (j = 0; j < n1; j++)
                {
                    c = cos[a];
                    s = sin[a];
                    a += 1 << (m - i - 1);

                    for (k = j; k < n; k = k + n2)
                    {
                        t1 = c * x[k + n1] - s * y[k + n1];
                        t2 = s * x[k + n1] + c * y[k + n1];
                        x[k + n1] = x[k] - t1;
                        y[k + n1] = y[k] - t2;
                        x[k] = x[k] + t1;
                        y[k] = y[k] + t2;
                    }
                }
            }
        }


    }

第二种方式:

傅里叶变换的计算中需要用到复数,无意中发现C#中有复数的定义,考虑使用复数直接计算的方式实现,这样可以利用C#中的复数计算。C#中使用复数需要引用using System.Numerics;

FFT filter = new FFT(256);

filter.DoFFT(x) ; x为输入参数(Complex[]类型),计算后x为FFT后的结果。

突然发现C#好好用啊。。。。。。

class FFT
    {
       
        private Complex[] W;//定义旋转因子

        /// <summary>
        /// 构造函数用于初始化旋转因子。
        /// </summary>
        public FFT(int N)
        {                
            W = initW(N);
        }


        /// <summary>
        /// 快速傅立叶正变换函数
        /// W:输入旋转因子序列数组名
        /// x: 输入信号序列数组名
        /// </summary>
        public void DoFFT(Complex[] x)
        {
            int i = 0, j = 0, k = 0, l = 0;
            Complex up, down, product;
            ReverseOder(x);         ///对输入序列进行倒序
            for (i = 0; i < Math.Log(N, 2); i++)            ///外循环,变换级数循环
            {
                l = 1 << i;
                for (j = 0; j < N; j += 2 * l)             ///中间循环,旋转因子循环
                {
                    for (k = 0; k < l; k++)                     ///内循环,序列循环
                    {
                        product = x[j + k + l] * W[N * k / 2 / l];
                        up = x[j + k] + product;
                        down = x[j + k] - product;
                        x[j + k] = up;
                        x[j + k + l] = down;
                    }
                }
            }
        }

       

        /// <summary>
        /// 倒序函数
        /// W:输入旋转因子序列长度
        /// </summary>
        private Complex[] initW(int N)
        {
            Complex[] W = new Complex[N];
            for (int i = 0; i < N; i++)
            {
                double real = Math.Cos(2 * Math.PI * i / N);        //旋转因子实部展开
                double imag = -1 * Math.Sin(2 * Math.PI * i / N);   //旋转因子虚部展开
                W[i] = new Complex(real, imag);
            }
            return W;
        }


        /// <summary>
        /// ReverseOder()倒序变换
        /// </summary>
        /// <param name="x"></param>
        private void ReverseOder(Complex[] x)
        {
            Complex temp;
            int i = 0, j = 0, k = 0;
            double t;
            for (i = 0; i < N; i++)
            {
                k = i; j = 0;
                t = Math.Log(N, 2);
                while ((t--) > 0)
                {
                    j = j << 1;
                    j |= (k & 1);
                    k = k >> 1;
                }
                if (j > i)
                {
                    temp = x[i];
                    x[i] = x[j];
                    x[j] = temp;
                }
            }
        }


    }

 

  • 6
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
FFT(Fast Fourier Transform,快速傅里叶变换)算法是一种高效的计算离散傅里叶变换(DFT)的算法。这种算法可以在较短的时间内计算出离散信号的频谱信息,而不需要进行大量的乘法和加法运算。 FFT算法的应用非常广泛,尤其在信号处理和图像处理领域被广泛使用。在C语言实现FFT算法需要使用复数的数据结构和相关的运算函数。 要实现8点FFT算法,首先需要准备一个长度为8的复数数组,这个数组用来存储输入信号的时域数据。接下来,需要定义一个函数来进行FFT计算。 在C语言,可以使用递归的方法实现FFT算法。具体步骤如下: 1.定义一个函数,输入参数是待计算的信号数组和信号的长度。 2.在函数内部,首先通过判断信号长度是否等于1来判断是否需要终止递归。如果长度等于1,则直接返回这个信号。 3.如果信号长度大于1,需要进行递归计算。将信号分为奇数索引和偶数索引的两个子数组,分别递归调用FFT函数。 4.然后将两个子数组的结果进行合并,得到FFT计算的结果。 5.最后通过复数相乘的方式计算频谱各个频率的幅值和相位。 实现8点FFT算法的C代码如下: ```c #include <stdio.h> #include <math.h> #include <complex.h> #define N 8 void fft(complex double* signal, int n) { if (n == 1) { return; } complex double even[n / 2]; complex double odd[n / 2]; for (int i = 0; i < n / 2; i++) { even[i] = signal[2 * i]; odd[i] = signal[2 * i + 1]; } fft(even, n / 2); fft(odd, n / 2); for (int k = 0; k < n / 2; k++) { complex double t = cexp(-I * 2 * M_PI * k / n) * odd[k]; signal[k] = even[k] + t; signal[k + n / 2] = even[k] - t; } } int main() { complex double signal[N] = {1, 1, 1, 1, 0, 0, 0, 0}; fft(signal, N); for (int i = 0; i < N; i++) { printf("频率%d的幅值:%f 相位:%f\n", i, cabs(signal[i]), carg(signal[i])); } return 0; } ``` 这段代码实现了一个简单的8点FFT算法。首先定义了长度为8的复数数组signal,然后调用fft函数进行FFT计算,最后输出每个频率的幅值和相位。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值