UNPKG

ts-scikit

Version:

A scientific toolkit written in Typescript

1,185 lines 40.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Fft = void 0; const fft_real_1 = require("./fft-real"); const fft_complex_1 = require("./fft-complex"); const sampling_1 = require("./sampling"); const utils_1 = require("../utils"); /** * An easy-to-use fast Fourier transform. * <p> * <em>WARNING: NOT YET TESTED!</em> * This class is less flexible than {@link FftComplex} and {@link FftReal}. * For example, the user has less control over the sampling of frequency. * However, for many applications this class may be simpler to use. * <p> * For example, the following program shows how to use this class to * filter a real-valued sequence in the frequency domain. * <pre> * const fft = new Fft(nx); // nx = number of samples of f(x) * const sk = fft.getFrequencySampling1(); * const nk = sk.count; // number of frequencies sampled. * const f: number[] = ... // nx real samples of input f(x) * const g: number[] = fft.applyForward(f); * for (let kk=0, kr=0, ki=kr + 1; kk &lt; nk; ++kk, kr+=2, ki+=2 ) { * let k = sk.getValue(kk); // frequency k in cycles/sample. * // modify g[kr], the real part of g(k) * // modify g[ki], the imag part of g(k) * } * const h[] = fft.applyInverse(g); // nk real samples of output h(x) * </pre> * <p> * This example is almost as simple for multi-dimensional transforms. * <p> * A forward transform computes an output array of complex values g(x) * from an input array of real or complex values f(x). An inverse * transform computes the corresponding real of complex values f(x) * from g(k). For definiteness, in this documentation, the variable x * represents spatial coordinates and the variable k represents spatial * frequencies (or wavenumbers). For functions of time, simply replace * the word "space" with "time" in this documentation. * <p> * This class enables transforms of 1D, 2D, and 3D arrays. For example, * a 2D array f[nx2][nx1] represents nx2*nx1 samples of a function * f(x1, x2) of two spatial coordinates x1 and x2. In addition to * numbers of samples nx1 and nx2, sampling intervals dx1 and dx2 * and first sampled coordinates fx1 and fx2 may also be specified. * (The default sampling interval is 1.0 and the default first sample * coordinate is 0.0.) These sampling parameters may be specified with * samplings sx1 and sx2. * <p> * For each specified spatial sampling sx, this class defines a * corresponding frequency sampling sk, in which units of frequency * are cycles per unit distance. The number of frequencies sampled * is computed so that the Fourier transform is fast, but the number * of frequency samples is never less than the number of space samples. * Arrays to be transformed may be padded with zeros to obtain the * required frequency sampling. Optional additional padding may be * specified to sample frequency more finely. * <p> * A frequency sampling sk may be centered. Such a centered frequency * sampling always has an odd number of samples, and zero frequency * corresponds to the middle sample in the array of complex transformed * values. The default is not centered, so that zero frequency corresponds * to the first sample, the one with index 0. * <p> * Arrays input to forward transforms may contain either real or * complex values. If complex, values are packed sequentially as * (real, imag) pairs of consecutive numbers. The default input * type is real. * <p> * Signs of the exponents in the complex exponentials used in forward * transforms may be specified. The opposite signs are used for inverse * transforms. The default signs are -1 for forward transforms and 1 for * inverse transforms. */ class Fft { constructor(n1, n2, n3, complex) { this._padding1 = 0; this._padding2 = 0; this._padding3 = 0; if (n1 instanceof sampling_1.Sampling && n2 instanceof sampling_1.Sampling && n3 instanceof sampling_1.Sampling) { this._init(n1, n2, n3); } else if (n1 instanceof sampling_1.Sampling && n2 instanceof sampling_1.Sampling) { this._init(n1, n2); } else if (n1 instanceof sampling_1.Sampling) { this._init(n1); if (typeof n1 === 'number' && typeof n2 === 'number' && typeof n3 === 'number') { this._init(new sampling_1.Sampling(n1), new sampling_1.Sampling(n2), new sampling_1.Sampling(n3)); } else if (typeof n1 === 'number' && typeof n2 === 'number') { this._init(new sampling_1.Sampling(n1), new sampling_1.Sampling(n2)); } else if (typeof n1 === 'number') { this._init(new sampling_1.Sampling(n1)); } else if (n1[0] instanceof Array) { if (n1[0][0] instanceof Array) { n1 = n1; this._init(new sampling_1.Sampling(n1[0][0].length / 2), new sampling_1.Sampling(n1[0].length), new sampling_1.Sampling(n1.length)); } else { n1 = n1; this._init(new sampling_1.Sampling(n1[0].length / 2), new sampling_1.Sampling(n1.length)); } } else if (n1 instanceof Array) { this._init(new sampling_1.Sampling(n1.length / 2)); } } if (complex) { this.complex = complex; } ; } static FromData(f, complex = false) { switch (utils_1.arrayDimensions(f)) { case 1: f = f; return new Fft(f.length, complex); case 2: f = f; return new Fft(f[0].length, f.length, complex); default: f = f; return new Fft(f[0][0].length, f[0].length, f.length, complex); } } /** * Sets the type of input (output) values for forward (inverse) transforms. * <p> * The default type is real. * @param complex true, for complex values; false, for real values. */ set complex(complex) { if (this._complex !== complex) { this._complex = complex; this._updateSampling1(); } } /** * Sets the ability of this transform to overwrite specified arrays. * <p> * The array specified in an inverse transform is either copied or * overwritten internally by the inverse transform. Copying preserves * the values in the specified array, but wastes memory in the case * when those values are no longer needed. If overwrite is true, then * the inverse transform will be performed in place, so that no copy * is necessary. The default is false. * @param overwrite true, to overwrite; false, to copy. */ set overwrite(overwrite) { if (this._overwrite !== overwrite) { this._overwrite = overwrite; } } /** * Sets the centering of frequency samplings for all dimensions. * <p> * If centered, the number of frequency samples is always odd, * and zero frequency corresponds to the middle sample. The * default center is false, so that zero frequency corresponds to * the sample with index zero in the output transformed array. * @param center true, for centering; false, otherwise. */ set center(center) { this.center1 = center; this.center2 = center; this.center3 = center; } /** * Sets the centering of frequency sampling for the 1st dimension. * <p> * If centered, the number of frequency samples is always odd, * and zero frequency corresponds to the middle sample. The * default center is false, so that zero frequency corresponds to * the sample with index zero in the output transformed array. * @param center true, for centering; false, otherwise. */ set center1(center) { if (this._center1 !== center) { this._center1 = center; this._updateSampling1(); } } /** * Sets the centering of frequency sampling for the 2nd dimension. * <p> * If centered, the number of frequency samples is always odd, * and zero frequency corresponds to the middle sample. The * default center is false, so that zero frequency corresponds to * the sample with index zero in the output transformed array. * @param center true, for centering; false, otherwise. */ set center2(center) { if (this._center2 !== center) { this._center2 = center; this._updateSampling2(); } } /** * Sets the centering of frequency sampling for the 3rd dimension. * <p> * If centered, the number of frequency samples is always odd, * and zero frequency corresponds to the middle sample. The * default center is false, so that zero frequency corresponds to * the sample with index zero in the output transformed array. * @param center true, for centering; false, otherwise. */ set center3(center) { if (this._center3 !== center) { this._center3 = center; this._updateSampling3(); } } /** * Sets the minimum padding with zeros for all array dimensions. * <p> * The default minimum is zero. However, some amount of padding * may be required by the FFT. * @param padding the minimum padding. */ set padding(padding) { this.padding1 = padding; this.padding2 = padding; this.padding3 = padding; } /** * Sets the minimum padding with zeros for the 1st array dimension. * <p> * The default minimum is zero. However, some amount of padding may * be required by the FFT. * @param padding the minimum padding. */ set padding1(padding) { if (this._padding1 !== padding) { this._padding1 = padding; this._updateSampling1(); } } /** * Sets the minimum padding with zeros for the 2nd array dimension. * <p> * The default minimum is zero. However, some amount of padding may * be required by the FFT. * @param padding the minimum padding. */ set padding2(padding) { if (this._padding2 !== padding) { this._padding2 = padding; this._updateSampling2(); } } /** * Sets the minimum padding with zeros for the 3rd array dimension. * <p> * The default minimum is zero. However, some amount of padding may * be required by the FFT. * @param padding the minimum padding. */ set padding3(padding) { if (this._padding3 !== padding) { this._padding3 = padding; this._updateSampling3(); } } /** * Gets the frequency sampling for the 1st dimension. * @returns the frequency sampling. */ get frequencySampling1() { return this._sk1; } /** * Gets the frequency sampling for the 2nd dimension. * @returns the frequency sampling. */ get frequencySampling2() { return this._sk2; } /** * Gets the frequency sampling for the 3rd dimension. * @returns the frequency sampling. */ get frequencySampling3() { return this._sk3; } /** * Sets the sign used for forward transforms in all dimensions. * <p> * The opposite sign is used for inverse transforms. * The default sign is -1. * @param sign the sign, -1 or 1. */ set sign(sign) { this.sign1 = sign; this.sign2 = sign; this.sign3 = sign; } /** * Sets the sign used for forward transforms in the 1st dimension. * <p> * The opposite sign is used for inverse transforms. * The default sign is -1. * @param sign the sign, -1 or 1. */ set sign1(sign) { this._sign1 = (sign > 0) ? 1 : -1; } /** * Sets the sign used for forward transforms in the 2nd dimension. * <p> * The opposite sign is used for inverse transforms. * The default sign is -1. * @param sign the sign, -1 or 1. */ set sign2(sign) { this._sign2 = (sign > 0) ? 1 : -1; } /** * Sets the sign used for forward transforms in the 3rd dimension. * <p> * The opposite sign is used for inverse transforms. * The default sign is -1. * @param sign the sign, -1 or 1. */ set sign3(sign) { this._sign3 = (sign > 0) ? 1 : -1; } applyForward(f) { const dim = utils_1.arrayDimensions(f); switch (dim) { case 1: return this._applyForward1(f); case 2: return this._applyForward2(f); default: return this._applyForward3(f); } } applyInverse(g) { const dim = utils_1.arrayDimensions(g); switch (dim) { case 1: return this._applyInverse1(g); case 2: return this._applyInverse2(g); default: return this._applyInverse3(g); } } /** @internal */ _applyInverse1(g) { this._ensureSamplingK1(g); const nx1 = this._sx1.count; const gpad = (this._overwrite) ? g : utils_1.copy(g); this._uncenter(gpad, 1); this._unphase(gpad, 1); if (this._complex) { this._fft1c.complexToComplex(-this._sign1, gpad, gpad); this._fft1c.scale(gpad, nx1); return utils_1.ccopy(gpad, nx1); } else { this._fft1r.complexToReal(-this._sign1, gpad, gpad); this._fft1r.scale(gpad, nx1); return utils_1.copy(gpad, nx1); } } /** @internal */ _applyInverse2(g) { this._ensureSamplingK2(g); const gpad = (this._overwrite) ? g : utils_1.copy(g); const nx1 = this._sx1.count; const nx2 = this._sx2.count; this._uncenter(gpad, 2); this._unphase(gpad, 2); if (this._complex) { this._fft2.complexToComplex2(-this._sign2, gpad, gpad, this._nfft1); this._fft2.scale(gpad, this._nfft1, nx2); this._fft1c.complexToComplex1(-this._sign1, gpad, gpad, nx2); this._fft1c.scale(gpad, nx1, nx2); return utils_1.ccopy(gpad, nx1, nx2); } else { this._fft2.complexToComplex2(-this._sign2, gpad, gpad, this._nfft1 / 2 + 1); this._fft2.scale(gpad, this._nfft1 / 2 + 1, nx2); this._fft1r.complexToReal1(-this._sign1, gpad, gpad, nx2); this._fft1r.scale(gpad, nx1, nx2); return utils_1.copy(gpad, nx1, nx2); } } /** @internal */ _applyInverse3(g) { this._ensureSamplingK3(g); const gpad = (this._overwrite) ? g : utils_1.copy(g); const nx1 = this._sx1.count; const nx2 = this._sx2.count; const nx3 = this._sx3.count; this._uncenter(gpad, 3); this._unphase(gpad, 3); if (this._complex) { this._fft3.complexToComplex3(-this._sign3, gpad, gpad, this._nfft1, this._nfft2); this._fft3.scale(gpad, this._nfft1, this._nfft2, nx3); this._fft2.complexToComplex2(-this._sign2, gpad, gpad, this._nfft1, nx3); this._fft2.scale(gpad, this._nfft1, nx2, nx3); this._fft1c.complexToComplex1(-this._sign1, gpad, gpad, nx2, nx3); this._fft1c.scale(gpad, nx1, nx2, nx3); return utils_1.ccopy(gpad, nx1, nx2, nx3); } else { this._fft3.complexToComplex3(-this._sign3, gpad, gpad, this._nfft1 / 2 + 1, this._nfft2); this._fft3.scale(gpad, this._nfft1 / 2 + 1, this._nfft2, nx3); this._fft2.complexToComplex2(-this._sign2, gpad, gpad, this._nfft1 / 2 + 1, nx3); this._fft2.scale(gpad, this._nfft1 / 2 + 1, nx2, nx3); this._fft1r.complexToReal1(-this._sign1, gpad, gpad, nx2, nx3); this._fft1r.scale(gpad, nx1, nx2, nx3); return utils_1.copy(gpad, nx1, nx2, nx3); } } /** @internal */ _applyForward1(f) { this._ensureSamplingX1(f); const fpad = this._pad1(f); if (this._complex) { this._fft1c.complexToComplex(this._sign1, fpad, fpad); } else { this._fft1r.realToComplex(this._sign1, fpad, fpad); } this._phase(fpad, 1); this._center(fpad, 1); return fpad; } /** @interal */ _applyForward2(f) { this._ensureSamplingX2(f); const fpad = this._pad2(f); const nx2 = this._sx2.count; if (this._complex) { this._fft1c.complexToComplex1(this._sign1, fpad, fpad, nx2); this._fft2.complexToComplex2(this._sign2, fpad, fpad, this._nfft1); } else { this._fft1r.realToComplex1(this._sign1, fpad, fpad, nx2); this._fft2.complexToComplex2(this._sign2, fpad, fpad, this._nfft1 / 2 + 1); } this._phase(fpad, 2); this._center(fpad, 2); return fpad; } /** @internal */ _applyForward3(f) { this._ensureSamplingX3(f); const fpad = this._pad3(f); const nx2 = this._sx2.count; const nx3 = this._sx3.count; if (this._complex) { this._fft1c.complexToComplex1(this._sign1, fpad, fpad, nx2, nx3); this._fft2.complexToComplex2(this._sign2, fpad, fpad, this._nfft1, nx3); this._fft3.complexToComplex3(this._sign3, fpad, fpad, this._nfft1, this._nfft2); } else { this._fft1r.realToComplex1(this._sign1, fpad, fpad, nx2, nx3); this._fft2.complexToComplex2(this._sign2, fpad, fpad, this._nfft1 / 2 + 1, nx3); this._fft3.complexToComplex3(this._sign3, fpad, fpad, this._nfft1 / 2 + 1, this._nfft2); } this._phase(fpad, 3); this._center(fpad, 3); return fpad; } /** @internal */ _cswap(f, n, i, j, dim) { switch (dim) { case 1: this._doCswap1(f, n, i, j); break; case 2: this._doCswap2(f, n, i, j); break; default: this._doCswap3(f, n, i, j); } } /** @internal */ _doCswap1(f, n, i, j) { let ir = 2 * i, ii = ir + 1; let jr = 2 * j, ji = jr + 1; for (const k = 0; k < n; ++i, ir += 2, ii += 2, jr += 2, ji += 2) { const fir = f[ir]; f[ir] = f[jr]; f[jr] = fir; const fii = f[ii]; f[ii] = f[ji]; f[ji] = fii; } } /** @internal */ _doCswap2(f, n, i, j) { for (let k = 0; k < n; ++k, ++i, ++j) { const fi = f[i]; f[i] = f[j]; f[j] = fi; } } /** @internal */ _doCswap3(f, n, i, j) { for (let k = 0; k < n; ++k, ++i, ++j) { const fi = f[i]; f[i] = f[j]; f[j] = fi; } } /** @internal */ _cshift(f, n, i, j) { if (i < j) { let ir = 2 * (i + n - 1), ii = ir + 1; let jr = 2 * (j + n - 1), ji = jr + 1; for (let k = 0; k < n; ++k, ir -= 2, ii -= 2, jr -= 2, ji -= 2) { f[jr] = f[ir]; f[ji] = f[ii]; } } else { let ir = 2 * i, ii = ir + 1; let jr = 2 * j, ji = jr + 1; for (let k = 0; k < n; ++k, ir += 2, ii += 2, jr += 2, ji += 2) { f[jr] = f[ir]; f[ji] = f[ii]; } } } /** @internal */ _crotateLeft(f, n, j, dim) { switch (dim) { case 1: this._doCrotateLeft1(f, n, j); break; case 2: this._doCrotateLeft2(f, n, j); break; default: this._doCrotateLeft3(f, n, j); } } /** @internal */ _doCrotateLeft1(f, n, j) { const fjr = f[j * 2]; const fji = f[j * 2 + 1]; const i = j + 1; let ir = 2 * i; let ii = ir + 1; for (let k = 1; k < n; ++k, ir += 2, ii += 2) { f[ir - 2] = f[ir]; f[ii - 2] = f[ii]; } f[ir - 2] = fjr; f[ii - 2] = fji; } /** @internal */ _doCrotateLeft2(f, n, j) { // nfft odd // 4 5 6 3 0 1 2 // 4 5 6 0 1 2 3 // _doCrotateLeft(f,n=4,j=3); const fj = f[j]; const m = j + n; let i = j + 1; for (; i < m; ++i) f[i - 1] = f[i]; f[i - 1] = fj; } /** @internal */ _doCrotateLeft3(f, n, j) { // nfft odd // 4 5 6 3 0 1 2 // 4 5 6 0 1 2 3 // _doCrotateLeft(f, n=4,j=3); const fj = f[j]; const m = j + n; let i = j + 1; for (; i < m; ++i) f[i - 1] = f[i]; f[i - 1] = fj; } /** @internal */ _crotateRight(f, n, j, dim) { switch (dim) { case 1: this._doCrotateRight1(f, n, j); break; case 2: this._doCrotateRight2(f, n, j); break; default: this._doCrotateRight3(f, n, j); } } /** @internal */ _doCrotateRight1(f, n, j) { const m = j + n - 1; const fmr = f[m * 2]; const fmi = f[m * 2 + 1]; const i = m; let ir = 2 * i; let ii = ir + 1; for (let k = 1; k < n; ++k, ir -= 2, ii -= 2) { f[ir] = f[ir - 2]; f[ii] = f[ii - 2]; } f[ir] = fmr; f[ii] = fmi; } /** @internal */ _doCrotateRight2(f, n, j) { const m = j + n - 1; const fm = f[m]; let i = m; for (; i > j; --i) { f[i] = f[i - 1]; } f[i] = fm; } /** @internal */ _doCrotateRight3(f, n, j) { const m = j + n - 1; const fm = f[m]; let i = m; for (; i > j; --i) { f[i] = f[i - 1]; } f[i] = fm; } /** @internal */ _creflect(f, n, i, dim) { switch (dim) { case 1: this._doCreflect1(f, n, i); break; case 2: this._doCreflect2(f, n, i); break; default: this._doCreflect3(f, n, i); } } /** @internal */ _doCreflect1(f, n, i) { let ir = 2 * (i + 1), ii = ir + 1; let jr = 2 * (i - 1), ji = jr + 1; for (let k = 0; k < n; ++k, ir += 2, ii += 2, jr -= 2, ji -= 2) { f[jr] = f[ir]; f[ji] = -f[ii]; } } /** @internal */ _doCreflect2(f, n, i) { const n2 = f.length; for (let i2 = 0, j2 = n2 - 1; i2 < n2; ++i2, --j2) { let ir = 2 * (i + 1), ii = ir + 1; let jr = 2 * (i - 1), ji = jr + 1; const fj2 = f[j2]; const fi2 = f[i2]; for (let k = 0; k < n; ++k, ir += 2, ii += 2, jr -= 2, ji -= 2) { fj2[jr] = fi2[ir]; fj2[ji] = -fi2[ii]; } } } /** @internal */ _doCreflect3(f, n, i) { const n2 = f[0].length; const n3 = f.length; for (let i3 = 0, j3 = n3 - 1; i3 < n3; ++i3, --j3) { for (let i2 = 0, j2 = n2 - 1; i2 < n2; ++i2, --j2) { let ir = 2 * (i + 1), ii = ir + 1; let jr = 2 * (i - 1), ji = jr + 1; const fj3j2 = f[j3][j2]; const fi3i2 = f[i3][i2]; for (let k = 0; k < n; ++k, ir += 2, ii += 2, jr -= 2, ji -= 2) { fj3j2[jr] = fi3i2[ir]; fj3j2[ji] = -fi3i2[ii]; } } } } /** @internal */ _init(sx1, sx2, sx3) { this._sx1 = sx1; this._sx2 = sx2; this._sx3 = sx3; this._sign1 = -1; this._sign2 = -1; this._sign3 = -1; this._updateSampling1(); this._updateSampling2(); this._updateSampling3(); } _updateSampling1() { if (!this._sx1) { return; } const nx = this._sx1.count; const dx = this._sx1.delta; const npad = nx + this._padding1; let nfft, nk, dk, fk; if (this._complex) { nfft = fft_complex_1.FftComplex.SmallNFFT(npad); dk = 1.0 / (nfft * dx); if (this._center1) { const even = nfft % 2 === 0; nk = even ? nfft + 1 : nfft; fk = even ? -0.5 / dx : -0.5 / dx + 0.5 * dk; } else { nk = nfft; fk = 0.0; } if (!this._fft1c || this._nfft1 !== nfft) { this._fft1c = new fft_complex_1.FftComplex(nfft); this._fft1r = null; this._nfft1 = nfft; } } else { nfft = fft_real_1.FftReal.SmallNFFT(npad); dk = 1.0 / (nfft * dx); if (this._center1) { nk = nfft + 1; fk = -0.5 / dx; } else { nk = nfft / 2 + 1; fk = 0.0; } if (!this._fft1r || this._nfft1 !== nfft) { this._fft1r = new fft_real_1.FftReal(nfft); this._fft1c = null; this._nfft1 = nfft; } } this._sk1 = new sampling_1.Sampling(nk, dk, fk); } _updateSampling2() { if (!this._sx2) { return; } const nx = this._sx2.count; const dx = this._sx2.delta; const npad = nx + this._padding2; const nfft = fft_complex_1.FftComplex.SmallNFFT(npad); const dk = 1.0 / (nfft * dx); let fk, nk; if (this._center2) { const even = nfft % 2 === 0; nk = even ? nfft + 1 : nfft; fk = even ? -0.5 / dx : -0.5 / dx + 0.5 * dk; } else { nk = nfft; fk = 0.0; } if (!this._fft2 || this._nfft2 !== nfft) { this._fft2 = new fft_complex_1.FftComplex(nfft); this._nfft2 = nfft; } this._sx2 = new sampling_1.Sampling(nk, dk, fk); } _updateSampling3() { if (!this._sx3) { return; } const nx = this._sx3.count; const dx = this._sx3.delta; const npad = nx + this._padding3; const nfft = fft_complex_1.FftComplex.SmallNFFT(npad); const dk = 1.0 / (nfft * dx); let fk, nk; if (this._center3) { const even = nfft % 2 === 0; nk = even ? nfft + 1 : nfft; fk = even ? -0.5 / dx : -0.5 / dx + 0.5 * dk; } else { nk = nfft; fk = 0.0; } if (!this._fft3 || this._nfft3 !== nfft) { this._fft3 = new fft_complex_1.FftComplex(nfft); this._nfft3 = nfft; } this._sk3 = new sampling_1.Sampling(nk, dk, fk); } _pad1(f) { const nk1 = this._sk1.count; return (this._complex) ? utils_1.copy(f, f.length).concat(Array(nk1).fill(0.0)) : utils_1.ccopy(f, f.length / 2).concat(Array(nk1).fill(0.0)); } _pad2(f) { const nk2 = this._sk2.count; const g = utils_1.copy(f); for (let i2 = 0; i2 < nk2; ++i2) { g[i2] = this._pad1(g[i2]); } return g; } _pad3(f) { const nk3 = this._sk3.count; const g = utils_1.copy(f); for (let i3 = 0; i3 < nk3; ++i3) { g[i3] = this._pad2(g[i3]); } return g; } _ensureSamplingX1(f) { utils_1.Check.state(this._sx1 !== null, 'sampling sx1 exists for 1st dimension'); const l1 = f.length; let n1 = this._sx1.count; if (this._complex) { n1 *= 2; } utils_1.Check.argument(n1 === l1, 'array length consistent with sampling sx1'); } _ensureSamplingX2(f) { utils_1.Check.state(this._sx2 !== null, 'sampling sx2 exists for 2nd dimension'); this._ensureSamplingX1(f[0]); const l2 = f.length; const n2 = this._sx2.count; utils_1.Check.argument(n2 === l2, 'array length consistent with sampling sx2'); } _ensureSamplingX3(f) { utils_1.Check.state(this._sx3 !== null, 'sampling sx3 exists for 3rd dimension'); this._ensureSamplingX2(f[0]); const l3 = f.length; const n3 = this._sx3.count; utils_1.Check.argument(n3 === l3, 'array length consistent with sampling sx3'); } _ensureSamplingK1(f) { utils_1.Check.state(this._sk1 !== null, 'sampling sk1 exists for 1st dimension'); const l1 = f.length; const n1 = this._sk1.count; console.log(l1, n1); utils_1.Check.argument(2 * n1 === l1, 'array length consistent with sampling sk1'); } _ensureSamplingK2(f) { utils_1.Check.state(this._sk2 !== null, 'sampling sk2 exists for 2nd dimension'); const l2 = f.length; const n2 = this._sk2.count; utils_1.Check.argument(n2 === l2, 'array length consistent with sampling sk2'); } _ensureSamplingK3(f) { utils_1.Check.state(this._sk3 !== null, 'sampling sk3 exists for 3rd dimension'); this._ensureSamplingK2(f[0]); const l3 = f.length; const n3 = this._sk3.count; utils_1.Check.argument(n3 === l3, 'array length consistent with sampling sk3'); } /** @ignore */ _center(f, dim) { switch (dim) { case 1: this._doCenter1(f); break; case 2: this._doCenter2(f); break; default: this._doCenter3(f); } } /** @internal */ _doCenter1(f) { this._center1d(f); if (this._center1 && !this._complex) { // real, nfft = 8 // x x x x 0 1 2 3 4 // 4 3 2 1 0 1 2 3 4 this._creflect(f, this._nfft1 / 2, this._nfft1 / 2, 1); } } /** @ignore */ _doCenter2(f) { if (this._center1) { for (let i2 = 0; i2 < this._nfft2; ++i2) { this._center1d(f[i2]); } this._center2d(f); if (this._center1 && !this._complex) { this._creflect(f, this._nfft1 / 2, this._nfft1 / 2, 2); } } } /** @ignore */ _doCenter3(f) { if (this._center3) { return; } const nk3 = this._sk3.count; const nfft3 = this._nfft3; const even3 = nfft3 % 2 === 0; if (even3) { // nfft even // 0 1 2 3 4 5 6 7 | 8 // 4 5 6 7 0 1 2 3 | 4 this._cswap(f, nfft3 / 2, 0, nfft3 / 2, 3); f[nk3 - 1] = utils_1.ccopy(f[0]); } else { // nfft odd // 0 1 2 3 4 5 6 // 4 5 6 3 0 1 2 // 4 5 6 0 1 2 3 this._cswap(f, (nfft3 - 1) / 2, 0, (nfft3 + 1) / 2, 3); this._crotateLeft(f, (nfft3 + 1) / 2, (nfft3 - 1) / 2, 3); } } /** @internal */ _phase(f, dim) { switch (dim) { case 1: this._doPhase1(f, this._sign1); break; case 2: this._doPhase2(f, this._sign1, this._sign2); break; default: this._doPhase3(f, this._sign1, this._sign2, this._sign3); } } /** @internal */ _unphase(f, dim) { switch (dim) { case 1: this._doPhase1(f, -this._sign1); break; case 2: this._doPhase2(f, -this._sign1, -this._sign2); break; default: this._doPhase3(f, -this._sign1, -this._sign2, -this._sign3); } } /** @internal */ _doPhase1(f, sign1 = this._sign1) { const fx = this._sx1.first; if (fx === 0.0) { return; } const nk = (this._complex) ? this._nfft1 : this._nfft1 / 2 + 1; const dp = sign1 * 2.0 * Math.PI * this._sk1.delta * fx; let p, cosp, sinp, fr, fi; for (let i = 0, ir = 0, ii = 1; i < nk; ++i, ir += 2, ii += 2) { p = i * dp; cosp = Math.cos(p); sinp = Math.sin(p); fr = f[ir]; fi = f[ii]; f[ir] = fr * cosp - fi * sinp; f[ii] = fi * cosp + fr * sinp; } } /** @internal */ _doPhase2(f, sign1 = this._sign1, sign2 = this._sign2) { const fx1 = this._sx1.first; const fx2 = this._sx2.first; let p, cosp, sinp, fr, fi; let p2, f2; if (fx1 === 0.0 && fx2 === 0.0) { return; } const nk1 = (this._complex) ? this._nfft1 : this._nfft1 / 2 + 1; const nk2 = this._nfft2; const dp1 = sign1 * 2.0 * Math.PI * this._sk1.delta * fx1; const dp2 = sign2 * 2.0 * Math.PI * this._sk1.delta * fx2; for (let i2 = 0; i2 < nk2; ++i2) { p2 = i2 * dp2; f2 = f[i2]; for (let i1 = 0, ir = 0, ii = 1; i1 < nk1; ++i1, ir += 2, ii += 2) { p = i1 * dp1 * p2; cosp = Math.cos(p); sinp = Math.sin(p); fr = f2[ir]; fi = f2[ii]; f2[ir] = fr * cosp - fi * sinp; f2[ii] = fi * cosp + fr * sinp; } } } /** @internal */ _doPhase3(f, sign1 = this._sign1, sign2 = this._sign2, sign3 = this._sign3) { const fx1 = this._sx1.first; const fx2 = this._sx2.first; const fx3 = this._sx3.first; let p, cosp, sinp, fr, fi; if (fx1 === 0.0 && fx2 === 0.0 && fx3 === 0.0) { return; } const nk1 = (this._complex) ? this._nfft1 : this._nfft1 / 2 + 1; const nk2 = this._nfft2; const nk3 = this._nfft3; const dp1 = sign1 * 2.0 * Math.PI * this._sk1.delta * fx1; const dp2 = sign2 * 2.0 * Math.PI * this._sk2.delta * fx2; const dp3 = sign3 * 2.0 * Math.PI * this._sk3.delta * fx3; for (let i3 = 0; i3 < nk3; ++i3) { for (let i2 = 0; i2 < nk2; ++i2) { const p23 = i2 * dp2 + i3 * dp3; const f32 = f[i3][i2]; for (let i1 = 0, ir = 0, ii = 1; i1 < nk1; ++i1, ir += 2, ii += 2) { p = i1 * dp1 * p23; cosp = Math.cos(p); sinp = Math.sin(p); fr = f32[ir]; fi = f32[ii]; f32[ir] = fr * cosp - fi * sinp; f32[ii] = fi * cosp + fr * sinp; } } } } /** @internal */ _center1d(f) { if (!this._center1) { return; } const nk1 = this._sk1.count; const nfft1 = this._nfft1; const even1 = nfft1 % 2 === 0; if (this._complex) { if (even1) { // complex, nfft = 8 // 0 1 2 3 4 5 6 7 | 8 // 4 5 6 7 0 1 2 3 | 4 this._cswap(f, nfft1 / 2, 0, nfft1 / 2, 1); f[2 * (nk1 - 1)] = f[0]; f[2 * (nk1 - 1) + 1] = f[1]; } else { // complex, nfft = 7 // 0 1 2 3 4 5 6 // 4 5 6 3 0 1 2 // 4 5 6 0 1 2 3 this._cswap(f, (nfft1 - 1) / 2, 0, (nfft1 + 1) / 2, 1); this._crotateLeft(f, (nfft1 + 1) / 2, (nfft1 - 1) / 2, 1); } } else { // real, nfft = 8 // 0 1 2 3 4 // 0 1 2 3 0 1 2 3 4 this._cshift(f, nfft1 / 2 + 1, 0, nfft1 / 2); } } /** @internal */ _center2d(f) { if (!this._center2) { return; } const nk2 = this._sk2.count; const nfft2 = this._nfft2; const even2 = nfft2 % 2 === 0; if (even2) { // nfft even // 0 1 2 3 4 5 6 7 | 8 // 4 5 6 7 0 1 2 3 | 4 this._cswap(f, nfft2 / 2, 0, nfft2 / 2, 2); f[nk2 - 1] = utils_1.ccopy(f[0]); } else { // nfft odd // 0 1 2 3 4 5 6 // 4 5 6 3 0 1 2 // 4 5 6 0 1 2 3 this._cswap(f, (nfft2 - 1) / 2, 0, (nfft2 + 1) / 2, 2); this._crotateLeft(f, (nfft2 + 1) / 2, (nfft2 - 1) / 2, 2); } } /** @ignore */ _center3d(f) { if (!this._center3) { return; } const nk3 = this._sk3.count; const nfft3 = this._nfft3; const even3 = nfft3 % 2 === 0; if (even3) { // nfft even // 0 1 2 3 4 5 6 7 | 8 // 4 5 6 7 0 1 2 3 | 4 this._cswap(f, nfft3 / 2, 0, nfft3 / 2, 3); f[nk3 - 1] = utils_1.ccopy(f[0]); } else { // nfft odd // 0 1 2 3 4 5 6 // 4 5 6 3 0 1 2 // 4 5 6 0 1 2 3 this._cswap(f, (nfft3 - 1) / 2, 0, (nfft3 + 1) / 2, 3); this._crotateLeft(f, (nfft3 + 1) / 2, (nfft3 - 1) / 2, 3); } } /** @internal */ _uncenter(f, dims) { switch (dims) { case 1: this._uncenter1(f); case 2: this._uncenter2(f); default: this._uncenter3(f); } } ; /** @internal */ _uncenter1(f) { if (!this._center1) { return; } const nfft1 = this._nfft1; const even1 = nfft1 % 2 === 0; if (this._complex) { if (even1) { // complex, nfft = 8 // 4 5 6 7 0 1 2 3 | 8 // 0 1 2 3 4 5 6 7 | 8 this._cswap(f, nfft1 / 2, 0, nfft1 / 2, 1); } else { // complex, nfft = 7 // 4 5 6 0 1 2 3 // 4 5 6 3 0 1 2 // 0 1 2 3 4 5 6 this._crotateRight(f, (nfft1 + 1) / 2, (nfft1 - 1) / 2, 1); this._cswap(f, (nfft1 - 1) / 2, 0, (nfft1 + 1) / 2, 1); } } else { // real, nfft = 8 // 4 3 2 1 0 1 2 3 4 // 0 1 2 3 4 1 2 3 4 this._cshift(f, nfft1 / 2 + 1, nfft1 / 2, 0); } } /** @internal */ _uncenter2(f) { if (!this._center2) { return; } const nfft2 = this._nfft2; const even2 = nfft2 % 2 === 0; if (even2) { // nfft even // 4 5 6 7 0 1 2 3 | 8 // 0 1 2 3 4 5 6 7 | 8 this._cswap(f, nfft2 / 2, 0, nfft2 / 2, 2); } else { // nfft odd // 4 5 6 0 1 2 3 // 4 5 6 3 0 1 2 // 0 1 2 3 4 5 6 this._crotateRight(f, (nfft2 + 1) / 2, (nfft2 - 1) / 2, 2); this._cswap(f, (nfft2 - 1) / 2, 0, (nfft2 + 1) / 2, 2); } } /** @internal */ _uncenter3(f) { if (!this._center3) { return; } const nfft3 = this._nfft3; const even3 = nfft3 % 2 === 0; if (even3) { // nfft even // 4 5 6 7 0 1 2 3 | 8 // 0 1 2 3 4 5 6 7 | 8 this._cswap(f, nfft3 / 2, 0, nfft3 / 2, 3); } else { // nfft odd // 4 5 6 0 1 2 3 // 4 5 6 3 0 1 2 // 0 1 2 3 4 5 6 this._crotateRight(f, (nfft3 + 1) / 2, (nfft3 - 1) / 2, 3); this._cswap(f, (nfft3 - 1) / 2, 0, (nfft3 + 1) / 2, 3); } } } exports.Fft = Fft; //# sourceMappingURL=fft.js.map