diff --git a/README.md b/README.md index e61b143..6ec3206 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,67 @@ # fft-js Pure Node.js implementation of the Fast Fourier Transform (Cooley-Tukey Method). -# Example +# Simple Example - var fft = require('fft-js'), + var fft = require('fft-js').fft, signal = [1,0,1,0]; var coef = fft(signal); console.log(coef); +# Frequency/Magnitude Example + + var fft = require('fft-js').fft, + fftUtil = require('fft-js').util, + signal = [1,0,1,0]; + + var coef = fft(signal); + + var frequencies = fftUtil.fftFreq(coef, 8000), // Sample rate and coef is just used for length + magnitudes = fftUtil.fftMag(coef); + + var both = frequencies.map(function (f, ix) { + return {frequency: f, magnitude: magnitudes[ix]}; + }); + + console.log(coef); + +# Command Line + +For testing, you can run from the command line. Input is assumed to be from standard input and contain +a comma-delimited list of real numbers. + +Command: + node fft.js < test/signal8.js + +Console: + + Signal: [ 1, 1, 1, 1, 0, 0, 0, 0 ] + FFT: [ [ 4, 0 ], + [ 1, -2.414213562373095 ], + [ 0, 0 ], + [ 1, -0.4142135623730949 ], + [ 0, 0 ], + [ 0.9999999999999999, 0.4142135623730949 ], + [ 0, 0 ], + [ 0.9999999999999997, 2.414213562373095 ] ] + FFT Magnitudes: [ 4, + 2.613125929752753, + 0, + 1.0823922002923938, + 0, + 1.0823922002923938, + 0, + 2.6131259297527527 ] + FFT Frequencies: [ 0, 62.5, 125, 187.5, 250, 312.5, 375, 437.5 ] + +# Testing + +See `test/test.js`. Using Mocha: + + mocha + # License The MIT License (MIT) diff --git a/fft.js b/fft.js index 6b29a48..f42c6f2 100644 --- a/fft.js +++ b/fft.js @@ -6,7 +6,8 @@ * This code is not designed to be highly optimized but as an educational * tool to understand the Fast Fourier Transform. \*===========================================================================*/ -var fft = require('./'); +var fft = require('./').fft, + fftutil = require('./').util; process.stdin.on('data', function (data) { var signal = data.toString().split(',').map(parseFloat); @@ -14,4 +15,10 @@ process.stdin.on('data', function (data) { var complexCoef = fft(signal); console.log('FFT: ', complexCoef); + + var magnitudes = fftutil.fftMag(complexCoef); + console.log('FFT Magnitudes: ', magnitudes); + + var frequencies = fftutil.fftFreq(complexCoef, 1000); + console.log('FFT Frequencies: ', frequencies); }); diff --git a/index.js b/index.js index 2adac4a..0bee01b 100644 --- a/index.js +++ b/index.js @@ -6,4 +6,7 @@ * This code is not designed to be highly optimized but as an educational * tool to understand the Fast Fourier Transform. \*===========================================================================*/ -module.exports = require('./src/fft'); +module.exports = { + fft: require('./src/fft'), + util: require('./src/fftutil') +}; diff --git a/src/complex.js b/src/complex.js new file mode 100644 index 0000000..2c003b9 --- /dev/null +++ b/src/complex.js @@ -0,0 +1,46 @@ +//------------------------------------------------- +// Add two complex numbers +//------------------------------------------------- +var complexAdd = function (a, b) +{ + return [a[0] + b[0], a[1] + b[1]]; +}; + +//------------------------------------------------- +// Subtract two complex numbers +//------------------------------------------------- +var complexSubtract = function (a, b) +{ + return [a[0] - b[0], a[1] - b[1]]; +}; + +//------------------------------------------------- +// Multiply two complex numbers +// +// (a + bi) * (c + di) = (ac - bd) + (ad + bc)i +//------------------------------------------------- +var complexMultiply = function (a, b) +{ + return [(a[0] * b[0] - a[1] * b[1]), + (a[0] * b[1] + a[1] * b[0])]; +}; + +//------------------------------------------------- +// Calculate |a + bi| +// +// sqrt(a*a + b*b) +//------------------------------------------------- +var complexMagnitude = function (c) +{ + return Math.sqrt(c[0]*c[0] + c[1]*c[1]); +}; + +//------------------------------------------------- +// Exports +//------------------------------------------------- +module.exports = { + add: complexAdd, + subtract: complexSubtract, + multiply: complexMultiply, + magnitude: complexMagnitude +}; diff --git a/src/fft.js b/src/fft.js index 5828b22..83601e6 100644 --- a/src/fft.js +++ b/src/fft.js @@ -22,33 +22,7 @@ // The following code assumes a complex number is // an array: [real, imaginary] //------------------------------------------------- - -//------------------------------------------------- -// Add two complex numbers -//------------------------------------------------- -var complexAdd = function (a, b) -{ - return [a[0] + b[0], a[1] + b[1]]; -}; - -//------------------------------------------------- -// Subtract two complex numbers -//------------------------------------------------- -var complexSubtract = function (a, b) -{ - return [a[0] - b[0], a[1] - b[1]]; -}; - -//------------------------------------------------- -// Multiply two complex numbers -// -// (a + bi) * (c + di) = (ac - bd) + (ad + bc)i -//------------------------------------------------- -var complexMultiply = function (a, b) -{ - return [(a[0] * b[0] - a[1] * b[1]), - (a[0] * b[1] + a[1] * b[0])]; -}; +var complex = require('./complex'); //------------------------------------------------- // By Eulers Formula: @@ -88,10 +62,10 @@ var fft = function (vector) { { // t is a complex number! var t = X_evens[k], - e = complexMultiply(exponent(k, N), X_odds[k]); + e = complex.multiply(exponent(k, N), X_odds[k]); - X[k] = complexAdd(t, e); - X[k + (N/2)] = complexSubtract(t, e); + X[k] = complex.add(t, e); + X[k + (N/2)] = complex.subtract(t, e); } function even(__, ix) {return ix % 2 == 0;} diff --git a/src/fftutil.js b/src/fftutil.js new file mode 100644 index 0000000..af1d385 --- /dev/null +++ b/src/fftutil.js @@ -0,0 +1,44 @@ +/*===========================================================================*\ + * Fast Fourier Transform Frequency/Magnitude passes + * + * (c) Vail Systems. Joshua Jung and Ben Bryan. 2015 + * + * This code is not designed to be highly optimized but as an educational + * tool to understand the Fast Fourier Transform. +\*===========================================================================*/ + +//------------------------------------------------- +// The following code assumes a complex number is +// an array: [real, imaginary] +//------------------------------------------------- +var complex = require('./complex'); + +//------------------------------------------------- +// Calculate FFT Magnitude for complex numbers. +//------------------------------------------------- +var fftMag = function (fftBins) { + return fftBins.map(complex.magnitude); +}; + +//------------------------------------------------- +// Calculate Frequency Bins +// +// Returns an array of the frequencies (in hertz) of +// each FFT bin provided, assuming the sampleRate is +// samples taken per second. +//------------------------------------------------- +var fftFreq = function (fftBins, sampleRate) { + var stepFreq = sampleRate / (fftBins.length * 2); + + return fftBins.map(function (__, ix) { + return ix * stepFreq; + }); +}; + +//------------------------------------------------- +// Exports +//------------------------------------------------- +module.exports = { + fftMag: fftMag, + fftFreq: fftFreq +}; diff --git a/test/test.js b/test/test.js index e548c70..c58ff0b 100644 --- a/test/test.js +++ b/test/test.js @@ -1,5 +1,5 @@ var assert = require('assert'), - fft = require('../'); + fft = require('../').fft; describe('FFT (Cooley-Tukey)', function () { describe('1,0,1,0', function () {