ml-fft
Version:
232 lines (226 loc) • 5.42 kB
JavaScript
/**
* Fast Fourier Transform module
* 1D-FFT/IFFT, 2D-FFT/IFFT (radix-2)
*/
var FFT = (function(){
var FFT;
if(typeof exports !== 'undefined') {
FFT = exports; // for CommonJS
} else {
FFT = {};
}
var version = {
release: '0.3.0',
date: '2013-03'
};
FFT.toString = function() {
return "version " + version.release + ", released " + version.date;
};
// core operations
var _n = 0, // order
_bitrev = null, // bit reversal table
_cstb = null; // sin/cos table
var core = {
init : function(n) {
if(n !== 0 && (n & (n - 1)) === 0) {
_n = n;
core._initArray();
core._makeBitReversalTable();
core._makeCosSinTable();
} else {
throw new Error("init: radix-2 required");
}
},
// 1D-FFT
fft1d : function(re, im) {
core.fft(re, im, 1);
},
// 1D-IFFT
ifft1d : function(re, im) {
var n = 1/_n;
core.fft(re, im, -1);
for(var i=0; i<_n; i++) {
re[i] *= n;
im[i] *= n;
}
},
// 1D-IFFT
bt1d : function(re, im) {
core.fft(re, im, -1);
},
// 2D-FFT Not very useful if the number of rows have to be equal to cols
fft2d : function(re, im) {
var tre = [],
tim = [],
i = 0;
// x-axis
for(var y=0; y<_n; y++) {
i = y*_n;
for(var x1=0; x1<_n; x1++) {
tre[x1] = re[x1 + i];
tim[x1] = im[x1 + i];
}
core.fft1d(tre, tim);
for(var x2=0; x2<_n; x2++) {
re[x2 + i] = tre[x2];
im[x2 + i] = tim[x2];
}
}
// y-axis
for(var x=0; x<_n; x++) {
for(var y1=0; y1<_n; y1++) {
i = x + y1*_n;
tre[y1] = re[i];
tim[y1] = im[i];
}
core.fft1d(tre, tim);
for(var y2=0; y2<_n; y2++) {
i = x + y2*_n;
re[i] = tre[y2];
im[i] = tim[y2];
}
}
},
// 2D-IFFT
ifft2d : function(re, im) {
var tre = [],
tim = [],
i = 0;
// x-axis
for(var y=0; y<_n; y++) {
i = y*_n;
for(var x1=0; x1<_n; x1++) {
tre[x1] = re[x1 + i];
tim[x1] = im[x1 + i];
}
core.ifft1d(tre, tim);
for(var x2=0; x2<_n; x2++) {
re[x2 + i] = tre[x2];
im[x2 + i] = tim[x2];
}
}
// y-axis
for(var x=0; x<_n; x++) {
for(var y1=0; y1<_n; y1++) {
i = x + y1*_n;
tre[y1] = re[i];
tim[y1] = im[i];
}
core.ifft1d(tre, tim);
for(var y2=0; y2<_n; y2++) {
i = x + y2*_n;
re[i] = tre[y2];
im[i] = tim[y2];
}
}
},
// core operation of FFT
fft : function(re, im, inv) {
var d, h, ik, m, tmp, wr, wi, xr, xi,
n4 = _n >> 2;
// bit reversal
for(var l=0; l<_n; l++) {
m = _bitrev[l];
if(l < m) {
tmp = re[l];
re[l] = re[m];
re[m] = tmp;
tmp = im[l];
im[l] = im[m];
im[m] = tmp;
}
}
// butterfly operation
for(var k=1; k<_n; k<<=1) {
h = 0;
d = _n/(k << 1);
for(var j=0; j<k; j++) {
wr = _cstb[h + n4];
wi = inv*_cstb[h];
for(var i=j; i<_n; i+=(k<<1)) {
ik = i + k;
xr = wr*re[ik] + wi*im[ik];
xi = wr*im[ik] - wi*re[ik];
re[ik] = re[i] - xr;
re[i] += xr;
im[ik] = im[i] - xi;
im[i] += xi;
}
h += d;
}
}
},
// initialize the array (supports TypedArray)
_initArray : function() {
if(typeof Uint32Array !== 'undefined') {
_bitrev = new Uint32Array(_n);
} else {
_bitrev = [];
}
if(typeof Float64Array !== 'undefined') {
_cstb = new Float64Array(_n*1.25);
} else {
_cstb = [];
}
},
// zero padding
_paddingZero : function() {
// TODO
},
// makes bit reversal table
_makeBitReversalTable : function() {
var i = 0,
j = 0,
k = 0;
_bitrev[0] = 0;
while(++i < _n) {
k = _n >> 1;
while(k <= j) {
j -= k;
k >>= 1;
}
j += k;
_bitrev[i] = j;
}
},
// makes trigonometiric function table
_makeCosSinTable : function() {
var n2 = _n >> 1,
n4 = _n >> 2,
n8 = _n >> 3,
n2p4 = n2 + n4,
t = Math.sin(Math.PI/_n),
dc = 2*t*t,
ds = Math.sqrt(dc*(2 - dc)),
c = _cstb[n4] = 1,
s = _cstb[0] = 0;
t = 2*dc;
for(var i=1; i<n8; i++) {
c -= dc;
dc += t*c;
s += ds;
ds -= t*s;
_cstb[i] = s;
_cstb[n4 - i] = c;
}
if(n8 !== 0) {
_cstb[n8] = Math.sqrt(0.5);
}
for(var j=0; j<n4; j++) {
_cstb[n2 - j] = _cstb[j];
}
for(var k=0; k<n2p4; k++) {
_cstb[k + n2] = -_cstb[k];
}
}
};
// aliases (public APIs)
var apis = ['init', 'fft1d', 'ifft1d', 'fft2d', 'ifft2d'];
for(var i=0; i<apis.length; i++) {
FFT[apis[i]] = core[apis[i]];
}
FFT.bt = core.bt1d;
FFT.fft = core.fft1d;
FFT.ifft = core.ifft1d;
return FFT;
}).call(this);