UNPKG

@dxzmpk/js-algorithms-data-structures

Version:

Algorithms and data-structures implemented on JavaScript

296 lines (284 loc) 7.53 kB
import ComplexNumber from '../../complex-number/ComplexNumber'; export const fourierTestCases = [ { input: [ { amplitude: 1 }, ], output: [ { frequency: 0, amplitude: 1, phase: 0, re: 1, im: 0, }, ], }, { input: [ { amplitude: 1 }, { amplitude: 0 }, ], output: [ { frequency: 0, amplitude: 0.5, phase: 0, re: 0.5, im: 0, }, { frequency: 1, amplitude: 0.5, phase: 0, re: 0.5, im: 0, }, ], }, { input: [ { amplitude: 2 }, { amplitude: 0 }, ], output: [ { frequency: 0, amplitude: 1, phase: 0, re: 1, im: 0, }, { frequency: 1, amplitude: 1, phase: 0, re: 1, im: 0, }, ], }, { input: [ { amplitude: 1 }, { amplitude: 0 }, { amplitude: 0 }, ], output: [ { frequency: 0, amplitude: 0.33333, phase: 0, re: 0.33333, im: 0, }, { frequency: 1, amplitude: 0.33333, phase: 0, re: 0.33333, im: 0, }, { frequency: 2, amplitude: 0.33333, phase: 0, re: 0.33333, im: 0, }, ], }, { input: [ { amplitude: 1 }, { amplitude: 0 }, { amplitude: 0 }, { amplitude: 0 }, ], output: [ { frequency: 0, amplitude: 0.25, phase: 0, re: 0.25, im: 0, }, { frequency: 1, amplitude: 0.25, phase: 0, re: 0.25, im: 0, }, { frequency: 2, amplitude: 0.25, phase: 0, re: 0.25, im: 0, }, { frequency: 3, amplitude: 0.25, phase: 0, re: 0.25, im: 0, }, ], }, { input: [ { amplitude: 0 }, { amplitude: 1 }, { amplitude: 0 }, { amplitude: 0 }, ], output: [ { frequency: 0, amplitude: 0.25, phase: 0, re: 0.25, im: 0, }, { frequency: 1, amplitude: 0.25, phase: -90, re: 0, im: -0.25, }, { frequency: 2, amplitude: 0.25, phase: 180, re: -0.25, im: 0, }, { frequency: 3, amplitude: 0.25, phase: 90, re: 0, im: 0.25, }, ], }, { input: [ { amplitude: 0 }, { amplitude: 0 }, { amplitude: 1 }, { amplitude: 0 }, ], output: [ { frequency: 0, amplitude: 0.25, phase: 0, re: 0.25, im: 0, }, { frequency: 1, amplitude: 0.25, phase: 180, re: -0.25, im: 0, }, { frequency: 2, amplitude: 0.25, phase: 0, re: 0.25, im: 0, }, { frequency: 3, amplitude: 0.25, phase: 180, re: -0.25, im: 0, }, ], }, { input: [ { amplitude: 0 }, { amplitude: 0 }, { amplitude: 0 }, { amplitude: 2 }, ], output: [ { frequency: 0, amplitude: 0.5, phase: 0, re: 0.5, im: 0, }, { frequency: 1, amplitude: 0.5, phase: 90, re: 0, im: 0.5, }, { frequency: 2, amplitude: 0.5, phase: 180, re: -0.5, im: 0, }, { frequency: 3, amplitude: 0.5, phase: -90, re: 0, im: -0.5, }, ], }, { input: [ { amplitude: 0 }, { amplitude: 1 }, { amplitude: 0 }, { amplitude: 2 }, ], output: [ { frequency: 0, amplitude: 0.75, phase: 0, re: 0.75, im: 0, }, { frequency: 1, amplitude: 0.25, phase: 90, re: 0, im: 0.25, }, { frequency: 2, amplitude: 0.75, phase: 180, re: -0.75, im: 0, }, { frequency: 3, amplitude: 0.25, phase: -90, re: 0, im: -0.25, }, ], }, { input: [ { amplitude: 4 }, { amplitude: 1 }, { amplitude: 0 }, { amplitude: 2 }, ], output: [ { frequency: 0, amplitude: 1.75, phase: 0, re: 1.75, im: 0, }, { frequency: 1, amplitude: 1.03077, phase: 14.03624, re: 0.99999, im: 0.25, }, { frequency: 2, amplitude: 0.25, phase: 0, re: 0.25, im: 0, }, { frequency: 3, amplitude: 1.03077, phase: -14.03624, re: 1, im: -0.25, }, ], }, { input: [ { amplitude: 4 }, { amplitude: 1 }, { amplitude: -3 }, { amplitude: 2 }, ], output: [ { frequency: 0, amplitude: 1, phase: 0, re: 1, im: 0, }, { frequency: 1, amplitude: 1.76776, phase: 8.13010, re: 1.75, im: 0.25, }, { frequency: 2, amplitude: 0.5, phase: 180, re: -0.5, im: 0, }, { frequency: 3, amplitude: 1.76776, phase: -8.13010, re: 1.75, im: -0.24999, }, ], }, { input: [ { amplitude: 1 }, { amplitude: 2 }, { amplitude: 3 }, { amplitude: 4 }, ], output: [ { frequency: 0, amplitude: 2.5, phase: 0, re: 2.5, im: 0, }, { frequency: 1, amplitude: 0.70710, phase: 135, re: -0.5, im: 0.49999, }, { frequency: 2, amplitude: 0.5, phase: 180, re: -0.5, im: 0, }, { frequency: 3, amplitude: 0.70710, phase: -134.99999, re: -0.49999, im: -0.5, }, ], }, ]; export default class FourierTester { /** * @param {function} fourierTransform */ static testDirectFourierTransform(fourierTransform) { fourierTestCases.forEach((testCase) => { const { input, output: expectedOutput } = testCase; // Try to split input signal into sequence of pure sinusoids. const formattedInput = input.map((sample) => sample.amplitude); const currentOutput = fourierTransform(formattedInput); // Check the signal has been split into proper amount of sub-signals. expect(currentOutput.length).toBeGreaterThanOrEqual(formattedInput.length); // Now go through all the signals and check their frequency, amplitude and phase. expectedOutput.forEach((expectedSignal, frequency) => { // Get template data we want to test against. const currentSignal = currentOutput[frequency]; const currentPolarSignal = currentSignal.getPolarForm(false); // Check all signal parameters. expect(frequency).toBe(expectedSignal.frequency); expect(currentSignal.re).toBeCloseTo(expectedSignal.re, 4); expect(currentSignal.im).toBeCloseTo(expectedSignal.im, 4); expect(currentPolarSignal.phase).toBeCloseTo(expectedSignal.phase, 4); expect(currentPolarSignal.radius).toBeCloseTo(expectedSignal.amplitude, 4); }); }); } /** * @param {function} inverseFourierTransform */ static testInverseFourierTransform(inverseFourierTransform) { fourierTestCases.forEach((testCase) => { const { input: expectedOutput, output: inputFrequencies } = testCase; // Try to join frequencies into time signal. const formattedInput = inputFrequencies.map((frequency) => { return new ComplexNumber({ re: frequency.re, im: frequency.im }); }); const currentOutput = inverseFourierTransform(formattedInput); // Check the signal has been combined of proper amount of time samples. expect(currentOutput.length).toBeLessThanOrEqual(formattedInput.length); // Now go through all the amplitudes and check their values. expectedOutput.forEach((expectedAmplitudes, timer) => { // Get template data we want to test against. const currentAmplitude = currentOutput[timer]; // Check if current amplitude is close enough to the calculated one. expect(currentAmplitude).toBeCloseTo(expectedAmplitudes.amplitude, 4); }); }); } }