WFft.mjs

import get from 'lodash-es/get.js'
import size from 'lodash-es/size.js'
import each from 'lodash-es/each.js'
import range from 'lodash-es/range.js'
import take from 'lodash-es/take.js'
import reverse from 'lodash-es/reverse.js'
import ispnum from 'wsemi/src/ispnum.mjs'
import isp0num from 'wsemi/src/isp0num.mjs'
import cdbl from 'wsemi/src/cdbl.mjs'
import ml from 'ml-fft'


//https://github.com/mljs/fft
//https://github.com/IQEngine/WebFFT/blob/main/lib/mljs/fftlib.js
let FFT = ml.FFT
// let FFTUtils = ml.FFTUtils


function get2n(n) {
    let i = 1
    let j = Math.pow(2, 52)
    while (true) {
        i *= 2
        // console.log('n', n, 'i', i, 'n <= i', n <= i)
        if (n <= i) {
            break
        }
        // console.log('j', j, 'i >= j', i >= j)
        if (i >= j) {
            break
        }
    }
    return i
}


function _fft1d(arr, mode = 'norm') {

    //check
    if (mode !== 'norm' && mode !== 'inv') {
        throw new Error(`invalid mode[${mode}]`)
    }

    //n
    let n = size(arr)
    // console.log('n', n)

    //nCols
    let nCols = get2n(n)
    // console.log('nCols', nCols)

    //init
    FFT.init(nCols)

    //fill
    let re = new Array(nCols)
    let im = new Array(nCols)
    if (mode === 'norm') {
        for (let i = 0; i < nCols; i++) {
            let _i = get(arr, i, 0) //超過n至nCols之元素自動補0
            let _j = 0
            // console.log('_i', _i, '_j', _j)
            re[i] = _i
            im[i] = _j
        }
    }
    else {
        for (let i = 0; i < nCols; i++) {
            let _i = get(arr, `${i}.0`, 0)
            let _j = get(arr, `${i}.1`, 0)
            // console.log('_i', _i, '_j', _j)
            re[i] = _i
            im[i] = _j
        }
    }

    let res = []
    if (mode === 'norm') {
        FFT.fft(re, im)
        for (let i = 0; i < nCols; i++) {
            let _i = re[i]
            let _j = im[i]
            res.push([_i, _j])
        }
    }
    else {
        FFT.ifft(re, im)
        for (let i = 0; i < nCols; i++) {
            let _i = re[i]
            // let _j = im[i]
            // let _l = Math.sqrt(_i * _i, _j * _j)
            res.push(_i)
        }
    }

    return res
}


/**
 * FFT1D
 *
 * @param {Array} arr 輸入數據陣列
 * @return {Array} 回傳轉換後數據陣列
 * @example
 *
 * let arr
 * let res
 *
 * arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
 * res = wf.fft1d(arr)
 * console.log(res)
 * // => [
 * //   [ 120, 0 ],
 * //   [ -8, 40.21871593700678 ],
 * //   [ -8, 19.31370849898476 ],
 * //   [ -7.999999999999999, 11.972846101323913 ],
 * //   [ -8, 8 ],
 * //   [ -8, 5.345429103354391 ],
 * //   [ -8, 3.313708498984761 ],
 * //   [ -7.999999999999999, 1.5912989390372623 ],
 * //   [ -8, 0 ],
 * //   [ -7.999999999999999, -1.5912989390372623 ],
 * //   [ -8, -3.313708498984761 ],
 * //   [ -8, -5.345429103354391 ],
 * //   [ -8, -8 ],
 * //   [ -7.999999999999999, -11.972846101323913 ],
 * //   [ -8, -19.31370849898476 ],
 * //   [ -8, -40.21871593700678 ]
 * // ]
 *
 */
let fft1d = (arr) => {
    return _fft1d(arr, 'norm')
}


/**
 * iFFT1D
 *
 * @param {Array} arr 輸入數據陣列
 * @return {Array} 回傳轉換後數據陣列
 * @example
 *
 * let arr
 * let res
 *
 * arr = [
 *     [120, 0],
 *     [-8, 40.21871593700678],
 *     [-8, 19.31370849898476],
 *     [-7.999999999999999, 11.972846101323913],
 *     [-8, 8],
 *     [-8, 5.345429103354391],
 *     [-8, 3.313708498984761],
 *     [-7.999999999999999, 1.5912989390372623],
 *     [-8, 0],
 *     [-7.999999999999999, -1.5912989390372623],
 *     [-8, -3.313708498984761],
 *     [-8, -5.345429103354391],
 *     [-8, -8],
 *     [-7.999999999999999, -11.972846101323913],
 *     [-8, -19.31370849898476],
 *     [-8, -40.21871593700678]
 * ]
 * res = wf.ifft1d(arr)
 * console.log(res)
 * // => [
 * //    0,                  1,
 * //    2, 3.0000000000000018,
 * //    4,  5.000000000000002,
 * //    6,                  7,
 * //    8,                  9,
 * //   10, 10.999999999999998,
 * //   12, 12.999999999999998,
 * //   14,                 15
 * // ]
 *
 */
let ifft1d = (arr) => {
    return _fft1d(arr, 'inv')
}


// function __fft2d(re, im) {
//     let tre = []
//     let tim = []
//     let i = 0
//     // x-axis
//     for (let y = 0; y < _n; y++) {
//         i = y * _n
//         for (let x1 = 0; x1 < _n; x1++) {
//             tre[x1] = re[x1 + i]
//             tim[x1] = im[x1 + i]
//         }
//         fft1d(tre, tim)
//         for (let x2 = 0; x2 < _n; x2++) {
//             re[x2 + i] = tre[x2]
//             im[x2 + i] = tim[x2]
//         }
//     }
//     // y-axis
//     for (let x = 0; x < _n; x++) {
//         for (let y1 = 0; y1 < _n; y1++) {
//             i = x + y1 * _n
//             tre[y1] = re[i]
//             tim[y1] = im[i]
//         }
//         fft1d(tre, tim)
//         for (let y2 = 0; y2 < _n; y2++) {
//             i = x + y2 * _n
//             re[i] = tre[y2]
//             im[i] = tim[y2]
//         }
//     }
// }


// function __ifft2d(re, im) {
//     let tre = []
//     let tim = []
//     let i = 0
//     // x-axis
//     for (let y = 0; y < _n; y++) {
//         i = y * _n
//         for (let x1 = 0; x1 < _n; x1++) {
//             tre[x1] = re[x1 + i]
//             tim[x1] = im[x1 + i]
//         }
//         ifft1d(tre, tim)
//         for (let x2 = 0; x2 < _n; x2++) {
//             re[x2 + i] = tre[x2]
//             im[x2 + i] = tim[x2]
//         }
//     }
//     // y-axis
//     for (let x = 0; x < _n; x++) {
//         for (let y1 = 0; y1 < _n; y1++) {
//             i = x + y1 * _n
//             tre[y1] = re[i]
//             tim[y1] = im[i]
//         }
//         ifft1d(tre, tim)
//         for (let y2 = 0; y2 < _n; y2++) {
//             i = x + y2 * _n
//             re[i] = tre[y2]
//             im[i] = tim[y2]
//         }
//     }
// }


// function fft2d(mat, mode = 'norm') {

//     //check
//     if (mode !== 'norm' && mode !== 'inv') {
//         throw new Error(`invalid mode[${mode}]`)
//     }

//     //m, n
//     let m = size(mat)
//     let n = size(get(mat, 0, []))
//     console.log('m', m)
//     console.log('n', n)

//     //nRows, nCols
//     let nRows = get2n(m)
//     let nCols = get2n(n)
//     console.log('nRows', nRows)
//     console.log('nCols', nCols)

//     //data
//     let data = new Array(nRows * nCols)
//     for (let i = 0; i < nRows; i++) {
//         for (let j = 0; j < nCols; j++) {
//             data[i * nCols + j] = get(mat, `${i}.${j}`, 0) //超過元素自動補0
//         }
//     }
//     console.log('data', data)

//     let res = []
//     if (mode === 'norm') {
//         let ftData = FFTUtils.fft2DArray(data, nRows, nCols)
//         console.log('ftData', ftData, size(ftData))
//     }
//     else {

//     }

//     // let ftData = FFTUtils.fft2DArray(data, nRows, nCols)
//     // let ftRows = nRows * 2
//     // let ftCols = nCols / 2 + 1
//     // let iftData = FFTUtils.ifft2DArray(ftData, ftRows, ftCols)
// }


let fft2d = (mat) => {
    //有問題待之後處理
    //return fft2d(mat, 'norm')
}
let ifft2d = (mat) => {
    //有問題待之後處理
    // return fft2d(mat, 'inv')
}


function spectrum1d(arr, dt) {

    //check dt
    if (!ispnum(dt)) {
        throw new Error(`dt[${dt}] is not a positive number`)
    }
    dt = cdbl(dt)

    //fft1d
    let rm = fft1d(arr)
    // console.log('rm', rm)
    // console.log('n1', size(arr))

    //n
    let n = size(rm)
    // console.log('n', n)

    //T
    let T = dt * (n - 1)
    // console.log('T', T)

    //df
    let df = 1 / T
    // console.log('df', df)

    //F
    let F = df * (n - 1)
    // console.log('F', F)

    //hzs
    // let hzs = range(df, F + df, df)
    let hzs = range(0, F, df)
    let hzsHalf = take(hzs, n / 2)
    // hzs = [
    //     ...hzsHalf,
    //     ...reverse(hzsHalf),
    // ]
    hzs = hzsHalf
    // console.log('hzs', JSON.stringify(hzs), size(hzs))

    //rs
    let rs = []
    each(hzs, (v, k) => {
        let freq = v
        let real = rm[k][0]
        let imag = rm[k][1]
        let ampl = Math.sqrt(rm[k][0] ** 2 + rm[k][1] ** 2)
        let r = {
            freq,
            real,
            imag,
            ampl,
        }
        rs.push(r)
    })

    return rs
}


/**
 * FFT1D Filter
 *
 * @param {Array} arr 輸入數據陣列
 * @param {Number} dt 輸入數據點時間間隔數字,單位s
 * @param {Number} hzStart 輸入過濾用帶通頻率下限數字,單位Hz
 * @param {Number} hzEnd 輸入過濾用帶通頻率上限數字,單位Hz
 * @return {Array} 回傳帶通處理後數據陣列
 * @example
 *
 * let dt
 * let arr
 * let res
 *
 * //dt=0.0078125s, 3+6hz
 * dt = 0.0078125
 *
 * arr = [
 *     0,
 *     0.437015151709824,
 *     0.845854910274064,
 *     1.20056554679302,
 *     1.47944976553089,
 *     1.66674368151922,
 *     1.75379573376597,
 *     1.73964987434863,
 *     1.63098631369783,
 *     1.44142799002054,
 *     1.19027504868833,
 *     0.900778315875612,
 *     0.598101848038141,
 *     0.307150781019375,
 *     0.0504516520458098,
 *     -0.153732804251563,
 *     -0.292893218813452,
 *     -0.361241031239776,
 *     -0.360072875476548,
 *     -0.297503430771426,
 *     -0.187593110348962,
 *     -0.0489494660021425,
 *     0.0970731816865672,
 *     0.228416556922734,
 *     0.324423348821458,
 *     0.367818520155133,
 *     0.346391996239585,
 *     0.254233601317238,
 *     0.0924099202087415,
 *     -0.130978839760706,
 *     -0.401370102712605,
 *     -0.698891832710318,
 *     -1,
 *     -1.27946118721924,
 *     -1.51251056875181,
 *     -1.67699974648618,
 *     -1.75534914481383,
 *     -1.73613585202716,
 *     -1.61517856456688,
 *     -1.39602400854158,
 *     -1.08979021355164,
 *     -0.714376916729265,
 *     -0.293107462345689,
 *     0.147084814656977,
 *     0.577773754381215,
 *     0.971283137555866,
 *     1.30286634912854,
 *     1.55263964022464,
 *     1.70710678118655,
 *     1.76014786721285,
 *     1.7133908766509,
 *     1.57593734934667,
 *     1.36346871276832,
 *     1.09681259653473,
 *     0.80009440465607,
 *     0.498634516368545,
 *     0.216772751324739,
 *     -0.0241926543480825,
 *     -0.207774827040493,
 *     -0.323625771825179,
 *     -0.368309299491684,
 *     -0.345455359932455,
 *     -0.265285555765141,
 *     -0.1435542027991,
 *     -3.67544536472586E-16,
 *     0.1435542027991,
 *     0.26528555576514,
 *     0.345455359932455,
 *     0.368309299491684,
 *     0.323625771825179,
 *     0.207774827040493,
 *     0.0241926543480835,
 *     -0.216772751324738,
 *     -0.498634516368544,
 *     -0.800094404656069,
 *     -1.09681259653473,
 *     -1.36346871276832,
 *     -1.57593734934667,
 *     -1.7133908766509,
 *     -1.76014786721285,
 *     -1.70710678118655,
 *     -1.55263964022464,
 *     -1.30286634912855,
 *     -0.971283137555864,
 *     -0.577773754381218,
 *     -0.14708481465698,
 *     0.293107462345686,
 *     0.714376916729258,
 *     1.08979021355163,
 *     1.39602400854158,
 *     1.61517856456688,
 *     1.73613585202716,
 *     1.75534914481383,
 *     1.67699974648618,
 *     1.51251056875181,
 *     1.27946118721924,
 *     1,
 *     0.698891832710321,
 *     0.40137010271261,
 *     0.130978839760704,
 *     -0.0924099202087405,
 *     -0.254233601317238,
 *     -0.346391996239584,
 *     -0.367818520155133,
 *     -0.324423348821457,
 *     -0.228416556922734,
 *     -0.0970731816865679,
 *     0.0489494660021418,
 *     0.18759311034896,
 *     0.297503430771423,
 *     0.360072875476548,
 *     0.361241031239776,
 *     0.292893218813452,
 *     0.153732804251566,
 *     -0.0504516520458086,
 *     -0.307150781019376,
 *     -0.598101848038137,
 *     -0.900778315875611,
 *     -1.19027504868833,
 *     -1.44142799002054,
 *     -1.63098631369783,
 *     -1.73964987434863,
 *     -1.75379573376597,
 *     -1.66674368151922,
 *     -1.47944976553089,
 *     -1.20056554679302,
 *     -0.845854910274064,
 *     -0.43701515170983,
 * ]
 *
 * res = wf.filter1d(arr, dt, 0, 2)
 * fs.writeFileSync('./filter1d_128p_0_2Hz.txt', _.join(_.map(res, w.cstr), '\n'), 'utf8')
 * console.log(res)
 * // => [
 * //     1.900130354665816e-17,   3.171289289530446e-17,   4.332415707367346e-17,
 * //    5.3695846472494235e-17,   6.269785986213638e-17,   7.021057013202281e-17,
 * //     7.612605570310054e-17,   8.034922556306299e-17,   8.279882688317987e-17,
 * //     8.340832529158958e-17,   8.212664909001537e-17,   7.891878999746445e-17,
 * //     7.376625437307838e-17,   6.666736029780011e-17,    5.76373773671691e-17,
 * //     4.670850755121418e-17,   3.392970699761463e-17,   1.936635017644406e-17,
 * //     3.099739274245762e-18, -1.4773536772583848e-17,  -3.414238777438079e-17,
 * //    -5.488214509861833e-17,  -7.685561326121688e-17,  -9.991424053039947e-17,
 * //   -1.2389939763250017e-16,  -1.486437526110982e-16, -1.7397272896700838e-16,
 * //   -1.9970603340589478e-16,  -2.256592388503064e-16,  -2.516454078402015e-16,
 * //     -2.77476741055675e-16, -3.0296623545138124e-16, -3.2792933639642097e-16,
 * //   -3.5218556826715847e-16, -3.7556012814304826e-16,  -3.978854276051409e-16,
 * //   -4.1900256813015115e-16,  -4.387627362050729e-16,  -4.570285050522503e-16,
 * //    -4.736750307452075e-16,  -4.885911315028551e-16,  -5.016802400642502e-16,
 * //    -5.128612202571727e-16,  -5.220690401697757e-16,  -5.292552957029921e-16,
 * //    -5.343885797091161e-16,  -5.374546933952765e-16,  -5.384566981752595e-16,
 * //    -5.374148076748214e-16,   -5.34366121119686e-16,  -5.293642008471598e-16,
 * //    -5.224784981672418e-16,  -5.137936332429572e-16,  -5.034085360485657e-16,
 * //    -4.914354567849328e-16,  -4.779988553710472e-16,  -4.632341807774995e-16,
 * //    -4.472865520107286e-16,  -4.303093534859731e-16,  -4.124627583332443e-16,
 * //    -3.939121938565287e-16, -3.7482676390538224e-16, -3.5537764331493207e-16,
 * //    -3.357364598212945e-16, -3.1607367896215424e-16,  -2.965570074258067e-16,
 * //    -2.773498301168155e-16,  -2.586096958645067e-16, -2.4048686621515183e-16,
 * //    -2.231229411245869e-16,  -2.066495746112324e-16, -1.9118729254734605e-16,
 * //   -1.7684442376738718e-16, -1.6371615456625556e-16, -1.5188371545758223e-16,
 * //   -1.4141370777480338e-16,  -1.323575763378787e-16, -1.2475123298934835e-16,
 * //   -1.1861483433865337e-16, -1.1395271555741253e-16, -1.1075348055508646e-16,
 * //   -1.0899024734876648e-16, -1.0862104593732587e-16, -1.0958936451335684e-16,
 * //   -1.1182483841044193e-16, -1.1524407480220627e-16,  -1.197516048565951e-16,
 * //   -1.2524095381656962e-16, -1.3159581833879772e-16, -1.3869133938591487e-16,
 * //   -1.4639545804553208e-16, -1.5457034084925898e-16, -1.6307386049529978e-16,
 * //   -1.7176111734511303e-16, -1.8048598667334478e-16, -1.8910267640450308e-16,
 * //    -1.974672799720086e-16,  -2.054393089862649e-16, -2.1288319059773535e-16,
 * //   -2.1966971478677226e-16,
 * //   ... 28 more items
 * // ]
 *
 * res = wf.filter1d(arr, dt, 0, 4)
 * fs.writeFileSync('./filter1d_128p_0_4Hz.txt', _.join(_.map(res, w.cstr), '\n'), 'utf8')
 * console.log(res)
 * // => [
 * //   -4.827486540753305e-16,    0.1467304744553614,  0.29028467725446216,
 * //      0.42755509343028203,    0.5555702330196024,   0.6715589548470189,
 * //       0.7730104533627374,    0.8577286100002727,   0.9238795325112873,
 * //        0.970031253194545,    0.9951847266721979,   0.9987954562051734,
 * //       0.9807852804032313,    0.9415440651830219,   0.8819212643483562,
 * //        0.803207531480646,    0.7071067811865485,   0.5956993044924345,
 * //       0.4713967368259986,   0.33688985339222094,  0.19509032201612894,
 * //     0.049067674327418584,  -0.09801714032956012, -0.24298017990326354,
 * //     -0.38268343236508945,   -0.5141027441932216,  -0.6343932841636454,
 * //      -0.7409511253549594,   -0.8314696123025456,  -0.9039892931234439,
 * //      -0.9569403357322095,   -0.9891765099647818,  -1.0000000000000007,
 * //       -0.989176509964782,   -0.9569403357322102,  -0.9039892931234446,
 * //      -0.8314696123025467,   -0.7409511253549607,  -0.6343932841636468,
 * //      -0.5141027441932232,  -0.38268343236509117,  -0.2429801799032654,
 * //     -0.09801714032956207,   0.04906767432741662,    0.195090322016127,
 * //      0.33688985339221894,   0.47139673682599664,   0.5956993044924324,
 * //       0.7071067811865467,    0.8032075314806444,   0.8819212643483545,
 * //       0.9415440651830205,    0.9807852804032301,   0.9987954562051723,
 * //       0.9951847266721967,    0.9700312531945441,   0.9238795325112867,
 * //       0.8577286100002725,    0.7730104533627374,   0.6715589548470189,
 * //       0.5555702330196027,    0.4275550934302826,   0.2902846772544628,
 * //      0.14673047445536228, 4.752026505856477e-16, -0.14673047445536128,
 * //     -0.29028467725446194,   -0.4275550934302818,  -0.5555702330196022,
 * //      -0.6715589548470184,    -0.773010453362737,  -0.8577286100002722,
 * //      -0.9238795325112868,   -0.9700312531945445,  -0.9951847266721975,
 * //      -0.9987954562051732,   -0.9807852804032311,  -0.9415440651830217,
 * //      -0.8819212643483559,   -0.8032075314806458,  -0.7071067811865485,
 * //      -0.5956993044924345,   -0.4713967368259986, -0.33688985339222105,
 * //     -0.19509032201612905,  -0.04906767432741875,  0.09801714032955992,
 * //      0.24298017990326332,    0.3826834323650892,   0.5141027441932213,
 * //       0.6343932841636452,     0.740951125354959,   0.8314696123025451,
 * //       0.9039892931234434,     0.956940335732209,   0.9891765099647813,
 * //       1.0000000000000002,    0.9891765099647816,   0.9569403357322097,
 * //       0.9039892931234439,
 * //   ... 28 more items
 * // ]
 *
 * res = wf.filter1d(arr, dt, 4, 8)
 * fs.writeFileSync('./filter1d_128p_4_8Hz.txt', _.join(_.map(res, w.cstr), '\n'), 'utf8')
 * console.log(res)
 * // => [
 * //   -5.79305934263002e-16,     0.2902846772544621,   0.5555702330196025,
 * //      0.7730104533627378,     0.9238795325112876,   0.9951847266721983,
 * //       0.980785280403232,     0.8819212643483566,   0.7071067811865489,
 * //       0.471396736825999,    0.19509032201612922, -0.09801714032956009,
 * //     -0.3826834323650895,    -0.6343932841636457,  -0.8314696123025459,
 * //     -0.9569403357322098,     -1.000000000000001,  -0.9569403357322103,
 * //     -0.8314696123025468,    -0.6343932841636469,   -0.382683432365091,
 * //    -0.09801714032956177,    0.19509032201612747,  0.47139673682599736,
 * //      0.7071067811865476,     0.8819212643483555,   0.9807852804032312,
 * //      0.9951847266721979,     0.9238795325112878,   0.7730104533627384,
 * //      0.5555702330196037,     0.2902846772544636, 1.11697531048158e-15,
 * //    -0.29028467725446144,    -0.5555702330196017,   -0.773010453362737,
 * //     -0.9238795325112868,    -0.9951847266721975,  -0.9807852804032314,
 * //     -0.8819212643483562,    -0.7071067811865488,  -0.4713967368259991,
 * //    -0.19509032201612952,     0.0980171403295596,   0.3826834323650889,
 * //       0.634393284163645,     0.8314696123025451,   0.9569403357322093,
 * //      1.0000000000000004,     0.9569403357322098,   0.8314696123025465,
 * //      0.6343932841636467,    0.38268343236509095,   0.0980171403295618,
 * //    -0.19509032201612736,   -0.47139673682599714,  -0.7071067811865472,
 * //     -0.8819212643483553,     -0.980785280403231,  -0.9951847266721975,
 * //     -0.9238795325112876,     -0.773010453362738,  -0.5555702330196034,
 * //    -0.29028467725446333, -8.663552482114443e-16,  0.29028467725446166,
 * //      0.5555702330196018,     0.7730104533627369,   0.9238795325112867,
 * //      0.9951847266721974,     0.9807852804032311,   0.8819212643483559,
 * //      0.7071067811865485,    0.47139673682599875,  0.19509032201612922,
 * //    -0.09801714032955984,   -0.38268343236508906,   -0.634393284163645,
 * //      -0.831469612302545,    -0.9569403357322089,  -1.0000000000000002,
 * //     -0.9569403357322096,    -0.8314696123025461,  -0.6343932841636465,
 * //     -0.3826834323650908,   -0.09801714032956171,   0.1950903220161273,
 * //     0.47139673682599703,     0.7071067811865471,   0.8819212643483548,
 * //      0.9807852804032305,     0.9951847266721973,   0.9238795325112874,
 * //       0.773010453362738,     0.5555702330196033,   0.2902846772544634,
 * //   9.714153559505677e-16,   -0.29028467725446144,  -0.5555702330196017,
 * //     -0.7730104533627368,
 * //   ... 28 more items
 * // ]
 *
 */
function filter1d(arr, dt, hzStart, hzEnd) {

    //check dt
    if (!ispnum(dt)) {
        throw new Error(`dt[${dt}] is not a positive number`)
    }
    dt = cdbl(dt)

    //check hzStart
    if (!isp0num(hzStart)) {
        throw new Error(`hzStart[${hzStart}] is not a positive number`)
    }
    hzStart = cdbl(hzStart)

    //check hzEnd
    if (!isp0num(hzEnd)) {
        throw new Error(`hzEnd[${hzEnd}] is not a positive number`)
    }
    hzEnd = cdbl(hzEnd)

    //fft1d
    let rm = fft1d(arr)
    // console.log('rm', rm)
    // console.log('n1', size(arr))

    //n
    let n = size(rm)
    // console.log('n', n)

    //T
    let T = dt * (n - 1)
    // console.log('T', T)

    //df
    let df = 1 / T
    // console.log('df', df)

    //F
    let F = df * (n - 1)
    // console.log('F', F)

    //hzs
    // let hzs = range(df, F + df, df)
    let hzs = range(0, F, df)
    let hzsHalf = take(hzs, n / 2)
    hzs = [
        ...hzsHalf,
        ...reverse(hzsHalf),
    ]
    // console.log('hzs', JSON.stringify(hzs), size(hzs))

    //帶通, 依照指定起訖頻率清除rm
    each(hzs, (v, k) => {
        let b = hzStart <= v && v <= hzEnd //允許通過
        if (!b) {
            rm[k][0] = 0
            rm[k][1] = 0
        }
    })
    // console.log('rm(clean)', JSON.stringify(rm))

    //ifft1d
    let res = ifft1d(rm)
    // console.log('res', res)

    return res
}


/**
 * FFT與iFFT
 *
 * @returns {Array} 回傳數據陣列
 * @example
 *
 * let arr
 * let res
 *
 * arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
 * res = wf.fft1d(arr)
 * console.log(res)
 * // => [
 * //   [ 120, 0 ],
 * //   [ -8, 40.21871593700678 ],
 * //   [ -8, 19.31370849898476 ],
 * //   [ -7.999999999999999, 11.972846101323913 ],
 * //   [ -8, 8 ],
 * //   [ -8, 5.345429103354391 ],
 * //   [ -8, 3.313708498984761 ],
 * //   [ -7.999999999999999, 1.5912989390372623 ],
 * //   [ -8, 0 ],
 * //   [ -7.999999999999999, -1.5912989390372623 ],
 * //   [ -8, -3.313708498984761 ],
 * //   [ -8, -5.345429103354391 ],
 * //   [ -8, -8 ],
 * //   [ -7.999999999999999, -11.972846101323913 ],
 * //   [ -8, -19.31370849898476 ],
 * //   [ -8, -40.21871593700678 ]
 * // ]
 *
 * arr = [
 *     [120, 0],
 *     [-8, 40.21871593700678],
 *     [-8, 19.31370849898476],
 *     [-7.999999999999999, 11.972846101323913],
 *     [-8, 8],
 *     [-8, 5.345429103354391],
 *     [-8, 3.313708498984761],
 *     [-7.999999999999999, 1.5912989390372623],
 *     [-8, 0],
 *     [-7.999999999999999, -1.5912989390372623],
 *     [-8, -3.313708498984761],
 *     [-8, -5.345429103354391],
 *     [-8, -8],
 *     [-7.999999999999999, -11.972846101323913],
 *     [-8, -19.31370849898476],
 *     [-8, -40.21871593700678]
 * ]
 * res = wf.ifft1d(arr)
 * console.log(res)
 * // => [
 * //    0,                  1,
 * //    2, 3.0000000000000018,
 * //    4,  5.000000000000002,
 * //    6,                  7,
 * //    8,                  9,
 * //   10, 10.999999999999998,
 * //   12, 12.999999999999998,
 * //   14,                 15
 * // ]
 *
 */
let WFft = {

    fft1d,
    ifft1d,

    fft2d,
    ifft2d,

    spectrum1d,
    // spectrum2d,

    filter1d,
    // filter2d,

}


export default WFft