我正在进行Android项目的开发。我需要使用FFT算法来处理Android加速度计数据。是否在Android SDK中有FFT库可用?
我正在进行Android项目的开发。我需要使用FFT算法来处理Android加速度计数据。是否在Android SDK中有FFT库可用?
public 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))
throw new RuntimeException("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);
}
}
public 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;
}
}
}
}
}
警告:此代码似乎派生自这里,并带有GPLv2许可证。
使用位于https://www.ee.columbia.edu/~ronw/code/MEAPsoft/doc/html/FFT_8java-source.html的类:
简要说明:调用fft()函数,提供幅度数据x和零数组y,函数返回后,第一个答案将为a[0]=x[0]^2+y[0]^2。
完整说明:FFT是一个复杂的变换,它需要N个复数并产生N个复数。因此,x[0]是第一个数字的实部,y[0]是复数部分。这个函数在原地计算,所以当函数返回时,x和y将具有变换的实部和复数部分。
一个典型的用途是计算音频的功率谱。您的音频样本只有实部,您的复数部分为0。要计算功率谱,您需要添加实部和复数部分的平方P[0]=x[0]^2+y[0]^2。
还要注意,傅里叶变换在应用于实数时,结果呈对称形式(x[0]==x[x.lenth-1])。x[x.length / 2]处的数据具有频率为f=0Hz的数据。x[0]==x[x.length-1]具有与采样率相等的频率数据(例如,如果您的采样速率为44000Hz,则f[0]表示22kHz)。
完整过程:
然后根据您的口味调整固定数字。
数字512定义了采样窗口,我不会解释。只需避免将其减少太多。
数字1024必须始终是上一个数字的两倍。
数字50定义了更新速率。如果您的采样率为每秒44000个样本,则更新速率为:R=44000/1024/50 = 0.85秒。
使用这个class(EricLarch的答案来源之一)。
用法说明
此函数将用FFT输出替换您的输入数组。
输入
例如,如果您的输入为 (1+8i, 2+3j, 7-i, -10-3i)
输出
public double[] fftCalculator(double[] re, double[] im) {
if (re.length != im.length) return null;
FFT fft = new FFT(re.length);
fft.fft(re, im);
double[] fftMag = new double[re.length];
for (int i = 0; i < re.length; i++) {
fftMag[i] = Math.pow(re[i], 2) + Math.pow(im[i], 2);
}
return fftMag;
}
JTransforms
,它在github上维护这里并且作为Maven插件这里可用。compile group: 'com.github.wendykierp', name: 'JTransforms', version: '3.1'
dependencies {
...
implementation 'com.github.wendykierp:JTransforms:3.1'
}
@J Wang 您的输出幅值似乎比您链接的线程中给出的答案更好,但仍然是幅值平方...一个复数的幅值
z = a + ib
|z|=sqrt(a^2+b^2)
a_(i+N/2) = -a_(i),
使用b_(i) = a_(i+N/2)
表示他们表格中的复数部分在输出表格的第二部分。
即对于实数输入表格,输出表格的后一半是实数的共轭...
所以z = a-ia
得到一个大小。
|z|=sqrt(2a^2) = sqrt(2)a
因此值得注意的是缩放因子...我建议查阅书籍或维基百科以确保。
不幸的是,顶部答案仅适用于大小为2的幂的数组,这非常有限。
我使用了Jtransforms库,它完美地工作,你可以将其与Matlab使用的函数进行比较。
这里是我的代码,带有注释引用如何转换任何信号并获取频率幅度的Matlab(https://la.mathworks.com/help/matlab/ref/fft.html)
首先,在build.gradle(app)中添加以下内容
implementation 'com.github.wendykierp:JTransforms:3.1'
double Fs = 8000;
double T = 1/Fs;
int L = 1600;
double freq = 338;
double sinValue_re_im[] = new double[L*2]; // because FFT takes an array where its positions alternate between real and imaginary
for( int i = 0; i < L; i++)
{
sinValue_re_im[2*i] = Math.sin( 2*Math.PI*freq*(i * T) ); // real part
sinValue_re_im[2*i+1] = 0; //imaginary part
}
// matlab
// tf = fft(y1);
DoubleFFT_1D fft = new DoubleFFT_1D(L);
fft.complexForward(sinValue_re_im);
double[] tf = sinValue_re_im.clone();
// matlab
// P2 = abs(tf/L);
double[] P2 = new double[L];
for(int i=0; i<L; i++){
double re = tf[2*i]/L;
double im = tf[2*i+1]/L;
P2[i] = sqrt(re*re+im*im);
}
// P1 = P2(1:L/2+1);
double[] P1 = new double[L/2]; // single-sided: the second half of P2 has the same values as the first half
System.arraycopy(P2, 0, P1, 0, L/2);
// P1(2:end-1) = 2*P1(2:end-1);
System.arraycopy(P1, 1, P1, 1, L/2-2);
for(int i=1; i<P1.length-1; i++){
P1[i] = 2*P1[i];
}
// f = Fs*(0:(L/2))/L;
double[] f = new double[L/2 + 1];
for(int i=0; i<L/2+1;i++){
f[i] = Fs*((double) i)/L;
}