C语言实现快速傅立叶(FFT)(二)

#ifndef _MFFT_H_
#define _MFFT_H_

/******************************************************************************
* 文件       :    MFFT.h
* 作者       :    dhs
* 版本       :    V1.0
* 日期       :    2020-7-14
* 描述       :    实现快速傅里叶变换的功能(fft)
*                  公式:
******************************************************************************/
#include 

#define twoPi 6.28318531
#define fourPi 12.56637061
#define sixPi 18.84955593

/* 窗口类型 */
enum  WinType
{
    FFT_WIN_TYP_RECTANGLE=0      ,//  矩形窗
    FFT_WIN_TYP_HAMMING          ,//  汉明窗
    FFT_WIN_TYP_HANN             ,//  汉宁窗
    FFT_WIN_TYP_TRIANGLE         ,//  三角形窗
    FFT_WIN_TYP_NUTTALL          ,//  nuttall窗 
    FFT_WIN_TYP_BLACKMAN         ,//  布莱克曼窗
    FFT_WIN_TYP_BLACKMAN_NUTTALL ,//  blackman nuttall
    FFT_WIN_TYP_BLACKMAN_HARRIS  ,//  blackman harris
    FFT_WIN_TYP_FLT_TOP          ,//  flat top
    FFT_WIN_TYP_WELCH            ,//  welch

};

struct MFFT
{  
    /*变量区*/                  
    uint16_t _samples;                                                                 //采样信号长度  输入数据的个数 为2的n次方
	float _samplingFrequency;                                                          //采样频率
	float *_vReal;                                                                     //实部值  输出时用于保存幅值
	float *_vImag;                                                                     //虚部值  输出时用于保存频率的值
	uint8_t _power;                                                                    //2的n次方  n的值
    float _step;                                                                       //表示输出每一值之间的频率步长
    float _revalue;                                                                    //幅值恢复系数
    /*函数方法区*/
    void (*Init)(struct MFFT *self,float *vReal, float *vImag, uint16_t samples, float samplingFrequency);   //初始化                                              //初始化函数
    uint8_t (*Compute)(struct MFFT *self);                                             //fft结果计算
    uint8_t (*Exponent)(struct MFFT *self, uint16_t value);                            //指数
    void (*Windowing)(struct MFFT *self, enum WinType windowType);                     //窗口函数获取
    void (*DCRemoval)(struct MFFT *self);              //去除直流分量
    float (*MajorPeak)(float *vD, uint16_t samples, float samplingFrequency);          //

};

void MFFT_Create(struct MFFT *self);

#endif
/******************************************************************************
* 文件       : MFFT.c
* 作者       :    dhs
* 版本       :    V1.0
* 日期       :    2020-7-14
* 描述       :    实现快速傅里叶变换的功能(fft)
*                  公式:
******************************************************************************/
#include "MFFT.h"
#include 
#include "math.h"

static const float PI = 3.1415926535897932384626f;

#define sq(x) ((x)*(x))
static void DCRemoval(struct MFFT *self);

/*******************************************************************************
 * 函 数 名         : Init
 * 函数功能         : fft对象各参数初始化
 * 输入参数         : *vReal --> 信号输入、输出向量指针,保存fft计算的结果(实部)
 *                   *vImag --> 信号输入、输出向量指针,保存fft计算的结果(虚部)
 *                   samples--> 信号采样的个数
 *                   samplingFrequency    -->采样频率   hz
 * 返 回 值         : 无
 *******************************************************************************/
static void Init(struct MFFT *self, float *vReal, float *vImag, uint16_t samples, float samplingFrequency)
{
    self->_vReal = vReal;
    self->_vImag = vImag;
    self->_samples = samples;
    self->_samplingFrequency = samplingFrequency;
    self->_power = self->Exponent(self, samples);
    self->_step  = self->_samplingFrequency/self->_samples;
    memset(self->_vImag, 0, self->_samples * sizeof(float));
    DCRemoval(self);
}
/*******************************************************************************
 * 函 数 名         : Compute
 * 函数功能         : fft结果计算
 * 输入参数         : *vReal --> 信号输入、输出向量指针,保存fft计算的结果(实部)
 *                   *vImag --> 信号输入、输出向量指针,保存fft计算的结果(虚部)
 *                   samples--> 信号采样的个数
 *                    dir    -->
 * 返 回 值         : 1--> 失败   0 --> 成功
 *******************************************************************************/
static uint8_t Compute(struct MFFT *self)
{

    uint16_t j = 0;
    uint16_t i = 0;
    float temp;
    uint16_t k;

    float SR = -1.0;
    float SI = 0.0;
    float TR = 0;
    float TI = 0;
    float UR = 0;
    float UI = 0;
    uint16_t l2 = 1;
    uint16_t l1 = 0;
    uint16_t l = 0;
    uint16_t i1 = 0;
    uint8_t power = 0;
    uint16_t samples_1;

    if (self->_samples != 0x0001 << self->_power) //samples  不满足2的power次方 失败返回
    {
        return 1;
    }

    samples_1 = self->_samples - 1;
    /*时域分解  位反转*/
    for (i = 0; i < samples_1; i++)
    {
        if (i < j)
        {
            temp = self->_vReal[i];
            self->_vReal[i] = self->_vReal[j];
            self->_vReal[j] = temp;
        }
        k = (self->_samples >> 1);
        while (k <= j)
        {
            j -= k;
            k >>= 1;
        }
        j += k;
    }

    /*频域合成  */

    for (l = 0; l < self->_power; l++) //l=0 1 2 3
    {
        l1 = l2;  // 1 2 4  8
        l2 <<= 1; // 2 4 8 16
        UR = 1.0;
        UI = 0.0;
        SR = cos(PI / l1);
        SI = -sin(PI / l1);
        for (j = 0; j < l1; j++) //分组dft循环
        {
            for (i = j; i < self->_samples; i += l2) // 蝶形运算
            {
                i1 = i + l1;
                TR = UR * self->_vReal[i1] - UI * self->_vImag[i1];
                TI = UI * self->_vReal[i1] + UR * self->_vImag[i1];
                self->_vReal[i1] = self->_vReal[i] - TR;
                self->_vImag[i1] = self->_vImag[i] - TI;
                self->_vReal[i] += TR;
                self->_vImag[i] += TI;
            }
            TR = UR;
            UR = TR * SR - UI * SI;
            UI = TR * SI + UI * SR;
        }
    }

    temp = self->_samples / 2.0f;
    for (i = 0; i < self->_samples; i++) //幅值计算
    {
        self->_vReal[i] = self->_vReal[i] / temp;
        self->_vImag[i] = self->_vImag[i] / temp;
        self->_vReal[i] = sqrt(sq(self->_vReal[i])  + sq(self->_vImag[i]))*self->_revalue;  //保存幅值的值
        self->_vImag[i] =  self->_step *i;                                                  //保存对应频率的值
    }
    return 0;
}
/*******************************************************************************
 * 函 数 名         : Exponent
 * 函数功能         : 求 value 是2的多少次方
 * 输入参数         : value --> 待求的值
 * 返 回 值         : 返回 幂 值
 *******************************************************************************/
static uint8_t Exponent(struct MFFT *self, uint16_t value)
{
    uint8_t result = 0;
    while (((value >> result) & 1) != 1)
        result++;
    return (result);
}
/*******************************************************************************
 * 函 数 名         : Windowing
 * 函数功能         : 给输入的信号加窗
 * 输入参数         : windowType --> 窗口类型
 * 返 回 值         : 返回 幂 值
 *******************************************************************************/
static void Windowing(struct MFFT *self,  enum WinType windowType)
{
  uint16_t i ;
  float samplesMinusOne = (float)(self->_samples) - 1.0;
	for ( i = 0; i < (self->_samples >> 1); i++)
	{
		float indexMinusOne = (float)i;
		float ratio = (indexMinusOne / samplesMinusOne);
		float weighingFactor = 1.0;
		// Compute and record weighting factor
		switch (windowType)
		{
		case FFT_WIN_TYP_RECTANGLE: // rectangle (box car)
			weighingFactor = 1.0;
            self->_revalue = 1;
			break;
		case FFT_WIN_TYP_HAMMING: // hamming
			weighingFactor = 0.54 - (0.46 * cos(twoPi * ratio));
            self->_revalue = 1.852;
			break;
		case FFT_WIN_TYP_HANN: // hann
			weighingFactor = 0.54 * (1.0 - cos(twoPi * ratio));
            self->_revalue = 2;
			break;
		case FFT_WIN_TYP_TRIANGLE: // triangle (Bartlett)
			weighingFactor = 1.0 - ((2.0 * abs(indexMinusOne - (samplesMinusOne / 2.0))) / samplesMinusOne);
            self->_revalue = 2;
			break;
		case FFT_WIN_TYP_NUTTALL: // nuttall
			weighingFactor = 0.355768 - (0.487396 * (cos(twoPi * ratio))) + (0.144232 * (cos(fourPi * ratio))) - (0.012604 * (cos(sixPi * ratio)));
            self->_revalue = 1;
			break;
		case FFT_WIN_TYP_BLACKMAN: // blackman
			weighingFactor = 0.42323 - (0.49755 * (cos(twoPi * ratio))) + (0.07922 * (cos(fourPi * ratio)));
            self->_revalue = 2.381;
			break;
		case FFT_WIN_TYP_BLACKMAN_NUTTALL: // blackman nuttall
			weighingFactor = 0.3635819 - (0.4891775 * (cos(twoPi * ratio))) + (0.1365995 * (cos(fourPi * ratio))) - (0.0106411 * (cos(sixPi * ratio)));
            self->_revalue = 1;
			break;
		case FFT_WIN_TYP_BLACKMAN_HARRIS: // blackman harris
			weighingFactor = 0.35875 - (0.48829 * (cos(twoPi * ratio))) + (0.14128 * (cos(fourPi * ratio))) - (0.01168 * (cos(sixPi * ratio)));
            self->_revalue = 1;
			break;
		case FFT_WIN_TYP_FLT_TOP: // flat top
			weighingFactor = 0.2810639 - (0.5208972 * cos(twoPi * ratio)) + (0.1980399 * cos(fourPi * ratio));
            self->_revalue = 1;
			break;
		case FFT_WIN_TYP_WELCH: // welch
			weighingFactor = 1.0 - sq((indexMinusOne - samplesMinusOne / 2.0) / (samplesMinusOne / 2.0));
            self->_revalue = 1;
			break;
		}

        self->_vReal[i] *= weighingFactor;
        self->_vReal[self->_samples - (i + 1)] *= weighingFactor;
	}
}
/*******************************************************************************
 * 函 数 名         : DCRemoval
 * 函数功能         : 消除直流分量
 * 输入参数         :  
 * 返 回 值         : 
 *******************************************************************************/
static void DCRemoval(struct MFFT *self)
{
	/*计算信号的平均值*/
	float mean = 0;
	uint16_t i =0;
	for ( i = 0; i < self->_samples; i++)
	{
		mean += self->_vReal[i];
	}
	mean /= self->_samples;
	/*信号依次减去平均值*/
	for (i = 0; i < self->_samples; i++)
	{
		self->_vReal[i] -= mean;
	}
}
/*******************************************************************************
 * 函 数 名         : MFFT_Create
 * 函数功能         : 创建fft对象
 * 输入参数         :  
 * 返 回 值         : 返回 幂 值
 *******************************************************************************/
void MFFT_Create(struct MFFT *self)
{
    memset(self, 0, sizeof(struct MFFT));

    self->Init = Init;
    self->Compute = Compute;
    self->Exponent = Exponent;
    self-> Windowing= Windowing;
}

以上代码在几个实际产品等工程用过,可根据实际应用修改调整,后续给一些测试案列。

你可能感兴趣的:(算法,算法)