FFT 相关知识,强烈推荐看这篇博文:深入浅出的讲解傅里叶变换(真正的通俗易懂)
程序参考文章:
java实现快速傅里叶变换(FFT)
Java实现算法导论中快速傅里叶变换FFT递归算法
话不多说,直接上代码,。
class Complex {
private double re = 0; // the real part
private double im = 0; // the imaginary part
private int index = 0; // the index ! to find 5 max
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public double getRe() {
return re;
}
public void setRe(double re) {
this.re = re;
}
public double getIm() {
return im;
}
public void setIm(double im) {
this.im = im;
}
// create a new object with the given real and imaginary parts 构造函数。
public Complex(double real, double imag) {
re = real;
im = imag;
}
// return a string representation of the invoking Complex object
public String toString() {
if (im == 0)
return re + "";
if (re == 0)
return im + "i";
if (im < 0)
return re + " - " + (-im) + "i";
return re + " + " + im + "i";
}
// return abs/modulus/magnitude
public double abs() {
return Math.hypot(re, im);
}
// return angle/phase/argument, normalized to be between -pi and pi
public double phase() {
return Math.atan2(im, re);
}
// return a new Complex object whose value is (this + b)
public Complex plus(Complex b) {
Complex a = this; // invoking object
double real = a.re + b.re;
double imag = a.im + b.im;
return new Complex(real, imag);
}
// return a new Complex object whose value is (this - b)
public Complex minus(Complex b) {
Complex a = this;
double real = a.re - b.re;
double imag = a.im - b.im;
return new Complex(real, imag);
}
// return a new Complex object whose value is (this * b)
public Complex multiple(Complex b) {
Complex a = this;
double real = a.re * b.re - a.im * b.im;
double imag = a.re * b.im + a.im * b.re;
return new Complex(real, imag);
}
// scalar multiplication
// return a new object whose value is (this * alpha)
public Complex multiple(double alpha) {
return new Complex(alpha * re, alpha * im);
}
// return a new object whose value is (this * alpha)
public Complex scale(double alpha) {
return new Complex(alpha * re, alpha * im);
}
// return a new Complex object whose value is the conjugate of this
public Complex conjugate() {
return new Complex(re, -im);
}
// return a new Complex object whose value is the reciprocal of this
public Complex reciprocal() {
double scale = re * re + im * im;
return new Complex(re / scale, -im / scale);
}
// return the real or imaginary part
public double re() {
return re;
}
public double im() {
return im;
}
// return a / b
public Complex divides(Complex b) {
Complex a = this;
return a.multiple(b.reciprocal());
}
// return a new Complex object whose value is the complex exponential of
// this
public Complex exp() {
return new Complex(Math.exp(re) * Math.cos(im), Math.exp(re) * Math.sin(im));
}
// return a new Complex object whose value is the complex sine of this
public Complex sin() {
return new Complex(Math.sin(re) * Math.cosh(im), Math.cos(re) * Math.sinh(im));
}
// return a new Complex object whose value is the complex cosine of this
public Complex cos() {
return new Complex(Math.cos(re) * Math.cosh(im), -Math.sin(re) * Math.sinh(im));
}
// return a new Complex object whose value is the complex tangent of this
public Complex tan() {
return sin().divides(cos());
}
// a static version of plus
public static Complex plus(Complex a, Complex b) {
double real = a.re + b.re;
double imag = a.im + b.im;
return new Complex(real, imag);
}
public boolean equals(Object x) {
if (x == null)
return false;
if (this.getClass() != x.getClass())
return false;
Complex that = (Complex) x;
return (this.re == that.re) && (this.im == that.im);
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public int hashCode() {
return Objects.hash(re, im);
}
}
Complex 类有其构造函数,初始化时候需要你传入 实部和虚部。
比如这里我将一个存放在 List<>
中需要做 fft 变换的数据,转换成 Complex 类型的数据。尤其是注意,Complex 类中的 fft 方法 传入和返回的类型都是 Complex 类数组,而类数组都需要二次初始化,就像下面我写的那样。
/**
* List<> 数据转换 Complex[] 类型数据,以供其做 fft
* @param Data 传入的数据,将其传入 实部
* @return result 返回经傅里叶变换后的数据,类型为 Complex[]
*/
public static Complex[] list2complex(List<Float> Data) {
Complex[] complex = new Complex[Data.size()];
//类数组需要二次初始化,切记,不然会造成空指针情况
for (int index = 0; index < complex.length; index++) {
complex[index] = new Complex(0, 0);
}
for (int index = 0; index < complex.length; index++) {
//将时域的 y 传入实部即可,虚部默认为 0,注意这一步需要结合你的需要来做。
complex[index].setRe( (double) Data.get(index) );
}
return complex;
}
对数据做 fft 变换这样使用即可。我这里将 Complex 类和调用它的代码放在了同一个 .java
文件内,所以可以像下面这样直接调用 fft。如果你单独将 Complex 类放在了一个 .java
文件下,你需要这样 Complex.fft()
,也很简单不是嘛?
Complex[] dataFFT = fft( list2complex(data2T) );
另外如果熟悉 python ,推荐看这篇博文来加深使用 fft 的理解:使用python(scipy和numpy)实现快速傅里叶变换(FFT)最详细教程
这里我用 MPAndroidChart 画了两个图验证算法正确性:
这是时域图:直接用上面Python 文章中的那个了(偷懒~)
y=7 * np.sin(2 * np.pi * 200 * t) + 5 * np.sin(2 * np.pi * 400 * t)+3 * np.sin(2 * np.pi * 600 * t)
原始信号的三个正弦波的频率分别为,200Hz、400Hz、600Hz,最大频率为600赫兹。根据采样定理,fs至少是600赫兹的2倍,这里选择1400赫兹,即在一秒内选择1400个点。
这是频域图:
学过信号系统都知道,明显很对嘛~(我没学过我也知道很对)
哦了,fft 就这,打完收工