C 语言 – FFT
C 语言 – FFT

C 语言 – FFT

算法笔记

C 语言实现离散傅利叶变换(DFT)代码如下:

/*********************************************************************************************************
* 函数名称: DFT
* 函数功能: 离散傅利叶变换(DFT)
* 输入参数: x:双精度实型一维数组,长度为 n。存放要变换数据的实部。
*            y:双精度实型一维数组,长度为 n。存放要变换数据的虚部。
*            a:双精度实型一维数组,长度为 n。存放变换结果的实部。
*            b:双精度实型一维数组,长度为 n。存放变换结果的虚部。
*            n:整形变量。数据长度
*            sign:整形变量。当 sign=1 时,子函数 DFT() 计算离散傅利叶正变换;当 sign=-1 时,做反变换
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年08月14日
* 注    意:
*********************************************************************************************************/
void DFT(double* x, double* y, double* a, double* b, int n, int sign)
{
  int i, k;
  double c, d, q, w, s;
  q = 6.28318530718 / (double)n;
  for (k = 0; k < n; k++)
  {
    w = k * q;
    a[k] = b[k] = 0.0;
    for (i = 0; i < n; i++)
    {
      d = i * w;
      c = cos(d);
      s = sin(d) * sign;
      a[k] += c * x[i] + s * y[i];
      b[k] += c * y[i] - s * x[i];
    }
  }
  if (sign == -1)
  {
    c = 1.0 / (double)n;
    for (k = 0; k < n; k++)
    {
      a[k] = c * a[k];
      b[k] = c * b[k];
    }
  }
}

C 语言实现快速傅利叶变换(FFT),代码如下:

/*********************************************************************************************************
* 函数名称: FFT
* 函数功能: 快速傅利叶变换
* 输入参数: x:双精度实型一维数组,长度为 n。开始时存放要变换数据的实部,最后存放变换结果的实部。
*            y:双精度实型一维数组,长度为 n。开始时存放要变换数据的虚部,最后存放变换结果的虚部。
*            n:整形变量。数据长度。必须是 2 的整数次幂,即 n=2^m 。
*            sign:整形变量。当 sign=1 时,子函数 FFT() 计算离散傅利叶正变换;当 sign=-1 时,做反变换
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年08月14日
* 注    意:
*********************************************************************************************************/
void FFT(double* x, double* y, int n, int sign)
{
  int i, j, k, l, m, n1, n2;
  double c, c1, e, s, s1, t, tr, ti;
  for (j = 1, i = 1; i < 16; i++)
  {
    m = i;
    j = 2 * j;
    if (j == n)
    {
      break;
    }
  }
  n1 = n - 1;
  for (j = 0, i = 0; i < n1; i++)
  {
    if (i < j)
    {
      tr = x[j];
      ti = y[j];
      x[j] = x[i];
      y[j] = y[i];
      x[i] = tr;
      y[i] = ti;
    }
    k = n / 2;
    while (k < (j + 1))
    {
      j = j - k;
      k = k / 2;
    }
    j = j + k;
  }
  n1 = 1;
  for (l = 1; l <= m; l++)
  {
    n1 = 2 * n1;
    n2 = n1 / 2;
    e = 3.14159265359 / n2;
    c = 1.0;
    s = 0.0;
    c1 = cos(e);
    s1 = -sign * sin(e);
    for (j = 0; j < n2; j++)
    {
      for (i = j; i < n; i += n1)
      {
        k = i + n2;
        tr = c * x[k] - s * y[k];
        ti = c * y[k] + s * x[k];
        x[k] = x[i] - tr;
        y[k] = y[i] - ti;
        x[i] = x[i] + tr;
        y[i] = y[i] + ti;
      }
      t = c;
      c = c * c1 - s * s1;
      s = t * s1 + s * c1;
    }
  }
  if (sign == -1)
  {
    for (i = 0; i < n; i++)
    {
      x[i] /= n;
      y[i] /= n;
    }
  }
}

分裂基快速傅利叶变换代码如下。

/*********************************************************************************************************
* 函数名称: SRFFT
* 函数功能: 分裂基快速傅利叶变换
* 输入参数: x:双精度实型一维数组,长度为 n。开始时存放要变换数据的实部,最后存放变换结果的实部。
*            y:双精度实型一维数组,长度为 n。开始时存放要变换数据的虚部,最后存放变换结果的虚部。
*            n:整形变量。数据长度。必须是 2 的整数次幂,即 n=2^m 。
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年08月18日
* 注    意:
*********************************************************************************************************/
void SRFFT(double* x, double* y, int n)
{
  int i, j, k, m, i1, i2, i3, n1, n2, n4, id, is;
  double a, e, a3, r1, r2;
  double s1, s2, s3, cc1, cc3, ss1, ss3;
  for (j = 1, i = 1; i < 16; i++)
  {
    m = i;
    j = 2 * j;
    if (j == n)
    {
      break;
    }
  }
  n2 = 2 * n;
  for (k = 1; k < m; k++)
  {
    n2 = n2 / 2;
    n4 = n2 / 4;
    e = 6.28318530718 / n2;
    a = 0;
    for (j = 0; j < n4; j++)
    {
      a3 = 3 * a;
      cc1 = cos(a);
      ss1 = sin(a);
      cc3 = cos(a3);
      ss3 = sin(a3);
      a = ((double)j + 1.0) * e;
      is = j;
      id = 2 * n2;
      do
      {
        for (i = is; i < (n - 1); i = i + id)
        {
          i1 = i + n4;
          i2 = i1 + n4;
          i3 = i2 + n4;
          r1 = x[i] - x[i2];
          x[i] = x[i] + x[i2];
          r2 = x[i1] - x[i3];
          x[i1] = x[i1] + x[i3];
          s1 = y[i] - y[i2];
          y[i] = y[i] + y[i2];
          s2 = y[i1] - y[i3];
          y[i1] = y[i1] + y[i3];
          s3 = r1 - s2;
          r1 = r1 + s2;
          s2 = r2 - s1;
          r2 = r2 + s1;
          x[i2] = r1 * cc1 - s2 * ss1;
          y[i2] = -s2 * cc1 - r1 * ss1;
          x[i3] = s3 * cc3 + r2 * ss3;
          y[i3] = r2 * cc3 - s3 * ss3;
        }
        is = 2 * id - n2 + j;
        id = 4 * id;
      }while(is < (n - 1));
    }
  }
  is = 0;
  id = 4;
  do
  {
    for (i = is; i < n; i = i + id)
    {
      i1 = i + 1;
      r1 = x[i];
      r2 = y[i];
      x[i] = r1 + x[i1];
      y[i] = r2 + y[i1];
      x[i1] = r1 - x[i1];
      y[i1] = r2 - y[i1];
    }
    is = 2 * id - 2;
    id = 4 * id;
  }while(is < (n - 1));
  n1 = n - 1;
  for (j = 0, i = 0; i < n1; i++)
  {
    if (i < j)
    {
      r1 = x[j];
      s1 = y[j];
      x[j] = x[i];
      y[j] = y[i];
      x[i] = r1;
      y[i] = s1;
    }
    k = n / 2;
    while (k < (j + 1))
    {
      j = j - k;
      k = k / 2;
    }
    j = j + k;
  }
}

实序列快速傅利叶变换(一),代码如下:

/*********************************************************************************************************
* 函数名称: RFFT
* 函数功能: 实序列快速傅利叶变换(一)
* 输入参数: x:双精度实型一维数组,长度为 n。开始时存放要变换的实数据,最后存放变换结果的前 (n/2)+1 个值。
*               存储顺序为:[Re(0),Re(1),...,Re(n/2),Im((n/2)-1),...,Im(1)]。
*               其中 Re(0)=X(0),Re(n/2)=X(n/2)。根据 X(k) 的共轭对称性,很容易写出后半部分的值
*            n:整形变量。数据长度。必须是 2 的整数次幂,即 n=2^m 。
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年08月18日
* 注    意:
*********************************************************************************************************/
void RFFT(double* x, double n)
{
  int i, j, k, m, i1, i2, i3, i4, n1, n2, n4;
  double a, e, cc, ss, xt, t1, t2;
  for (j = 1, i = 1; i < 16; i++)
  {
    m = i;
    j = 2 * j;
    if (j == n)
    {
      break;
    }
  }
  n1 = n - 1;
  for (j = 0, i = 0; i < n1; i++)
  {
    if (i < j)
    {
      xt = x[j];
      x[j] = x[i];
      x[i] = xt;
    }
    k = n / 2;
    while (k < (j + 1))
    {
      j = j - k;
      k = k / 2;
    }
    j = j + k;
  }
  for (i = 0; i < n; i += 2)
  {
    xt = x[i];
    x[i] = xt + x[i + 1];
    x[i + 1] = xt - x[i + 1];
  }
  n2 = 1;
  for (k = 2; k <= m; k++)
  {
    n4 = n2;
    n2 = 2 * n4;
    n1 = 2 * n2;
    e = 6.28318530718 / n1;
    for (i = 0; i < n; i += n1)
    {
      xt = x[i];
      x[i] = xt + x[i + n2];
      x[i + n2] = xt - x[i + n2];
      x[i + n2 + n4] = -x[i + n2 + n4];
      a = e;
      for (j = 1; j <= (n4 - 1); j++)
      {
        i1 = i + j;
        i2 = i - j + n2;
        i3 = i + j + n2;
        i4 = i - j + n1;
        cc = cos(a);
        ss = sin(a);
        a = a + e;
        t1 = cc * x[i3] + ss * x[i4];
        t2 = ss * x[i3] - cc * x[i4];
        x[i4] = x[i2] - t2;
        x[i3] = -x[i2] - t2;
        x[i2] = x[i1] - t1;
        x[i1] = x[i1] + t1;
      }
    }
  }
}

实序列快速傅利叶变换(二),代码如下:

/*********************************************************************************************************
* 函数名称: RLFFT
* 函数功能: 实序列快速傅利叶变换(二)
* 输入参数: x:双精度实型一维数组,长度为 n。开始时存放要变换的实数据,最后存放变换结果的前 (n/2)+1 个值。
*               存储顺序为:[Re(0),Re(1),...,Re(n/2),Im((n/2)-1),...,Im(1)]。
*               其中 Re(0)=X(0),Re(n/2)=X(n/2)。根据 X(k) 的共轭对称性,很容易写出后半部分的值
*            n:整形变量。数据长度。必须是 2 的整数次幂,即 n=2^m 。
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年08月18日
* 注    意:
*********************************************************************************************************/
void RLFFT(double* x, int n)
{
  int i, n1;
  double a, c, e, s, fr, fi, gr, gi, *f, *g;
  f = malloc((n / 2) * sizeof(double));
  g = malloc((n / 2) * sizeof(double));
  n1 = n/ 2;
  e = 3.141592653589793 / n1;
  for (i = 0; i < n1; i++)
  {
    f[i] = x[2 * i];
    g[i] = x[2 * i + 1];
  }
  FFT(f, g, n1, 1);
  x[0] = f[0] + g[0];
  x[n1] = f[0] - g[0];
  for (i = 1; i < n1; i++)
  {
    fr = (f[i] + f[n1 - i]) / 2;
    fi = (g[i] - g[n1 - i]) / 2;
    gr = (g[n1 - i] + g[i]) / 2;
    gi = (f[n1 - i] - f[i]) / 2;
    a = i * e;
    c = cos(a);
    s = sin(a);
    x[i] = fr + c * gr + s * gi;
    x[n - i] = fi + c * gi - s * gr;
  }
  free(f);
  free(g);
}

Chirp Z 变换,代码如下:

#include "math.h"
#include "stdlib.h"
#include "malloc.h"
/*********************************************************************************************************
* 函数名称: CZT
* 函数功能: Chirp Z 变换
* 输入参数: xr:双精度实型一维数组,其长度大于或等于(n + m - 1),且是 2 的 整数次幂。
*                开始时存放输入数据的实部,最后存放变换结果的实部。
*            xi:双精度实型一维数组,其长度大于或等于(n + m - 1),且是 2 的 整数次幂。
*                开始时存放输入数据的虚部,最后存放变换结果的虚部。
*            n:整形变量。输入数据的长度。
*            m:整形变量。输出数据的长度,即频率采样点数。
*            f1:双精度实型变量。起始数字频率,单位为 Hz-s。
*            f2:双精度实型变量。终止数字频率。单位为 Hz-s。
* 输出参数: void
* 返 回 值: void
* 创建日期: 2023年08月29日
* 注    意: 
*********************************************************************************************************/
void CZT(double* xr, double* xi, int n, int m, double f1, double f2)
{
  int i, j, n1, n2, len;
  double e, t, ar, ai, ph, pi, tr, ti, *wr, *wr1, *wi, *wi1;
  len = n + m - 1;
  for (j = 1, i = 1; i < 16; i++)
  {
    j = 2 * j;
    if (j >= len)
    {
      len = j;
      break;
    }
  }
  wr = malloc(len * sizeof(double));
  wi = malloc(len * sizeof(double));
  wr1 = malloc(len * sizeof(double));
  wi1 = malloc(len * sizeof(double));
  pi = 3.14159265358979;
  ph = 2.0 * pi * (f2 - f1)/(m - 1);
  n1 = (n >= m)? n:m;
  for (i = 0; i < n1; i++)
  {
    e = ph * i * i / 2.0;
    wr[i] = cos(e);
    wi[i] = sin(e);
    wr1[i] = wr[i];
    wi1[i] = -wi[i];
  }
  n2 = len - n + 1;
  for (i = m; i < n2; i++)
  {
    wr[i] = 0.0;
    wi[i] = 0.0;
  }
  for (i = n2; i < len; i++)
  {
    j = len - i;
    wr[i] = wr[j];
    wi[i] = wi[j];
  }
  FFT(wr, wi, len, 1);
  ph = -2.0 * pi * f1;
  for (i = 0; i < n; i++)
  {
    e = ph * i;
    ar = cos(e);
    ai = sin(e);
    tr = ar * wr1[i] - ai * wi1[i];
    ti = ai * wr1[i] + ar * wi1[i];
    t = xr[i] * tr - xi[i] * ti;
    xi[i] = xr[i] * ti + xi[i] * tr;
    xr[i] = t;
  }
  for (i = n; i < len; i++)
  {
    xr[i] = 0.0;
    xi[i] = 0.0;
  }
  FFT(xr, xi, len, 1);
  for (i = 0; i < len; i++)
  {
    tr = xr[i] * wr[i] - xi[i] * wi[i];
    xi[i] = xr[i] * wi[i] + xi[i] * wr[i];
    xr[i] = tr;
  }
  FFT(xr, xi, len, -1);
  for (i = 0; i < m; i++)
  {
    tr = xr[i] * wr1[i] - xi[i] * wi1[i];
    xi[i] = xr[i] * wi1[i] + xi[i] * wr1[i];
    xr[i] = tr;
  }
  free(wr);
  free(wi);
  free(wr1);
  free(wi1);
}