傅里叶变换是将时域信号变换为频域信号的一种方式,我主要用它来做两件事情:
1 求一段数据的周期性。
2 通过傅里叶变换及其逆变换,进行低通滤波(去躁)。
首先需要做几点说明:
1.快速傅里叶变换是离散傅里叶变换的快速算法,当数据源较大时(大于1000),快速傅里叶变换有明显优势。
2.快速傅里叶变换的信号源长度必须是2^N(2的N次方),如果不是,则需要在末尾补0。而离散傅里叶变换没有这个要求。因此当信号源点数是2^N时,离散傅里叶变换和快速傅里叶变换的计算结果是一致的。如果长度不是2^N时,两个算法的结果是不同的,离散傅里叶变换(慢方法)更可信。
3.下面算法中快速傅里叶变换是根据原理自创的,没有采用现在常用的位逆序的方法。
4.下面所有方法均经过matlab验证。
过多的理论的东西不深究,我也不是大学老师,这里直接给出离散傅里叶变换和快速傅里叶变换的C#代码:
public class FFTHelp
{
///
/// 快速傅里叶变换(当信号源长度等于2^N时,结果与dft相同,当长度不等于2^N时,先在尾部补零,所以计算结果与dft不同)
///
///
///
public static double[] fft(double[] array)
{
array = FillArray(array);
int N = array.Length;
//生成WN表,以免运行时进行重复计算
Complex[] WN = new Complex[N];
for (int i = 0; i < N / 2; i++)
{
WN[i] = new Complex(Math.Cos(2 * Math.PI / N * i), -1 * Math.Sin(2 * Math.PI / N * i));
}
int stageNum = ReLog2N(N);
int[] stage = new int[stageNum];
stage[0] = 0;
for (int i = 1; i < stageNum; i++)
{
stage[i] = Convert.ToInt32(Math.Round(Math.Pow(2, stageNum - 1 - i)));
}
//重排数据
Complex[] Register = new Complex[N];
for (int i = 0; i < N; i++)
{
int index = ReArrange(i, stageNum);
Register[i] = new Complex(array[index], 0);
}
//蝶形运算
Complex[] p = new Complex[N];
Complex[] q = new Complex[N];
Complex[] w = new Complex[N];
int group = N;
for (int i = 0; i < stageNum; i++)
{
group = group >> 1;
int subnum = N / group;
for (int k = 0; k < group; k++)
{
for (int n = 0; n < subnum / 2; n++)
{
p[k * subnum + n] = p[k * subnum + n + subnum / 2] = Register[k * subnum + n];
w[k * subnum + n] = WN[stage[i] * n];
}
for (int n = subnum / 2; n < subnum; n++)
{
q[k * subnum + n] = q[k * subnum + n - subnum / 2] = Register[k * subnum + n];
w[k * subnum + n] = -1 * w[n - subnum / 2];
}
}
for (int k = 0; k < N; k++)
{
Register[k] = p[k] + w[k] * q[k];
}
}
double[] dest = new double[N];
for (int k = 0; k < N; k++)
{
dest[k] = Register[k].Modulus();
}
return dest;
}
///
/// 离散傅里叶变换
///
///
///
public static double[] dft(double[] array)
{
//array = FillArray(array);
int N = array.Length;
//重排数据
Complex[] Register = new Complex[N];
Complex[] dest = new Complex[N];
for (int i = 0; i < N; i++)
{
Register[i] = new Complex(array[i], 0);
}
for (int k = 0; k < N; k++)
{
Complex sum = new Complex();
for (int n = 0; n < N; n++)
{
sum += Register[n] * (new Complex(Math.Cos(2 * Math.PI / N * n * k), -1 * Math.Sin(2 * Math.PI / N * n * k)));
}
dest[k] = sum;
}
double[] dest2 = new double[N];
for (int k = 0; k < N; k++)
{
dest2[k] = dest[k].Modulus();
}
return dest2;
}
///
/// 离散傅里叶逆变换
///
///
///
public static double[] idft(double[] array)
{
//array = FillArray(array);
int N = array.Length;
//重排数据
Complex[] Register = new Complex[N];
Complex[] dest = new Complex[N];
for (int i = 0; i < N; i++)
{
Register[i] = new Complex(array[i], 0);
}
for (int k = 0; k < N; k++)
{
Complex sum = new Complex();
for (int n = 0; n < N; n++)
{
sum += Register[n] * (new Complex(Math.Cos(2 * Math.PI / N * n * k), -1 * Math.Sin(2 * Math.PI / N * n * k)));
}
dest[k] = sum / N;
}
double[] dest2 = new double[N];
for (int k = 0; k < N; k++)
{
dest2[k] = dest[k].Modulus();
}
return dest2;
}
///
/// 离散傅里叶变换
///
///
///
public static Complex[] dft(Complex[] array)
{
//array = FillArray(array);
int N = array.Length;
//重排数据
Complex[] Register = array;
Complex[] dest = new Complex[N];
//for (int i = 0; i < N; i++)
//{
// Register[i] = new Complex(array[i], 0);
//}
for (int k = 0; k < N; k++)
{
Complex sum = new Complex();
for (int n = 0; n < N; n++)
{
sum += Register[n] * (new Complex(Math.Cos(2 * Math.PI / N * n * k), -1 * Math.Sin(2 * Math.PI / N * n * k)));
}
dest[k] = sum;
}
//double[] dest2 = new double[N];
//for (int k = 0; k < N; k++)
//{
// dest2[k] = dest[k].Modulus();
//}
//return dest2;
return dest;
}
///
/// 离散傅里叶逆变换
///
///
///
public static Complex[] idft(Complex[] array)
{
//array = FillArray(array);
int N = array.Length;
//重排数据
Complex[] Register = array;
Complex[] dest = new Complex[N];
//for (int i = 0; i < N; i++)
//{
// Register[i] = new Complex(array[i], 0);
//}
for (int k = 0; k < N; k++)
{
Complex sum = new Complex();
for (int n = 0; n < N; n++)
{
sum += Register[n] * (new Complex(Math.Cos(-2 * Math.PI / N * n * k), -1 * Math.Sin(-2 * Math.PI / N * n * k)));
}
dest[k] = sum / N;
}
//double[] dest2 = new double[N];
//for (int k = 0; k < N; k++)
//{
// dest2[k] = dest[k].Modulus();
//}
//return dest2;
return dest;
}
///
/// 利用傅里叶变换实现的低通滤波(消除高频信号)
///
/// 信号源
/// 截止频率
///
public static double[] FFTFilter(double[] array, int n)
{
if (array == null || n <= 0 || array.Length <= n)
return array;
int N = array.Length;
Complex[] Register = new Complex[N];
for (int i = 0; i < N; i++)
{
Register[i] = new Complex(array[i], 0);
}
Complex[] dest = dft(Register);
for (int i = 0; i < dest.Length; i++)
{
if (i > n && i < dest.Length - n)
dest[i].Im = dest[i].Re = 0;
}
Complex[] dest2 = idft(dest);
double[] result = new double[dest2.Length];
for (int i = 0; i < dest2.Length; i++)
{
result[i] = dest2[i].Modulus();
}
return result;
}
private static double[] FillArray(double[] array)
{
//补零后长度
int relog2N = ReLog2N(array.Length);
int bitlenghth = relog2N;
int N = 0x01 << bitlenghth;
double[] ret = new double[N];
for (int i = 0; i < N; i++)
{
if (i < array.Length)
ret[i] = array[i];
else ret[i] = 0;
}
return ret;
}
// 获取扩展长度后的幂次
// 由于fft要求长度为2^n,所以用此函数来获取所需长度
public static int ReLog2N(int count)
{
int log2N = 0;
uint mask = 0x80000000;
for (int i = 0; i < 32; i++)
{
if (0 != ((mask >> i) & count))
{
if ((mask >> i) == count) log2N = 31 - i;
else log2N = 31 - i + 1;
break;
}
}
return log2N;
}
// 获取按位逆序,bitlenght为数据长度
// fft函数内使用
private static int ReArrange(int dat, int bitlenght)
{
int ret = 0;
for (int i = 0; i < bitlenght; i++)
{
if (0 != (dat & (0x01 << i))) ret |= ((0x01 << (bitlenght - 1)) >> i);
}
return ret;
}
}
///
/// 表示一个复数
///
public class Complex
{
public double Re;
public double Im;
public Complex()
{
Re = 0;
Im = 0;
}
public Complex(double re)
{
Re = re;
Im = 0;
}
public Complex(double re, double im)
{
Re = re;
Im = im;
}
public double Modulus()
{
return Math.Sqrt(Re * Re + Im * Im);
}
public override string ToString()
{
string retStr;
if (Math.Abs(Im) < 0.0001) retStr = Re.ToString("f4");
else if (Math.Abs(Re) < 0.0001)
{
if (Im > 0) retStr = "j" + Im.ToString("f4");
else retStr = "- j" + (0 - Im).ToString("f4");
}
else
{
if (Im > 0) retStr = Re.ToString("f4") + "+ j" + Im.ToString("f4");
else retStr = Re.ToString("f4") + "- j" + (0 - Im).ToString("f4");
}
retStr += " ";
return retStr;
}
//操作符重载
public static Complex operator +(Complex c1, Complex c2)
{
return new Complex(c1.Re + c2.Re, c1.Im + c2.Im);
}
public static Complex operator +(double d, Complex c)
{
return new Complex(d + c.Re, c.Im);
}
public static Complex operator -(Complex c1, Complex c2)
{
return new Complex(c1.Re - c2.Re, c1.Im - c2.Im);
}
public static Complex operator -(double d, Complex c)
{
return new Complex(d - c.Re, -c.Im);
}
public static Complex operator *(Complex c1, Complex c2)
{
return new Complex(c1.Re * c2.Re - c1.Im * c2.Im, c1.Re * c2.Im + c2.Re * c1.Im);
}
public static Complex operator *(Complex c, double d)
{
return new Complex(c.Re * d, c.Im * d);
}
public static Complex operator *(double d, Complex c)
{
return new Complex(c.Re * d, c.Im * d);
}
public static Complex operator /(Complex c, double d)
{
return new Complex(c.Re / d, c.Im / d);
}
public static Complex operator /(double d, Complex c)
{
double temp = d / (c.Re * c.Re + c.Im * c.Im);
return new Complex(c.Re * temp, -c.Im * temp);
}
public static Complex operator /(Complex c1, Complex c2)
{
double temp = 1 / (c2.Re * c2.Re + c2.Im * c2.Im);
return new Complex((c1.Re * c2.Re + c1.Im * c2.Im) * temp, (-c1.Re * c2.Im + c2.Re * c1.Im) * temp);
}
}
如果想求一个数组的周期性,调用dft函数后,取变换后数据(去掉第一个数)的最大值索引,就是数组的频率,周期是频率的倒数。例如,数据源为数组a, b=dft(a),频率=maxindex(b),周期数T=a.length/频率。
FFTFilter函数可直接进行低通滤波。