From 1b139719c0a78334d25f9c7afc81de8878e25510 Mon Sep 17 00:00:00 2001 From: "Dmitry Iv." Date: Wed, 25 Mar 2026 23:24:34 -0400 Subject: [PATCH 01/26] Update the docs --- .gitignore | 1 + .npmignore | 4 + .travis.yml | 5 - README.md | 346 +++++++++++++++--------- apply.js | 17 -- bartlett-hann.js | 12 - bartlett.js | 7 - blackman-harris.js | 13 - blackman-nuttall.js | 13 - blackman.js | 12 - cosine.js | 7 - docs/data/bartlett-hann.csv | 81 ------ docs/data/bartlett.csv | 81 ------ docs/data/blackman-harris.csv | 81 ------ docs/data/blackman-nuttall.csv | 81 ------ docs/data/blackman.csv | 81 ------ docs/data/cosine.csv | 81 ------ docs/data/exact-blackman.csv | 81 ------ docs/data/flattop.csv | 81 ------ docs/data/gaussian.csv | 81 ------ docs/data/hamming.csv | 81 ------ docs/data/hann.csv | 81 ------ docs/data/lanczos.csv | 81 ------ docs/data/nuttall.csv | 81 ------ docs/data/rectangular.csv | 81 ------ docs/data/triangular.csv | 81 ------ docs/data/tukey.csv | 81 ------ docs/data/welch.csv | 81 ------ docs/equations/bartlett-hann-1.png | Bin 1984 -> 0 bytes docs/equations/bartlett-hann-2.png | Bin 1017 -> 0 bytes docs/equations/blackman-1.png | Bin 2073 -> 0 bytes docs/equations/blackman-2.png | Bin 904 -> 0 bytes docs/equations/blackman-harris-1.png | Bin 2545 -> 0 bytes docs/equations/blackman-harris-2.png | Bin 1611 -> 0 bytes docs/equations/blackman-nuttall-1.png | Bin 2545 -> 0 bytes docs/equations/blackman-nuttall-2.png | Bin 1531 -> 0 bytes docs/equations/cosine.png | Bin 1559 -> 0 bytes docs/equations/flattop-1.png | Bin 2838 -> 0 bytes docs/equations/flattop-2.png | Bin 1388 -> 0 bytes docs/equations/gaussian-1.png | Bin 971 -> 0 bytes docs/equations/gaussian-2.png | Bin 420 -> 0 bytes docs/equations/hamming-1.png | Bin 1328 -> 0 bytes docs/equations/hamming-2.png | Bin 761 -> 0 bytes docs/equations/hann.png | Bin 1528 -> 0 bytes docs/equations/lanczos.png | Bin 1279 -> 0 bytes docs/equations/nuttall-1.png | Bin 2545 -> 0 bytes docs/equations/nuttall-2.png | Bin 1740 -> 0 bytes docs/equations/rectangular.png | Bin 462 -> 0 bytes docs/equations/triangular.png | Bin 1109 -> 0 bytes docs/equations/tukey.png | Bin 5841 -> 0 bytes docs/equations/welch.png | Bin 1187 -> 0 bytes docs/generate.js | 216 +++++++-------- docs/plot.py | 65 ----- docs/plots/bartlett-hann.png | Bin 38995 -> 0 bytes docs/plots/bartlett.png | Bin 38612 -> 0 bytes docs/plots/bartlett.svg | 45 ++++ docs/plots/bartlettHann.svg | 45 ++++ docs/plots/blackman-harris.png | Bin 35826 -> 0 bytes docs/plots/blackman-nuttall.png | Bin 35419 -> 0 bytes docs/plots/blackman.png | Bin 35160 -> 0 bytes docs/plots/blackman.svg | 45 ++++ docs/plots/blackmanHarris.svg | 45 ++++ docs/plots/blackmanNuttall.svg | 45 ++++ docs/plots/bohman.svg | 45 ++++ docs/plots/cauchy.svg | 45 ++++ docs/plots/confinedGaussian.svg | 45 ++++ docs/plots/connes.svg | 45 ++++ docs/plots/cosine.png | Bin 33944 -> 0 bytes docs/plots/cosine.svg | 45 ++++ docs/plots/dolphChebyshev.svg | 45 ++++ docs/plots/dpss.svg | 45 ++++ docs/plots/exact-blackman.png | Bin 35361 -> 0 bytes docs/plots/exactBlackman.svg | 45 ++++ docs/plots/exponential.svg | 45 ++++ docs/plots/flatTop.svg | 51 ++++ docs/plots/flattop.png | Bin 33519 -> 0 bytes docs/plots/gaussian.png | Bin 34331 -> 0 bytes docs/plots/gaussian.svg | 45 ++++ docs/plots/generalizedNormal.svg | 45 ++++ docs/plots/hamming.png | Bin 33455 -> 0 bytes docs/plots/hamming.svg | 45 ++++ docs/plots/hann.png | Bin 34501 -> 0 bytes docs/plots/hann.svg | 45 ++++ docs/plots/hannPoisson.svg | 45 ++++ docs/plots/kaiser.svg | 45 ++++ docs/plots/kaiserBesselDerived.svg | 45 ++++ docs/plots/lanczos.png | Bin 34112 -> 0 bytes docs/plots/lanczos.svg | 45 ++++ docs/plots/nuttall.png | Bin 34327 -> 0 bytes docs/plots/nuttall.svg | 45 ++++ docs/plots/parzen.svg | 45 ++++ docs/plots/planckTaper.svg | 45 ++++ docs/plots/powerOfSine.svg | 45 ++++ docs/plots/rectangular.png | Bin 27622 -> 0 bytes docs/plots/rectangular.svg | 45 ++++ docs/plots/rifeVincent.svg | 45 ++++ docs/plots/taylor.svg | 45 ++++ docs/plots/triangular.png | Bin 35842 -> 0 bytes docs/plots/triangular.svg | 45 ++++ docs/plots/tukey.png | Bin 36118 -> 0 bytes docs/plots/tukey.svg | 45 ++++ docs/plots/ultraspherical.svg | 45 ++++ docs/plots/welch.png | Bin 33843 -> 0 bytes docs/plots/welch.svg | 45 ++++ exact-blackman.js | 12 - flat-top.js | 14 - gaussian.js | 10 - generate.js | 17 -- hamming.js | 7 - hann.js | 7 - index.d.ts | 56 ++++ index.js | 367 ++++++++++++++++++++++++-- lanczos.js | 11 - nuttall.js | 15 -- package-lock.json | 26 ++ package.json | 39 ++- rectangular.js | 7 - sinc.js | 0 test.js | 280 ++++++++++++++++---- triangular.js | 7 - tukey.js | 15 -- welch.js | 9 - 122 files changed, 2543 insertions(+), 1987 deletions(-) create mode 100644 .npmignore delete mode 100644 .travis.yml delete mode 100644 apply.js delete mode 100644 bartlett-hann.js delete mode 100644 bartlett.js delete mode 100644 blackman-harris.js delete mode 100644 blackman-nuttall.js delete mode 100644 blackman.js delete mode 100644 cosine.js delete mode 100644 docs/data/bartlett-hann.csv delete mode 100644 docs/data/bartlett.csv delete mode 100644 docs/data/blackman-harris.csv delete mode 100644 docs/data/blackman-nuttall.csv delete mode 100644 docs/data/blackman.csv delete mode 100644 docs/data/cosine.csv delete mode 100644 docs/data/exact-blackman.csv delete mode 100644 docs/data/flattop.csv delete mode 100644 docs/data/gaussian.csv delete mode 100644 docs/data/hamming.csv delete mode 100644 docs/data/hann.csv delete mode 100644 docs/data/lanczos.csv delete mode 100644 docs/data/nuttall.csv delete mode 100644 docs/data/rectangular.csv delete mode 100644 docs/data/triangular.csv delete mode 100644 docs/data/tukey.csv delete mode 100644 docs/data/welch.csv delete mode 100644 docs/equations/bartlett-hann-1.png delete mode 100644 docs/equations/bartlett-hann-2.png delete mode 100644 docs/equations/blackman-1.png delete mode 100644 docs/equations/blackman-2.png delete mode 100644 docs/equations/blackman-harris-1.png delete mode 100644 docs/equations/blackman-harris-2.png delete mode 100644 docs/equations/blackman-nuttall-1.png delete mode 100644 docs/equations/blackman-nuttall-2.png delete mode 100644 docs/equations/cosine.png delete mode 100644 docs/equations/flattop-1.png delete mode 100644 docs/equations/flattop-2.png delete mode 100644 docs/equations/gaussian-1.png delete mode 100644 docs/equations/gaussian-2.png delete mode 100644 docs/equations/hamming-1.png delete mode 100644 docs/equations/hamming-2.png delete mode 100644 docs/equations/hann.png delete mode 100644 docs/equations/lanczos.png delete mode 100644 docs/equations/nuttall-1.png delete mode 100644 docs/equations/nuttall-2.png delete mode 100644 docs/equations/rectangular.png delete mode 100644 docs/equations/triangular.png delete mode 100644 docs/equations/tukey.png delete mode 100644 docs/equations/welch.png delete mode 100644 docs/plot.py delete mode 100644 docs/plots/bartlett-hann.png delete mode 100644 docs/plots/bartlett.png create mode 100644 docs/plots/bartlett.svg create mode 100644 docs/plots/bartlettHann.svg delete mode 100644 docs/plots/blackman-harris.png delete mode 100644 docs/plots/blackman-nuttall.png delete mode 100644 docs/plots/blackman.png create mode 100644 docs/plots/blackman.svg create mode 100644 docs/plots/blackmanHarris.svg create mode 100644 docs/plots/blackmanNuttall.svg create mode 100644 docs/plots/bohman.svg create mode 100644 docs/plots/cauchy.svg create mode 100644 docs/plots/confinedGaussian.svg create mode 100644 docs/plots/connes.svg delete mode 100644 docs/plots/cosine.png create mode 100644 docs/plots/cosine.svg create mode 100644 docs/plots/dolphChebyshev.svg create mode 100644 docs/plots/dpss.svg delete mode 100644 docs/plots/exact-blackman.png create mode 100644 docs/plots/exactBlackman.svg create mode 100644 docs/plots/exponential.svg create mode 100644 docs/plots/flatTop.svg delete mode 100644 docs/plots/flattop.png delete mode 100644 docs/plots/gaussian.png create mode 100644 docs/plots/gaussian.svg create mode 100644 docs/plots/generalizedNormal.svg delete mode 100644 docs/plots/hamming.png create mode 100644 docs/plots/hamming.svg delete mode 100644 docs/plots/hann.png create mode 100644 docs/plots/hann.svg create mode 100644 docs/plots/hannPoisson.svg create mode 100644 docs/plots/kaiser.svg create mode 100644 docs/plots/kaiserBesselDerived.svg delete mode 100644 docs/plots/lanczos.png create mode 100644 docs/plots/lanczos.svg delete mode 100644 docs/plots/nuttall.png create mode 100644 docs/plots/nuttall.svg create mode 100644 docs/plots/parzen.svg create mode 100644 docs/plots/planckTaper.svg create mode 100644 docs/plots/powerOfSine.svg delete mode 100644 docs/plots/rectangular.png create mode 100644 docs/plots/rectangular.svg create mode 100644 docs/plots/rifeVincent.svg create mode 100644 docs/plots/taylor.svg delete mode 100644 docs/plots/triangular.png create mode 100644 docs/plots/triangular.svg delete mode 100644 docs/plots/tukey.png create mode 100644 docs/plots/tukey.svg create mode 100644 docs/plots/ultraspherical.svg delete mode 100644 docs/plots/welch.png create mode 100644 docs/plots/welch.svg delete mode 100644 exact-blackman.js delete mode 100644 flat-top.js delete mode 100644 gaussian.js delete mode 100644 generate.js delete mode 100644 hamming.js delete mode 100644 hann.js create mode 100644 index.d.ts delete mode 100644 lanczos.js delete mode 100644 nuttall.js create mode 100644 package-lock.json delete mode 100644 rectangular.js delete mode 100644 sinc.js delete mode 100644 triangular.js delete mode 100644 tukey.js delete mode 100644 welch.js diff --git a/.gitignore b/.gitignore index cc6c4b6..49e1f75 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,4 @@ Desktop.ini # Compiled MEX binaries (all platforms) *.mex* +/.claude diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..9c1fe68 --- /dev/null +++ b/.npmignore @@ -0,0 +1,4 @@ +docs/ +test.js +.travis.yml +.claude/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 50a1d86..0000000 --- a/.travis.yml +++ /dev/null @@ -1,5 +0,0 @@ -language: node_js -node_js: - - "0.12" - - "0.11" - - "0.10" diff --git a/README.md b/README.md index a609c50..1d15e22 100644 --- a/README.md +++ b/README.md @@ -1,197 +1,285 @@ # window-function -[![Build Status](https://travis-ci.org/scijs/window-function.svg?branch=master)](https://travis-ci.org/scijs/window-function) [![npm version](https://badge.fury.io/js/window-function.svg)](http://badge.fury.io/js/window-function) [![Dependency Status](https://david-dm.org/scijs/window-function.svg)](https://david-dm.org/scijs/window-function) +Complete, encyclopedic collection of window functions for signal processing and spectral analysis. +**34 window functions** in a single file. Zero dependencies. Pure ESM. -Window functions for digital signal processing +Covers every window function in scipy.signal.windows, MATLAB Signal Processing Toolbox, Harris (1978) "On the Use of Windows for Harmonic Analysis with the DFT", and the Wikipedia "Window function" article — plus scientific niche windows used in FTIR spectroscopy (Connes), gravitational wave detection (Planck-taper), audio codecs (KBD), multitaper estimation (DPSS), antenna design (Ultraspherical), and time-frequency optimization (Confined Gaussian). -# Introduction -Among other uses, [window functions](http://en.wikipedia.org/wiki/Window_function) help control [spectral leakage](http://en.wikipedia.org/wiki/Spectral_leakage) when doing Fourier Analysis. This collection of window functions is copied directly from Wikipedia. Caveat emptor. +## Why so many windows? -## Usage +Every window is a tradeoff. You can optimize for: + +- **Frequency resolution** — narrow main lobe, distinguish close frequencies +- **Spectral leakage** — low sidelobes, weak signals aren't masked by strong ones +- **Amplitude accuracy** — flat main lobe top, measured amplitudes are correct -Apply window to a signal: +No single window optimizes all three. The rectangular window has the best resolution but worst leakage (-13 dB). The flat-top has the best amplitude accuracy but worst resolution. The Hann window is the go-to general-purpose compromise. Everything else exists because different measurement contexts shift the tradeoff. -```javascript -var blackmanHarris = require('window-function/blackman-harris') -var applyWindow = require('window-function/apply') +## Usage -var signal = [-1, 0, 1, 0, -1, 0] +```js +import { hann, kaiser, generate, apply } from 'window-function' -var windowedSignal = applyWindow(signal, blackmanHarris) +hann(50, 101) // → 1.0 (single sample) +generate(hann, 1024) // → Float64Array(1024) +generate(kaiser, 1024, 8.6) // → parameterized window +apply(signal, kaiser, 8.6) // → signal, windowed in-place ``` -Apply the window functions yourself: +Compare windows quantitatively: -```javascript -var wfuncs = require('window-function') +```js +import { hann, blackmanHarris, enbw, scallopLoss, cola } from 'window-function' -var value = wfuncs.blackmanHarris( 50, 101 ) +enbw(hann, 1024) // → 1.5 (noise bandwidth in bins) +scallopLoss(hann, 1024) // → 1.42 dB (worst-case amplitude error) +cola(hann, 1024, 512) // → 0 (perfect STFT reconstruction at 50% overlap) ``` ## API -### `require('window-funciton/')(i, total)` - -To calculate the value of a window function, pass the sample number `i` and `total` number of samples to one of the window functions listed below, along with any additional parameters it may require. The plots below are calculated from the npm module and plotted with Fourier transform to illustrate the spectral leakage. See [the Wikipedia page on window functions](http://en.wikipedia.org/wiki/Window_function) for more details. - -- [Bartlett-Hann](#bartletthann-i-n-) -- [Bartlett](#bartlett-i-n-) -- [Blackman-Harris](#blackmanharris-i-n-) -- [Blackman-Nuttall](#blackmannuttall-i-n-) -- [Cosine](#cosine-i-n-) -- [Exact Blackman](#exactblackman-i-n-) -- [Flat top](#flattop-i-n-) -- [Gaussian](#gaussian-i-n-sigma-) -- [Hamming](#hamming-i-n-) -- [Hann](#hann-i-n-) -- [Lanczos](#lanczos-i-n-) -- [Nuttall](#nuttall-i-n-) -- [Rectangular](#rectangular-i-n-) -- [Triangular](#triangular-i-n-) -- [Tukey](#tukey-i-n-alpha-) -- [Welch](#welch-i-n-) - - -#### `bartlettHann( i, N )`: - -![Bartlett-Hann Window Equation 1](docs/equations/bartlett-hann-1.png) - -![Bartlett-Hann Window Equation 2](docs/equations/bartlett-hann-2.png) - -![Bartlett-Hann](docs/plots/bartlett-hann.png) - -#### `bartlett( i, N )`: - -![Bartlett](docs/plots/bartlett.png) - -#### `blackman( i, N )`: - -![Blackman Window Equation 1](docs/equations/blackman-1.png) +Every window: `fn(i, N, ...params) → number` — sample `i` of window length `N`. + +| Function | Parameters | Returns | +|---|---|---| +| `generate(fn, N, ...params)` | window function, length, params | `Float64Array` | +| `apply(signal, fn, ...params)` | signal array, window function, params | `signal` (modified) | +| `enbw(fn, N, ...params)` | window function, length, params | Equivalent noise bandwidth (bins) | +| `scallopLoss(fn, N, ...params)` | window function, length, params | Worst-case amplitude error (dB) | +| `cola(fn, N, hop, ...params)` | window function, length, hop, params | COLA deviation (0 = perfect) | + +## Complete Window Reference + +### Simple — no parameters + +| Window | Peak sidelobe | Rolloff | What it does | When to use | +|---|---|---|---|---| +| `rectangular` | -13 dB | -6 dB/oct | No windowing at all | Transient signals already zero at edges; harmonic analysis with integer cycles | +| `triangular` | -27 dB | -12 dB/oct | Linear taper, nonzero endpoints | Simple smoothing, 2nd-order B-spline | +| `bartlett` | -27 dB | -12 dB/oct | Linear taper, zero endpoints | Bartlett's method PSD estimation. Bartlett 1950 | +| `welch` | -21 dB | -12 dB/oct | Parabolic taper | Welch's method PSD estimation. Welch 1967 | +| `connes` | — | -24 dB/oct | Welch squared (4th power parabolic) | FTIR spectroscopy, interferogram apodization. Connes 1961 | +| `hann` | -32 dB | -18 dB/oct | Raised cosine, zero endpoints | General-purpose spectral analysis, STFT with 50% overlap (COLA). The default choice. Blackman & Tukey 1958 | +| `hamming` | -43 dB | -6 dB/oct | Raised cosine, nonzero endpoints | FIR filter design (window method), speech processing. Hamming 1977 | +| `cosine` | -23 dB | -12 dB/oct | Half-period sine | MDCT audio codecs: MP3, AAC, Vorbis. Princen & Bradley 1987 | +| `blackman` | -58 dB | -18 dB/oct | 3-term cosine sum | Spectral analysis needing better leakage than Hann. Blackman & Tukey 1958 | +| `exactBlackman` | -69 dB | -6 dB/oct | Blackman with exact zero placement | Precision analysis, zeros at 3rd/4th sidelobes. Harris 1978 | +| `nuttall` | -93 dB | -18 dB/oct | 4-term, continuous 1st derivative | High-dynamic-range analysis without edge discontinuity. Nuttall 1981 | +| `blackmanNuttall` | -98 dB | -6 dB/oct | 4-term, lowest sidelobes | Maximum sidelobe suppression among 4-term windows. Nuttall 1981 | +| `blackmanHarris` | -92 dB | -6 dB/oct | 4-term, minimum sidelobe | ADC testing, measurement instrumentation, >80 dB dynamic range. Harris 1978 | +| `flatTop` | -93 dB | -6 dB/oct | 5-term, near-zero scalloping. Peak ~4.64 | Amplitude calibration, transducer calibration (~0.01 dB accuracy). ISO 18431. Heinzel 2002 | +| `bartlettHann` | -36 dB | — | Bartlett × Hann hybrid | Balanced near/far sidelobe levels. Ha & Pearce 1989 | +| `lanczos` | -26 dB | — | Sinc main lobe | Image resampling, interpolation (FFmpeg, ImageMagick). Duchon 1979 | +| `parzen` | -53 dB | -24 dB/oct | 4th-order B-spline | Kernel density estimation, always-positive spectrum. Parzen 1961 | +| `bohman` | -46 dB | -24 dB/oct | Autocorrelation of cosine window | Fast sidelobe decay, spectral estimation | + +

+rectangular +triangular +bartlett +welch +connes +hann +hamming +cosine +blackman +exactBlackman +nuttall +blackmanNuttall +blackmanHarris +flatTop +bartlettHann +lanczos +parzen +bohman +

+ +
+Formulas + +$$w(n) = 1 \quad \text{(rectangular)}$$ -![Blackman Window Equation 2](docs/equations/blackman-2.png) +$$w(n) = 1 - \left|\frac{2n - N + 1}{N}\right| \quad \text{(triangular)}$$ -![Blackman](docs/plots/blackman.png) +$$w(n) = 1 - \left|\frac{2n - N + 1}{N - 1}\right| \quad \text{(bartlett)}$$ -#### `blackmanHarris( i, N )`: +$$w(n) = 1 - \left(\frac{2n - N + 1}{N - 1}\right)^2 \quad \text{(welch)}$$ -![Blackman-Harris Window Equation 1](docs/equations/blackman-harris-1.png) +$$w(n) = \left[1 - \left(\frac{2n - N + 1}{N - 1}\right)^2\right]^2 \quad \text{(connes)}$$ -![Blackman-Harris Window Equation 2](docs/equations/blackman-harris-2.png) +$$w(n) = 0.5 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right) \quad \text{(hann)}$$ -![Blackman-Harris](docs/plots/blackman-harris.png) +$$w(n) = 0.54 - 0.46\cos\!\left(\frac{2\pi n}{N-1}\right) \quad \text{(hamming)}$$ -#### `blackmanNuttall( i, N )`: +$$w(n) = \sin\!\left(\frac{\pi n}{N-1}\right) \quad \text{(cosine)}$$ -![Blackman-Nuttall Window Equation 1](docs/equations/blackman-nuttall-1.png) +$$w(n) = \sum_{k=0}^{K} (-1)^k\, a_k \cos\!\left(\frac{2\pi k n}{N-1}\right) \quad \text{(cosine-sum family: blackman, nuttall, etc.)}$$ -![Blackman-Nuttall Window Equation 2](docs/equations/blackman-nuttall-2.png) +$$w(n) = 0.62 - 0.48\left|\frac{n}{N-1} - 0.5\right| - 0.38\cos\!\left(\frac{2\pi n}{N-1}\right) \quad \text{(bartlettHann)}$$ -![Blackman-nuttall](docs/plots/blackman-nuttall.png) +$$w(n) = \operatorname{sinc}\!\left(\frac{2n}{N-1} - 1\right) \quad \text{(lanczos)}$$ -#### `cosine( i, N )`: +$$w(n) = \begin{cases} 1 - 6a^2(1-a) & |a| \le 0.5 \\ 2(1-a)^3 & |a| > 0.5 \end{cases},\quad a = \left|\frac{2n-N+1}{N-1}\right| \quad \text{(parzen)}$$ -![Cosine Window Equation 1](docs/equations/cosine.png) +$$w(n) = (1-|a|)\cos(\pi|a|) + \frac{\sin(\pi|a|)}{\pi},\quad a = \frac{2n-N+1}{N-1} \quad \text{(bohman)}$$ -![Cosine](docs/plots/cosine.png) +
-#### `exactBlackman( i, N )`: +### Parameterized — adjustable tradeoff -![Exact Blackman](docs/plots/exact-blackman.png) +| Window | Parameters | What it does | When to use | +|---|---|---|---| +| `kaiser(i, N, beta)` | `beta`: 0→rect, 5.4→Hamming, 8.6→Blackman | Near-optimal DPSS approximation via Bessel I0 | FIR filter design — the standard parameterized window. Kaiser 1974 | +| `gaussian(i, N, sigma)` | `sigma`: width, default 0.4 | Gaussian bell, minimum time-bandwidth product | STFT/Gabor transform, frequency estimation via parabolic interpolation. Gabor 1946 | +| `generalizedNormal(i, N, sigma, p)` | `sigma`, `p`: shape (2=Gaussian, →∞=rect) | Continuous family between Gaussian and rectangular | Adjustable time-frequency tradeoff, controllable flat-top width | +| `tukey(i, N, alpha)` | `alpha`: 0→rect, 1→Hann | Flat center with cosine-tapered edges | Preserving signal amplitude while tapering edges. Vibration analysis, LIGO | +| `planckTaper(i, N, epsilon)` | `epsilon`: taper fraction, default 0.1 | C∞-smooth bump function (infinitely differentiable) | Gravitational wave analysis (LIGO/Virgo). McKechan 2010 | +| `powerOfSine(i, N, alpha)` | `alpha`: 0→rect, 1→cosine, 2→Hann | `sin^α` family | Codec design, parameterized spectral analysis | +| `exponential(i, N, tau)` | `tau`: time constant, default 1 | Exponential decay from center | Modal analysis, impact testing (compensates underdamped responses). Harris 1978 | +| `hannPoisson(i, N, alpha)` | `alpha`: ≥2 → no sidelobes | Hann × exponential product | Frequency estimators using convex optimization — unique no-sidelobe property | +| `cauchy(i, N, alpha)` | `alpha`: width, default 3 | Lorentzian 1/(1+x²) shape | Spectroscopy (matches spectral line shapes). Harris 1978 | +| `rifeVincent(i, N, order)` | `order`: 1=Hann, 2, 3 | Cosine-sum optimized for sidelobe fall-off | Power grid harmonic analysis, interpolated DFT. Rife & Vincent 1970 | +| `confinedGaussian(i, N, sigmaT)` | `sigmaT`: temporal width, default 0.1 | Optimal RMS time-frequency bandwidth | Time-frequency analysis, audio coding (MP3/AAC). Starosielec 2014 | -The same as the Blackman window, except a0 = 0.42659 a1 = 0.49656, and a2 = 0.076849. These place zeros at the third and fourth sidelobes. +

+kaiser +gaussian +generalizedNormal +tukey +planckTaper +powerOfSine +exponential +hannPoisson +cauchy +rifeVincent +confinedGaussian +

-#### `flatTop( i, N )`: +
+Formulas -![Flat top Window Equation 1](docs/equations/flattop-1.png) +$$w(n) = \frac{I_0\!\left(\beta\sqrt{1 - \left(\frac{2n-N+1}{N-1}\right)^2}\right)}{I_0(\beta)} \quad \text{(kaiser)}$$ -![Flat top Window Equation 2](docs/equations/flattop-2.png) +$$w(n) = \exp\!\left[-\frac{1}{2}\left(\frac{2n-N+1}{\sigma(N-1)}\right)^2\right] \quad \text{(gaussian)}$$ -![Flat Top](docs/plots/flattop.png) +$$w(n) = \exp\!\left[-\frac{1}{2}\left|\frac{2n-N+1}{\sigma(N-1)}\right|^p\right] \quad \text{(generalizedNormal)}$$ -#### `gaussian( i, N, sigma )`: +$$w(n) = \begin{cases} \frac{1}{2}\left[1+\cos\!\left(\pi\!\left(\frac{n}{\alpha(N-1)/2}-1\right)\right)\right] & n \le \frac{\alpha(N-1)}{2} \\ 1 & \text{center} \\ \text{symmetric} & n \ge N-1-\frac{\alpha(N-1)}{2} \end{cases} \quad \text{(tukey)}$$ -Sigma controls the width of the window. +$$w(n) = \sin^\alpha\!\left(\frac{\pi n}{N-1}\right) \quad \text{(powerOfSine)}$$ -![Gaussian Window Equation 1](docs/equations/gaussian-1.png) +$$w(n) = \exp\!\left(\frac{-|2n-N+1|}{\tau(N-1)}\right) \quad \text{(exponential)}$$ -![Gaussian Window Equation 2](docs/equations/gaussian-2.png) +$$w(n) = \frac{1}{2}\left(1-\cos\frac{2\pi n}{N-1}\right)\exp\!\left(\frac{-\alpha|2n-N+1|}{N-1}\right) \quad \text{(hannPoisson)}$$ -![Gaussian](docs/plots/gaussian.png) +$$w(n) = \frac{1}{1+\left(\frac{\alpha(2n-N+1)}{N-1}\right)^2} \quad \text{(cauchy)}$$ -#### `hamming( i, N )`: +$$w(n) = \frac{1}{Z}\sum_{k=0}^{K}(-1)^k a_k\cos\frac{2\pi kn}{N-1} \quad \text{(rifeVincent, class I)}$$ -![Hamming Window Equation 1](docs/equations/hamming-1.png) +
-![Hamming Window Equation 2](docs/equations/hamming-2.png) +### Array-computed — require full-window computation (cached) -![Hamming](docs/plots/hamming.png) +| Window | Parameters | What it does | When to use | +|---|---|---|---| +| `dolphChebyshev(i, N, dB)` | `dB`: sidelobe attenuation, default 100 | Optimal: narrowest main lobe for given equiripple sidelobe level | Antenna array design, radar beam patterns. Dolph 1946 | +| `taylor(i, N, nbar, sll)` | `nbar`: constant lobes (4), `sll`: level dB (30) | Dolph-Chebyshev variant with monotonically decreasing sidelobes | Radar, SAR image formation — the radar community standard. Taylor 1955 | +| `kaiserBesselDerived(i, N, beta)` | `beta`: shape, default 8.6. N must be even | Princen-Bradley condition for perfect MDCT reconstruction | AAC, Vorbis, Opus audio codecs (long blocks). Princen & Bradley 1987 | +| `dpss(i, N, W)` | `W`: half-bandwidth [0, 0.5], default 0.1 | Provably optimal energy concentration in frequency band | Thomson multitaper spectral estimation, neuroscience (EEG/MEG), climate science. Slepian 1978 | +| `ultraspherical(i, N, mu, xmu)` | `mu`: 0→Dolph-Cheb, 1→Saramaki; `xmu`: sidelobe control | Gegenbauer polynomial window — independent sidelobe level and taper rate | Advanced antenna design, beamforming. Streit 1984 | -#### `hann( i, N )`: +

+dolphChebyshev +taylor +kaiserBesselDerived +dpss +ultraspherical +

-![Hann Window Equation](docs/equations/hann.png) +
+Formulas -![Hann](docs/plots/hann.png) +$$W(k) = (-1)^k T_{N-1}\!\left(\beta\cos\frac{\pi k}{N}\right),\quad w = \operatorname{IDFT}(W) \quad \text{(dolphChebyshev)}$$ -#### `lanczos( i, N )`: +$$w(n) = 1 + 2\sum_{m=1}^{\bar{n}-1} F_m \cos\frac{2\pi m(n-(N-1)/2)}{N} \quad \text{(taylor)}$$ -![Lanczos Window Equation](docs/equations/lanczos.png) +$$w(n) = \sqrt{\frac{\sum_{j=0}^{n} K(j)}{\sum_{j=0}^{N/2} K(j)}},\quad K(j) = I_0\!\left(\beta\sqrt{1-\left(\frac{2j-N/2}{N/2}\right)^2}\right) \quad \text{(kaiserBesselDerived)}$$ -![Lanczos](docs/plots/lanczos.png) +$$\mathbf{T}\mathbf{v} = \lambda\mathbf{v},\quad T_{jk} = \frac{\sin 2\pi W(j-k)}{\pi(j-k)} \quad \text{(dpss — dominant eigenvector)}$$ -#### `nuttall( i, N )`: -![Nuttall Window Equation 1](docs/equations/nuttall-1.png) +$$W(k) = C_n^\mu\!\left(x_\mu\cos\frac{\pi k}{N}\right),\quad w = \operatorname{IDFT}(W) \quad \text{(ultraspherical)}$$ -![Nuttall Window Equation 2](docs/equations/nuttall-2.png) +
-![Nuttall](docs/plots/nuttall.png) +## Which window should I pick? -#### `rectangular( i, N )`: +| I need to... | Use | Why | +|---|---|---| +| Just get started | `hann` | Good all-round, zero edges, 50% COLA | +| Design FIR filters | `kaiser` or `hamming` | Kaiser is tunable, Hamming is the classic | +| Measure amplitudes accurately | `flatTop` | < 0.01 dB scalloping loss | +| High dynamic range (>80 dB) | `blackmanHarris` | -92 dB equiripple sidelobes | +| Audio codec (MDCT) | `kaiserBesselDerived` or `cosine` | Princen-Bradley perfect reconstruction | +| Preserve center, taper edges | `tukey` | Adjustable flat-top fraction | +| Robust spectral estimation | `dpss` | Optimal for multitaper method | +| Frequency estimation via optimization | `hannPoisson` | Monotonically decreasing (convex) transform | +| Radar / SAR | `taylor` | Monotonic sidelobes, radar standard | +| Antenna array design | `dolphChebyshev` or `ultraspherical` | Optimal equiripple or tunable taper | +| Tune resolution/leakage continuously | `kaiser` or `gaussian` | Single-parameter adjustment | +| Modal / impact analysis | `exponential` | Controlled decay for underdamped systems | +| FTIR spectroscopy | `connes` | Smooth apodization for interferograms | +| Gravitational waves | `planckTaper` | C∞ smooth, no spectral artifacts | -![Rectangular Window Equation](docs/equations/rectangular.png) +## Quantitative metrics -![Rectangular](docs/plots/rectangular.png) +The decision tables above give qualitative guidance. These three functions let you verify and compare numerically: -#### `triangular( i, N )`: +- **ENBW** (Equivalent Noise Bandwidth) — how many frequency bins of noise power leak through the window. Rectangular = 1.0 (theoretical minimum), Hann = 1.5, Blackman-Harris = 2.0. Lower means less noise contaminates your measurement. -![Triangular Window Equation](docs/equations/triangular.png) +- **Scallop loss** — the worst-case amplitude error when a tone falls exactly between two DFT bins. Rectangular = 3.92 dB (worst), Hann = 1.42 dB, flat-top ≈ 0 dB (best). This is why flat-top windows exist: amplitude accuracy at the cost of frequency resolution. -![Triangular](docs/plots/triangular.png) +- **COLA** (Constant Overlap-Add) — whether overlapping windows sum to a constant, which guarantees perfect STFT reconstruction. Returns 0 for perfect COLA. Hann at 50% overlap is the classic COLA pair. -#### `tukey( i, N, alpha )`: +## Migrating from v2 -A tapered cosine window. Alpha controls the relative width of the flat section. Alpha=0 is rectangular, alpha=1 is Hann. ![Tukey Window Equation](docs/equations/tukey.png) +v3 is a complete rewrite: CJS → ESM, 20 files → 1, 18 → 34 windows. -![Tukey](docs/plots/tukey.png) - -#### `welch( i, N )`: - -![Welch Window Equation](docs/equations/welch.png) - -![Welch](docs/plots/welch.png) - -### `require('window-function/apply')(array, fn)` - -Apply a windowing function to an array, modifies an array in-place. - -### `require('window-function/generate')(fn, n)` - -Generate an array of `n` samples of the window function `fn`. - -## Testing - -The tests ensure the window functions aren't returning NaN, but it's hard to have confidence in the accuracy with tests. Instead, I opted to focus on visual testing via plots and Fourier Transforms. To generate the plots, run - -```bash -$ npm run generate-plots +```diff +- const hann = require('window-function/hann') +- const apply = require('window-function/apply') ++ import { hann, apply } from 'window-function' ``` -It pipes them through matplotlib. Don't worry. I realize the irony. - - - -## Credits -Window function definitions and equation images from [Wikipedia: Window Function](http://en.wikipedia.org/wiki/Window_function). - -(c) 2015 Ricky Reusser. MIT License +The per-sample API (`fn(i, N, ...params) → number`) is unchanged. + +## References + +| Year | Reference | Windows | +|---|---|---| +| 1946 | Dolph, *Proc. IRE* 34 | Dolph-Chebyshev | +| 1946 | Gabor, *J. IEE* 93 | Gaussian | +| 1950 | Bartlett, *Biometrika* 37 | Bartlett | +| 1955 | Taylor, *IRE Trans. Antennas Propag.* AP-4 | Taylor | +| 1958 | Blackman & Tukey, *The Measurement of Power Spectra* | Hann, Blackman | +| 1961 | Connes, *Revue d'Optique* 40 | Connes | +| 1961 | Parzen, *Technometrics* 3 | Parzen | +| 1967 | Welch, *IEEE Trans. Audio Electroacoustics* AU-15 | Welch | +| 1970 | Rife & Vincent, *IEEE Trans. Instrumentation* | Rife-Vincent | +| 1974 | Kaiser, *IEEE Int. Symp. Circuits and Systems* | Kaiser | +| 1977 | Hamming, *Digital Filters* | Hamming | +| 1978 | Harris, *Proc. IEEE* 66 — **the comprehensive survey** | Blackman-Harris, survey of all | +| 1978 | Slepian, *Bell System Technical Journal* 57 | DPSS | +| 1979 | Duchon, *J. Applied Meteorology* 18 | Lanczos | +| 1981 | Nuttall, *IEEE Trans. ASSP* 29 | Nuttall, Blackman-Nuttall | +| 1984 | Streit, *IEEE Trans. ASSP* 32 | Ultraspherical | +| 1987 | Princen, Johnson & Bradley, *ICASSP* | KBD, Cosine | +| 1989 | Ha & Pearce, *IEEE* | Bartlett-Hann | +| 2002 | Heinzel, Rudiger & Schilling (ISO 18431) | Flat-top | +| 2010 | McKechan, Robinson & Sathyaprakash, *Class. Quantum Grav.* | Planck-taper | +| 2014 | Starosielec & Hagemeier, *Signal Processing* | Confined Gaussian | + +## License + +MIT • diff --git a/apply.js b/apply.js deleted file mode 100644 index 5dd3b58..0000000 --- a/apply.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict' - -module.exports = function applyWindow(signal, func) { - var i, n=signal.length, args=[0,n] - - // pass rest of args - for(i=2; i=0; i--) { - args[0] = i - signal[i] *= func.apply(null,args) - } - - return signal; -} diff --git a/bartlett-hann.js b/bartlett-hann.js deleted file mode 100644 index 84715fd..0000000 --- a/bartlett-hann.js +++ /dev/null @@ -1,12 +0,0 @@ -'use strict' - -function bartlettHann (i,N) { - var inm1 = i/(N-1), - a0 = 0.62, - a1 = 0.48, - a2 = 0.38 - - return a0 - a1 * Math.abs(inm1 - 0.5) - a2 * Math.cos(6.283185307179586*inm1) -} - -module.exports = bartlettHann diff --git a/bartlett.js b/bartlett.js deleted file mode 100644 index 1b00711..0000000 --- a/bartlett.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict' - -function bartlett (i,N) { - return 1 - Math.abs( 2 * (i - 0.5*(N-1)) / (N-1) ) -} - -module.exports = bartlett diff --git a/blackman-harris.js b/blackman-harris.js deleted file mode 100644 index 1075917..0000000 --- a/blackman-harris.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict' - -function blackmanHarris (i,N) { - var a0 = 0.35875, - a1 = 0.48829, - a2 = 0.14128, - a3 = 0.01168, - f = 6.283185307179586*i/(N-1) - - return a0 - a1*Math.cos(f) +a2*Math.cos(2*f) - a3*Math.cos(3*f) -} - -module.exports = blackmanHarris diff --git a/blackman-nuttall.js b/blackman-nuttall.js deleted file mode 100644 index a99d4ef..0000000 --- a/blackman-nuttall.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict' - -function blackmanNuttall (i,N) { - var a0 = 0.3635819, - a1 = 0.4891775, - a2 = 0.1365995, - a3 = 0.0106411, - f = 6.283185307179586*i/(N-1) - - return a0 - a1*Math.cos(f) +a2*Math.cos(2*f) - a3*Math.cos(3*f) -} - -module.exports = blackmanNuttall diff --git a/blackman.js b/blackman.js deleted file mode 100644 index 152bcc7..0000000 --- a/blackman.js +++ /dev/null @@ -1,12 +0,0 @@ -'use strict' - -function blackman (i,N) { - var a0 = 0.42, - a1 = 0.5, - a2 = 0.08, - f = 6.283185307179586*i/(N-1) - - return a0 - a1 * Math.cos(f) + a2*Math.cos(2*f) -} - -module.exports = blackman diff --git a/cosine.js b/cosine.js deleted file mode 100644 index d0f377c..0000000 --- a/cosine.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict' - -function cosine (i,N) { - return Math.sin(3.141592653589793*i/(N-1)) -} - -module.exports = cosine diff --git a/docs/data/bartlett-hann.csv b/docs/data/bartlett-hann.csv deleted file mode 100644 index 36a50c5..0000000 --- a/docs/data/bartlett-hann.csv +++ /dev/null @@ -1,81 +0,0 @@ -0, 0 -0.0125, 0.007171413181411401 -0.025, 0.016678430573847658 -0.0375, 0.028499430248882907 -0.05, 0.04259852380784168 -0.0625, 0.05892577764571105 -0.075, 0.07741752080842024 -0.0875, 0.09799673754544502 -0.1, 0.12057354213751997 -0.1125, 0.14504573307198826 -0.125, 0.1712994231491119 -0.1375, 0.19920974163453026 -0.15, 0.22864160412886023 -0.1625, 0.2594505454079394 -0.175, 0.29148361009897217 -0.1875, 0.32458029570126584 -0.2, 0.35857354213751996 -0.2125, 0.3932907617347559 -0.225, 0.42855490328471224 -0.2375, 0.4641855436234189 -0.25, 0.5 -0.2625, 0.5358144563765811 -0.275, 0.5714450967152878 -0.2875, 0.6067092382652439 -0.3, 0.64142645786248 -0.3125, 0.6754197042987341 -0.325, 0.7085163899010278 -0.3375, 0.7405494545920606 -0.35, 0.7713583958711399 -0.3625, 0.8007902583654698 -0.375, 0.8287005768508882 -0.3875, 0.8549542669280118 -0.4, 0.8794264578624801 -0.4125, 0.902003262454555 -0.425, 0.9225824791915798 -0.4375, 0.941074222354289 -0.45, 0.9574014761921583 -0.4625, 0.9715005697511171 -0.475, 0.9833215694261523 -0.4875, 0.9928285868185887 -0.5, 1 -0.5125, 0.9928285868185887 -0.525, 0.9833215694261523 -0.5375, 0.9715005697511171 -0.55, 0.9574014761921583 -0.5625, 0.941074222354289 -0.575, 0.92258247919158 -0.5875, 0.9020032624545551 -0.6, 0.8794264578624802 -0.6125, 0.8549542669280117 -0.625, 0.8287005768508882 -0.6375, 0.80079025836547 -0.65, 0.7713583958711399 -0.6625, 0.7405494545920608 -0.675, 0.7085163899010278 -0.6875, 0.6754197042987343 -0.7, 0.6414264578624801 -0.7125, 0.6067092382652439 -0.725, 0.5714450967152878 -0.7375, 0.535814456376581 -0.75, 0.5000000000000001 -0.7625, 0.46418554362341913 -0.775, 0.42855490328471235 -0.7875, 0.3932907617347562 -0.8, 0.35857354213752 -0.8125, 0.3245802957012658 -0.825, 0.2914836100989723 -0.8375, 0.2594505454079393 -0.85, 0.2286416041288603 -0.8625, 0.19920974163453006 -0.875, 0.171299423149112 -0.8875, 0.14504573307198848 -0.9, 0.12057354213751997 -0.9125, 0.09799673754544513 -0.925, 0.07741752080842024 -0.9375, 0.058925777645711164 -0.95, 0.04259852380784168 -0.9625, 0.028499430248882907 -0.975, 0.016678430573847713 -0.9875, 0.007171413181411401 -1, 0 diff --git a/docs/data/bartlett.csv b/docs/data/bartlett.csv deleted file mode 100644 index d3fcfcc..0000000 --- a/docs/data/bartlett.csv +++ /dev/null @@ -1,81 +0,0 @@ -0, 0 -0.0125, 0.025000000000000022 -0.025, 0.050000000000000044 -0.0375, 0.07499999999999996 -0.05, 0.09999999999999998 -0.0625, 0.125 -0.075, 0.15000000000000002 -0.0875, 0.17500000000000004 -0.1, 0.19999999999999996 -0.1125, 0.22499999999999998 -0.125, 0.25 -0.1375, 0.275 -0.15, 0.30000000000000004 -0.1625, 0.32499999999999996 -0.175, 0.35 -0.1875, 0.375 -0.2, 0.4 -0.2125, 0.42500000000000004 -0.225, 0.44999999999999996 -0.2375, 0.475 -0.25, 0.5 -0.2625, 0.525 -0.275, 0.55 -0.2875, 0.575 -0.3, 0.6 -0.3125, 0.625 -0.325, 0.65 -0.3375, 0.675 -0.35, 0.7 -0.3625, 0.725 -0.375, 0.75 -0.3875, 0.775 -0.4, 0.8 -0.4125, 0.825 -0.425, 0.85 -0.4375, 0.875 -0.45, 0.9 -0.4625, 0.925 -0.475, 0.95 -0.4875, 0.975 -0.5, 1 -0.5125, 0.975 -0.525, 0.95 -0.5375, 0.925 -0.55, 0.9 -0.5625, 0.875 -0.575, 0.85 -0.5875, 0.825 -0.6, 0.8 -0.6125, 0.775 -0.625, 0.75 -0.6375, 0.725 -0.65, 0.7 -0.6625, 0.675 -0.675, 0.65 -0.6875, 0.625 -0.7, 0.6 -0.7125, 0.575 -0.725, 0.55 -0.7375, 0.525 -0.75, 0.5 -0.7625, 0.475 -0.775, 0.44999999999999996 -0.7875, 0.42500000000000004 -0.8, 0.4 -0.8125, 0.375 -0.825, 0.35 -0.8375, 0.32499999999999996 -0.85, 0.30000000000000004 -0.8625, 0.275 -0.875, 0.25 -0.8875, 0.22499999999999998 -0.9, 0.19999999999999996 -0.9125, 0.17500000000000004 -0.925, 0.15000000000000002 -0.9375, 0.125 -0.95, 0.09999999999999998 -0.9625, 0.07499999999999996 -0.975, 0.050000000000000044 -0.9875, 0.025000000000000022 -1, 0 diff --git a/docs/data/blackman-harris.csv b/docs/data/blackman-harris.csv deleted file mode 100644 index 1eac637..0000000 --- a/docs/data/blackman-harris.csv +++ /dev/null @@ -1,81 +0,0 @@ -0, 0.000060000000000001025 -0.0125, 0.00014856320048716766 -0.025, 0.00042996859045934914 -0.0375, 0.0009513516281427874 -0.05, 0.0017912028767559669 -0.0625, 0.0030591666260749786 -0.075, 0.004895570196272532 -0.0875, 0.007470514188844727 -0.1, 0.010982331276248888 -0.1125, 0.015655215513927012 -0.125, 0.021735837018679604 -0.1375, 0.02948878837962662 -0.15, 0.03919075830314313 -0.1625, 0.05112339248964909 -0.175, 0.06556487825644733 -0.1875, 0.0827803737041467 -0.2, 0.10301148934566377 -0.2125, 0.12646511474996391 -0.225, 0.15330195948028424 -0.2375, 0.1836252412376065 -0.25, 0.21746999999999997 -0.2625, 0.25479354124383136 -0.275, 0.295467511275357 -0.2875, 0.3392720817753709 -0.3, 0.3858926687237511 -0.3125, 0.43491953420378243 -0.325, 0.48585052085579145 -0.3375, 0.5380970519039445 -0.35, 0.5909933997662717 -0.3625, 0.6438090891786056 -0.375, 0.6957641629813204 -0.3875, 0.7460469069278407 -0.4, 0.7938335106543362 -0.4125, 0.8383090414175616 -0.425, 0.8786890306914886 -0.4375, 0.914240925465996 -0.45, 0.9443046390538291 -0.4625, 0.9683114518465225 -0.475, 0.9858005606538993 -0.4875, 0.996432654318075 -0.5, 1 -0.5125, 0.9964326543180752 -0.525, 0.9858005606538993 -0.5375, 0.9683114518465225 -0.55, 0.9443046390538294 -0.5625, 0.914240925465996 -0.575, 0.8786890306914887 -0.5875, 0.8383090414175615 -0.6, 0.7938335106543365 -0.6125, 0.7460469069278411 -0.625, 0.6957641629813205 -0.6375, 0.6438090891786061 -0.65, 0.590993399766272 -0.6625, 0.5380970519039444 -0.675, 0.4858505208557916 -0.6875, 0.43491953420378227 -0.7, 0.3858926687237512 -0.7125, 0.33927208177537127 -0.725, 0.2954675112753573 -0.7375, 0.25479354124383174 -0.75, 0.21747000000000014 -0.7625, 0.1836252412376064 -0.775, 0.15330195948028444 -0.7875, 0.1264651147499638 -0.8, 0.1030114893456638 -0.8125, 0.08278037370414688 -0.825, 0.06556487825644736 -0.8375, 0.051123392489649186 -0.85, 0.039190758303143176 -0.8625, 0.029488788379626575 -0.875, 0.021735837018679642 -0.8875, 0.015655215513927 -0.9, 0.01098233127624889 -0.9125, 0.007470514188844762 -0.925, 0.004895570196272565 -0.9375, 0.0030591666260749925 -0.95, 0.0017912028767559148 -0.9625, 0.0009513516281427405 -0.975, 0.0004299685904593769 -0.9875, 0.00014856320048716246 -1, 0.000060000000000001025 diff --git a/docs/data/blackman-nuttall.csv b/docs/data/blackman-nuttall.csv deleted file mode 100644 index 6719faf..0000000 --- a/docs/data/blackman-nuttall.csv +++ /dev/null @@ -1,81 +0,0 @@ -0, 0.0003628000000000381 -0.0125, 0.000483018898944634 -0.025, 0.0008595418416410434 -0.0375, 0.0015399030449880745 -0.05, 0.0026030862749787907 -0.0625, 0.004159080069511702 -0.075, 0.006348092798430795 -0.0875, 0.00933928235439788 -0.1, 0.013328836896113066 -0.1125, 0.018537239946952812 -0.125, 0.025205566515401852 -0.1375, 0.033590686883606904 -0.15, 0.0439593003191265 -0.1625, 0.056580780129479286 -0.175, 0.0717188811442391 -0.1875, 0.08962243700094036 -0.2, 0.11051525304987178 -0.2125, 0.1345854764485952 -0.225, 0.16197479318690108 -0.2375, 0.19276785767979368 -0.25, 0.22698239999999995 -0.2625, 0.2645604753579553 -0.275, 0.30536131761777907 -0.2875, 0.34915623214966685 -0.3, 0.3956259131038869 -0.3125, 0.4443604974856761 -0.325, 0.49486257571470954 -0.3375, 0.5465532693321763 -0.35, 0.5987813658346322 -0.3625, 0.6508353737018671 -0.375, 0.7019582334845982 -0.3875, 0.7513642994675733 -0.4, 0.7982580969501282 -0.4125, 0.8418542681839465 -0.425, 0.8813980503426205 -0.4375, 0.9161855854438719 -0.45, 0.9455833475712625 -0.4625, 0.9690459883567498 -0.475, 0.9861319473536787 -0.4875, 0.9965162480633065 -0.5, 1 -0.5125, 0.9965162480633065 -0.525, 0.9861319473536787 -0.5375, 0.9690459883567499 -0.55, 0.9455833475712628 -0.5625, 0.9161855854438721 -0.575, 0.8813980503426205 -0.5875, 0.8418542681839464 -0.6, 0.7982580969501283 -0.6125, 0.7513642994675737 -0.625, 0.7019582334845982 -0.6375, 0.6508353737018675 -0.65, 0.5987813658346324 -0.6625, 0.5465532693321761 -0.675, 0.49486257571470965 -0.6875, 0.4443604974856758 -0.7, 0.39562591310388706 -0.7125, 0.3491562321496673 -0.725, 0.30536131761777935 -0.7375, 0.26456047535795557 -0.75, 0.2269824000000001 -0.7625, 0.1927678576797936 -0.775, 0.16197479318690125 -0.7875, 0.13458547644859511 -0.8, 0.11051525304987185 -0.8125, 0.08962243700094051 -0.825, 0.07171888114423913 -0.8375, 0.05658078012947939 -0.85, 0.04395930031912655 -0.8625, 0.03359068688360686 -0.875, 0.025205566515401838 -0.8875, 0.0185372399469528 -0.9, 0.013328836896113068 -0.9125, 0.009339282354397911 -0.925, 0.006348092798430826 -0.9375, 0.004159080069511711 -0.95, 0.0026030862749787257 -0.9625, 0.0015399030449880415 -0.975, 0.0008595418416410157 -0.9875, 0.00048301889894463053 -1, 0.0003628000000000381 diff --git a/docs/data/blackman.csv b/docs/data/blackman.csv deleted file mode 100644 index e80c1ae..0000000 --- a/docs/data/blackman.csv +++ /dev/null @@ -1,81 +0,0 @@ -0, -1.3877787807814457e-17 -0.0125, 0.0005564003810470258 -0.025, 0.0022403510060433895 -0.0375, 0.005095561736231144 -0.05, 0.00919310140241901 -0.0625, 0.014628776239280425 -0.075, 0.021519558089213887 -0.0875, 0.02999915780211764 -0.1, 0.040212862362522056 -0.1125, 0.05231177440320299 -0.125, 0.0664466094067262 -0.1375, 0.08276121863168966 -0.15, 0.10138601430376763 -0.1625, 0.12243147766286179 -0.175, 0.14598192994682874 -0.1875, 0.17208974132253127 -0.2, 0.20077014326253045 -0.2125, 0.23199679613697785 -0.225, 0.26569824617627225 -0.2375, 0.3017553848884664 -0.25, 0.3399999999999999 -0.2625, 0.3802144806163114 -0.275, 0.422132711216503 -0.2875, 0.46544215999288324 -0.3, 0.5097871376374778 -0.3125, 0.554773173687621 -0.325, 0.5999724296863754 -0.3375, 0.6449300423788106 -0.35, 0.6891712665962407 -0.3625, 0.7322092669618733 -0.375, 0.7735533905932737 -0.3875, 0.8127177400032339 -0.4, 0.8492298567374694 -0.4125, 0.8826393221562098 -0.425, 0.9125260822775817 -0.4375, 0.9385083087505672 -0.45, 0.9602496176975726 -0.4625, 0.9774654821339077 -0.475, 0.9899286916011811 -0.4875, 0.997473734114175 -0.5, 0.9999999999999999 -0.5125, 0.9974737341141751 -0.525, 0.9899286916011811 -0.5375, 0.9774654821339077 -0.55, 0.9602496176975727 -0.5625, 0.9385083087505672 -0.575, 0.9125260822775818 -0.5875, 0.8826393221562097 -0.6, 0.8492298567374695 -0.6125, 0.8127177400032343 -0.625, 0.7735533905932739 -0.6375, 0.7322092669618737 -0.65, 0.6891712665962408 -0.6625, 0.6449300423788106 -0.675, 0.5999724296863755 -0.6875, 0.5547731736876209 -0.7, 0.509787137637478 -0.7125, 0.46544215999288363 -0.725, 0.42213271121650325 -0.7375, 0.3802144806163118 -0.75, 0.3400000000000001 -0.7625, 0.3017553848884664 -0.775, 0.26569824617627236 -0.7875, 0.23199679613697777 -0.8, 0.20077014326253056 -0.8125, 0.17208974132253152 -0.825, 0.1459819299468288 -0.8375, 0.12243147766286197 -0.85, 0.1013860143037677 -0.8625, 0.08276121863168961 -0.875, 0.06644660940672628 -0.8875, 0.05231177440320297 -0.9, 0.04021286236252208 -0.9125, 0.02999915780211771 -0.925, 0.021519558089213915 -0.9375, 0.014628776239280453 -0.95, 0.009193101402418996 -0.9625, 0.0050955617362310884 -0.975, 0.002240351006043431 -0.9875, 0.0005564003810470258 -1, -1.3877787807814457e-17 diff --git a/docs/data/cosine.csv b/docs/data/cosine.csv deleted file mode 100644 index 2ede283..0000000 --- a/docs/data/cosine.csv +++ /dev/null @@ -1,81 +0,0 @@ -0, 0 -0.0125, 0.03925981575906861 -0.025, 0.07845909572784494 -0.0375, 0.11753739745783764 -0.05, 0.15643446504023087 -0.0625, 0.19509032201612825 -0.075, 0.2334453638559054 -0.0875, 0.27144044986507426 -0.1, 0.3090169943749474 -0.1125, 0.34611705707749296 -0.125, 0.3826834323650898 -0.1375, 0.418659737537428 -0.15, 0.45399049973954675 -0.1625, 0.4886212414969549 -0.175, 0.5224985647159488 -0.1875, 0.5555702330196022 -0.2, 0.5877852522924731 -0.2125, 0.619093949309834 -0.225, 0.6494480483301837 -0.2375, 0.6788007455329417 -0.25, 0.7071067811865475 -0.2625, 0.7343225094356856 -0.275, 0.7604059656000308 -0.2875, 0.785316930880745 -0.3, 0.8090169943749475 -0.3125, 0.8314696123025452 -0.325, 0.8526401643540922 -0.3375, 0.8724960070727971 -0.35, 0.8910065241883678 -0.3625, 0.9081431738250813 -0.375, 0.9238795325112867 -0.3875, 0.9381913359224842 -0.4, 0.9510565162951535 -0.4125, 0.9624552364536473 -0.425, 0.9723699203976766 -0.4375, 0.9807852804032304 -0.45, 0.9876883405951378 -0.4625, 0.9930684569549263 -0.475, 0.996917333733128 -0.4875, 0.9992290362407229 -0.5, 1 -0.5125, 0.9992290362407229 -0.525, 0.996917333733128 -0.5375, 0.9930684569549263 -0.55, 0.9876883405951378 -0.5625, 0.9807852804032304 -0.575, 0.9723699203976767 -0.5875, 0.9624552364536473 -0.6, 0.9510565162951536 -0.6125, 0.9381913359224843 -0.625, 0.9238795325112867 -0.6375, 0.9081431738250815 -0.65, 0.8910065241883679 -0.6625, 0.8724960070727971 -0.675, 0.8526401643540923 -0.6875, 0.8314696123025451 -0.7, 0.8090169943749475 -0.7125, 0.7853169308807452 -0.725, 0.760405965600031 -0.7375, 0.7343225094356858 -0.75, 0.7071067811865476 -0.7625, 0.6788007455329417 -0.775, 0.6494480483301838 -0.7875, 0.6190939493098339 -0.8, 0.5877852522924732 -0.8125, 0.5555702330196025 -0.825, 0.5224985647159489 -0.8375, 0.48862124149695524 -0.85, 0.45399049973954686 -0.8625, 0.41865973753742797 -0.875, 0.3826834323650899 -0.8875, 0.34611705707749285 -0.9, 0.3090169943749475 -0.9125, 0.2714404498650746 -0.925, 0.23344536385590553 -0.9375, 0.1950903220161286 -0.95, 0.15643446504023098 -0.9625, 0.11753739745783755 -0.975, 0.07845909572784507 -0.9875, 0.039259815759068506 -1, 1.2246063538223773e-16 diff --git a/docs/data/exact-blackman.csv b/docs/data/exact-blackman.csv deleted file mode 100644 index aae6658..0000000 --- a/docs/data/exact-blackman.csv +++ /dev/null @@ -1,81 +0,0 @@ -0, 0.006879000000000024 -0.0125, 0.007463590047873739 -0.025, 0.009231219814844649 -0.0375, 0.012222952704681642 -0.05, 0.01650552326919892 -0.0625, 0.02216882836360047 -0.075, 0.02932250920244832 -0.0875, 0.038091715902816445 -0.1, 0.04861216827389646 -0.1125, 0.06102464592552537 -0.125, 0.07546905673400794 -0.1375, 0.0920782449172873 -0.15, 0.11097170812092924 -0.1625, 0.13224939679016404 -0.175, 0.1559857685959064 -0.1875, 0.18222426579738604 -0.2, 0.2109723742724558 -0.2125, 0.2421974097463597 -0.225, 0.2758231598188567 -0.2375, 0.3117274901389856 -0.25, 0.34974099999999997 -0.2625, 0.38964678728822294 -0.275, 0.4311813557396106 -0.2875, 0.47403666949893647 -0.3, 0.5178633317261034 -0.3125, 0.5622748361478039 -0.325, 0.6068528136972451 -0.3375, 0.6511531713808671 -0.35, 0.6947129978776301 -0.3625, 0.7370580906749592 -0.375, 0.7777109432659921 -0.3875, 0.816199018482228 -0.4, 0.8520631257275442 -0.4125, 0.8848657159261524 -0.425, 0.9141989085044001 -0.4375, 0.9396920696912096 -0.45, 0.9610187707322418 -0.4625, 0.9779029680500221 -0.475, 0.9901242646266879 -0.4875, 0.9975221325249177 -0.5, 0.9999990000000001 -0.5125, 0.9975221325249178 -0.525, 0.9901242646266879 -0.5375, 0.9779029680500222 -0.55, 0.961018770732242 -0.5625, 0.9396920696912096 -0.575, 0.9141989085044003 -0.5875, 0.8848657159261524 -0.6, 0.8520631257275444 -0.6125, 0.8161990184822283 -0.625, 0.7777109432659921 -0.6375, 0.7370580906749596 -0.65, 0.6947129978776303 -0.6625, 0.6511531713808669 -0.675, 0.6068528136972452 -0.6875, 0.5622748361478039 -0.7, 0.5178633317261038 -0.7125, 0.47403666949893686 -0.725, 0.43118135573961086 -0.7375, 0.3896467872882233 -0.75, 0.34974100000000014 -0.7625, 0.3117274901389855 -0.775, 0.2758231598188568 -0.7875, 0.24219740974635964 -0.8, 0.21097237427245588 -0.8125, 0.1822242657973863 -0.825, 0.15598576859590646 -0.8375, 0.1322493967901642 -0.85, 0.11097170812092931 -0.8625, 0.09207824491728726 -0.875, 0.07546905673400803 -0.8875, 0.061024645925525346 -0.9, 0.04861216827389648 -0.9125, 0.038091715902816514 -0.925, 0.029322509202448353 -0.9375, 0.022168828363600505 -0.95, 0.016505523269198893 -0.9625, 0.0122229527046816 -0.975, 0.009231219814844704 -0.9875, 0.007463590047873739 -1, 0.006879000000000024 diff --git a/docs/data/flattop.csv b/docs/data/flattop.csv deleted file mode 100644 index 39c4c76..0000000 --- a/docs/data/flattop.csv +++ /dev/null @@ -1,81 +0,0 @@ -0, 8.673617379884035e-17 -0.0125, -0.0005824413952433723 -0.025, -0.002433646870456045 -0.0375, -0.005855057753143846 -0.05, -0.011315355752944935 -0.0625, -0.019400921773791935 -0.075, -0.030748664504367817 -0.0875, -0.04596363046116787 -0.1, -0.06552475842498531 -0.1125, -0.08968319305263801 -0.125, -0.11835865658965636 -0.1375, -0.15104039186602872 -0.15, -0.18670000718813418 -0.1625, -0.22372403614153216 -0.175, -0.2598740396462006 -0.1875, -0.2922815135808902 -0.2, -0.31748365222735253 -0.2125, -0.3315041386285914 -0.225, -0.32998063380695103 -0.2375, -0.308337630490113 -0.25, -0.26200000000000007 -0.2625, -0.18663912333281385 -0.275, -0.07844022654954846 -0.2875, 0.06562328035098046 -0.3, 0.2475247584249851 -0.3125, 0.4679460181195976 -0.325, 0.7260831370466226 -0.3375, 1.0195125726851226 -0.35, 1.3441312100157725 -0.3625, 1.6941803071497041 -0.375, 2.0623586565896557 -0.3875, 2.4400249479439045 -0.4, 2.817483652227352 -0.4125, 3.184343145660819 -0.425, 3.5299296637339506 -0.4375, 3.8437364172350845 -0.45, 4.1158841529253065 -0.4625, 4.337567864287511 -0.475, 4.501464410596949 -0.4875, 4.602077525043228 -0.5, 4.635999999999999 -0.5125, 4.602077525043228 -0.525, 4.50146441059695 -0.5375, 4.337567864287512 -0.55, 4.115884152925308 -0.5625, 3.843736417235085 -0.575, 3.5299296637339515 -0.5875, 3.184343145660818 -0.6, 2.8174836522273536 -0.6125, 2.440024947943908 -0.625, 2.0623586565896574 -0.6375, 1.6941803071497075 -0.65, 1.3441312100157732 -0.6625, 1.0195125726851217 -0.675, 0.7260831370466235 -0.6875, 0.46794601811959713 -0.7, 0.24752475842498584 -0.7125, 0.06562328035098161 -0.725, -0.07844022654954827 -0.7375, -0.18663912333281324 -0.75, -0.2619999999999998 -0.7625, -0.30833763049011303 -0.775, -0.32998063380695075 -0.7875, -0.33150413862859135 -0.8, -0.31748365222735264 -0.8125, -0.2922815135808908 -0.825, -0.2598740396462009 -0.8375, -0.2237240361415326 -0.85, -0.18670000718813407 -0.8625, -0.1510403918660291 -0.875, -0.11835865658965658 -0.8875, -0.08968319305263814 -0.9, -0.06552475842498537 -0.9125, -0.04596363046116768 -0.925, -0.03074866450436744 -0.9375, -0.01940092177379143 -0.95, -0.011315355752945736 -0.9625, -0.005855057753143718 -0.975, -0.002433646870456059 -0.9875, -0.0005824413952435284 -1, 8.673617379884035e-17 diff --git a/docs/data/gaussian.csv b/docs/data/gaussian.csv deleted file mode 100644 index fd4c266..0000000 --- a/docs/data/gaussian.csv +++ /dev/null @@ -1,81 +0,0 @@ -0, 0.04393693362340742 -0.0125, 0.05126725037065775 -0.025, 0.059587318761986086 -0.0375, 0.0689876259172394 -0.05, 0.07955950871822769 -0.0625, 0.09139375535604724 -0.075, 0.10457900128900145 -0.0875, 0.11919992840264731 -0.1, 0.1353352832366127 -0.1125, 0.15305573773635817 -0.125, 0.17242162389375282 -0.1375, 0.19348058160704673 -0.15, 0.2162651668298873 -0.1625, 0.24079047427224856 -0.175, 0.26705183522634335 -0.1875, 0.29502265617444284 -0.2, 0.32465246735834974 -0.2125, 0.35586525214213843 -0.225, 0.3885581275123641 -0.2375, 0.4226004432231888 -0.25, 0.45783336177161427 -0.2625, 0.49406997353163834 -0.275, 0.5310959910353452 -0.2875, 0.568671053718672 -0.3, 0.6065306597126334 -0.3125, 0.6443887248251953 -0.325, 0.6819407511903481 -0.3375, 0.7188675697091758 -0.35, 0.7548396019890073 -0.3625, 0.7895215696607879 -0.375, 0.8225775623986646 -0.3875, 0.8536763613451477 -0.4, 0.8824969025845955 -0.4125, 0.9087337563610701 -0.425, 0.9321024923595276 -0.4375, 0.9523447998951764 -0.45, 0.9692332344763441 -0.4625, 0.9825754689579004 -0.475, 0.9922179382602435 -0.4875, 0.9980487811074755 -0.5, 1 -0.5125, 0.9980487811074755 -0.525, 0.9922179382602435 -0.5375, 0.9825754689579004 -0.55, 0.9692332344763441 -0.5625, 0.9523447998951764 -0.575, 0.9321024923595276 -0.5875, 0.9087337563610701 -0.6, 0.8824969025845955 -0.6125, 0.8536763613451477 -0.625, 0.8225775623986646 -0.6375, 0.7895215696607879 -0.65, 0.7548396019890073 -0.6625, 0.7188675697091758 -0.675, 0.6819407511903481 -0.6875, 0.6443887248251953 -0.7, 0.6065306597126334 -0.7125, 0.568671053718672 -0.725, 0.5310959910353452 -0.7375, 0.49406997353163834 -0.75, 0.45783336177161427 -0.7625, 0.4226004432231888 -0.775, 0.3885581275123641 -0.7875, 0.35586525214213843 -0.8, 0.32465246735834974 -0.8125, 0.29502265617444284 -0.825, 0.26705183522634335 -0.8375, 0.24079047427224856 -0.85, 0.2162651668298873 -0.8625, 0.19348058160704673 -0.875, 0.17242162389375282 -0.8875, 0.15305573773635817 -0.9, 0.1353352832366127 -0.9125, 0.11919992840264731 -0.925, 0.10457900128900145 -0.9375, 0.09139375535604724 -0.95, 0.07955950871822769 -0.9625, 0.0689876259172394 -0.975, 0.059587318761986086 -0.9875, 0.05126725037065775 -1, 0.04393693362340742 diff --git a/docs/data/hamming.csv b/docs/data/hamming.csv deleted file mode 100644 index af2b1e8..0000000 --- a/docs/data/hamming.csv +++ /dev/null @@ -1,81 +0,0 @@ -0, 0.08000000000000002 -0.0125, 0.08141802648276114 -0.025, 0.08566336332623664 -0.0375, 0.09270983661706877 -0.05, 0.10251400250422937 -0.0625, 0.11501541504480811 -0.075, 0.13013699887335078 -0.0875, 0.14778552439711762 -0.1, 0.1678521825875242 -0.1125, 0.1902132558239858 -0.125, 0.21473088065418816 -0.1375, 0.2412538977681155 -0.15, 0.2696187839454624 -0.1625, 0.29965066023066356 -0.175, 0.3311643701198085 -0.1875, 0.3639656211120587 -0.2, 0.3978521825875242 -0.2125, 0.4326151326262835 -0.225, 0.4680401460814938 -0.2375, 0.5039088159651913 -0.25, 0.54 -0.2625, 0.5760911840348086 -0.275, 0.6119598539185062 -0.2875, 0.6473848673737165 -0.3, 0.6821478174124758 -0.3125, 0.7160343788879413 -0.325, 0.7488356298801915 -0.3375, 0.7803493397693365 -0.35, 0.8103812160545376 -0.3625, 0.8387461022318845 -0.375, 0.8652691193458119 -0.3875, 0.8897867441760143 -0.4, 0.9121478174124757 -0.4125, 0.9322144756028825 -0.425, 0.9498630011266492 -0.4375, 0.9649845849551919 -0.45, 0.9774859974957707 -0.4625, 0.9872901633829313 -0.475, 0.9943366366737634 -0.4875, 0.998581973517239 -0.5, 1 -0.5125, 0.998581973517239 -0.525, 0.9943366366737634 -0.5375, 0.9872901633829313 -0.55, 0.9774859974957708 -0.5625, 0.964984584955192 -0.575, 0.9498630011266493 -0.5875, 0.9322144756028824 -0.6, 0.912147817412476 -0.6125, 0.8897867441760146 -0.625, 0.865269119345812 -0.6375, 0.8387461022318847 -0.65, 0.8103812160545377 -0.6625, 0.7803493397693364 -0.675, 0.7488356298801916 -0.6875, 0.7160343788879412 -0.7, 0.682147817412476 -0.7125, 0.6473848673737168 -0.725, 0.6119598539185063 -0.7375, 0.576091184034809 -0.75, 0.5400000000000001 -0.7625, 0.5039088159651912 -0.775, 0.4680401460814939 -0.7875, 0.4326151326262834 -0.8, 0.3978521825875243 -0.8125, 0.363965621112059 -0.825, 0.3311643701198086 -0.8375, 0.2996506602306638 -0.85, 0.26961878394546246 -0.8625, 0.24125389776811546 -0.875, 0.21473088065418822 -0.8875, 0.19021325582398574 -0.9, 0.16785218258752427 -0.9125, 0.14778552439711778 -0.925, 0.13013699887335084 -0.9375, 0.11501541504480822 -0.95, 0.10251400250422937 -0.9625, 0.09270983661706877 -0.975, 0.0856633633262367 -0.9875, 0.08141802648276114 -1, 0.08000000000000002 diff --git a/docs/data/hann.csv b/docs/data/hann.csv deleted file mode 100644 index 0fabc62..0000000 --- a/docs/data/hann.csv +++ /dev/null @@ -1,81 +0,0 @@ -0, 0 -0.0125, 0.001541333133436018 -0.025, 0.006155829702431115 -0.0375, 0.013815039801161721 -0.05, 0.024471741852423234 -0.0625, 0.03806023374435663 -0.075, 0.05449673790581605 -0.0875, 0.07367991782295391 -0.1, 0.09549150281252627 -0.1125, 0.11979701719998453 -0.125, 0.1464466094067262 -0.1375, 0.17527597583490812 -0.15, 0.20610737385376343 -0.1625, 0.23875071764202555 -0.175, 0.2730047501302266 -0.1875, 0.3086582838174551 -0.2, 0.3454915028125263 -0.2125, 0.38327731807204724 -0.225, 0.4217827674798845 -0.2375, 0.4607704521360775 -0.25, 0.49999999999999994 -0.2625, 0.5392295478639224 -0.275, 0.5782172325201153 -0.2875, 0.6167226819279527 -0.3, 0.6545084971874737 -0.3125, 0.6913417161825448 -0.325, 0.7269952498697734 -0.3375, 0.7612492823579744 -0.35, 0.7938926261462365 -0.3625, 0.8247240241650917 -0.375, 0.8535533905932737 -0.3875, 0.8802029828000155 -0.4, 0.9045084971874737 -0.4125, 0.9263200821770461 -0.425, 0.9455032620941839 -0.4375, 0.9619397662556434 -0.45, 0.9755282581475768 -0.4625, 0.9861849601988383 -0.475, 0.9938441702975689 -0.4875, 0.998458666866564 -0.5, 1 -0.5125, 0.998458666866564 -0.525, 0.9938441702975689 -0.5375, 0.9861849601988384 -0.55, 0.9755282581475768 -0.5625, 0.9619397662556435 -0.575, 0.9455032620941839 -0.5875, 0.9263200821770461 -0.6, 0.9045084971874737 -0.6125, 0.8802029828000157 -0.625, 0.8535533905932738 -0.6375, 0.824724024165092 -0.65, 0.7938926261462367 -0.6625, 0.7612492823579743 -0.675, 0.7269952498697735 -0.6875, 0.6913417161825448 -0.7, 0.6545084971874737 -0.7125, 0.616722681927953 -0.725, 0.5782172325201155 -0.7375, 0.5392295478639227 -0.75, 0.5000000000000001 -0.7625, 0.46077045213607737 -0.775, 0.42178276747988463 -0.7875, 0.3832773180720472 -0.8, 0.3454915028125264 -0.8125, 0.3086582838174554 -0.825, 0.2730047501302267 -0.8375, 0.23875071764202582 -0.85, 0.20610737385376354 -0.8625, 0.17527597583490806 -0.875, 0.14644660940672632 -0.8875, 0.11979701719998448 -0.9, 0.09549150281252633 -0.9125, 0.07367991782295408 -0.925, 0.054496737905816106 -0.9375, 0.03806023374435674 -0.95, 0.024471741852423234 -0.9625, 0.013815039801161666 -0.975, 0.00615582970243117 -0.9875, 0.001541333133436018 -1, 0 diff --git a/docs/data/lanczos.csv b/docs/data/lanczos.csv deleted file mode 100644 index 5c459ed..0000000 --- a/docs/data/lanczos.csv +++ /dev/null @@ -1,81 +0,0 @@ -0, 3.898043091051478e-17 -0.0125, 0.025614672647398466 -0.025, 0.05241540711808222 -0.0375, 0.080332937512548 -0.05, 0.10929240478705181 -0.0625, 0.13921362262920473 -0.075, 0.17001136976543227 -0.0875, 0.20159570747385178 -0.1, 0.23387232094715982 -0.1125, 0.2667428830274397 -0.125, 0.3001054387190354 -0.1375, 0.33385480877740875 -0.15, 0.36788301057177425 -0.1625, 0.40207969432782176 -0.175, 0.43633259277448355 -0.1875, 0.47052798214592234 -0.2, 0.5045511524271048 -0.2125, 0.538286884678828 -0.225, 0.5716199332361733 -0.2375, 0.6044355105433143 -0.25, 0.6366197723675814 -0.2625, 0.6680603011268212 -0.275, 0.6986465850664342 -0.2875, 0.7282704910360613 -0.3, 0.7568267286406569 -0.3125, 0.7842133035765372 -0.325, 0.8103319580097552 -0.3375, 0.8350885959116298 -0.35, 0.8583936913341397 -0.3625, 0.8801626776858957 -0.375, 0.9003163161571061 -0.3875, 0.9187810415389589 -0.4, 0.935489283788639 -0.4125, 0.9503797638053009 -0.425, 0.9633977620041159 -0.4375, 0.9744953584044327 -0.45, 0.983631643083466 -0.4625, 0.9907728959880915 -0.475, 0.9958927352435615 -0.4875, 0.9989722332485386 -0.5, 1 -0.5125, 0.9989722332485386 -0.525, 0.9958927352435615 -0.5375, 0.9907728959880915 -0.55, 0.983631643083466 -0.5625, 0.9744953584044327 -0.575, 0.963397762004116 -0.5875, 0.9503797638053009 -0.6, 0.935489283788639 -0.6125, 0.9187810415389588 -0.625, 0.9003163161571061 -0.6375, 0.8801626776858956 -0.65, 0.8583936913341397 -0.6625, 0.8350885959116298 -0.675, 0.810331958009755 -0.6875, 0.7842133035765372 -0.7, 0.7568267286406571 -0.7125, 0.7282704910360613 -0.725, 0.6986465850664342 -0.7375, 0.668060301126821 -0.75, 0.6366197723675814 -0.7625, 0.6044355105433145 -0.775, 0.5716199332361733 -0.7875, 0.538286884678828 -0.8, 0.5045511524271046 -0.8125, 0.47052798214592234 -0.825, 0.4363325927744838 -0.8375, 0.40207969432782176 -0.85, 0.36788301057177425 -0.8625, 0.33385480877740853 -0.875, 0.3001054387190354 -0.8875, 0.2667428830274399 -0.9, 0.23387232094715982 -0.9125, 0.20159570747385178 -0.925, 0.1700113697654321 -0.9375, 0.13921362262920473 -0.95, 0.10929240478705197 -0.9625, 0.080332937512548 -0.975, 0.05241540711808222 -0.9875, 0.025614672647398317 -1, 3.898043091051478e-17 diff --git a/docs/data/nuttall.csv b/docs/data/nuttall.csv deleted file mode 100644 index 03fc44b..0000000 --- a/docs/data/nuttall.csv +++ /dev/null @@ -1,81 +0,0 @@ -0, -2.42861286636753e-17 -0.0125, 0.00007499347183394754 -0.025, 0.00031519077470459206 -0.0375, 0.0007662864841678808 -0.05, 0.001504551996600407 -0.0625, 0.002636894654697404 -0.075, 0.004300726647967143 -0.0875, 0.00666345065546092 -0.1, 0.009921342339417317 -0.1125, 0.014297601661769709 -0.125, 0.020039357146876702 -0.1375, 0.02741343990589814 -0.15, 0.03670079637235443 -0.1625, 0.04818947786764342 -0.175, 0.062166227725156974 -0.1875, 0.07890677816265879 -0.2, 0.09866506407404252 -0.2125, 0.12166165364250406 -0.225, 0.14807178027768625 -0.2375, 0.17801343120395316 -0.25, 0.21153599999999992 -0.2625, 0.24861003931461095 -0.275, 0.28911865280574833 -0.2875, 0.3328510403640225 -0.3, 0.37949865766058255 -0.3125, 0.4286543713091448 -0.325, 0.4798148872575469 -0.3375, 0.532386606615488 -0.35, 0.5856949253622705 -0.3625, 0.6389968485707366 -0.375, 0.6914966428531232 -0.3875, 0.7423641098615955 -0.4, 0.7907549359259574 -0.4125, 0.8358324648614076 -0.425, 0.8767901583693286 -0.4375, 0.9128739558734987 -0.45, 0.9434037262687744 -0.4625, 0.9677930195093055 -0.475, 0.9855663761418605 -0.4875, 0.9963735360096019 -0.5, 1 -0.5125, 0.9963735360096019 -0.525, 0.9855663761418605 -0.5375, 0.9677930195093055 -0.55, 0.9434037262687747 -0.5625, 0.9128739558734987 -0.575, 0.8767901583693288 -0.5875, 0.8358324648614074 -0.6, 0.7907549359259576 -0.6125, 0.7423641098615958 -0.625, 0.6914966428531233 -0.6375, 0.638996848570737 -0.65, 0.5856949253622707 -0.6625, 0.5323866066154876 -0.675, 0.4798148872575472 -0.6875, 0.42865437130914463 -0.7, 0.3794986576605827 -0.7125, 0.3328510403640229 -0.725, 0.2891186528057486 -0.7375, 0.24861003931461126 -0.75, 0.21153600000000009 -0.7625, 0.17801343120395308 -0.775, 0.1480717802776864 -0.7875, 0.12166165364250399 -0.8, 0.09866506407404257 -0.8125, 0.07890677816265895 -0.825, 0.06216622772515698 -0.8375, 0.048189477867643445 -0.85, 0.03670079637235449 -0.8625, 0.027413439905898097 -0.875, 0.02003935714687674 -0.8875, 0.014297601661769693 -0.9, 0.009921342339417319 -0.9125, 0.006663450655460944 -0.925, 0.004300726647967178 -0.9375, 0.002636894654697421 -0.95, 0.0015045519966003403 -0.9625, 0.0007662864841678618 -0.975, 0.00031519077470464757 -0.9875, 0.0000749934718339406 -1, -2.42861286636753e-17 diff --git a/docs/data/rectangular.csv b/docs/data/rectangular.csv deleted file mode 100644 index 31bc137..0000000 --- a/docs/data/rectangular.csv +++ /dev/null @@ -1,81 +0,0 @@ -0, 1 -0.0125, 1 -0.025, 1 -0.0375, 1 -0.05, 1 -0.0625, 1 -0.075, 1 -0.0875, 1 -0.1, 1 -0.1125, 1 -0.125, 1 -0.1375, 1 -0.15, 1 -0.1625, 1 -0.175, 1 -0.1875, 1 -0.2, 1 -0.2125, 1 -0.225, 1 -0.2375, 1 -0.25, 1 -0.2625, 1 -0.275, 1 -0.2875, 1 -0.3, 1 -0.3125, 1 -0.325, 1 -0.3375, 1 -0.35, 1 -0.3625, 1 -0.375, 1 -0.3875, 1 -0.4, 1 -0.4125, 1 -0.425, 1 -0.4375, 1 -0.45, 1 -0.4625, 1 -0.475, 1 -0.4875, 1 -0.5, 1 -0.5125, 1 -0.525, 1 -0.5375, 1 -0.55, 1 -0.5625, 1 -0.575, 1 -0.5875, 1 -0.6, 1 -0.6125, 1 -0.625, 1 -0.6375, 1 -0.65, 1 -0.6625, 1 -0.675, 1 -0.6875, 1 -0.7, 1 -0.7125, 1 -0.725, 1 -0.7375, 1 -0.75, 1 -0.7625, 1 -0.775, 1 -0.7875, 1 -0.8, 1 -0.8125, 1 -0.825, 1 -0.8375, 1 -0.85, 1 -0.8625, 1 -0.875, 1 -0.8875, 1 -0.9, 1 -0.9125, 1 -0.925, 1 -0.9375, 1 -0.95, 1 -0.9625, 1 -0.975, 1 -0.9875, 1 -1, 1 diff --git a/docs/data/triangular.csv b/docs/data/triangular.csv deleted file mode 100644 index d1dd083..0000000 --- a/docs/data/triangular.csv +++ /dev/null @@ -1,81 +0,0 @@ -0, 0.012345679012345734 -0.0125, 0.03703703703703709 -0.025, 0.06172839506172845 -0.0375, 0.0864197530864198 -0.05, 0.11111111111111116 -0.0625, 0.13580246913580252 -0.075, 0.16049382716049387 -0.0875, 0.18518518518518523 -0.1, 0.2098765432098766 -0.1125, 0.23456790123456794 -0.125, 0.2592592592592593 -0.1375, 0.28395061728395066 -0.15, 0.308641975308642 -0.1625, 0.33333333333333337 -0.175, 0.3580246913580247 -0.1875, 0.3827160493827161 -0.2, 0.40740740740740744 -0.2125, 0.4320987654320988 -0.225, 0.45679012345679015 -0.2375, 0.4814814814814815 -0.25, 0.5061728395061729 -0.2625, 0.5308641975308642 -0.275, 0.5555555555555556 -0.2875, 0.5802469135802469 -0.3, 0.6049382716049383 -0.3125, 0.6296296296296297 -0.325, 0.654320987654321 -0.3375, 0.6790123456790124 -0.35, 0.7037037037037037 -0.3625, 0.7283950617283951 -0.375, 0.7530864197530864 -0.3875, 0.7777777777777778 -0.4, 0.8024691358024691 -0.4125, 0.8271604938271605 -0.425, 0.8518518518518519 -0.4375, 0.8765432098765432 -0.45, 0.9012345679012346 -0.4625, 0.9259259259259259 -0.475, 0.9506172839506173 -0.4875, 0.9753086419753086 -0.5, 1 -0.5125, 0.9753086419753086 -0.525, 0.9506172839506173 -0.5375, 0.9259259259259259 -0.55, 0.9012345679012346 -0.5625, 0.8765432098765432 -0.575, 0.8518518518518519 -0.5875, 0.8271604938271605 -0.6, 0.8024691358024691 -0.6125, 0.7777777777777778 -0.625, 0.7530864197530864 -0.6375, 0.7283950617283951 -0.65, 0.7037037037037037 -0.6625, 0.6790123456790124 -0.675, 0.654320987654321 -0.6875, 0.6296296296296297 -0.7, 0.6049382716049383 -0.7125, 0.5802469135802469 -0.725, 0.5555555555555556 -0.7375, 0.5308641975308642 -0.75, 0.5061728395061729 -0.7625, 0.4814814814814815 -0.775, 0.45679012345679015 -0.7875, 0.4320987654320988 -0.8, 0.40740740740740744 -0.8125, 0.3827160493827161 -0.825, 0.3580246913580247 -0.8375, 0.33333333333333337 -0.85, 0.308641975308642 -0.8625, 0.28395061728395066 -0.875, 0.2592592592592593 -0.8875, 0.23456790123456794 -0.9, 0.2098765432098766 -0.9125, 0.18518518518518523 -0.925, 0.16049382716049387 -0.9375, 0.13580246913580252 -0.95, 0.11111111111111116 -0.9625, 0.0864197530864198 -0.975, 0.06172839506172845 -0.9875, 0.03703703703703709 -1, 0.012345679012345734 diff --git a/docs/data/tukey.csv b/docs/data/tukey.csv deleted file mode 100644 index e155859..0000000 --- a/docs/data/tukey.csv +++ /dev/null @@ -1,81 +0,0 @@ -0, 0 -0.0125, 0.00615582970243117 -0.025, 0.024471741852423234 -0.0375, 0.054496737905816106 -0.05, 0.09549150281252633 -0.0625, 0.14644660940672627 -0.075, 0.2061073738537635 -0.0875, 0.2730047501302266 -0.1, 0.34549150281252633 -0.1125, 0.42178276747988447 -0.125, 0.5 -0.1375, 0.5782172325201156 -0.15, 0.6545084971874737 -0.1625, 0.7269952498697734 -0.175, 0.7938926261462365 -0.1875, 0.8535533905932737 -0.2, 0.9045084971874737 -0.2125, 0.9455032620941839 -0.225, 0.9755282581475768 -0.2375, 0.9938441702975689 -0.25, 1 -0.2625, 1 -0.275, 1 -0.2875, 1 -0.3, 1 -0.3125, 1 -0.325, 1 -0.3375, 1 -0.35, 1 -0.3625, 1 -0.375, 1 -0.3875, 1 -0.4, 1 -0.4125, 1 -0.425, 1 -0.4375, 1 -0.45, 1 -0.4625, 1 -0.475, 1 -0.4875, 1 -0.5, 1 -0.5125, 1 -0.525, 1 -0.5375, 1 -0.55, 1 -0.5625, 1 -0.575, 1 -0.5875, 1 -0.6, 1 -0.6125, 1 -0.625, 1 -0.6375, 1 -0.65, 1 -0.6625, 1 -0.675, 1 -0.6875, 1 -0.7, 1 -0.7125, 1 -0.725, 1 -0.7375, 1 -0.75, 1 -0.7625, 0.9938441702975689 -0.775, 0.9755282581475768 -0.7875, 0.945503262094184 -0.8, 0.9045084971874735 -0.8125, 0.8535533905932737 -0.825, 0.7938926261462368 -0.8375, 0.7269952498697734 -0.85, 0.6545084971874738 -0.8625, 0.5782172325201151 -0.875, 0.5 -0.8875, 0.4217827674798849 -0.9, 0.3454915028125262 -0.9125, 0.27300475013022685 -0.925, 0.20610737385376326 -0.9375, 0.14644660940672627 -0.95, 0.09549150281252644 -0.9625, 0.054496737905815995 -0.975, 0.02447174185242329 -0.9875, 0.006155829702431115 -1, 0 diff --git a/docs/data/welch.csv b/docs/data/welch.csv deleted file mode 100644 index 0c64f4f..0000000 --- a/docs/data/welch.csv +++ /dev/null @@ -1,81 +0,0 @@ -0, 0 -0.0125, 0.04937500000000006 -0.025, 0.09750000000000003 -0.0375, 0.14437499999999992 -0.05, 0.18999999999999995 -0.0625, 0.234375 -0.075, 0.2775000000000001 -0.0875, 0.3193750000000001 -0.1, 0.3599999999999999 -0.1125, 0.3993749999999999 -0.125, 0.4375 -0.1375, 0.474375 -0.15, 0.51 -0.1625, 0.5443749999999999 -0.175, 0.5774999999999999 -0.1875, 0.609375 -0.2, 0.64 -0.2125, 0.669375 -0.225, 0.6975 -0.2375, 0.724375 -0.25, 0.75 -0.2625, 0.774375 -0.275, 0.7975 -0.2875, 0.819375 -0.3, 0.84 -0.3125, 0.859375 -0.325, 0.8775000000000001 -0.3375, 0.894375 -0.35, 0.91 -0.3625, 0.924375 -0.375, 0.9375 -0.3875, 0.949375 -0.4, 0.96 -0.4125, 0.969375 -0.425, 0.9775 -0.4375, 0.984375 -0.45, 0.99 -0.4625, 0.994375 -0.475, 0.9975 -0.4875, 0.999375 -0.5, 1 -0.5125, 0.999375 -0.525, 0.9975 -0.5375, 0.994375 -0.55, 0.99 -0.5625, 0.984375 -0.575, 0.9775 -0.5875, 0.969375 -0.6, 0.96 -0.6125, 0.949375 -0.625, 0.9375 -0.6375, 0.924375 -0.65, 0.91 -0.6625, 0.894375 -0.675, 0.8775000000000001 -0.6875, 0.859375 -0.7, 0.84 -0.7125, 0.819375 -0.725, 0.7975 -0.7375, 0.774375 -0.75, 0.75 -0.7625, 0.724375 -0.775, 0.6975 -0.7875, 0.669375 -0.8, 0.64 -0.8125, 0.609375 -0.825, 0.5774999999999999 -0.8375, 0.5443749999999999 -0.85, 0.51 -0.8625, 0.474375 -0.875, 0.4375 -0.8875, 0.3993749999999999 -0.9, 0.3599999999999999 -0.9125, 0.3193750000000001 -0.925, 0.2775000000000001 -0.9375, 0.234375 -0.95, 0.18999999999999995 -0.9625, 0.14437499999999992 -0.975, 0.09750000000000003 -0.9875, 0.04937500000000006 -1, 0 diff --git a/docs/equations/bartlett-hann-1.png b/docs/equations/bartlett-hann-1.png deleted file mode 100644 index a51fe0deca919cd71e2dd3883010102377e8a7e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1984 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 diff --git a/docs/equations/blackman-1.png b/docs/equations/blackman-1.png deleted file mode 100644 index dc8daeb6bd742d671c6c5662a5a209fce7f68044..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2073 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 Hu0mjfe{t-j diff --git a/docs/equations/blackman-harris-2.png b/docs/equations/blackman-harris-2.png deleted file mode 100644 index d9a0da52dab7e15effc0b8a9e3db10182df1863f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1611 zcmV-R2DJH!P)(R9FeEmtANaRTRhnyR+HN>}O&XW3{+KDHb2xrq+*#l10>q zzL-!Dg(53h#Rqjk1*z1IQcz1;x8Rc?Sy6nDRw7axd~qJchLW@^wOXaIOGHsxpyXWGm~sudmv}-Irp63+;ev3-U$#bn+6*^kw5!g3F$8~RoG`IO4$GB=FQ5C z&N97L&$xEn0(>xjN~@_G34L(FHG`M2e8wgTAP{&Y3_&iFXSj7Sie+`toT#nit}4q2 zUNko1C>!SyE27Yvt6?kbGS0PHtzj{FvYo_h)H{~^0;~q!Q!4Lb8|)|WM!R&G_Za#0 z8U!8*Ly)V2=wcLWvP?Ow@*m+5RTeoSBwIyCll-TVnMP``0-OMTK+l)Y993pSPRMl( zcv76P1!SAZRYb*r3r@O8zekEyak z8RAGckalVGH1{CKuG_c=T2us01sxIzSzbg7&*D9lgOl9>w_!rp44C|c*bk5@* zrP5s}l!si@3XiFH83(>`BmfwRbBl}CbtgkTS=zGULW#Kn(h{? zufvunkqn!9a1%%T&OJ!A9Ia@VDW$6D>$mRo^7)#n+j0jUua$+A0iPk23rP3ibV-mV z1A&G346hi?@_Jy;G?PVDmOYCC|G=hD59lReNp!KkjO>BCX=GQ~BGZs)2H*1)j&r#O zqm!4kl4IgC>@6A$4An4eddTt_Qn`Rs59p{-NFybBA-WjN@_XOKCHWwvj}g zT)zTyxD{BBB5<5GWCzNmxkV91=Jb*tfFA8_6m&nR2XWdK>Y{+xB(u<11n_7cxw7Tv zx0_zgkje$5dq50L3&zVrI+{C!KJcC>#VD5715ghhR%H!U=m49P-v*2Ofj&;G8Q?Dt z93=31 zp+7OVbOnNi5$=J_|1p%nGCfM?4;13=wgJG+cR?tqOfkAR~83PQd5FSs~9_>7_}dS+$%UbLvu;j3i(!c zgc(P86TEtfmuQo{I&nhb*P{pLA2<-=uIvc+UYmYfI;nC+Us3 zI(cqsxZP@9T8QU;{TrxxBnWL&NbvLO}P$?{{T-Z6Ub1Jm`(ry002ov JPDHLkV1kSh-$?)f diff --git a/docs/equations/blackman-nuttall-1.png b/docs/equations/blackman-nuttall-1.png deleted file mode 100644 index e88e6334656017f6b7688c723a2609ec513197ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2545 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 Hu0mjfe{t-j diff --git a/docs/equations/blackman-nuttall-2.png b/docs/equations/blackman-nuttall-2.png deleted file mode 100644 index 2bac4d397c88234050f92cb68be91d18f7c13264..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1531 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 JPDHLkV1n5*>e2uJ diff --git a/docs/equations/flattop-1.png b/docs/equations/flattop-1.png deleted file mode 100644 index 7d7e5b7dc8926fe318dcf722b48f81cad6b323ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2838 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 diff --git a/docs/equations/gaussian-2.png b/docs/equations/gaussian-2.png deleted file mode 100644 index dba380914c59c44f7f0202620807314f149f421c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 420 zcmV;V0bBlwP)81AuHMC?8Hmslk~11q>_#a3y^A|NlP#l48>W z>4Sj$2@K2&fK2A_A|SbIM*TCexK;s}0u%i}B?o{k76x_!2DX0;ykOmIA`M_!FacD; zasa~p$-rX3z^ee}@ZEg?5d@Mzi+C46c!wGIA24uDfN(f`H$WJ`@F-y56##N985q_w z^fNH@CqQL5455k{7@?m2#K3SEq{IlS0_Z8l0tlP+0?2p<1_cI=-wccmoEP9q7#M;e zY_=5)wG3>_8J;un8ZfY#ha;(gGn;|Y$ab88Tge?RL@fq~AP5Ly8V3N8lr_KIld94H O0000-&6r zvM3k0(2v{Ml_z$+hD`o`e5TS|tU%K9FMhz{B0hwb18$qHVvXVp%XP=Ry#Z+x(5+pR zaue{h%A9H|l(G_H6VUpooNTB(1rJH4>gZ=soSPiI_azkYC)OIVvq;}r4M^MJLo{gU;c0fDc zb0|bB;yFycQINS@v)IR8P;*l)DhQG|Dxf%~ple$dTyi|cX9o`*(% zU#2DKk>fSOdTZJ#*YP!;_gl(i;dRp;%ot{f%K(KAXVxKGqtZ}nZUku0xpv5Jxbm^2 z7u+!mThJ#}wv=ze3qAp+e`VBfOinUHS3BKcK>wYF8JG52c&in%HfbdD-Lwj zA?X5q@4%aYnWuB^QfF$>-9Dep>fe)DJZ003p$n~AN~Ahn3F9u)4QAFKK)ET1gRbsq z`*6s<40@M`2s#eOZxgNW1$Os-y0^hAiE1hB@Pes{yES$IzN9j@KO!|_mNt8!cOO<9 zpS0;NoE3a|Z5{gB7yTs+=}n>k$?!7*)A+iFQkDl}0DYo3VRxO$J9I`R`cr~(sKzx> z+x-|>Q#p;f3dPO-ssRtxB~ScC7d>p<{cw#FqQa&;qb_LD4|rT0pywPXPfCMz40t8t zRH|{J+-B2{9P?pZ!L9ah4{l6!NLN|7E!51ejm8z3U*-Fc z+7i&kLDL>{)Lp|OGOrGk32K{JtR|Iks-Iyg)k&x3uw0U*&;9us`lQ`RhTO3;JAjWO mY?A8MQNjG5GNZhf*Y+Put1oZ!s#x*>0000 diff --git a/docs/equations/hamming-2.png b/docs/equations/hamming-2.png deleted file mode 100644 index 186c42477d37195f3b4bdd5c530e0c369213d633..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 761 zcmVTii(P2Vq!2bFwD%%wzjqw78ahKo^*6{P*6}nKtSf^<^TWya~exM00001 zbW%=J06^y0W&i*JcS%G+R7ef2l|6_PK@`XT`Eql&mx-lzf>+2166`F5RCY>0un>!g zD2i946Fe|!&&J>fhhPx|3qfHEK_ypg;awpptDwhmDtpIuB6o!!5z+YGn{0M=*{mnz z6|?_&@6GSO+1c3u38B``NcOgWjIS$3%Y(&^F=P29HcyxaJVB|YrGyqqWgGcAi++EB z#$u1Pj>!%V1zKN=`=j%uS-=z2yr1hNHBs&qu!l>RPxDwZd^dpuoEwIky*1P$MZgo( zG}b#KI`08@xa3Oxnc-W5^+%H7a4wXiRKNqF?0uuUfM38RuDU#^r-rYCoWn^YcHkTE z7?t$~Yt@HEgThjM1>UlF*L8t{75e5kZ%%W;jJR(Kk5T0p-Pf}Gp+Du>q)K-=c(*L_ zeZ#dno&(nnUmJ~iF04v#g(c#?DLh6sFXBEgQ#};7ud*5j`<~Uaauv0RFS4y+pB6FS zG#;ax6=Z6(a#tJb2a7QHIUm;3jA#3XFTKtaPS_|)`(EL=Mu`-jptfA(+SFGj&u>*B z31XU*A-PG6YK>enxvA-C+_tcW{j(>dr z{S((Y=pJwQbWPdc6rP|aJme~b*lO_>N8=>GsVTWD^LteX_%}r&w!bMn`ScaqdVlFW z&dpd65;huk1shJ$!BHwq_Mi?*gIYnsi?$9TmKH*;w`Mxpe|#eiq7OV+le^_bl8f7zXq@?-YZyno-T r{;<&T9aBUC3yGJ>#00000NkvXXu0mjf_5)`s diff --git a/docs/equations/hann.png b/docs/equations/hann.png deleted file mode 100644 index 2944b23fd0db740bb9b77ffd071748394792b5a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1528 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 Hu0mjfe{t-j diff --git a/docs/equations/nuttall-2.png b/docs/equations/nuttall-2.png deleted file mode 100644 index 9ace46921ef46a0570a2f7b22e4e247a5831a07d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1740 zcmV;-1~d7IP)wZ<+Oj`l$EYA7 z+7LpaYzQjCeoT-MhWRW(Gn0{mAcz?$gvx6Cr$mb0ACY0ZFt&wOyEg0}{UOR4iL1Hp zbk4c&y?Hb5F}n>OnDfp(_nyx?@4Wl&y#oY`wq#5?E{rV#J~O^nDslGRI}B(O6TpXK zLsOf^%N-xm_dEw>;AT+UpBD;NS_frVnSRPT!1Ny0l zseSL5W&04SbjP^ua3x))hHl{|pw;?Bscfs&x`uN`q@*?Qo;$dgVS@-Y2MH2}aFd&{ z3qtD!aQYmOej9s;CW=5CRF+~ngL__AgWfZsfKx@_JR&vdAK^jUhv_Eu2g?ay3-x42 z@LdV7{y-+H(A&>!En^LaN|`3IrQpy`uMMKuIY^K&gqtcOX9%q`B)DaRv>hLFKhZ>m zfN4``Vg-kGdNXB2o8%JU-M<%sJoGCF*R{u#4_L|l!SXq9gnD8{E{x1PRvAjhNEFBl zSqm(T4FV3#sUTqpH)kQ-5E=pJ6&Lgh4<(wYXal*j(8QXTLIY9_q%68BV7v%C0nFeH za@oE_j~R0X*v=z?ZDYWZFo zA&QrY+#h>s-F@WnQVf|g#8DSU#~jnWx}Z#w9)*MGf%tyJS-#|1zB7e6+{e@_0Yb+?t-GJ!M5%VlEMg0l-i#pOWCQsq(BfC zCTwI&F2OPbpGxfvPZvpySdZpfYyNuV+RpKxHK}?5L#ypgEULu*y*)1lEMg06!4l$Bdb#+ zqb>Zpyh4j_q(%+}l)}X802Sr|@oAiCuxnbeRA>*m$Lu7coGqdhI(YMk|@%ylPO`b zp_{a}i9&|6T}7KzqKvpPNHef4tMKPtO8 zKEMUZ`HEKO)GJ@Ht}^9g+XP_(16qUQzp%ut6I}A7c7rZd75;-*dnZ zpWovvjNZ`rtLszrC1?AIj8%M5&(D}X-vS)Zb&pB5S{J*03u*g6_-0RiA%Qn^jPLdI iO*i|F>f}oRw3RxwOP_)oNgn*7JT^t0V^##n3CR{O%K%n1k9! zEdrbfp-mIH#joGvAgE>ouL7T^NQ)%zEntMe`Sa_x2PA)f4cMobYmyDv`|1V8A&bTZ z<1|w-Y_Yt+R17D%Z2CEs>?(DbhE-x1$+H66Bk3o71D0e(m($j=vj6}907*qoM6N<$ Eg3;E+Q~&?~ diff --git a/docs/equations/triangular.png b/docs/equations/triangular.png deleted file mode 100644 index 7fdbe2cc12328ac2e2a64ff9340cc568730eed65..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1109 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 peak) peak = mag[i] + let db = new Float64Array(mag.length) + for (let i = 0; i < mag.length; i++) db[i] = 20 * Math.log10(Math.max(mag[i] / peak, 1e-15)) + + // Time-domain y-range + let tMax = 0 + for (let i = 0; i < N; i++) if (samples[i] > tMax) tMax = samples[i] + let yTop = tMax <= 1.1 ? 1 : Math.ceil(tMax) + let yTicks = tMax <= 1.1 ? [0, 0.5, 1] : Array.from({ length: yTop + 1 }, (_, i) => i) + + let svg = `\n` + svg += ` \n` + svg += ` ${name}\n` + + svg += panel(L, samples, 0, 1, 0, yTop, [0, 0.5, 1], yTicks, true) + svg += panel(R, db, 0, 0.5, -120, 0, [0, 0.1, 0.2, 0.3, 0.4, 0.5], [0, -40, -80, -120], false) + + // Axis labels + svg += ` n / N\n` + svg += ` Normalized frequency (\u00d7 F\u209b)\n` + + svg += `\n` + writeFileSync(`docs/plots/${name}.svg`, svg) +} + +console.log(`${wins.length} SVGs → docs/plots/`) + +// ── Panel renderer ── + +function panel (p, data, xMin, xMax, yMin, yMax, xTicks, yTicks, fill) { + let xR = xMax - xMin, yR = yMax - yMin + let sx = v => p.x + ((v - xMin) / xR) * p.w + let sy = v => p.y + p.h - ((Math.max(yMin, Math.min(yMax, v)) - yMin) / yR) * p.h + let s = '' + + // Grid — horizontal + for (let yt of yTicks) { + let y = sy(yt).toFixed(1) + s += ` \n` + s += ` ${yt}\n` + } -var c, N=81 + // Grid — vertical + for (let xt of xTicks) { + let x = sx(xt).toFixed(1) + s += ` \n` + s += ` ${xt}\n` + } -for(c=0; c\n` + s += ` \n` - var csv = '' - for(i=0; i\n` + } - exec('python docs/plot.py ' + filename + ' "' + plot.name + '" ' + plot.prefix, function (error, stdout, stderr) { - console.log('stdout = ',stdout) - console.log('stderr = ',stderr) - }) - }) - }(plots[c])) - + s += ` \n` + return s } diff --git a/docs/plot.py b/docs/plot.py deleted file mode 100644 index 9110211..0000000 --- a/docs/plot.py +++ /dev/null @@ -1,65 +0,0 @@ -# I'm aware of the irony of a Python script for the plotting. Still -# waiting for a js plotting library that can begin to compete... - -import numpy as np -import matplotlib -import matplotlib.pyplot as pl -import sys - -print sys.argv - -datafile = sys.argv[1] -name = sys.argv[2] -prefix = sys.argv[3] - -data = np.loadtxt( datafile, delimiter=',') - - -x = data[:,0] -y = data[:,1] - -font = {'family' : 'normal', - 'size' : 9} - -matplotlib.rc('font', **font) - -sc = 0.6 -f = pl.figure(figsize=(10*sc,5*sc)) - -pl.subplot(1,2,1) -ax = pl.gca() -pl.plot(x,y,color="black",lw=1) -ax.fill_between(x,0,y,color="#006699") - -if prefix == 'flatTop': - pl.axis([0,1,-1,5]) -else: - pl.axis([0,1,0,1.05]) -pl.title("%s" % name) -pl.xlabel('samples') -pl.ylabel('amplitude') - -a = ax.get_xticks().tolist() -for i in range(len(a)): - a[i] = '' -a[0] = '0' -a[-1] = 'N-1' -ax.set_xticklabels(a) -pl.grid() - -pl.subplot(1,2,2) -ax = pl.gca() -yh = 20*np.log10(np.abs(np.fft.fftshift(np.fft.fft(y, n=len(y)*10+1)))) - 40 -yh -= np.max(yh) -f = np.linspace(-40,40,len(yh)) -ax.fill_between(f,-1000,yh,color="#de7c00") -pl.title("Fourier transform") -pl.xlabel('bins') -pl.ylabel('decibels') -pl.axis([np.min(f),np.max(f),-130,10]) -pl.grid() - - -pl.tight_layout() - -pl.savefig("docs/plots/%s.png" % prefix) diff --git a/docs/plots/bartlett-hann.png b/docs/plots/bartlett-hann.png deleted file mode 100644 index 80c834a5b486431198ff5e9c8dbf14697a77582e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38995 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 diff --git a/docs/plots/bartlett.png b/docs/plots/bartlett.png deleted file mode 100644 index 0fe74c0f679ff6c85ae2ae145007996a6a64ce12..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38612 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 + + bartlett + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/bartlettHann.svg b/docs/plots/bartlettHann.svg new file mode 100644 index 0000000..a6f1c74 --- /dev/null +++ b/docs/plots/bartlettHann.svg @@ -0,0 +1,45 @@ + + + bartlettHann + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/blackman-harris.png b/docs/plots/blackman-harris.png deleted file mode 100644 index 54ed0c0e5f33a92d7845b8f632230b3e3f7f0cf1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35826 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 V^R=KpO%(jOrJ#26ot(v^{{@P(5PJXs diff --git a/docs/plots/blackman-nuttall.png b/docs/plots/blackman-nuttall.png deleted file mode 100644 index 19b1a1b24acfb208e207e3a7866c8d3ef580c421..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35419 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ë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 Vq9?^ApBfGRk(QK~C=%26{Xgn&^soQ` diff --git a/docs/plots/blackman.png b/docs/plots/blackman.png deleted file mode 100644 index c06db05fb7bb1145a734b67a056fae995dae040e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35160 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 diff --git a/docs/plots/blackman.svg b/docs/plots/blackman.svg new file mode 100644 index 0000000..185fb78 --- /dev/null +++ b/docs/plots/blackman.svg @@ -0,0 +1,45 @@ + + + blackman + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/blackmanHarris.svg b/docs/plots/blackmanHarris.svg new file mode 100644 index 0000000..7e7bac6 --- /dev/null +++ b/docs/plots/blackmanHarris.svg @@ -0,0 +1,45 @@ + + + blackmanHarris + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/blackmanNuttall.svg b/docs/plots/blackmanNuttall.svg new file mode 100644 index 0000000..7cbf7cc --- /dev/null +++ b/docs/plots/blackmanNuttall.svg @@ -0,0 +1,45 @@ + + + blackmanNuttall + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/bohman.svg b/docs/plots/bohman.svg new file mode 100644 index 0000000..5104feb --- /dev/null +++ b/docs/plots/bohman.svg @@ -0,0 +1,45 @@ + + + bohman + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/cauchy.svg b/docs/plots/cauchy.svg new file mode 100644 index 0000000..7f04b29 --- /dev/null +++ b/docs/plots/cauchy.svg @@ -0,0 +1,45 @@ + + + cauchy + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/confinedGaussian.svg b/docs/plots/confinedGaussian.svg new file mode 100644 index 0000000..16efa41 --- /dev/null +++ b/docs/plots/confinedGaussian.svg @@ -0,0 +1,45 @@ + + + confinedGaussian + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/connes.svg b/docs/plots/connes.svg new file mode 100644 index 0000000..3ccba20 --- /dev/null +++ b/docs/plots/connes.svg @@ -0,0 +1,45 @@ + + + connes + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/cosine.png b/docs/plots/cosine.png deleted file mode 100644 index 49b682fdea4685e4912cdd588cf54365c2908230..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33944 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 diff --git a/docs/plots/cosine.svg b/docs/plots/cosine.svg new file mode 100644 index 0000000..332c9e4 --- /dev/null +++ b/docs/plots/cosine.svg @@ -0,0 +1,45 @@ + + + cosine + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/dolphChebyshev.svg b/docs/plots/dolphChebyshev.svg new file mode 100644 index 0000000..e1c8744 --- /dev/null +++ b/docs/plots/dolphChebyshev.svg @@ -0,0 +1,45 @@ + + + dolphChebyshev + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/dpss.svg b/docs/plots/dpss.svg new file mode 100644 index 0000000..9496115 --- /dev/null +++ b/docs/plots/dpss.svg @@ -0,0 +1,45 @@ + + + dpss + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/exact-blackman.png b/docs/plots/exact-blackman.png deleted file mode 100644 index 4828e9316ba7fbffbb6591b22328942f827c9685..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35361 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 diff --git a/docs/plots/exactBlackman.svg b/docs/plots/exactBlackman.svg new file mode 100644 index 0000000..1500d6d --- /dev/null +++ b/docs/plots/exactBlackman.svg @@ -0,0 +1,45 @@ + + + exactBlackman + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/exponential.svg b/docs/plots/exponential.svg new file mode 100644 index 0000000..4f113bc --- /dev/null +++ b/docs/plots/exponential.svg @@ -0,0 +1,45 @@ + + + exponential + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/flatTop.svg b/docs/plots/flatTop.svg new file mode 100644 index 0000000..ad56373 --- /dev/null +++ b/docs/plots/flatTop.svg @@ -0,0 +1,51 @@ + + + flatTop + + 0 + + 1 + + 2 + + 3 + + 4 + + 5 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/flattop.png b/docs/plots/flattop.png deleted file mode 100644 index 1656b8c2661afd8d8a44c05e6da4925d750367f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33519 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

Hs)az^>IMB#Jr23Nn=wAjwt<>bOu81Z&zH zup;fYt3S53Bhp78p(2p7e;A)8O^k3?Mzm5KP@REnTrln*!M?28eV+r;KVW%Af?Sbv z6#$MQD?%2L9s`gVum=G$#Thu74@j=U17n@$MFq9-7T8orf(7slo-r6idAez!?S{oD zFj3GNNj8Abj*Klh0-&Lg^B26F1j=V1@j(j7y!G4ugYm)4xRrI0I`1zK#n3^3D(p_D|-)lJn*(OLxkCDV*JXdl|UjOLedcWnY zZN@>IhBW+!*vcal?3zzw_e`eo<${7}yaR4vVT^hrk)e}uJSKabb5D?YpQ`@5?9E3? zrZ*WgjutvEFS%`(Y)#_UH>N1}-0L@ECfC<_Q^yh}TrA%$J&2(Y$42%Ne0=XF3i6oT zgofwekI!v{xmEu?&Pqe9rr`{bK?%(&uV-oErYplehjL`O|5jOl&G?@vcV)t zDJdyIV;*N4l_xV-m&T>LgM)(!tcK`tt<0Cfc$}q*s;!}qB!z^Klenz1G^=fuaur&p zBPA%lHGho^A>?9p*wl<=((Vl>6_#54`1Gyb_H48HR80)4Vdtp)CrY@?t8V3Vk&x}3 zojG_|Dm2NwA=^K{Xy0Q#>V$Xb*(Kp~jvg-1m|t4Tsv7?NqsnIf&959R64&i(&h;s+ zA%uhZYHW!dX0cnMZ!QTuM=er4W}sf~@zC-rjEuluXNekpav zGA6Crh(`u#zGtkmT|~b+o4ImaN-z#m3Z)P?Fffp-u{TMGj$!ygE<~TeW{kNpUe0D$ z_;nyph3WkK9EtngI>Ez`_g`M+{hph9-WAKJR&E-jcA1^(K5m|YrgL?9kuzlaYXTB|cWbN8$r4@;3`S^OBrzeu;>(_x{VO*IUR@2Gw-^<+Y;s0d8T7PL9soRT~{+@RH{V0iUKiIkc7r@c0k zYJ)3}X?bFF^!?I3(~-iS(Gq>>Ew-)HV0@-5t$OEZMvd57hpnaK0A?zx_gLg2a+wmi zu-uYup^tc+b~Dt7!8O*$N*}Np-HxGq^*fL{rCr{`4;>eY;HiDiD%KlEM{X{wX>g`e z(;>R|w;y;{R#vXsopdoZ{>)XRc6D>x0UH@JPF)1cH(JEocdrDWgoX94t$l{L;A(d2 z(ma2pMDITZwteB ze9~N?QHgLL$yf$mFonAP0m<8so<@DA6g=y=pM5KmHwDRb5?TyC}hFHQjx- z)hMf=px_l|Tq!SS0sj7hkSp{S4%IzuY#{>!itA{Y-HK*{f=I@_$)tRIkC>m@S&Wx` zQY$rbbaqZ$(s*6x7~$)S!iGj)JM3*bT4h5Ht|OG*fA8T#iM^#Br?rC0?%nnA^5Ew_ zH~O=r2{|psf50vKx)azQaG2gl+L)+p>*)y*_Q$*?(uP>a=qIleA!Z=8ChccR#;w5e zrHlxQUtXSBDJUxDzRHWGEf5KLc+$%^Xj-29%pnv!5q~AFN>JyHb3O4#8DnR|$P@iy z+r`fFoi-Aw6yAhadCEJxT};awMu%sa5}Ci-BgoH>=fhl&rX0-2zP9Ysu$ndO@7veB z+vuFGcS+!}qax%qPlL#4zcC(zc9%GQ?0BT|2)#+w%4Gl}i`7^VDIp8^TZbpQxFO z&Dmz3y0gt%L?~S!E%7nzitR7a7yUgypQ~O@2j-bB5krRvlW_H968Gb4o~iDqfe?KL zvt>yt%*TYltxw=368PO})TUtj9Opy02w3z`o;hr0ysmu*dkqH>2S_~5Sx8Aqef|B} z9Jh6D+$LxmlogPE^M(eNWpOc1n~H;jW2S-uOUxVD?&{J_JX1j&yr+UYYsgf(D4Chy zcBFD%jK~K9QoghG(yaaL6laM)GmVXE({5*n;&d?*HI6%s zGZ{6jptIl2l4P1rw3ho zuJu|>Cgtj@;166=cPVpqjv$nuJ<$#sD~i%8iuP`CYSl>J#Fn7-twjF z=h=f;cz8xEx#dXNM&;0^fyn`?J(oRgf!w4`ib(02sdtUr(@qrV_`7b(}+-*Ohdzr>g3F@W(LX-N~-u z>EVVXe5sXV*3CJD%rF*%r2`2F^J zX&;$5HY_*C6!*02p0rFdck_=x9Fu{JTex&eIsm}TC#(ADY9Z4?XxV|Fu{*3`6_J=I z0f8!k%Zm8&^74eZFVNd`Z+Dl~Mf-~qttYR_X?1Pw{MJ@q1i5HHK)@{A-G1fgHRh+P zklpxn?2FNNa~K8gfJ?6&IMb;Vn#VG&o#o`vl&Ld@@g2sirF0*$u&{XAlzaoD0;I4E zTTus@3!rHNi+*c(N=ol1VgGJRR#Z}e4L0+wHnUCFzNi#oHtqEZ$f~Nwx6B&>%87mQ z+Vs^4po+eJBduV@SVoOj$Ub*fFLmzS3<}E2qc2b|@7x&$e9g%3|K!P&^NIWYmxe5zo3!fjJNo5u0J7er# z$VJ}6jK!_0slmQ?uL)Kvf!!pMwjOdAp7y4Ri3tLo0`g+j`*BmzdCB*W6kr&$#Jp&6 zbMt(^4akNiU|svpd|(y}5ZyCXinOw{(W@WznPlXqji>448Q6Z-ZwrO$AQJwOuYG4R zcwjP8_y{7#`b=Z0*+}6I0>o%2yFY}QBOo|9xTXE|@DVP(@{gXL9x~aba|{v^k|;~& zC+yrdzfoq~jvh%TvNwPK?gLTy9v)sRfX&3BrF`@0x&-jwq-1*LUN4u+ljQfc5;^R;J;1yN?+~NUv?4#(-QL?PjX%m5Sqlse<#fYj^B@8TjOV$-GewB8 zd%byNBo5$0F+~4h{V{x}_8lK`mj_^T^RPs){{G;hi39V$e>3zlwlCYXhm+oZ!0NlQ z@@BY5yU(A*T?~-Y$x@P)ZFg$*HeKVHw#+!HpD4GgGOJNH!Rh|$cdN#WR!GElJ#i^1 zq0KtXPWw9NxTPk8RGONaQYHYJD&s%Y!Rlysy|uBKOE6BEg9>P&d_;@<$&=o7{1-B9 z7V+oPf);RiN;S)PqUj1e*l1$(D^D?$^Or;z}3NQfX@ZQ%{4aj z@71b&p|}bq5$LUR+GE%S(7e-(Mxw(^u*(3Cp5?mRL5&0n)BCGom+f-zqrriFh`d}Q zhP!B(LI(Qutl%7n(b2ne-e z{}e(eiq0vE?dkyK;Ke+I2E4F(3}+AL&FbaRax;7m(_i6~NV(PyOF)b$+L6B1_P>+G=zxxe?DSlw2@O3Z%C#V*filv`7 zLkS-9hW%mdhX>!G((Pz)J6oBm1_l{3E= z7Y*MD&El%iC~DayKYXp(g34*V*(R@K0gseG9BPE4LRK~H9I3W5#$!+wJldR+=Bfp& z{in+1G=F2`Gu`MR0QiI99~d9840TsCY>xC+wrpx|Jd3_sX)gM9ysRtgA#S7@3oQ^IC0w0U*f>`O^3C(}z6bhg2O|(b~T*n{A{hwd* zUhEGdtO+o=KRA_43U6R)Dv5sUhmtRtnG!LdWj;R5S1Wx_C7lSROakxq&>JWyvvYIJ z6IP9gLM$*KKxcQMLn%+WN65qd;1)VM8LT|u#~^?**bp>;lju>tZp0*9pv(ry3E>7% zQLT=b)5F^8zyrAM^>7)5rV8@^pg0m@HXCLDhiL-~eZcR^jR>_~US7Pe$Kh}!NzVY{ zcu>vTHCBW6d;I6y!lzIjL4n`x{%L=GEDG!y5h70y))40>JaZ^iBNh-4_@b2iZA9Bc zx-W(A*6rIF@GbN4vRuJ7J9Jd}dluDx;WX_``KCi(5{TsuLouPQ;CFGB;l%jzNj8} z_UCXsncL>udNS|^$k(r*K(0glg-R+~Rf~~^^~5g)k+vr)t*BX9BjdE31EG@J9ycpO zyuKna7Z;b1urS1#y7S$x?i9WxL}_7VwS_!G@--_C3NOGdJLh|SIQQ@Wz+kFt0z<5N zw~^S9TN(?@3KlMI3nIBf87{q-Y&)P>ZENU$b>Xl%(+CUV77s{3;PROKU~Q!G)E(UV z8L$pN0BTBt`JX@}N#J#2O&1O8W$~c;?tj-9GNkYO_q)G?nZ6qJJT&S_Yyl<(-Jr2} z29*+sJ1U)doh|NHK~xneF;03;*c~xl7kS)ZBGaIQo~+ zGZ6Xk|G~^DlKs{J9;C0#SNZY1QG_8V?!WOm2ylrNOk=31GRAd5TZ2lF7YZI=k6NY` zP`t6f&xZ;0!a={NS2us~Nj{b%)|f1n@b!7S*oo_Yw!t-6xDbl)+jutP(0arB1jd6H zz1d?4`&tdI31H}UThn}VI=IL#lcmv{+7jb9Z*dNHzbhZ*>uu>64#0bBIygA6L&hY@ z?nm#<#!r?lEnUUE|A=x+P2z}&awD+jmdl}#l&yo{pI^E9R=1Sr%$=K0*qCIA^)ufqfj4q@8L$yAJ;crvy?c)Pxeh0z5 z0cLhu8Zs^a@Q?!?(mVLx13qVV=Xx#D%zvp-r+H>f<#N-Zcy1dq$d&IJoU&y;A~>*W z*ArIY<`w{xNhNVgUyJG7?xZgo)$o4xf8H1%ZLml>q;#&CIs`@nzGAtgjTlkqejpJ@ z0USk_rtkm(Z2r%;BWi7?qbVsVdGX@K?ADf*gjxS*RNyNpsHn!}6I`Ln{=Xj}+pah= zG7@PU@M)%#vorF2LNqggBZsj%xD`Lmm@#1 z^;5L2@87R`9v?d@s%vTzApw-E45oVC&q8n;rD$BRK9or=OTt1^qE<0of>N{7c~+}{ zI3(36B1YqAR3l4J?#++iL|$nIbJm!#5}BQQRRI0~SWq)CbVt8i>V9B4oS%^GGXymU z`@@hi`p1twf&7hMoPf0YFr@YOc~g^Mu#*-f1reZBaB&S|iNQ4?CG-q@`~JQE4Ljb1 zHX;&1A(@z&nURs!|AyQ3xLD0uOzb9<)a5@pt!D+Z3)@5oeP~q*h_@Pt<{li&TO8UA%TDf z_RY|VegCS32idEM2)Omg`j24SbyKF zlLqM+!A8MePm^syRx+8Y;Q(v0{S#2!5~1Nh@-uUb-Zym;jU!QLuTXBoMs9KTh;ZL} zjVSpFXHz?UZ0~}5`^?srwN3qmMdZ{}>7*aBlYNIkCU?dY}QG=k^4cTaG>H3R|K5Y-rvH^p)G zV;v^!%@MR8rc=4r)3oNZd{cRhh;sk1Jp97dF;g83)j3#Bg+Kl zMJDf*LR%VZZGcL?a>v=^v>$@)Fx*=WXN(jT2^Z`A#_V!d!yKM*gCe4 z-rZqk6I#CxQ?qW zoB2~tOv$@g2l{%HjH|8f#D_Hw-xzc_C7Mip?ET-myo(D~9MBsD!z#`VUt9>g(Gb^6R5j zvD1C!4&%-Szt=Y+H29)5^?MTeyd=xBhufDLG=Rf41=aKII{K5D{0+))=@G(usQ1cGad|R9IO=^W2N}FlrL@ zDq>2*!G3ozDLh&z4&PZf+;e#*Vx$_G<~3%D2fl?KgGKG3NOqS%g`7=oD{@;~1pe|r zfic^+;8)%=DGd9|t=JU!6tVv_X@aqL!|1MyEi{JE3;>8CqQuFn#e@q|heM%oWyI{J z2_cqNSaa$3fVyK3-n~BouBG^uW(?M@toX}6e*CzHgY$i|+D<<2(L7%MKfxKhV!)D^ z^`h~ZKy2KAm5gb0($dC~%Vji*uT7^)y$|u`L6X>~P zMB}F1=Vz=-MYns{%xr3)Xp)z&@t`%4y``~Q8Rlg$Ic%mSfP}k>MQv_b$z7Smx84zA zT}*Tj(us>?anR(xR^7W`o+ttTrv+*9Xof$^Rxn@v+3U}Vc+vV}F|nXZQ{PJv^PiMd zTF&Suar-pmEbGW{3aOm_!BOA%uP67n-j(|ERRbz-q9V9`qvn9*1I4wES}d(a3drRT zQ=G5AR$zFo{UGauw3ncqUcZP|nH2k*jw%l=Cl+LXA?SuBF)t-KP2PEC?#ixO;r+Fh zk6yUeJ$dE;&CXpdJKqA%`rIB+6RBTwI7jIhbCc-KoDR?9F?}!Yr(*}xHD{i)Mbtu5 z!>w_AZ%)~dIds}wCadS7UypK z*L-aSA9h$MfTTT}{@homciYcOP(%+tSvRYQs{<{AVdF_Fsix zV%$;W=n^uq=zf(n{^VbqKn_7SFzjlcuRE3!eN*5S%eT&m7MlOwBh=>b+sl+Usw2~; z5-7jfRM>wU*gYXz;_7lD#*=gU>Do?ntLO50pL(tW8sDXC#TB6@?>)posSsMd%*^1^7$2aX zE)hF>*O%@=(XkYEGuW~-81mT@YZ0OZ0HMC0&}Cr|N0F8&L=E^F~Lhh zQ=GFJ-=}SNv()vs&6@Ue6@RYopDg3wMDgZj-h3Y47JXAZjFdx!h3}v3@g`%@JFg^& z)O+NzPlpqOK7L5LaVg!9d=^zd1fZ2#GQ>`qDUdKc}u(e(&K>cB{Yp-RF?BkD0PJlW{W7;vx^tP3WnY zXXeOqMrY8 zH|`yqi)8;K#rp7pdAq(eh512!yp(GsG}ID09C~>~UcxL+bGEF+B7(u2C&usCmFV8| ze;oDb+wRH|*g#k)H)*7ne{@Q?162&hX@-~~O301scmL4>+#{{~0W1DwO6;-a)~#U7~aZ|)jSo;VJ09V$mL!Q~`af&B;E z`sDcdczaYotRK4Eo0BXvLW|I0LNGAE-}Qerlmx2AlDeEH{hv%wV7|D-?N-CH_H0!? zpJ75Sd7_8-Z~&}N1{{XngQ)$jSe zc%lO4zqZsFOUkqC(u(%FduA=lyfUcxzxGdbPgIL%e=ftSsepmqb+_?HkWAIb=UGcs zvOZ6Ij;Q;J^ODc45%+e2_6e}^XwaUhs;%7v0t(R&R!lAjrUF{Q&BeOkt?lgeffn0+D`&>u~!pV=wS{hm8^%cB;po~qLP-&3#lA|dU_6ufY`Q~nN;{ZCY&NjwNKlh_O62~m6S0> zkNA#wI0itM`|&J_#?M9Q*xg#E6&5m2>{jMUu!<9&|Fd7)|M5N21X~me0<_kQc4+^$P+q5&2&vFInVv0Bh2l<^}p*9F%&3w z<;bUlSO7s+LT!{jvRv1ui0GW&xQ*x{Dt%E3^tz7r^up99pW$`QuW4)s=&GIXOt9um zVRxP-lGNFb@_!BzQtpW{6^?N}zsEacX}kL!>+FU2V+so2oSYmY?YkuW86qK9oZJE) zm%PB;88myN@*YjvW!VG89ekn;MUhgD9I5OC{NIc8{`h)iWaQCeT_3duAnZT&zhUUT zd;50T3fJE{LUodf<6%fk1qBpOK(!TWR_n#`dtACAx=yjNk&)ga>2uqB&c%2A`pJ~s56W#_=Ntcj|TdW#QzOl@?DS0)!oA=LxR7q+W>V2WqWB%0U9Y&ge`6KdPy;r zr9UlS_nxvF(}w}~rLe%oH1MOy!yq?`BWUO)fT&>)qJlg~4uFeGT`M}oz<NxD1j&Vz>cmU9`Mfn&tvmSGp$+=SX;f7IR3QEC*C zCt7u4z^R--LxC}w-V=CHY%Hv1c;5E*_F3TH$$;;HgX7OFdjA2o#`GGk%y(kvamGH< zBMF=4OSAtQ=4@`F(E|||5OA|JFpW{Y>@hDdZ<`Z(z4HMXm>ab6fB*h1CG3toVST#$ zYBPlgJ*1YPGJ!UQu1Yf*B zK>`XJ(Fy?G6=>NO5SfhCIhD55F?iWUXvDuK+FSbiTTZSH@gTGv1h*!;y1Jrco%T99 znoprm?{vIP3KxJz3^oZ#AGEHrK8#|wQ!5%jd=&mZ^k_v&`Jsc?2liGOhskPHgR7s10r2gtH>5L)R#%1KH{phH8D_hcc;DVoOmM*f4CuR}Lun@Zr$h@!s9 zoa~Kevd#TZt|z;AC$(?aL_lICE+a!lOB;y!=;=@3uz4+Lcy!BhaL&v*o|iBm#Gf9p zW16dD985gV{ThD%`ZGL1&5D&D_=n$i-Y%Kc+}58yQ|TEjO7XquVk|3hVVUIBPrI@N;;^-~WKR73IA5wWlzY&kf{zY=5k^4_AWo)~4MkmZ>v%S=?e_1-d zJ4H!d&hGefEy$sk)}&}u)|TX{J&m@<1;<@t-dTvQzz&7XefQT`$WtvLL+~D;eF3e- zz`%fNvCcJU928p@CdbI9i;QV_2d3gNz3+Ux8#+AQeQRECF{|2Hk!Gu_vUG_ykD7!i zSN3KQ$=?L_O<5~lMR$G{AL$>R1!yg@ z>L5)nK=%UCegL`DE>OQbJw<2U;WP6xnV=#h>sOKaKL2sASG7Kn)q8=0tH+ds{m+k~ zd6!3~9zvq4INrbiEjWr};=cc?)~Y}ew_-!f`!{{&)uS5x29cMwHFI-wGhUnf@f=#| z5u)z{9Iu2peTgfN+mosoZ|8yy#+PajQJZq5T~A*#(;Lftt$Ya3Mn`p#S^UO+Zhr+1 zxwR^h5sKujs==FbqVZF-UuGJvhmRO&7VAEVunDPL8Cg*R19OcfLk+*PC7g^XKQaJ{ zzVLPW%Jt?-No8HJ54~4Ak5voL?QZN2o;y-2Y-#HEgt~REbfAf@z-t8m#X-fHeflW$ zXTvJ-=;imJe;;5soW93z^f-#%dI5*5geoL;FGWvHVk(KnKSgKPf>?7D{-r0^?pm8$-JvHPQ7>E<8LbQV|4q?XG_@pFiEo8to{yB z{{O01*KzAPsrVx#{og(Jp$@_7!H}>CT1a_R$tqWgi&lB*C1X6rq|9S;7ywj87Psh1 z1zgP5ps?-fNDt3cIN4S2y_V{!bzNCY#BaJP=Q z6qe+(#PuAxh*^@jQH?;s$Hi=sPp;GQHP=KbJ%I{kLL|IN7kC3C&#T&PE*6heulIVk zwWxHs?CVWK^Ji5ghDp*IsK{P+ax|jitZA}U^o;~w!U)b~VSDa-&_pt`*)_;HQ$ptJ zu2)~qz0E_)6A5{5)!0+@o^^0K?=};=3CY>Y{X>RWYSj1!@P=J!J7NY#5?h=zs=XE5 z`Ae`M$}PYwa=%iVb#!Ky1kx%5qUp@st{K1UWa=DN&8x0sQnF~@%2OE=k>#hEqDG;_ z_@_`>08*hkWqGHi&6bDhG%^2an&W@jm2ik;^rl;tj4mHait;~BrihPJF5+gO`1Mad z_N?;gv-y3FdvdVgGXPKc^*>KIzT#mwMTMrLg{iTK1nZ;wGpplE=KuH!g0gwJ;nrx) zQspAVL9eH6*is%{=N+BKoVZqotfE5Y-YDqKcamlgCKTEdj1R!|pGMXx2rS+J6Ta9P zlMXU6gd9x<-A?46pj~Z|IU{ggVec~nK!4!`(nvYOF$zAAQ9@K_bKZXgDB&jb%s_qo zqpfX1z96mG;6-x#U~}gq+*JqJjzUp{59Rp+q1;HCNrij4qa&WKi!{DcdtP%Q>q3vk zO#tmXPzqyNSy`3t|6gSTb*A^X&$2-;QuouWsq)5)ufVFJ2ooVf`CHzn0k8 zC;;hRQ9`TXAFHR%F8F|UQaQ#|YsaJWGs69-%v}9{Z#m>&I=y9)A8wl|e3rML%iZU7 zUBJqq@Egik7QT3=$z2td5v}CJvFN4XCZr<+1sHVMsvCf$4-5%01mc82y^N-fH8Ls+ zq(bbHd;9woEG+VYboX!mwYIX%kV|~I#f0oH#+z+r{k!kWX#aJNZ$ zette`O)^uV1bTPS+KcUUBjCSM3SEwyY%YZvc?|%~z#5&{14c$R#B_w6~ z`TN7MD%%!bh7RaLFcME?{L>3zKo)-Q>&tAvB8OoCLRR}u=N0VDL}_b}tGlBImDSV2 zBeNSEctbkB->Kr&hQCQqSySS%Ph zqLhCLswXT}7~r>yh=>3r+r;fW1WW>mxSs3iJO;hokJeTNkj5GHCU<~XMXf{+Roit3 z6*NBOj`lYxw@!I*j$-*l$3yC0(xrE5oY875(ASP2)+WTwJe;eB$KdQtGs4wAA}CI4%w!sv867vG6=qIsD80@mQ?l z?9Iq-t1C^nVbr6up#7V=?{_rU*ezo|diIW8((dhBTo&)3cIbebt2cmVZ?@u&TsCD@ z%oAs?wh{dL+nKaqmTa}4A0*__H>c)#r}%B^z4kUJbO`vI%hc3C3c=%Y$N>D8ub);zWKEAm&4+l|8oO?pCRrSr#t)bTe@(2#*i`D!arlcc?^1)ladR)1KGSLYp2tdgNdap;kd);idt2pga z66#O!F*kgjEkam6qWHBH-2QcFd^Ie$qdM6O%NID*g`5<=Gs_FjguroTso32C%SRL! zhw{Gez6}m&L2lD)9eCARU=#FEQ%NVo-G%k@X0cJ5 zy9+(7yus_5dYxwEr}is?pdc4G-}dbbS&Iwahle&k3~zyMLgnmafIr>lN8w zr=%--g&J?CKc_npbz=)lc8XD5&T{=&KaT%p<~K1>_BS(2Pm`M1>41jU=vj+)m9-ae zdIz@pgUqZQBPN9AL(Y6OIaX`Upy|h#7 z3#Tl`)kU@>CHXEk(s+BYL3Gjk49Rr+1MjVT`M&D+Xbn1hhQ9pjA^Z zh^1A`l8YbBM6`8*=hJL(eIOo*Ap8)W)Nj7G@SGghM-lM}B=H$Qzfn+7SYI{ZwCZ*G zbwp85WK4?h#XWo!e7do~SQTPertL2(({nJPTU8v#)0nS6$vlytQ}!y=TKOTDWjEK1 z>vvGnIL-OtBc8}|M!w1!6|&KpSrr%(AKpY%j$xjA;wxsa%12`dpgT|;`k zF1^X%i0nD8c-zQ_!0F!?$`Z3H#$!}}At!gQuuwz*^ot?TSV>4LwJiMX-K=a70`wpwIEnn<6;&COoOT|EQ6$vncqVSrTfUsmSU?q61BQm^e~ zVZlHqm0uc)`;dnm-}?H(u3f)zKM#vR!+L4U?8l{xvU#vRh7w)QOo|!x5e{ygjui)` zH$-@w{T2Dctr^*=nzz>oSPgE{E8nD6(Rozk`|L#l$u}QumxLt^!%2atKDFJ*o!Pq8 zt1`1%rz%V4yCZ5`JtDJ?suuyw^w1Tb2XUJY;3fp60Mk@oVP+(6UWbzMflsBAlT*R~ zYh{F(lvD-~C|UcXqpB3*dTP+3yk*?KEI;kQ&0*kcVRk}5Oj_D-rok;EI~ySepZ2(N%j@h&=E5Sm()OLG zs^UgsTc$IcMu^y9dIQp6+d@aw7>qFG=H|Z44gJ0iy@C#{rNWT4Ps2EJ z({I?C70TTr&g)7XBHfKWyQ#5%%lP}uOrs!^*TFwXGY&%fNW{*z6_Q>>2ouo6P&ROYTxYo_>A8^0QO9&NNdVGY6ZZR9Oxu4(12p=BC9c5$~di~8aY}Y z!wC`5KSaNIQwJeQJ$bh=?kA$pbL}ZiRq4r$f?0yHSAz z$e+C|5RG~gd*JzbDBazKSd0H<*fdk;bP>cTr3Lqx$Vf7?#!g<9^nST)nMdar7x2hH zMYeus-~oCa)%jMw@j!-n%dnQR#*TmEd~aQwco?^dgkByTHQ)idR1c`{q$4=<=iN*!qrr$@p4~)z&;~VVZ)uDFRzlmC(xz)P|~iH z<-KlBW2=9Akn8_s>EUAHU9yvNQ=~7U=fR_HiZqJpd|I+@pOP!|J{0iVy-f6p;iDP` zSOi+^EKnaSKU0MXr^leWEtB70D{Ab;*Cu$@2Xa0PouGkp(2HY1qbV4V5ow-g97Y(_ zqrcqOuz$)&eL0EzRs&xnXaMzr%O~Qo^#!eDRC01RR8*t& zE?fc^1`}yT$jgs6w;igSharDP*jzE(5?xM{Weru$o_cD~ncZbko+5E4(i>~|l_~48 zr{U+|TEla~GwtHR{ebz5%lg1yja>3`9Y=v`R3463O#lg{ z&xhMW_!x5&;Yd9z`(e@lKlT9q&7=AH4VI^$a| zGQK;SXR(LMislb^Y$;&wQXeWtXlPw)t(O-#TW5Plk24xowyke4`7A^yjO8rC4 z2mHl}I_ZJxdvz(N$T8!m)%wUM5pI<;Vf?2705UpWn+K9r9&7vQHb)xH&SAXz)1jP& zI<_ptbG>3fGY_>YM)!~Antt=5U$Wb0ZtI11tcH^%LeP`yOY9vSP{JODeo4j$oRECD z-y#2FGsTmXFEA&BJYbqN=vQ8?R9jrplxcUIBZ+XL&5qKNSZsf8L>!0i$#MslIBSD# zQ@Ej}>M(AoWE{hd$K-#kK68dXw%mHbx|u?@n7Tpn@wJ*5Y4v>f#Sh~4=~`%ahN?4q zWHX8zgfS|sVn-@e+1iJ(*w)^$q@eoz%|fN3pg6{8L;+yFzk_-7+i&HXZl9}WLBBmG z_436FIp~)>qqVgJlI&$U^@H0ltgZrnnbnFvB@vqR%BApQ6eTp&T#sF zaohDVPk;4Ar~tb=EZ7gTg9i6aLmX$<{5Q7ckb|MN8BSD2=|+!Ow8q1QxteA%XU2XF zfyW0mZznDm-j^QWxF}{=O~F`5Vm)H|n5~cZJA^j6xCZPFZ&aTAM0ynMt7D9eY{BaZ zm8~5z*(w$q4UEN^OE)(9TljU0nH133NYVkv|aCGB~~y4CSpe z_RvDTpk~Hox2V~js=du@PcWWCPeJst=ljpOHzcU5-2rtv53hArxpdlZW)!ZrdVCc? z@7`ijCmr#SUX{-oWDQwzQ_bYeYs**;c{@lw{W4YW!;K<0>t=i2#Vtk(qEylDP+74? zm&9kmiA@>q4-`Ev-7w15 zm%~_vuiIuQn_AsxHniWh|Cm^Dv_v8_!!YFo<7uKmSeOib!37Dq0gxDnu~gyLI&>Z% z%veO{UfjQRRT(h)aXx2fhw=Aa_v?ZQ4CACoRcx(vb(*Xp3M7cw7lg5I~ah4DWcn1vZ*pK{RLIFo~d^f z+3H)dQ~O_;omi!IM3ym0bL!J!XpQv&BZD~7d$|gQ=Byv1s?p^Jj6q&=C%#XbHKX+* z$<<;dI&+cymv5rXbVR5n#+~{NYNGDRJa=nd|;1YehnJIHKp z;|dsO$L{mIOY^FLQ9%Uz&%lxm4eGFb#AnuX;ML9|OZsz{&2OTd89|#`X z3+#*E4G=hgB=1qD`_Qi=pe}%)!!dsXGa>FpBi(9Vc0h<&mt~Lea#Mu z&0f{vH~Zsa>7|sb3jM7F*D2SCdxnt3E*zHA?LLdwS0pXi9Ml$3DfQm7+0I9S^IxWE$}~9lR8{Q-o9}=j98oUqqgMkqVx*(mW^G|vmMdp zZN19ufC<7Ee*`APs)>T4BgDYWvi3sGkRM(d6{dALZqH~%tBK~>dZ66Q(R6f?PLaTH zVsO~4pvH+Ly{gI8l**%O=C(ym3%+cNj)>5q^k^iyO;>Mm-3z zB`iNu27T6Q^vUjxaNJ{Mm!qk9TWTC#LQXC5rf?=SfKBvf<5h9{%fP;99vDO+hH`8k z(y94O!$)X$AqHWv2?%nk@O+vDfr11u852yYGpKz<;o{QQnSr@Q=cCOYsO18K zgUOkh!-2$l_A5X`{#3>Jhd5Zw&lW$NlQW&N60X2$?Dm49`u^cpeMlLn8SV%UWGygBv3g3hgmYFK6&HR+rEqXL z(alV$`;%o`LeSjh`XMKz69o8IeI`BjI&T9V`rAYauEH)y*gNA~Z5|b!8HYRCl6J&# z-K(_14)kE8de?_yDa{!6WQQO2@1BPN9+F9AXj)%GWqJ(vnOl(@<}gegRJ&h1g(0B@ zU~{sLKnD!$Y6OI__JYzE{m{oH`zT=8OJ>b@ryO_uxWXC7aJ@YcJ8`Y@dVNY-b#Snz zlKj~Vn|Wxi)?FM<7@2@{dJ%r8PS$-^VKvi>m>Ci9;D;SUOjZFGKYxGZ|6r`YR1HtI zPdDBkXQ%KX?nt9vOtl~wz!7h>3tC~^B3~T{)O;4;BAF_Zy6;&lP_aDsevU$0U3RxR zCPPzGbf7xs@v3;k@k~QXLT@xFUy4lm2l`P~;sPX;pGFs78$Ixt3q#p>h!ev0exW8m zWmvt2`_T^L+MDz?pX%|bkGy|kN9Ow(_6F>2xTLR)pK82cpixXN{=h))XHtooux?lp zb~uL{+4q2}N;ZsJy!-Gj;3byvQ9=aX&G}g=R^Je&p?wzazI>ndYV+|@9}|c%_Mdrt za${%e4v#4PzWVRS1CN`@CAA_|D<-ZKDl3KiyR)g0*gh+hn0J#iyv5PR~+f@!Tn zFX0zHnU(ROLuG~a6`1US!4xh@gt#5d{gq}H=wfoT8~Gt|X6dyAA;#W-?rE_z@F%r!2+gw$x_PIZ5Pm z+2j2-M<`@-N8attj%fWL`6#@;$t2E=C8d6h71>ury0LUkfrL+4qQ0hU-!K`=2@B$z z_+0_L0X^HqNb;*QRK;$yB=z4`tDzK9t1 z`wt6bTGuD^4(PtYyx0e;Mh?r!lR1Bq&kDKi4ccWAnR>(dYP&S|Ra=6jYk#&*`cR{d zY6KyrJ$Lzi#I)R=5NyA1WopGCBfWHSqp7XUALjiMmQ!3pVb^Teh8f}aYIKM|X6e&Bk`?iTyn7_(9qi09cO~{v4JgJeOvmgNOnFd&`n4W2M4_j9@&=lWgmmAJGjZU(CK^Zlu{)jznYJnVEGlwQye zz{bai-*b_53%w6UVA0(+*Zl;%YVnoaG(}#XSP$CDO8AS?_Pka1HlwBLyCl7;bCQeZ zm)C5stAum)m`&nf+24#edDo|E(P(I2OzW@OvS7)} zGCP09AK1+>P7G4W)OL?@JKvd~NZQ{e7T*i)L*du@iqWUw`MDeaYr;2&%PstT7Y+Rw z_x>gPy5{UPCURap8V0uh#r|N2l7)?RbjpM+Wm$sCB7#p_SznM*gnap~8xboppr7}~ zXuH2BrIUXe&s#7VlllLY_TJ%G|L_0*h3rJKGP9CR6d@#)NZDjXviFv8*?TptLdeL< zo>><%LRLmbSqa%IoA3Fo*ZcMP9KYlJJHCH>`>R8`uIG3>&c`|K=j~LCXI{KxKIZS5 zVV#oMUP(d}oX?cG5uRb)+{wFT$3;|vj=8cb`r`o0VAFUO=Nsh#)|Gwcqn%3=(Y<#l zw{QBr!dI{uh%PcU-e)HBqolsD^}LJ;pRa)-J@zbd3BP7Xc{IPO3<~TFIOf!eo7wh=*M{+FC1Xnr~J<{630BL#U#e}C*C~6crD7C ze}cygy>#WrHz_|q(=?i$!ZZTHI=`~s!e={^q8lkm%?l@9!QPRy;Y)O5$}#kTM-#l5 zc|z5`vK(Uf2YZauXKxDFj?%PH-@+M?^jCM8nuTT+t<^ex{8Bd4`|5wByD+k)q8M$tgI9W zya^M$C_eV{*RL>07I!X3hlF5Wuq$v~UzxIPZNBD$@AFt|n=zt``>cTF1Ww@H=a=q# zxL(Y8K#Xy82Wr(*MfcXpWxdCXlGfo#?czQeaxTGIZ9&q#AvW#R41q6_o)kS^m}U5s zc(3b4_czW12TLp5rA?av5q=}(7s(+nBo_K*Q$84{XPBGonEM)AU@tN9UbkfbW`(<_aX>2)EkIa-ev&Wn#Vdu% zxb9aY1_>$5Gv-ONHub3w$sgo@=;=!&xi(y@P-aeEK&-GfD zzLlL?nK`e7{X_Eb)^A!Z(ybe#mqJncZ0NuI~jA2%pak(gTfdy_Rlcne)p=M zSlTDdzo6;rN*YJI^~z?(+>g)EW)SCA4k3XUW$p)S+}wjV0&K?oISXG*d%*8}o{|!c z$Wk*hPKnrzV;~v!q%r||#%V#Wj1Z!Th>4M!F)+c5w&puHu=sd{=$c7!1s?rOFV(p#FH$$`tyh0rGo)J_ zCQk21WyXgql*|~9b=)si-bcUuk(rj^Bp+3|TJzqhBN9j91=UH*Na(Xm)xr<=g}A$J ztdF^|j}WdNpXoUM%qH*6cSP5VSKea~`<-3&ysvcXy6U!UIn)^z`bDosR^Jbd8O$az zu!&R@|820Jou+HrtOF_j7nBbvz0TOLj~E#n2SWB14s3kD@dUkmdCq0&tMAT)Hxudt zc_^@%{H{6vG=LUbGw9}&k&&TjHICIx+BhZf@>qAjg*mz6!p!p3mUYEv6rZ{cC^NHhh)mdibNVj7&v+iy%U2yJjze);iCD=dBL*m2O&%*@u47j8F(NhSrVqCyYBO^2laiB-r8Kv$J#kaxmE&2$|k9^cPpvodfuqdM&yc_ z-m}cU&6-wPQX&9(iGe&#(lckyWNQ{OA}I=^A`3GBR#-@J{P@a@$-yzu-b=o#z21iY z)|StyH)r*Qpvdut!4-T9&AID_?V#fh2i8KTb&b;kd26&t(ZXFd%FFVtBAi^CEozOz z_^T^C+#)%qExyXL{Q4Sido@OqtyV`Ze|OgRYiUCaOdGD=a;s3i%_w{SKxWWQ+~5;G zJJGdE>}p2nGOnY~X162Xdj1|U4T+9!1Eq5)$ZiioMGb`@wFJXg*>}E;SUxy;%J9_w zfzQzO@|fpD!Y@4r%YON4DvPWMh-o;FIqXWN7iS$oOL&Rk{Y+4&1cH`0IX$8*gDYMs z7hOa|Tp{^cdF2I(s+U!$m?_qeh6@M6nlm|9GCH+BM7^ulQs)j+Yc;wTx$eY;H%uJS z`|hE+e(r{9X-uE8Z{~0IrV>8gDj$e+M+_A2+_^(9;Z{VITt6HIh1lFd^ye%FX+0rK zi@L)3oZgM=Hq%3=sIOIel2)N99@&14ni(6;+g5X)I2Y92-cHcc(o(X0j7qJl3d1R| zmj4(n>kSJXKQ zvRY4R6x=pW^7r>24_ik)OGzQUbEl1#{Z?*4Q8vL8^-UkDRjk_=fxc0t+JkN9(o#JW zT-_b81NskN+xorRHe;0&=y2#-b&NS1oGXTUIQR+;SWaXD3e+&%5$B?cUb($yBE;I9m?%gTal~n-y`o%#< zeo@&`QB09iSy)fzecC5Zrh#h}+V+FH;s*CLIf^Va41kSAhN8fW3knYIL}y(q9&m*= z`7O8!d23hvB^=pEA3Q$o?p*S_53t(e##;){6gas2A{Z$Bxc^>!kgI6m>4O1006H{5 zU}y(5lUlcRY|!vwLe8=p*w}PCTZn0tDmZ608Ts1qeW*#~m!O*eng1dA;{4^ff|-Sj zLp9!(bzZ!KXz^cJlZAckGNH0>H*U1V?$?6~n5#TIN};J!TrDJ#y)>`zAm+JbPm(lNXIzzdlGH+brDgxMHAjmROfy016pO&DrBQ599f?Y_c zl&kz;b`5J`T+<|$zdFsi)wwG25rghetFcpNlM!CNzUpXs^@(%kEkLVK($$Rs;rPIN zZoNB&AkyxUHBnqpmM({@&XnQYXKjHnMRas;GvKAN$>Xd9iU| z>)QqsHXpy{AbA^~{tf*@Rf9F20#)Y0zMo#TA6#h8M!#vSQzRZ-4o%ve>tPiW(=J$Z za&r2Zr&%c4)zRCFagwokdm9%JAC=Fb1x3u+%?;0gZpi8oOQ8IR%?h`QknP+@?c*bj zq;EB^7OmXg!7USQ>StzUH4P2LrAM5`qEwSjSvxt>N>>O#4iR8R2*(mXwfyvgJ$9w1 z<5TlDg*hLM>)+q&cV{l%sqU8c(w);CrZ@ZP8_sd&CX2Gy2kk+Rw-rdG2lfxtix&J^ zadC0AJ~(mSj%NI9Z*o|6zpYdUc@UA)_L;EFpazI&2u{-opukq6P^~nlu(5vjq!d0s zWtmTc%eHho65*xPC*|TvdHFbo3|m zx3oa{61E(U92>comX-m8UP!DpoU^XN%0N0ZU@Y;Au4P6(k|-b2Q8VzIE^%@y(*!8s zYHY*KEbKoz8Dk<46~>n(aR!siAT=q5v%PS$J5n1bj-<%`;C?J_TrR9=BSS-9i0@e0 z*%8BF=iu<-h$A}N)@zL4J;6n*~oEeq%t34q4};9LxXI0R)= zV|(_rg2CS2-uyt$Ibbq^!byLIes{99&imPZ4@2te6L{@c*vYW1zqfs9;JYy8%tbrW zx0}`h`JHv2R$y$aDDc`A_Ps6 zAr#$WCEI#>Na2g$tGn`|%WcjXsNkD@k6ly?xmbo~y2Sahe|xN|TXq$&ly`q80KSpN zDB+4F6+EF}C}ZBZK!zC`OI#EtWIiQLLd^5%(Icol5EgP=_p1KAS7tY*Rurztq;lt0vg&GvpH$))$T`u=qWQ}xgY zwgMrgl=>f5%-Bg{({5`gT-F+W9&fKAM3yCcK=4A34zTiTes5raGp1Q&#%HEh1o zUrO%^*uawR?I0g4EQt<5j1Y z$}Y97H`(vOykJvP?`@0d>FTb{_pwG@p02d@78-XO5D9CGrKQyBNs)^8kiB!K6RmOQ z{{1s3%;>0g#pe!r_XD32>_q^m1;d|4K6H(B;zsAK8kbc(T2UMqdcLLnd#j;$0J%|X+fYOPJXvan?<#n`xHho!0$aqYCuClnSEXML+Ye5xcmz}B9 z4eBlmXtx1YwuuJ9vcb|)O&|2Vr>T5ie=Pe#V6b6-<4Q-bFv$kA$OOZI*7*H9qlt-$ zot+&9U?kiO8mWq~(50cIuiSw4Dzh9u0}3*SgF^i}BH-pEBqpYV(5VXY%p6ZOGK!;Y zgNOfY$-Ee<_|G}YzU5vAE~jaQ3BdGzt+1who1R{5In2k#o+DC!i;X-DY-E+=T$Gg# zXnDY6q$J7*>C_|$$;|+Sv9z=#U@>s%iLr5GclR0i1j<$#fE;~%Ts)o*S1JmuTHPAR zIES=^RdyZh?q2eA1K|qY<6^Df4f86{O$P*wy73BFA0aJ|1B->BF;zW5_A);|>EmJ= z%>v!$)&~yXE=n7mV!N)qG~V(}`d+|cMHN0~Z#m4mkREkh&C;+;>( z=Ej&qe};8uv|-r3p8W=#Y35@Y?>5F#zMne&`rzo&Sae05&$Nu^_K{0+eIe?Cs3;A{ zay8|wK^n$&A_f|FtG{#T;wD`QzQLqS*eGZh0_a!h-sT)Y4{@Max3;wvb3n?@ z{Beyyi{y4lrPT;I6pL;F6czRjpaG-6_)kL@!B?P*YPW6d?6iTV#s2rwL$IxrKT9kYR>e z_7ee|@0}@`TIEJkY$G}QB2{&2d!;u$Q=_j{E`}cl%n|I*`ZX17tP;MJg3!Vrm~y(c zZfD6uPeD`U9TSr*s|YgykB*Z_pb-0`X=lIOW*I~my?=YXN+mt?HUk60Sth1vsJAqO zkLi85jt`5NE1ABmNj4&~81zV){!d+3#ve{?0*WgdKD~Tqp|rMEqI$yhXqVMsf}+T> zANVOK;9v%uMs&Xv@Df*haP_OL6BUAHa?xHV{qj~#l;bX3%q&R zyLSmAA|l9WXb3z!JW$d$HU&#r$toHe8uGT2Q}XENQ?2Rs$`Sui>>T!jUf0#DS1BdI!9W@qfZCY~3j>9MysYQ$RIsS)O48C! z2*F+=BMS@5lV$G9tTYKv<&Fg}k`eHV3~GKATBzSR?8XXSX}1lRkEu}Pc9n7ZV%*Xx zxc;iYVwS_>G!gz#Y+?y-gWTv<2@Ui?&LxA;7iU>Fqg17`nZ~zf)MB=FBz1hx)?`YZ zaHy?oy;e%UvEqrD!>Z&w|FFo>ilA(PWQ(TLSxisW9iru#@?xjdF0OHbYq=X$%tUjO~g+AktC$lU6(E<=a zDBA|sg_4f}Tev-$Oz%tQjTKpOdE1^xC1~!oi%ez{@iP%>m!x>HndTk48TQjeXDI8x zc}Q2}9LA8ydb>S+Y6uG)VIlMI@(Kc)Z{#YfsJIyEZU&Mvwxy*dv`OFqBUwJ2yC6nt z(?D)L+m<4lTg_l04anfqEYBwmlu(lcSlPC1&hoJbtRai12j2Bu8xz5|N?vOoauurS z0fA9-KnNG2{{=ZgNogsFqx=E6(XqTbm`4p=G~uxHp^|lH)g@lEt)Lq(a8rA6w#xv0cQQ!3@L1X zkDVDJN-g(x=we;GdPrU5f_hyxC&n;HyJmNu+N-C!#Y6hKf@iS>18N|w_+Ehc2gb$B z&OgUHC0Ef_Z|s<|Tu2p>6Mgnz;H6MTp{rP<+c$o!GKVO-e|EkaKqfOFJMrD!tgpk_ zCzP{SzM<8ejKOk(^b%+5p?XkF(KY4(8ZP*pdn)o`$C9oTW@B^$Iq>q;RndLMl9M;P ze-24+@`*Cg2k6zfIwq3{8qCgJxt#Iu_jfg2q08*7a+>dP=2DL*&-)NrKc&#kfd7_l z^NI`ZQJ3?@k3QcXT>k5iAQvoWsC+gGclmkusEs-DJ;NU4;7dpI!#lvG*O@f;ygdAh z45z|9;h(LqTwbtKJE(})HRhk-P?8ZpWQEm z8^<4T90wd^*%Z5y%p5MIb@Ra!UQP) zVHNMckARS?NOVi{$4!ZP2Ys>M9aY^m41#CJ+0_|5JE%ZJy(&_ zlGRs5i5hpTd5L?+-Hj#3Ukkq^Swz_GVIOurd{}R??Hk23S*Y4rb+Q!Lt(waV(76lX z302t}5d_MBmFFs}cHib2A0LN2Mif|bL~>xUJU9A6_1`xa7j@*(r-B*RUWd=$73~zA zSmT`w+i*T2BO_X0IL!R;`5o0+qCmty1SDsUlQ&R70Z}0c00(FgKoZ8#pVQvemEVu{ z>U~%J@59Z!>C4f>x-w*pX+9@#Ij737bH^z@=Hl9)m82PCd)*P1_xLq$dhdXVLF@bF z0d;lPp>Hb?85Hu(8|WUQ*RC~ob`oE_cyaCb@7o|8wx9kO%C$Ue|E5 zD5nzwvi{Ed!tGUOdY2!$?d>pq^;qH=9$5#EzQb64 ze*UwUFL6-qpFSynkP;Wyab#c)m}jWl&*JCeY66>UXk>(=qoV_uJSKK_cA{mdF9DKR zol4=?KMmQW#H7>G;0gd@KSoj?Vi^t&j@Y)Mq9P@l2eEJ7oKa9vK>1{5X7(S!7Zh$) z3HkZH3jV=lgiRSwO6YO%o93H|Ym>bUd)CUK{T1d24k z=19XGw|tjov$`mFA3w;RE*8Flp)nf0R<;jf>9bHkfM~{7 zFvzeu$Pj#0t>VXlx;1b}0jL*I72O2%s2xD$p?&al@EL|C5c1ECeutO?7`vO zb*{8~3frEKveqGu*v7R=%tkluEn}3JmN~LG{~8GOPa-SKb}mBoyEfwWk1pR4dCHka z>g_nnLO}%NaxCy(Mk)X~Y6y;IY_kJ)zwu5BRe4Da2nMMvIQhZ z194EFa|hS;h^Ut0>ryD|?}gg-xF~-U3lUDmnLkFmK)zma6swz|*gR57<#I_`iYl*) zl9CSu2`CJ7KPbW9`%e<1{(nkUed?oyr0CxD7i=FQ{-jd+60x~qVg{789Q<#58y_d5 zqsw#1m$`8R%jSD6A;gr{?{X@Xzs#XdV>~Q86EuXS;_9dWUWi5gb&gav4jdy>-XR;w z@`zWj&UbzO%uGgr+)To&SIj&-r~gw30--s?=d|K3OVZG`Z#5p9d$f-!dy)8hkMWPU z{#m5p7d!VL zNRi~b&rKORDw-EL0tgkCq={^NfJH)FQ z8ST+Sn-HS`0$UJf^DG!Qx*Yi=2``Agj$qrvxpQD~Y|Sr@Z9$o64 zVO?g+BKf-!e*HW+ylQ7p9cMirSRO9=qBCBS_~W_>O>pYoCntq8nq|yHE9w1>jpo&T%OAH8!CN^888(o+ zG4uk+nCDSFJxTyN*8mMnhgzF%Azm7Hmxq0X{$EunubuqFmzH@D= zsFxq4cZvta<=RMv6Jsj=9@Ke>h0>_4<5Mq0p3>}xZk%M0saWK0sQa8|`Qy&qvc;>$ z@hL7AM8Ke+H{x6xt9E?1WJ*mIJF|Qq~1~? zyeF=wi7#rFPI3g0y}eo|=HKE9QOedB4DtL>WgjpASR68qQ{+|aIuJQUWTEUq; z1TNZ52&M5N_O-~{!kXmIG%G~lN zfCMj+StzcsC9l1~r#=PSu*eXiE|WYH#kE#CR~n(1cz-y_BHwe11gNA*s=b_oh@L%outyrU!S z-;-2>WR|klcQ>8T=M{UeW39;uP-DCE4b$FZ_Fe{7_eHKE3xoCj;;q%@`&-v5eVzp)kSOzs~zwO*eaF z#++_q19vyR^x`HZEf$lTzhIC)QcwkW^!^S6`93$nEaaZoebLP zmm_L^aSa^`mjAQPzwEt!h~;m}%CW*J<}LafuWr~OeE+=7pIkXaNEYr7B+i;guVVX4 zz3k?fC3tjA7=K^!X4iH&jhL4t;`D{wfJlHOeU7K4u6L&YS}1Mhj2MHAMS-yh_(=`N zw&FjJOwaxK#l}?ZyfDelgEJhiXwoVY2$=?Lim=~AhW_>VhfM1ABz!fl7g^6z3xDz8 zjg2$?_a@XEHxz4%O+Q~wwYHtC>l-EAz4Pq1ks0~lXK;1XOPr81Nen%e5~mDpJ< zj&5@i#y)Q1GEbE^{|0?r>)UbHwWfq{%=)ot1Q^JE&Cgm)<|m52{u_VfhkHgU`jF0{ zfY)=~@O1JgcWzEjLdYJ%#G)XjOR`8R=okbEsX&c};Mv#T9|uxD!GJ}sz6!1Afr*K9 za6t6|?_Re7trxGSlw)-BU*9|rJABx-ZM4Vqu4z5vp)IC8U@?ZEuU>51$8Zx%oO;(& zjPc}~cfw+XCDj&d-pjsM|2gMi6~i<9zqaf5`^QLc*%x zgEYYuic(8HBC&yK2%Vf(70kBoP`vemn)8b!K0SnC~a0W`+?6 zK2bA>Ro2z@D`4fAo*#0SZu7O}Cw5TMJ#xIYBWUUO*ANfy(GFt;?V>){Oa~WI8+?tx z6+UlHsRs?UP{s*fTYl!(-Yy4e(#~=ij;C2Q(4KkI_1`z2U-|f@e)`>b$tvfcWNXK5 z(mnP*KI!os>~V=zgDrmo$F5uTw_725<B2T6%bKJK{CIi0`m74czG2Itf8_&&#-OE9x~Yc&)sHfq%^*eV_Q3R zXJ|crglfSyGT3rh3FnsCQeJ$6X^3Ny;-K^L*v7z4s_vF!MUEpQBh{&!N-gFOp|Ttd zlyxImBMHKH>uFq^ouQQ|V7vk-XONuB={M>oz%yZd;T=JEN(R87#Q!+qEebq{3qH$X zH|}(Pcm4HY#qK@xF;mWsG5;oCXX@F?S?arELGj{Ku3SV~4)TJPPup^s21sr8?YZRl zJyPCdLcaBeOj5s^07d&rzQXn>%`_C+B{ zGkhjbACO-+b#^LDRWB?oAmEq(Nt^xeRN1P2gR1^vgJLq7WQhE!k669CrW)_X{n?+( z39L#b_I?_qyc^Bp+gR#zteR8BlEL-sHIBb2&QaFnLZpJ(=m)*`TKa8WU8kY!1hAud zDBi%-PGStjzm!i8hydI71~meYR%Bl5b(+HL6;Omr3p@= zavo7W@$hjFL<&}$2lQQO>}$^(oPu1u9MR&C$O-B+5qgTc>#h`P*;6hk<`19msUJodnAmx=3$i^eC>GxaU>VY!K zA2@<`gOr&udPJ9F{+711RSmo(b`(x8f5AClIPIIZnej6C_28Ts@2uDvq+a?r7+y50 z-=;$~Q=^#NSl8FD*A9ek?CG{Oar##RZFh?0Wf6z9x<-!~U8yF?8xlJWe8{ z;Qsv#%X)0A-V(aW@v|XSK6xA`q;KAxaY&|AR{b|gqhd+KbNUsIuuZ2kRRB$vSQf@3 z-RI1aPaeK+0>NMpq@Zv#Ecwp9EjT}Xp6u~%*DcuM1*I1P$hrsLV23~0{oUaG#}6^B zpH*Q*a zv*Q7t`9JwicU6P;|Fr;n#imnXn+Gc9*Z(@Ro}KY)Veg~vDE(6lzhw);e~dH``hIBc&t9$RTYKF#lSgLJQU^&?sP-VU1NHoD~qlQ)(n>zT<_vlp&zrdW-4! z^a%^4^(RE(V^yvS$_j-#fBrlwBk!AL<@=;9*~p*y>Yw_g z<@0~J?Ekg6uO94eEZ8bWUsveU%dc7Q$F(I9L>$kYx8)62vN)2Gl3;1JJ!xQ)^h^Nm z=RlP;jLx!z0iHP0r0%^p??fZ>lt9v?h?X6oB_t%w2Ol-*gs$J8v3#KqK$wxcE0kcEVN=1W6~Q1*RbO9!#ex_Gla=J|Wt^y=d&>JdwUc!E#k&PeKnm zmwkZjaPQtdWSo*-W``w=B7=kdBa|?HR6MG4r-V^6U~}$(xZfVks6|_q*TK~fF3Uua zI)T^H44Gy?iF}c-{jW~I`)%I;ue%^ar`U>9w&Mjo1y^B`A|=F)&7l4)XDlr$B7+Z+ zg`odGg;uD+Ye!zQ{VoAjo@O|eU=1Z)wSH}dRS_u)lAmI{GH!X^C&&3PYtjma4wYELvb%SItO#aRT=KdPNC=3a z=;>hEr%svK^8%IG_u#(_0ig%1Tr1Q9SjOJGe%+D({{0A?TMrElna4^^NlVMijbX_R zhP{M;gLP}%zyvcH2?J%~l9KY*-H=%|P?V^3>YdU7z~EC4aho%91q?+fwCwMq}CSK5g-^8-?Z-D4IMJ#;U4BUnIwF^-c=zS@C z*Qn2uaVw3m?+n-~&%N1r1d>yAzh#pCx;w?SYu6Gao|ZJ#gNsz%BZRU856OK`fdP(} zY@JFzq&^G}vNMkLi-rWM3TBdwR@(9>)+i+w$MiW#@YUNoQI@8rq99U7s3ri#fr@2e zYyzN=c)@*`nw&(z;szJ<9``z&CCCgkLs;Vi&^l&4+`4F9giFOsq!{7?1aAP?lJV=; zV)z>lo1DdJp&Ct?rld8+di5$5EHOkQgg#uqliAk(`E#sSP%P-VRQ8{0Jd}WXb@jRf zEFZ{D`W6=QgOVr`rqz~D=bR%(!5M`E!)E{yb9d1zFGOrT*DrjPPVeKBh{mxoI!Lm$ z!oZC(+D(|aDC6RC6$<1&Aj&cX(3^JX4gg4yJqaM1xUqsrE%)NDrB~HFl+9#9`I?y&;;^NA+Ymj4W0>axA=uZHff_hf} zCcjIfmx_u?PC?2IAA{ltQARXL zT5o+e=rIVDWr0DOvMYfRAt64cr9zZi0cJkHmx3T3h$f`c?k3zE9Qfe3ws&`HXzRvO zJt~jP%w&h7fy)za@(@OXB2#Fg8f{=|8Vu9hfXhq`R1zE%%-4hJsLVC`neRMCH9<<_lRJW^6AiZH_# zY)CXE1w~O)lZ;+w7aeF)9wSYil9F%W94Q89S_vpgN?|BeD~%CUQ}3~n7sCj%-#!3;}$~Vq(t)q?A6wRk*I|XC7SnCeljN z+0$bRfEUDeBG?zmfL?qqS4Q$N5Vf5bl^-4(0c`)k2neWJ1f+Q$pO~2LTA4kz2`!KUtQuiBjJ6O9hlNA^RN@BXDmpXxu<1t*8mx61;l2y7ucJk1hyz zda5{L=>-~7XbOV}X1*K%vm)2)5IRswVThmryet!2+a9QtJcL+QV_GpqG8y)z zApCr!xxfEKo#&nkx0g}Gr|*_BX-dC0Va`wapZi5@o3|9eynD5~K!*So^#lOGj5iE} zlB=9t2y8wHqeQ~U=;&og`wzi{;|hSuj0l$RT}Ok)rP`cUm?50X<05i&_X+qj`2RVw zo1m(R9NjX)5kx$QlSD?6jvbDi?y}9<7*NF_zx;pu6|UYgb5$7R#V^0}P+NQY_~<~a zJ7g_TaF~!xEonb61=K^dB* z_D-icxl9=+7Z(vE_U5wbAo^_MSRNi+*T6212VXnA{5cNUPL@q_(c^d14|l3h79% zKip7&XoFcyj8--4UZ1fD0X{su!HwN@qkOGmY?%AT4W{32^QU4hbj>}JV}pUE{AASB z!Qg;kAesWVC&pt1h(hb2mq)ZrNHqmi&1)O83XobxbW}i8X@b+=c$pt0>=4(oLInXH zEF6tM4loi#(Ez~jtq0a<*6AmE&srKFg^F+gOXFfda9Chvh!#gB`NL&mZ1ilz4( zI{O8+VC=YWWF!g5YDtZZjGzW-2+>SQ1q`LS0e(BE>-eAq{gtAzaH7 zsj;!Kbf{d)6Z1f2Xru@3E-Ndm#CMJ3ev-7B8U=C?0XiROG_nVw=k6o7l?fB>2#AQ9 zAT2#PKOX`Tn@PAjx1;?lAX_^H8%OKm!!!^sUgF`2@oI;Y=Ihv4XOxkN2_Ecw2+=id zbnf4eT5^Toto{m8CvB2&6;|PBW0IV74f?0E|hj7TW%}rUch(4Lpur z@B*GEC7p&^lIQ-yJLI`Cer}nG>90?e#R*tl()6hPg+^xN+u>Qo(^(U zIhgQ#9nn#mLR0rr5l9!2<;TW$N(u(}LC#Bgxt4%AC|WQG8nvcC0N>uz(*{!5P~eB; zs2fmHQceLSMFyM$6b9UNY+s+COEjWfMRMg5=tCN08^C2i9C8{C1bEx5+}y;Vv_!V5 zg#{O)yV|M!MWGFag;sF1A%zwJ8)FhAtO){!eoJndZChJgh%g-4>l73esn96~{b5aD zlCe-A2)qrAr!+?JX?{=))?;A{P?HCdFO;SM^%szy&I%=ybr_7nZLK*HBM5$u830TW zBZOr2z!`;?F?uiAE~3L!g*PxT5N6OeL!ZF~3Zki%l_+S?N=QgBfYSwj4U~yZZ*PU$ z{TkpL*^KcbQ2(9AMn-_(L?Ow^fb?ZS8War}4xj?aOu0c60Syu?FmgByPUCd&JOSi} zD08t;m9`VDVCz6CeFlstXbmIiuc>_DCt?2R6Z2iE)N15%pci?l?C(-Q(>5LV3u&P2 zK&{C8(7*cGon@|p5RY(h@2j8hs`+Y;fJ2G|NgMs;4E^c pn9+dmgq$h=C1O + + gaussian + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/generalizedNormal.svg b/docs/plots/generalizedNormal.svg new file mode 100644 index 0000000..cbfa389 --- /dev/null +++ b/docs/plots/generalizedNormal.svg @@ -0,0 +1,45 @@ + + + generalizedNormal + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/hamming.png b/docs/plots/hamming.png deleted file mode 100644 index cc15d054b4f2a1be663ca36a8cce752c94d2adb7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33455 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 z 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 diff --git a/docs/plots/hamming.svg b/docs/plots/hamming.svg new file mode 100644 index 0000000..4a3eaa2 --- /dev/null +++ b/docs/plots/hamming.svg @@ -0,0 +1,45 @@ + + + hamming + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/hann.png b/docs/plots/hann.png deleted file mode 100644 index 5d4ebe8d556d910e95a0181db4b6b3a926c0fb53..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34501 zcmbTe1yok+_ce+L0-~ZIAfcdiw}6r=Escb9cOxBA(t>n{fPi#|q|)6jAT1zBH{A8A zzu))2TM*Mpvn?K7K35gU*?1i9$W8y}V zgRA_=Y1=G~xu@K!RurniuIlDcjoqVRIeOQCsJ`r_wufA6N-hC zqab?q z!0+iIhnwGVM`Xr1WNxvtvBmK?v99zcmB!w*t+M-x?dj=RRZ|ma8Xw5@nL$8{fSph> zM?>EG{r#sl8{;hAB@tB8a%s;{3p5*gz0t7t@VkP8Z+!jsjey^s2MLEtYVpT6zd%zw zbQIr(<>l7R?_WYXBj@2Fe`LmJlqjG6{Q1-MV8W2ysQ>M9Pl9TXiLrZ*DSHU~cbXY;3HMr(E1W zCSk+CP;gVi>Uc+A!}%vgjonWj#i!xqA3l5#JlEJ-WTnhJlVwN=n+8dNy9H8%d}1{@~Zt)z(UMr9zDm zZ||%4jB2T>l@{bvdiiH@H~0(*xXNK6Dg)-}BSMp#t@% zw2FEDF){dpf`S1@P_}G9-c;`-9gJOaDmuNxnYd?PcNV~K?`tC40kRwm^SusB_D~qa+?=-Ke9OZ#} z-^F3W?Xth*Kuk4vt`L3$PD`}Hc##MR%Iv4jGwmz;!adqr`Mki=g5~?d?$pb zihT8IwbL%8tgNh2`DSKEP_V3?X zy@}i#Gj$1bb6Se|uUlNU>I{cxs;n2X9J2zcHRhg4(Cb#LJrmkGI6*(tWu*?i*Zojz--R^)L2IaAnsXBR&clp)H=nRJqDZ z+w;l6)-09)>?kU!#JT1GoNJXo_)b>Irl+UJ%Zwx3pM2{{;B?yU;e;nfrmd|#oUaxaap_VYk12{aWR5$p;}lHpXqYdYeq-z25J+rj5z+peQ;eL~KIt z_770_eZ~Wi&(V5{oHXm&Tza$A9H4Argo>3 zPDo>`>uz}CW-PmLc*cOyb+pkkaa1(DpyeqPX4;wB&wdCl2X{l&Mq>VQJogf zp|7tmR&Fv94)sDmH4PF?xN!Ohi|;jXP2|6(tK|oc*asmP_uk0K$z?#*k=Uv~Eg%;Q zk4#LYhu9H=n2UyApFMk)5u@p@vU6|{ZU1W$@z-^i7pHPl73M)a9-fUF)Eg(Ima}|t zJjdhmVo*1fm-46GlXDiL$7W&dgduikN7iED58U3%%gNoCgo09kmzX%u;{p=A1S2Ek zAUx3!c7F7?rFQ#kuiR7=6{8ndRt)=-;i}>GUTttwQcxi7_2gj6@@A)P zVH6}fhMl904Ko@xHhJf>?Y2xvEH5?rU{`kx3=9aYKx9&c2$P-fk7qrIK^ zwNtnUu1o}_qNzbx|tfAZ{yY7fV2HZY8L?h+l zNrZs0I^0x$0{M00cx7bm=TET^LXJH$<;$~OET~l+@hDSS(j?w1aE6bNNKmd_TkBF& zQ(GO$jk%~W+%)fh?sw}7cLU>7hs~E-TBLqhB%?7)=Fdrs9In?~EcSLhd9!xc?PQPT zp7z>QCDZ%&?}>SxhxJq;ylCxP=3o;Wk9@|3%))uEXKT8et>N<_Q+{z#V=i<79etiG~cmLR*E|z78Vv%Cm|V`uz@um2L}i5 zmEPW7;Wx00%oNb5?K3s~ZrzV%(Y*#BXaRD>iP%QG)&sbV5&o^=92}@H`m^43d?}-s=lo8=H*cj@ecKP1SCv zk53PNEflvs7>a3YYa_Z(7p0{?pDO5mkKO1t3kwUP!XD0gq;y8nvFWx3Bd?Ereuc?g z-@0`0eDhQz%wF*49R#YwBHU*u&@IBla_zz5ot%r>m@8 z_Xb4ZLm}{e#ZJ2hjGFakRxhD6{_Ib?3VGTP%DtnrGpk`Q@!-(V&FDZEXJ-U-1<2dn z9mnRGxHNPBsr}+giig3`?;n|i^tZ>!a427<3I*ZqZGKO26MrmC zSHG&}F0k3&+4-nZ=Zqvwri?m4|I$>W_5J-W*n289b~J^xJJ)k^a=Mdwr?O|DSp6K$ zC(>jR2l$upRm?mB5oHNj-h;2&dGAqtdtdnuw+6G6GU&eIa4RGHB+Zjq;&kAG*@G| zv}os;Mt?_Bu4XqL%;^9AJ^v=upoD?z6rbj)DBIiPm8 z_i4*$(qnT%L@j{ZIy+uTA?9<9g_F~z$`R~r23lbLpHCtTSjzm4(?3E%7jl^I==S!u z_DrqQ-mfYAw*cpNfB2Eu>@G{D*FgVQW-<~ulqEg2r^oHtsCYYtd-wKMia;uT^hvi$ z54xgQKu{1FD=Ri2V>gxE<(>e4e^MG6F(Lhbgbeq%+S{na#2btBv)wH{JvSRIkn>>@kr{#jLI;DgXaiM%ydVP(zV0 zFfdL^j*c9W02`>STJyRbyvQ9P_^xh@V6_mZsm$hGpqKZvSY$kJXemI*xV!~~+=r8D`tbFhJyBjQnqYWM& zulmU7>GK+QMI|K}2AtHYoW1J>NfuV+;uNLXbv6$y?}J?D$+Ci(F7^U4`p3J8)fjLN`B|5*|?jYs;z2u*ojeq zlt|~5#mO^s$N9Z4r-7#>T?PFy^5K)4$|ddzHB^soM}`41VbT7IVltW+03-*L8)m|~ zyXYtf-XmsFY*1DBPB+-jcY(jy>1OLIf}T}4_@1Y5x^_o61(J`C&utu>^x3TS9?n_4 zv!m_Lt9Y@(ii(fFUkjPn8t0U$G?waLR7_vISyd&t;@Q7Pz#5vTKfLaCw14C{>dt|R zhL(F=M|%67%=CQX$4{I<22M4bAm?>NP_a4fEFx>F*4X7V*@}pWJbV5e_w$Eu~C0mtSd&@_q4`-hPS{lpT5UI|b`UGv#PsO6yjZ$I8YgiS?Tt>Ovz=fop-P zi9kwDiG-66lD+@2`qNKWpQ(HaJr8L+V)itIPK&qMO}+;-PmS4FV2lZWdi;H6 zM%VN!9>1-)%wP+*Nv+499U57b*(FM$qbQjXtXFZKquuWkvAxPV@bPg|{i&pwi#>mb z`fqgA<3~N2)O7(JIXt5kYylsXe)zIPLy)*8`&evY@3)CPSRYTy%ljD9Fg4^tUvx9i z$@=}^MP>#sg;}QhL_p*RTO#Etg-6F5hj^732j4AC#v_P;xTSw5m6bk%$rNF=D<@_3 zYN)T|+B%1K!ME!B?y*^D%5C^D0$~zBU|hL!1+u%(C&`4tYFoo%t~M#rFEwby4UYim0`pjP++{foB{b;!50_Cfvj5v3!CLaULRhtZUuZi3RT#}GcXt~@FAmFgf{ z9bMgkkP!Wgvtym5+@vu31=7`$8=Vh?iC>8XH=5i|v7jTr^x-qjBCTVKDb()@XG&G! zbj+(O@rrlwduOh~OcTq&zeQIR$XnZn?5XxA&6k!js5`T&uqf^Ic?2O5CyF+le-ZlS z<#w1rc(C}V&H!aO0(ItiKlyx}aADR3--Oe%!Yv9v1NvporgS{-S;vs4SUSO<$4^}7 zZ?^~hSw4xW&MJ>9j%A7)2@7u-BZ9L_@yap(yM8JU*GKc^q3A4*x2mct8xs7^n%lXz*bB{*`^gVba9>F!D`!4I*<0!Sk>vX1kjOj6?3fo1 zeNou>Qf@Fg(_CNp67`nU$qMe}xl#;1!joB% zNKEMP_T-}bWXmt{^0a2s1W5a&4+Q6&qAI8jc)OKfO;ARC;8~W0MMX(odU~gZqm_9G znRAEs87ER;<6U8r;L7-anT4q_P6N;g=VP9%5vwExFcmC-e zZ#A&wxbq7O=@lpu)S5ZhXM0jGLMFWrbxpcx?lp|cWMRm+g+fjrSf0^U9il5@_|FM9 zwUOlvJ`2WGPBmN_5w}YnwdqtKHq%WP8%QT~i*<)Hh=-Op>u^IXJoUWGM1qDmGGJhVIm?|o&jQ7;pX((e*w`;npjqZQ1u8}@EZ#Ft2zs{T_ zTNzeJXh9L@%*D#A>+sCxDDux5g+xTzeeqYKD8X*F=hjDvCE_7cSqsoRdadoxZA-zu z5cIeRP&mxoI^i#mf;>t@^>kZmppXjpyr!`pu>>|i;+IoI`X3s;3nz3!jkmuN5>geO zj#Kutr;cBW`O>e)B$tMzVjc&1>cyXcpfJ~`5+X3$xnad{GH_Wle2+Y-SLfhY_3UsF z73brfeC3LJoEC|b*nt!*+)+ZMS%P&2R=jHF+@F4o5hl{HUPOd;bapDLN(L06rraK9 z{F9~Vjp?2)JxFDtPKa|jl^644>~YLxtS(w{m;aVL?T^%T9g*Z!pB2|0|9DY?qib_O zxUm;MY4h^=0+Q!du{yVqLqmF*|NWH5e4_982PqG!Q{VgkkCPQ2Wadl#SO=7@^+#XE=fs}do7l0uK{*ISFUkv3Da~_u zq2G^oiK_mXed{9o_XguWN>sF3Prh@S#CzQ%AA+?tnro&97@oh<%RFd|`{XLf#hx89 zH)Xs2Eas6W?u;Tur{g`C{+E?i9_HkXa>v{JN_k2=QS;bvkJeIdAn(QqdsMty&!)!Q z*t-xqygMn8c?F*y6)D0Jp#6P9`VyTlQPn@mI>ifqX#H4;{_lZ5YAksMaOh)&4>G~t zg@-Mr-Qz8Tqi3f1PrSMx7A{BE^=CHk_IhOHW@X*W8=*ou@cf(kCI&NGaFs7EhMZEw zRv)ygHuL>AO;J$afv2-DUh$?iUFqPhi=@%`dlDrE3T_Tba}MY-u2IX*WptGoy`?CE zqz0e<)+T*b9gltebH&}Rxl5j!MXpwNK526bWTFM=nLk0N2i>(?sevRyjj@=0Je|I| zI*`t8Ig^M`n1D@_O5{olf9S(PPdLB0*bK-p`&BNUYK57s$}!pBjMf>wsjcI=L}J;e#8fxuY@_wx1cb}# zw4+x}vFa3?+|>F?B27@jDp$oSbM#VHF_QRh#zgKzM-IIowS{|{??=5jj(t*%+|T!w zk{#zUh>2|&3qfaTo1L9aec#Z)4?inC*YNyXOe6EZB4=H$n;ofj7EPl!@cy@oio(^7 zM5nGSFvW!9dXU8PYdU6{Jg!O-)Q(T!m$qrvq}QmFRN-yWlH`+(23i*X)~#Epe85+U zym;Xwc+O_n%WOUWV!1admq|iQtf{Bx_h?N*0+E=QSYYCFvA+qIMztoG+{%ib)}uu6 z!kGrI80%lb&BZ7JWr2X#nLRl<87Rgmpijb*)PJkRo*r%u6>9P$M7jO7k)N{-4L!5D z<-!2MyYQJ%w%XF1dCQlmz562Oe)N>ZRV2+xkz#@30{uSzk>l^VE1)!wjGY4kwmMNt z6@C~B%C>D;BrTk-;3)W$mBf{EJK50)z92&ZBCVf{o()q=m zAHdZ^x8~~R2Hd$w?g&DXOs`lJAQKDM`w%Uu_y5$ZWN*H`auxXKIq3BTvW5_1kqoGD zqXp`w3Y@^egWhL-vS*wS`YvjDJ!FbtKpL|T4eEnQm+X59zaPH|50Y-IOjKO7jN#vt z0VL~jwv7hDur4gc4MO(zxpJVwSuJOHyj8t`-J{^*imjY+?#j+DIl7p5#L^HD`vcK*J zl4wCe!QT2W<**Tc6F9%~;iG1yaNyu{_I(AbAL%rsrEc%b zk9-W3H`}`kb^Gn6-6~QVo=bp;Ru34B=zFO3(wuV8m`+ypwNlFd`Zs7r@_By$mHYa zm&@Q8;h{#!ddAE%fy&-eC1&?xpiq@e$k^WQUE5Xe-3Id)Go|lU`$p$*yu6Ngx|jk2 z0Ta%4QBn@3i(pA5^I!;o zIB>KSqnWK%T_QWBQ&neIw#DWj(O~8?X6R{)3cE(yhS(=0Yb)E@>{fa52YOWj*X8c# zwH_5sbsTkKb!YMcYtErZNjv4LD#{;H%f|N^o&g7mOo}XCsB;So-)fz9S3w6t0^JEV zXDmEb!r`Fz?>~Vuc8yFGHRSEmBlnx}y{XnI+voTZ+Kuela+2oUYu1<7%#y^~y>ML* zlFg}V<*~Qp*s!)QCazV+RFEwkScs}ZE?cf7CH_!|C-RIrW63aGb>f1WkcQ)4b2 z#Dy~I_n&HLrk^T!E6<~NnBkerCMU;6M~F4y>nJNbJ94;-{(hPCv7J!TGNS=6phsvZ zB&~%*ALPgXBGByd>l{hl#G&9{?O=??(W4RnT&+#SRUBYFv}~o* zNup7eQ*`!R@_vmkqb#a!Tl^PjD)*pA&)sQ}9Iw2qWbu9Zq~p9l!*)A|U`MXaN2~ia zb&i3Ep(FiU`7f1Wv3WDh4X^ngQWO2#)(kXX=2B_;nLGkM_17%x{I`Y*^qmgXcXuR= zRe4X!e*o!ef(G zwNar*+K%$)sQ%-vSB9#j9Inq~?Eh9j4ApRQLHj0`+sm_O1nhW3O(p*WV`E~&O^+Y^O8fJ&b5EL%{wKHcMpoxQ!J8?!Bk%vk4$aYwL0$(BQN}N_ ztm875wI=@47V7)vX=*k;(Zt*t>k)m_N9@^}B#_Qh=UMP=318p%<%nAE9YR#A%|@nX zDhhl}O@qh-48e(KfBOqFT#iTOMTN%uJxza;BcQP>3kKa+*?%j$lxHe)hB~}(j(yf% ztN$lhX?528BQu4IP)0-0i#oZUB)wPXFYrFOkZF2l&OI9P1%KG28EZCQQ8hq-R3@N| z2rb~)DHuaUG(#kLzG>7U%8LIvnc#twHHFH z#*px!g*t!v8bed{qlcv{U9?$Tk$Z5X&;Q)$bVWw+#}%z>M875vj96V;&yfup6FmPH z8uFK$Fx}$u|FO1x&K)2m*)g3=;?ldAOd?_kfEedX#SU|wkCT;N<0u;;W*(7CZ$Cp8 z15NHxpR3l@^z^+;{T7WFNzDJEYJK0@gGtlC@#8HeiHCGJ4=|Yv)83gc-N_(dHnNz$ zqAt!;szh7qqf7%&SMtx(?SQ6iU$FR1ne;MUdqmH}r%TCZuN(i<>E^L^hpvx4OKNKx zkvFHQ5aJ;Dy=O2H`0CFyxgMV6+HhBdH?XKAV(I3;M?-xomHcpfS;+C_##19!!kav! zp6zTR)dzj|%SWIy^`-7WSNzaM6|gR(@(e-Ue;|vVeXqY0lQyTuLdEM{JzZzwP9&~d zu>9_qz~Hu~@8h?MU#U>^@&AzjHj~g$;H$1w`hV5zk_w9Wzuh@P3HtcUP|v`|Y)pD? zjM8whqB`-r`Jb~H#L&*|l21LRVZ-Vggbh9QXG2E}MMowpn~<<8cK}51duZ9`)LU zq3I%_gSAe!U_yHVD3HtgYt7C>8ia}f-^R#$g?%Y7uQc^-zRg}6e#Cr}!vAOoz=c3R zc@eHpXHybB_B$#|kXyJZQutO6Ur*{Y3ci$v_Opt>Cb)X$6i^_UK}Q6W&^yY$~C@P*AA+-GA;A(0?(FIqehNZ5?~z06E^syZ;g|?52AT z51OrZ_q2_M>+Oi1Vm^@Vy>m8cWpF9Q%E~sPNG21Pno3G3mFN%9RPZ>OK^2M%BbX#w zIywTN0?FnoGJyT=W4N54@j%oZUK+MJy4h7wcvvVIl#?S)Kg&KX&m2b!K}Ph5OKUeF zbh6ul1r(K%Lb$+$3zPUg9(Z_sYaze5xJcyp(4cWbjtgUzXBIVk_4%>f(jBrQX9TN- zf`+D=_e*m9TjMExEwPl_ccq{gv$C?H-UI)W zAi%AK)(~9~=1?m>fY&b}Awe+iPq(lm<@gxwKT)Os$y34ZEr+fp#$*`Ae^(|CmXjo? z>~zYv!N0*q<6?kL^`!!Bj6ujX!itsUEx#%vhAr%kTIEhE6d@1~gzfC?(n4$Atb?l2 z4Z`U5?(S8AaPkZu&94AFg?IBoJB6lE{6mGy!A5{$E{7uu3QEzNbwzJyI3Nfz%6*c% zcbkE2ztIVH(E*ckXfB8%HLI*VRZ0yAB86VONF6b$c0Dc;JpSs78B%{FCod1567E`q zHsJcSv;}wz@VG-kX(34R%gxO-TX_gKmN695a7eas+$CfAnzFWZW|cj*3THIs3eCFg zTm4R_yTc@We0(K)B4E;x`17;Dp&yO=($W&esS}7MX(J{ei?z(ol0eS^R?Lve@T(!@ zxH#|ezX1m5d3Te@KQK@S8f>+C*Ele`LQqo12$!N`VP%23kO<~e2$|0eBCj+YPcO&! z&#~_v2}#|e_v9gO5g2!z!H8?&ObL-!4OxpU?@KH!MBrF1uEjGANm0>MFg5M14tPVe zU1GB&jxZ`(Nfd!q3}Rt-RMnE8TxoQMUsa&97nqRuX#Dj;ld}!6BZ~QHepa7nd~BjV zcVtLuF@jbW$Kh4(c4|LaVcrD_?(xOxmS>`-0g2n*2l{!GowT8!;|=E`*(*$5bk#X( zxF)=Tiqjzb`Ls}n_=3fHR0-=_ss#PaW zlY+*(2qu6Mf1Gxa@!qR?FGQB}9^7r0PPcDX=Wesazo4xzXe`KXI!6~5d9dq( z-6a$vIXRiTG7UT|l^SM0%6uIP$#YYO(O8~KvSFT<|I3}w1qQpR1(l@t6WckeI6*Cz#j3;#l zarO)W6zrU4UEf?PkA60?Oe924QEh|(@8Dl6XOQ}beN$7F6K0hg{f0f3GM%os{}N;J z`nP{Qp5oh)a#N*S4|qhKou8=J*gfRqO9nf{K&Avf;u{w=!W+!?9p)6ts0<@0sJn~_ zcjOMmsJFi6^;_y$zRoaaCf^$T1*Q*#+Xz%uR(*6d!{``)@aFY178hK6? zBO|>h;g|Yi&rMbWPT-C2U+}??hw#usF@B{;RYX;ykM0v`rliFiORVK~`m6F9Q5;?? zSct?aL`j*3qTMm$uoxU%WVA%nEA%w^x;YcLp%y zTwDimchGibfQJY12`~meTeqC5xCufxnEi6pzf|z81oMXR@8)yosh3_e8SbkVGNS3- zEK!?^{wfz=N9dJ{78G3=UarFJ_Vt0$UHimN3-#wg9bAyYJ&0)?b%(P z=@KjBn#n`Y@Z;`FZg436<>)9wwUT&U6fZA*FmN2VTksL8JsAGL%C<9Mm?9g$_#1rC z3y>4lYV0t?!V&CTEQH&`hYy9{IQ*IfJKb&5(Y%Mu%wf&VFE(e&n-s42hQ-rLBiEQ- zPXCxmX%IYB(laiUDPg6bs;4N5GzhtRRK7GgP)DBi>Brh5X*s28*RLANwT>(RDq1Hd z;(&5ME^;}vfbO@Tg!La)UkwQJZp2!aest0ImqU{b=>O`Mis}-=js$XXVq)T+J^=v% zBo3lqVhY5N@4FK?L+jm6k&D2o#CP`lxy4Mau|g&AEw!hcUv3aR&KtMy+Nr?tZgp!t zw59w;RN5#+&Y=(-D>}{-C6-1PN32(?8C@qI&2MW%I>Wy%(Ai0PJdGtf@w3^Cgu>a5lFn};pFZo zKdBBjzXyRak4!jN3lI!!ps~cEVmkNB-Rl@*mp)mu3aa80&(p6oJf6NjD`e;TXkShZbUdEN?IMOAJ>5=H>BDB4##VAUe#vU*!q z#%?RsX2QIGU?TVu^PG~()a4wHm74aQUq^K1L(b^Y>7tu@nQU*iO;q9|zEPP0Nuc!6 zukmlzjv7@o*eK`e6X&bBs~TwhJg#_5k{|u_Zlv?IRC!{Ji=1U;4w=d!2;}taFWzT z;W4U-T3HndPC)*-flafF4SIRl!`8~glfx}by9ZA>3p{rixkGD-RWM_2PEkBTwr60y z^~8PFpmvgx`*D`VOx@WncTD_)Zvm3;-juT1tj*M!D7)|9B~~-Hd$4hS%_5nDKBzW! zNZ7rbH?*!RxbN!X)6nrLnRLGOG2bi8rz2CA4dxY^nV&wz|57?e7*cNJD+U2h0kjHN z2!=G%;pogNuw8;fU#2^`ZU-FJQ~7m+lbz!J)Faj;mYnCY8yVSh6aSj_p{H z%S~W@#C~eOF$cCv!K|TyQbTEk6Ai&yDma|%{e;#9A0;l15Km2w+|5-=%wCd!s=HC)al$`{ji|C);niuROu z`t2l6Db%86+JsA;K2gY#INl!Fzm%73eD=KQt{Fv@Tp$=A`@t?}zcnM7|C)Q&0AVqN zuas4ZGyjOF$%SPVH!4eLj=6ktns5c|#eZ7({v#^7uNUnsH+Sj-!K|kjo2=Uovrql~ z{m7gS-3<+~FiT{++^r2>Ys#r&2cY}?9L&9)FXmSBDiTXVf{2Dqx}_61z2ZRE3xhOV z0$Y*zK8gBQaE^(?jRt;3Iq-^%@{G*vPt-u8vNQf_`Rh*Uiq_H!AP%i2FH{>&I_ifB z7Y?!Au}`@p@=(`?if(QzUCwQ2js+IdQL}7rZVERk=;>vMjyz%}63coX^C7aZXqe4d z+=r1DLyW$^=&2r75r|iw-rgHM(KI{K&X0YoXY+h^qEdTtSZQhBokjGRU&&-2v%GTa z)=Nxv3q`i&BOl%!a8|NGPU}tK4<#2n1CLekJr*xuadR1ZDE~sI!2FFeE(<9d>G=TP zW9YlymS4Qwesd#XG)+Q!K80!R_Lmj;h+E}<$)i|YjB=xauX6)c*19kY_7$9{^fC3U zVzBjd-Vgn>JFBpyI;!Xv8%Vu7kf~~(PAhI)5Qej{IaAS+XCS_gb07}Q>!7XrOksA9} zANx<4|5UeJ#3Y#c@9B{dTmS_KS9k&AL{ZBtpG^Ih=%gF#Q#j6UNHZZL(PZsQonl-? z;bex4{R0g-lP2`|xGSi!#^Glp^%6_Hx36pEbAGD@ z(D-^_3!JWZO8^t-E_ia+9NykW7k_R)f4`g})#aWDcg$y#uG{TzRkU}$Y}Ko3Ia$0$ z^|a9g;$m}*w?CD`Uz2~-*B14+Q{dHxvy2MtFF&9l9G#r7noqKWueA?`WsLe$J!P?9 zW%`GP8iGw!p-|&KU^tlG(k&`&hA9{0nObhB2*3p6J>vcx6`r&sV2+`fCP<2vc|s}m z^5w9lX+&(S(QoL|+9p&o&}8B%)+^H_0{S}Y$cy;SCd2UFczm$)jZ0g^?G2RxkIE}{t8+npB$a#=DZO>2MfD0eOmvgVra;8>HP7WJ@pUpbGd@Nz= zC%%^}gT-!hW7n@w)U*UJAtfW757BkBLCyNEITQlD? zPHY>d{3D}pxYJVLzhYg9R?AKUTAkHCNw4cdw|2TbLd-9I4dga%WXdvw1(aHlKg zc$lN99qBSM^tm3&S1mZw zOw`LW(E~r)<3O9GB%uU?-4TN! zZ{B3W9R?F}Tz?Bj{r)cwmN3XC2vQUl(zo)!bcG#Rc>a?AI}Vn=X{ju-L@Tk%)%nmlhxjr=~7O+WMR@ zaFtsc=2+lFzfmmI=z-wc3xyfZ^{$-@CMwuptwTel{eHsfEH&q7+?7c--c2yRMDS}0 zu$4-(^XJu`@&6O8L}OJT4r4vClpDW)TY>L&VBno?2&H6vbDm1+jZT<*?l&n1=4K+* z6*da+DnyY0bv}Xut!Vwu*Ox`F{Wh67OfOW%@3ucJ=4BvDa2Wk&dAxJm@(WVaRB*=j z3_J;@ZzbQxt|$9(SZ7&BA7aXKztNSyWa#G-u&1>3@a;GENU(e0RJ?RiwDOnBncFRwR%G0mXHwyP_PpQo8kwoifSqa-kiX;68vlnH4 zf|C{$eBF0bWQZi(mSI*C6iC*vQs-*s&k8w@5_-a6wn!jdI3)JT8`^L0ZZ3d1EDJ`4 zHm0j1;9=gz$M;J~Awd*>#55}Oxxpm-$+=2}c(Iid|3O3ZW)#Hdf#d2Vod%YDn*DWU zPRaKZ1rE3TjjMNtQz8wRO%g~TFMtUR`kNhe#f!2VzPu=zRNz}~elwP*j+R_Lf?0r6 zkSy0{Z!9ZOdZ;T?%!(PE@OV%sPwsY;VWv^Uvm$)5E06pV)#a|*R0NolSOL|-Jdo-6k#a+@s1-MQNx zT-e~3XZJyEc=Wfa_vU<|YpF3>`nB4O3c`ekoK?tESRYZ=BAmGm4Sj>ZtSBgk`?)UM z&rz}mMY~n-j@ASzkO(lk;_ev=_8tNreC6af>tllZ&T|GAgF2pGj>m!&4$%cC8s-#_ zySkVfx(gdFOl0im8n2w}(>ber5kN|nkRH8Exr1wQKV3<{;`2K-)-81;Qo4(1LLC$X zozYKkfQYPxAxDL58BgFe5d-}o{ybXNqG?P*W0~gmU^Uzl^fXVietRvy%4*R=@9qa= z0U0jfw1)evJ2L_0AZ8>egpgGq(QQ{;c+jxr`^R4=BPd{uF0m=oUm2h)x(~?o9TrJ1 z4E7_Ya$#;9F;DYCNQl5(`yq3PS9Hp<;pF^CSd#JmN;IqjMODwA+4&QNH|0zoVU9(b zeTfV|dZF?WX61m|A&6Y^V5H{$Q^a;A!(rSX3#VPG+K|NsHPe?t7KNM=7uG~6g zN_uxtNS%>=kFes^pPN^Ku6p$B%{)>L){u3MOnv)W%kc}91$e+2reg&V4-feH`Jt!^ z1NR0y{grCG2-7mh((yF2TOILi+SrqjwdzqWD8VuHv&7cQ{Z- zeN@PlpClEG`=J-Sq`It^a5~erHZf7NXKM zhNqH`_}1FP85am^YcTJ0#`MKCjMlrkjOOE1s!DkOX;b4O+CEr0;#r?k5!9zPJ-RZ= z*`{wfh@@`9j`g^{wQftZZ2ga~H1?4#9Mjxu z&i^cl5_Duip_cxUYeP25K?V*?*{}ULOuxl$U;KWS8_?Jk1_2H{(M^!(_Vi(JclwKA z_0r)%@SjVuTE86V7>v8oT@G+0v=rZR zKnnqEoFnK5IWU!8Uz`2C0ktLKP=bu9LH#JGnF#+!TxBG#0`dD^6FTPmrh^+}?>xSG zEW4ZWaPWM`%#(|ij(i1ALM!r+mXh)vV1&;Z?G=A;;7Z~NR-AzF@PuojGUeCLt9l;{ zE`?aRuvUUk1vn%K*&CD3o3UZ%CxIdZ138IF`Kw)>@8c@02oTsqN1FD_M?q7ra$jQ! z#nQP3-4R7yoU;}FOKo_ghR?fq$iIL8cn)A!B!$mSL6NcuFTBt1huSVpNklplh;=vV zL@QXDc=_cF0xvE%B2USadHf|JVyyDyRE1u7c`=*bQiK<>RN4QES9^`pQgpbIk_xjk zYQ;yL$Wv+J9N9m*Kt~!@)M0}O(FhQc;KqcXJwpbNMNUr6tlxRJJQE}DZC>*f&-l@k z!?8oErxZ1F;&`zhne0FBwE0imX+GaSABlcMjRb@q43$TN6>?|x@*)Q4!lJ(uzGvoP+{w0EBN<&Fg{b7ewG=C@HH8cJV+8BiQAX=fsWX>|`ZVW{YfpliP8;c44qAokdmD zzmkRv$XA*7epjD)?7Mw!ddX2W&%G(saLhw1a~1&5Aq3eW1#>Qp zh(rl_mzU_z$_{Q_US+sQuej?;Vch?2JYD)Pt~FO?UUAS@P}jxi$M z3&Z5{Gc)EMmsM&HnzGj*oSfyU@Ku@Wo-eeyvdJ0p5~6iwQ9ExYf6N@?z%UWB?X6Le z2=2iICJe*=t`@_95x1DvnRzd zr_)9xYL^0@&N-BIU+4WB-u=$|YuhkOo0B1$`glUjVeis#nm6ksHU;xVugkp;5&JxB zuU)U@p+evzf*yFDAz)~xzklC{t{N+1@;?)Tmm)vW;{SLKQNSuIykA;Ua*xlID+r&d zwWH&sn1}*mwEN`p&6E37hZi^M8Eoa)mC3dpMijN4jAmDR(G9zKsq>pZcBUzmMo)!w z7);C^zPg%-tAq7`hZNkap!wwHbw7q&v{)9Cgx&K^Ris@C65!HJ)&krT|9&}Y9qDkS zjz&d;y;&qZ3gNX`oa$nIe%Y&V;n0y1@xWYb+U4wBo6^QFU--pKR+ppMv1C1mHacV3|&XUJ1d03B7m5ye?vV89p@Q-Q(^5`hlqPaQgS>cE+L0i@noS2|3tl z{=8yn;k}K~jK40Yo^Bmc=Wbj!7-GMEMvI)5tA&!`^U+IL=Ea@o?*3ghw2E3v0(bCm zg-$-xX{w`UqI)&ggrm~P>0hCd%Ru+JE#QnD9_F)`Yv|2+=iZ??EYU*dVPuQmzkF5T$(!M&Xs`XtLgAfo9q*D=<4oN9d z0SN(t1xQOvhjh8Y0zp(-8U%zzcOxy`ozg8Jo%fx(f8*S7&pChHJ=~5hd%4zJbAIuD z&-=u}Q9WhfkD4;5KV<+`0r@%7at^zC8BQx{%EFRzXz)s5M|wIK`}_ME{gt*q&jUU9 z#gVALwbYZXhc8RthyN2!&x_|28}IM3;cCkI?QT~3!EV;n)3-rYnnoz?<5(i(t|D*) z%<&u;_n;3SDtG1t`vH6ZSW;HCF4%El#X;ojAVgJb0!hCj_~ZcpF9n41G$Jlt%y(Ps zy7$7_*GP4RFz%fmWE#`OiG4RoVR>!1%B+;aV@2uk`F4rH1W_*6>AT07WxqYA@y%!T zp~Dgc!#^5aXpr>=gz{}bXONN47}=HsYY(IszQ0M0PKCp%8PIT;u`PhtaD$2I3Jr}s zG=eBaT)4n&h}U{7f>FAqIufy%(a?zKM?-z4#u|(PwpLOa8X9T8n*u0RmIpU;4V!TA z@bcwVu5tr_2IIgOMF#j3@{((va4aZO%?cW|E)N7buSSU6(1=C!2+-eE6Tcl3Sk7LpE}Njp=S>&dU19v0gSjP|Ue@qL z!{X<=f=dc{>S653t+__-0w%gOsvlop8|A-tW!))GFp4VqIbt;ci$n^zIsjeeV{B{( z@HsRJ%}IgrfL7Lm4bc$(wH(gF3~sK6c9knwk3gd|4^?@5?12u!$0nfBuYY$1+4-P; z2={NcUtsDyE4~r!$Ef?V=GTK@VHPooZ?q4kzZYRgA8rJB-b&XY#>N zdeV4#y59)I;*EWIKKN(K!k`!L%i7}3i}sA_@$yn56`a%w;cqL&9QdeeWEB*UZ#e&^ zKvVuE3AjkOlwt$uz;=TY!a9ikfZEVyLYK3V9QStf1@!(t+xJ1B`YVA7iG-?IS zAoy?HmGcwiXGh{wqZCw=>z2$ujL#hj`*x=D58lUQKKyo(P;{FJo)Tg^2e%7s3!2;7 z3hLiUNl9t4Po2ZLbSc-q3;q7zmO*VMs@+XVrnX0mZgqun7MAyL_{x$h7d?xN&N zn<*UXYwUUX>~nm4d;nC!Zj(cfC+#4FxBfy?AR;0v8VSw?@JU~jlevK^R-l<*@wXQ8 z;pto%Ew;&Q^@Woc8&95tptmh~!P@gv!-5b(ucApn+Kw-XwsXZk#?M*c02JCPeb2Y?k`HF+OGPA~7TPyqfq*dHL9_ ztowm983Ge7zK9bK7s!(lu?f(s8UY5ky1uT)#K{??NajUY=3(Ysoj<1YDP6$F5d9DhnU4TDyFy%`qXg`bNRv8OERd+x~0@Sth z`or(a1VL&Iz~tQ|IpgWYBlO+f?dRmBhiezUX1LQnkYdAlvrT-2&qBiF@NjO$?)HZQ z=Q_1v0=3PRBsnq30X`MDk+;cshOJVRDWM};bUh=@W$pQkOp$b!oeX1p$S3j z>?`P-FkUS6yheO0;6ypR<^1r~PU2e-)61P~+0NF|+!oc;@5Hu5MAW_jSu}$dfyEf)Fn0-Buz}xq&;+^g0RGCTcOF?fWB5?EBS?6RE zx^?NBiLK)k6nYgFgB8wqnaFZHKXWHPlfL+y00stJzzAB0AO*kUed@t)J*JW3 zz@(9c|N5&njpUYp4Mkr2W>w=cZ^-FQ73pd1(0dm&74e^wvoj4>b6!8%@Zj0=Tz{xj zNN<;;{jrc${oe2LnsRe30-?aquBl_^oV&(;h)7nvIM_2*Si;GwgL_lralivGH82D3 zcpkn$&w%daC-}n6&du3@S6KLHPgD1vy5w$1vl)R2C)oodww5`gzGtKks^*w6d`zj6 ztAUJbsFs196_qh&_KMhdeIH5RD&wj=o2w$!SrfB!gk^=>OWZMbb@5Kux|pe4nn-!L zu$Dx>9dvR*OgW|AI@w&GkEa@Obh=>0r3x$UD@ZuE!% zA*0vPPZodlwGRdALRwS~KV-VybL7dt*atWJ@kq~-Uo9W_uTRM}oY6GvC0*vXEedII zh@!SOo#Oc7Y2zDT1j84dABs~*A&uP|Zru4X^xnuWA@`E?#q5M;DO<=llilvCvVGq@Vl6R)r(9LIYb|7iMXa;nR1 z)DT>7ETcS7`5}#=^~tn3-?V-yQGS&!&JOi`v_ePN^%7z$_m_>n%x7PISu*+PJK$!= z-lN3jHqpbhw)DNA-%^Q1MQGi;W@9PYe5Hz*0G;+-#K*Wrt}tXt<@8>h8=avpxY0Fq zzw@2DzPRo7VQtJnq1|>}z_c}u%6k=E@&Wx*;meBa1AT`gD~%aTru*#4M$?a^hgk!} zWowU#c=pa(iOE&(M(CgnuA)6;Z|)Tczyn~`A{Eq-TvX{CcZqZID;ci2!B#LKQ~z|& zulzbZ>OdvJZzU17Cx1L)14RQ4l&+G?BKtnDSzP|}p$C55N|RAf9jfh$h!e;V zwb|QY<(nkGik~(W@@2aJ$TG0`51m%XSmFt^!9Gi2 z(>{)YIdDJu$sqmW@Y{K1ihOdNX_;1T&z90WMHbR`>;z$J-xf8=Xazb`7K0TpL`c&r zW;TXI$xoEVwevM>Rx(fP3q|S$Nqu%@#`U2uc~a8fJ|8pwn6o5qKJ#~5Z&M8+k;4m4 z?n^QqYB&0>gXC?s$GXFhwBC#Oofu=IvKmq4+fVbeKPenq-uG}^VR_<c%yNXJVwgQz?USHmCPxRx-V~HK#_Zi0=-JFlI87UAo=54xULbp$> zB7s$U%f{vWVMu<(Sbht;nwDI@RX@J;2s$=1UAuMqL)P@1d%HJvKJkxzW860ZW-t6c z-44EDoafmVo%u^n8dfqAZi;}_qk~%|`PhAF&NKH~KXj0wXvhBCgX@(Or(IigUWeRc zrEae@8^EXaS$@%r!BYk|H=QlcQ=guNa|6u}E>m`k^mW(`57V1l}`DqqcnBU;D`T(w#XXvtNL9*dgDY9@)}#u5u$tE zYxrg8re^VKOk!SBSB zvBz27=?z=>1Gwr>w(OyDZ=E05{^}{sPGoEmX)?C^`>yCn%gM4eD&&bbgIlZ zzGHB{?7!RjwY4t7{qi{K_erFui`bi$b8BT^snMA21fg#dBVw&gEfR~&1$)Ah)`T)` z8fa#x&pJzQwI|>SeGd%;UahGYlyH=pfhJ9OTD127ivNm zIpX3UWmw0Yf@?KQAw7~`&AxK>wr7`@f+vyPdx^MBYxK6Kn5(IP-JG7)58?8(kpZ;3 zUKuXT7(d6e6T*TrY$~iUvHg8LTF!4bOQLjb#oL*7CI+ae3Jtoa8#=lf`&iVnGLGH6 zRV2tT)d>>u@5*CRM&S0?tQwm3J!|u?*3AzP-N*{^@DTlaowdjzho#6^i{^RfX;H=* zz3ixpT1O5?bH$TlX3o&#*79?=-<@1G)Ma_SDUG(QWZ6q*g$0^KW2ahlwn6XZE}@W? z|5TP4Z`Y~BX9c?J)2j{sD|kMEk91G2tCR1fFzhxq&Xm=YgzYsNoa<6E`?zK^7TEIP zhPfzrkcm#Gb4ozi#pJ_2hp*ONQLk*=23%UY#)+_7FL@l#X`4;qWeN+!+jkU{6N-pL z>~{`#s9o=~m&c4WqWuz-#uZgt!iX^&Gj1uAB^QuUa>Y-}S)4@_h0eWklRfdr62FNa z)7dlu2JVdat+Oq+JJWyTk%t8C3n;bZw;xowClZ_||w0 zhuAm*N9__GUJ_7Z;HY+;QBIfyB;TokeF7~Ov1|j%wnp+ymO_W$*~hZy_@z(KY^9km z`~=8!LxjzZU%P0mOFIUCo+!B$Ak?hS!`aAf)cA2VTz(EG!M+l|( zTZDlJcGGZZRfh|__mnapkL1JWol%;s6JDAJDMG)VY-+HoSo4^PSkD%vf21b-`F1_y z(-_OoM_M0>7I-|%o6SOm$iE!8Y#za*v*vMXXE63W!256`xLoNu|B2hj47n$y*u}-= zo!mZ_ugjM(_jD9S4Njns5sHqWIdEZjLr>EWbj+0Y{p^<>js<%v?fwB z&ZsHN#iimb=@OJ*KMf05EnE4gm5f(Y)~ZdLuHpPx-QE62)}gjv*`-GvBCD85LL(LW z^vdndu4*4*Vi=mRy=fC9CfiNfSiXclZ1^ST{1CT zr{VdbCA5I7m=%uj=+V*9)i%#L+uLLOXl`cy?E+@GYj>3&gec!m zIv30lzB)<%>9o}df3TOG;(!qx;K}&sy^DDcQIR!Ak+R zX0c2HVOR2W8W>fAqs2LB`lq(dfMZ(G%A?CNlVW}m-dKr>mHN!Anv~yg^Oar;yeCJW z)U@1Z`X-A@5ZDyB7OWK3{{1p#t`Ysohs7mFuKDC)-6UT-X6%)J9Z#c$9Nws;D&&HU zTkK#qSa}B@>jL0{h{fnNR@V1`Od`HyMK+V`7yCnJ#-Hl>W?|e_y}pNN^ zIY=$5tw#CbkFqi1pge$crvVCxJ>2kVv}BBOe3 z!&Rj>=j9Hz7&kg;CA}l>F>mc~*)!2Fb59jtOJDuINM6lZ+1@(wex6^JXU9~-b*zkS zIla_cGuH7S+=|Ube8SdIMc^ql)yig~V9?6DhOHeFK9PL9$8lK|IOHAa&RGgt)p4;=rjt)o?;Ga;;=bh2@T zFRw8+8Ysp&{qxmZ{I&X9Q!}1E&sqj;lZ$9rUog~a;wdTTXN?3LvOwV9e(-yt_3`;d z%j=yw(>*mYww+1wj*+eGeNxMPzU4=@E-QDHd5j9fmA)kEPe=bC4fCAIC@STqE*S~O~}E}M76iJf{dWe>Vnkk<=}$pr#(*cQgP-TU#(bv)Q8yUCL6c0`M=`u zw|g?rY$}n1%~V&#XIy@*v5ngGo1{0V5dV07$^` zr^bi}S?CbA)|-m4+UbZqWT@I(9B^__&*Y5p$57fvg$BR%?lt)~T3+XidKBSg%eQ?P zeDdl)Bb&vom_6QF0rx&jMgtRJC6HSn;3@UcHM9{n4JW4*!$ul*jdEVN+JCW+~4%fa-z*|H_B%c+G9uX7~{gyoC2UPm^_P$9BZ1^(wpRaUPB?(W?{D=$S{ZW)tmc`s zs-x_aZ5$58n$QPEv`^o0%n`~(Zn|wLsd#+}z0hCbQsK$xGk}(Lz;4_VMve`o#*?yv zOCjQoy<>!3Ze-NPuWL&lU{K^ucc<#zgzu`u&z&Y0s6Twc^yCYFq`XV9nViJ<8?Gt# zx5K0tLwpM9drO|fa!6}e;!0VQPFYmu6`pE#PBJM6aKU|QjM#I|SLHS|qNiEQF|26I zKj>fbdHtDDa{#;j8h-%!$+qpglGINDv?qKG_=t?vC z$3Cr}-0hiCd|ucqj%!>(D~hT=8+rYnnTbx;$?H+*SW!yRkbObr$e2KWrNLf8S?v9< z6mRhJeT~~>t{-)2Z!>%AKN9_Q@3nw54V&;rM+oJ7{H)WSTJ}G_7M83(CjOqN`nyLr zuWW43+K{A9$N87xLd5l%nx$TcecA~8$32zuDLc4KV-n%6?$Y<$-c`DSV z=yuNw5vQtt?nTkjPv2~twYo#cC+m2{3D7`mY2#2hF;m}}_obZ2);hvQPd>6zxHiR7 z5;ecPyf#J{fOX1$U^u#bMH|bZR#Nhmo|=1J_d=V>&6kC~sE)cz@vnWukA!<-x7%aZ zP#pD*AsPXUGDEaao9(w#kon<$vmuyES_R zF(`Ouew=!8zp-3kGbhGwm4f2G?q@rCDz+)!tL_V*ntip9deh&n%TIT*(N>*vfK%^w z)V)Ds`7-U#XGd(C4#I{dbk51RcQRSWjbw9fOw!n{;tU92|K}dx8ATs1tEqUE&3Ue; z(aSs){~Qq0YssFe{La^yQI}S{`S|rfvUfZ6okjDBH;jMr2#fUMqkF1e1dK*k(Q5H> z{yf!BZnmbhliCNrRjKj~E<$c>CjT({dkxDchGH%!af~tx?-11JI=ttFL=Nrwy*Tx# zRIC;E!HSyLXM8qwpZ<;(zW$X_3Lp3UpOf$5oyqju{PQa~W!sfV8pV}oZfXzweb?DX z=mCm+r+)d#pehh*iWjl>5 zFanvZ{J4+OE3b}UwaYR8kr3={yS>0SFN$r&?Z$+&Tf4_!a&(UW%>7EP-ylmCX9-Q@ z(V65{dbO&(rji;KZed`{L~jcVVKUEbu(zwg=Hyrqcaxp?qxzckYn&CE{C$~(I={`Lr1K$W`}j3eJj9fXMbc|5gK~GH2htC;dd&=r0lSH>HA(g!ct9pAPKiEeic8S{tVRQ{4@Rxs{8YbF6gP z3@o-9Owo4^5E8II04S%`SwR!0C;N zho@(GA3Bw;5V8ZQPG5nPgim`nWZz{4B3u3&USUkzsN76!MQ+%i7TNyAUdX+tSgT)m zIF8UTZ07t#Lrpr?Qz2ImUSncj!Z!mtaP}ig?-=)Fh^Mp(a3%4T$n%^8HDfDu?GM1$ zutg*m_yiC*at~ZHz|6gIa4=l_?4UDj=6~W9fC@TnSl1PBX7fHmiIU{ow|+%&i}ybx zhGBvAK&sMLjQt9o3j#7Tlu&CDF8&q~;`Zh;*boS0xLEN z9yyALh)8Lv2(i9^kr63iLkCQ{{15?9B^B_l)W<`o9C*;Ebe#uphMVtv)#I@_r~JDA zpHSm@6@Hr@<5ii-`dHG&`>k4lQHyx4@shj_&_{h?d}11Tv}pvuctP%2S0aO8z%E!6 z=uc1!FNd{mtk29m(AU>5<$TPPIs>0918yXdd=`L+=&#{BL*E+?@@T|59uAWpz5GBb z0nQHvr)N>zC)Q`PvJ`ps-;2aEXfrCw+neC*z0jk=_C&qiQSPSgu|1)O!}l^av0m*x z1AoWAhxY;F12>0pMkcId{)aW?)Up=t7VMV77CJXiK6(iz(o-k4d5drJjqT08|3knT zE9nUw#t2jv7&fRRvjv>MZF{i>RBB7WEG``3q6pw!AMy>@r`YdR9VV9k=4q5#`z3O0 z;zReno3%gfZZt z>Qt2qRhdzeG#R+aavewJc`1g@H;k6n@WqNq(@^eToFJcZd%{tCY}hy3#OwPySj z&Ook6d6+8!Qb}_sb%as9@|-$lSAA>l0ctiE8X77I3?uf1J+N7U=(msI;Z2|>UIIl_ z@+ym`SOk89oaHb(p7CSM;Xg~fUnNhs*;=fY0!>B~k>gLEy3;LF9LsZiqBDYZF$nIZ zHT7rT5Wb%+b%C%FXQzUa=uXqm`tvX-_4MIxDBearbyv=oTp?!7 z+9gYZAc9+)^)@1hwR$m`^I=j;4l(Q!MCQ!6K+Zx#w?bB*Cvs6}(Y52G43mxy>oM(W z9-a@Yt-E@O4cC#^@rP9D0Oz7FT4c$c&P5j`z7G7N(Oijz^X)2WGDiSo4i$%t&N#)_jNOFbJC1c5DOVqLp1l(VQOve^<9b~ym^C@BC-HNi8L%l~#HejfjDaFt)>7EpzhuwuqX zoGJe|tdMFj6W=sx#%$m~?0QiacaXNDTgHgh@T)_yjzT@upVNhj=~BPh)I?2}Xvo@G ztaOw+Jrg(6xR9nbH*vVA!j^24rB2_E)@vrw2FL>1QWxP6x=6Y6L%6z~W34>ExA4W1 zGMt`~ltYB;1hJBAPxHPuInrRSo4E| zrJ>iMTFydRsLUqS2&-b0uMfjZmbUW@XBD0%ING%}vWkkgI5?05^28#Jcgqml~*v?WP{Gx8^g!uDH64N*E>CG~>^ z41Jmbr7}*>2YWd()jk1ecm88!q>O(2aCh?$u3H$tH#gIq#;3OzPly(`&S9QP*mg47 zx(j?$Ayl5R9Eqa}_MlWYV9i>90}9g7%1ROV#x8OyP_ixDovN@Alv@q|GJbC*UmkES zD$}fK2uXC_Ror#EPM4T9G9IAvXYfgCx5yMJ*0o5Q^{0nwf;Gq7FEEx$R4YHo!#lN0lkPpOha zYB3R@2P6pwu*Sd=1RT;Ih_MA2DqE4VGTUpq9XZ45|3qG9LyU_|?$}(?){mq6E>a z5g+^mvAwf16*%KSAk}EGv~Eq2q1O%o zu3|cHVbj1H#}OjEz&Ik&q$7IC@~O{M5oF=0Bc#e@C`*e%0H$RPlu7~ws71sSB<9Po zU8#Z8`w3*-I)|thGl6aHxZ|KsIyCdlfsGZm)z_yw_v=?S_}ykeq8Ji-hyop`D$pzo zSHGsDq=PQGbrN2U(#%w2x^-)GYy!yneL!qgwYJV`X>Ug%ykNJff3Q~viobrnQ3w(e z$Ni0G$=zmskV7iFGaj}Zk~jI=WNoC-9Ezi5NI+4A2-BkSb|~8Tz+B34sfQ^!L>bh8 zst|l(4tbAG`L6SAl%e6_sUgZNOiYrZhl@$(fVey5!w-H{{Qj^!m>q|UgOd|7JffEA zlp}ldhEAoMW9cOsnB@`R)sqf4j0Gn6&PsMQOEQa7jz7YB?FT+Q&9&Z4U`6K~g0C40 zxYC(48K6Hycf<&5^{@4%1#6bQcphj4>5n^LJeCF~mJ1bxw3f!GR;xK8I!}fEv7{zwldqQI<`E8#B^xVsD>^T;t(vu;Qx8FA&qJLynCa zd^HsqTVeKEdYziWl4j_~378T&H^Hh0?$j1sk+7C*rXdKdXJ==*EQfNGo4}Viv#JvV zBDY$iPt$SXFZZF1SaVZqZHb!hzE_=F!17grGM z!jSF`czHnzQTY%;2!I|{0$etdX-g=!|D9tOnn~U#D|!gr4wEG}WOT-DKoc8%GwME` zkCGTw;2UU*Tmj1xP?5y2H?iYfzATCE4?FS?3}k}$g+oaYWCf_lD<`6IDwTa@bZ~m1nQvnNsjFv3_ zf#C5edFr$H!8Ok_FF#+%^N`oHGu9ZKUYv4~4oo+6Tj)GCB+&NmL-u~{v1<~J0L0K$lZo3rq- zDpV4+io!A#v01fJVQv7tRml5P1YB!u-#vZ_4Jb(JF`Dq)(}6){s^Siv9DR_H(};Uj z^l`gyXAXSO$Gmv43#>lSc$DDA2%4aV8sdx8Tps(jP?Nygd!da{3-VGS05KSjI4KUO zS&wmZ0C$NfC^XjPK$Ha1&W#3gy)MwSfH0>Gt}OywxS)o{jE7DqD2g-+so|Xg7DgLq zvu(Ro&Pk{|bq_6c+?tgEi=a-Bxg)+@86&n2C(|vmc?73jRr2(93(`UiVDixj*}sC! z(x)R1eGaJ9-k&owzF0mgjpyOyxX3}ifkVyW!;$8z0lbvVkIgX_d{ zX7PkFCMKqAYOj!N-Y6JeeT9$`l!r$Jm`tev3a$pCT2)B5lVwhZ9Awpd_g-oOy@`d5 zO%*&vm5axnvv4_ zJGZcauBZ?})-M{SIpLVA4sJyJ5Vk^Xsj)F>!X z*!S++Sz6t>bxYb4vi(>X86QN--z24^OatG)Z%7nz)M#sKYraP#e1U#&R{sV|zbbr@ zo+ZeL(1LXg9tqVQlK@!Rl#rDWipDS$s}F-}%xBio5M{92*Oi62x1wJOxxlhX-5Qm; z?rVI~KO8Mu$_Bu>Tm>fklgm`Rf9@}+V$n`VFlpxA#BU^Hj81{Qa=C$bTDp`=r=y@F^=Y#S{39s9Xcg~t2CMIT0 zpU+458l#4fmP01uigDg{-aePGm{Qdt}avU5W?lxJv zmAEhefKbFQLFsn~WWN(`E9_uHXx5u@8JGci1-W1>3HCoeuFPcl8h|qaS|g1PSJ<~GL3;+XxS^@(Er(8- zEvVv<8Ur5f^N|8(JTkVNK2Vz8g}(u=MdyK20aNi0M%DiUg6**`Q$2)ygc6WP9e_lH zonQ!DJzXKh7?KQ9pva?*a7*z*hzkW3ILUck&CwjQ@i4wl1*AM+oCt+NJkjyx@iFKzt^k z_;|W}_RITh2DZpsuR|mUqo6PkG2~6XDz$JDxJ?h8XK)C`As|SB?D*r!vlDut%U>50t5O}O6Ls52 z6+gKIFM)60we$kM3&Bf3*X&_ILs%caSeKCm4~kGKZ`DN7)~|1Gj$vMwUR> z>A|y?Uj(=sNZ>0V1PCet@JD9H<;$0oL3;u`1TGME!ndpiv;e#tXF>2@*V!2a;jNm> zVE-}>`x0@cnjBzWF!64)s%2obRdc*I$lfyZ}v zvXCbq59d;LZr~$2X8*b!h)rY}Dc*Y4!SH4cg4DD^L+%V`jee z5|sY|WbB$7FgzWaXq~wX8}a=ni7zJH2gU{aui)TdW;V93z(qruCxMy{SSeqiWjW$Wq(eqfl;2J^pZ#$)=qJlz z`C@D4K5i`#w0dF80eOc#yRA*W)l35r90bOL7-h?`l21_Eya8Mlr4`PAQ%IkD6@-Jc z;N%7q1`))-xBtW!GHlPI&e3L6O~%8V9R|YgRRyO7G`+)%+iuyaj^g- zsen@d=iJFE1ESKdvS);0S;=87UZI0VRh0t;B7IMI0O}BX_LB zcy2=8Ncnmh9>m)sVLEUclYqTaG#O7pYRoi2bMeL96or@;Sjs@V1bKxy@aO?vtz*ru zxDQJw!fk^Z2#V4gd)=pc_XUoWA=e#|oWPFH1MCUs$m2$vT?#=#SM3)cM-QXa|M|Fw5$Qw`%vBa67Sk-yTv wo68TJe>a>!cqae;?Einm!SVSY-|kd=ZpF%FDWih{1OAbbRCt(mU*G5d07E9o-2eap diff --git a/docs/plots/hann.svg b/docs/plots/hann.svg new file mode 100644 index 0000000..b98aa44 --- /dev/null +++ b/docs/plots/hann.svg @@ -0,0 +1,45 @@ + + + hann + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/hannPoisson.svg b/docs/plots/hannPoisson.svg new file mode 100644 index 0000000..019cbc4 --- /dev/null +++ b/docs/plots/hannPoisson.svg @@ -0,0 +1,45 @@ + + + hannPoisson + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/kaiser.svg b/docs/plots/kaiser.svg new file mode 100644 index 0000000..c15fbcb --- /dev/null +++ b/docs/plots/kaiser.svg @@ -0,0 +1,45 @@ + + + kaiser + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/kaiserBesselDerived.svg b/docs/plots/kaiserBesselDerived.svg new file mode 100644 index 0000000..e89945e --- /dev/null +++ b/docs/plots/kaiserBesselDerived.svg @@ -0,0 +1,45 @@ + + + kaiserBesselDerived + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/lanczos.png b/docs/plots/lanczos.png deleted file mode 100644 index a401e9b90e52a09a65da421833728eef41cecb98..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34112 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 NjqA7M@?=b({x3QMo7?~Z diff --git a/docs/plots/lanczos.svg b/docs/plots/lanczos.svg new file mode 100644 index 0000000..6544f6a --- /dev/null +++ b/docs/plots/lanczos.svg @@ -0,0 +1,45 @@ + + + lanczos + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/nuttall.png b/docs/plots/nuttall.png deleted file mode 100644 index 36ba1c72a90afc10dbb79cc5bf673f082929ef98..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34327 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 diff --git a/docs/plots/nuttall.svg b/docs/plots/nuttall.svg new file mode 100644 index 0000000..8e4cb46 --- /dev/null +++ b/docs/plots/nuttall.svg @@ -0,0 +1,45 @@ + + + nuttall + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/parzen.svg b/docs/plots/parzen.svg new file mode 100644 index 0000000..c5fd11d --- /dev/null +++ b/docs/plots/parzen.svg @@ -0,0 +1,45 @@ + + + parzen + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/planckTaper.svg b/docs/plots/planckTaper.svg new file mode 100644 index 0000000..ed7e5c4 --- /dev/null +++ b/docs/plots/planckTaper.svg @@ -0,0 +1,45 @@ + + + planckTaper + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/powerOfSine.svg b/docs/plots/powerOfSine.svg new file mode 100644 index 0000000..b3cf20a --- /dev/null +++ b/docs/plots/powerOfSine.svg @@ -0,0 +1,45 @@ + + + powerOfSine + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/rectangular.png b/docs/plots/rectangular.png deleted file mode 100644 index 2c129661dffc940fe6ff1d2767e80d6f4329ff9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27622 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 lUgGOt5GV5gbTN|)9gup$5e diff --git a/docs/plots/rectangular.svg b/docs/plots/rectangular.svg new file mode 100644 index 0000000..94a449b --- /dev/null +++ b/docs/plots/rectangular.svg @@ -0,0 +1,45 @@ + + + rectangular + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/rifeVincent.svg b/docs/plots/rifeVincent.svg new file mode 100644 index 0000000..dcc4262 --- /dev/null +++ b/docs/plots/rifeVincent.svg @@ -0,0 +1,45 @@ + + + rifeVincent + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/taylor.svg b/docs/plots/taylor.svg new file mode 100644 index 0000000..9eb9254 --- /dev/null +++ b/docs/plots/taylor.svg @@ -0,0 +1,45 @@ + + + taylor + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/triangular.png b/docs/plots/triangular.png deleted file mode 100644 index d4748d916aa316497724f45fd0f5e4afb4f8ca82..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35842 zcmb5Wby!th*FAjblJ4#V+mU_~V28Dgu0t;`m(81p>h|gZ_mn68~ZYflxtS$Vh6w%h=EK(8cQ|?QQi4K+eM> z%`1|Lt&~y23uf;j;!`;M!hjviK7%70iEi*#?Q! zFGst}%ii`5XQTxu??#VapRDby@K2vIFB=0Aw~at35)-KFUt_5%l7ryZFihLcKzaT5v@}9NwFqNz?;R$*w|2Awx0fv zjIrYnUyT~62%=AJ&TX}?GX!0rANNxFR}4Hp+&V;bo6E?`I(;qI`111j@^U+$9kzWz zx9U~o)V9uYi?8P|)$jI3pKHIVGF80l$GN*bYL%-tLyo5ql>&cPR#qUDx-s?j0=e{B zFUmp*Bvf(n@w@NXlN81pJsj8!>#^XFFkTH9)V?Wj%J}w;GcPaC^!4lB=$9G=2H))= z0TB_%J`dO1O4q+ib<7+b;!AbDnRi8Cm;KrMRoXc?h{mf-%ff=nZcw)&6aAbn=sDdQ z^!~`4TwS};cwX}kT$y)-pj<6KKD@5Eu;eCA<}ix-zT|~=F#r8odgcHQ9$v`){@aky z&|JA#5=S>Te0}w#5CL6=pzq(GoqE{Yb3k(Rs;$cYd??anQN;@ta@)&!nJLs{&5IB< zJm1q3rnOXMsaRiMzXEP2Cb&SA$?^9T`_=93I{3WoM(3MxXi5s<#7n*DGzqGm_}bc9 zs%7R-DHv8(R^GQWSm>nuy!Fj@cX!VIP2;?Fj-)+Q3=D{mx66;m?MReFlNibq?*}(4 zYHK&2>-`E+!cIpq@w=uM@;Hq7EFYhz{E5G!y1KL0cDm*z_Hd>!;lW(3^v`s@)x*QM z1X}sEpP!zUU8M8dMdjpBl^J};h$Z0*ijGDL!=#KWD`V4cZuB_Hvz`75UPF4m&d%x2 z#*0|y3gc$4O1W6b%360cR&@8ft7EgY?X%zCTJyXw4~vW%`3(&XN5RG|{BSGnIBoH} z{~>2UlYlHHCdMYecX2Qe33&05VfEquwr_9{4gyvTT!8Dd@mwLYTeH~Z(X#w^`$e$7 z;Zx-X>ob)W`XlEn?ZNLZ7d_OPz1(>17l_aZ*a8#^U?F&Tcx_Wt1k}{jO81_pTas)9 zv}|lK%%6nZSblgM8E>7_VGV_zZjXFcOyK}e5L{Zy3JVLHdc>&ez9T($Srw7@dIa3h zSeYKr%&#j}SeS>_=0@b`0i25(+i48&XJ_nlc#n(g^j9P6 z#UFh82M224IGpYMLc^pKd$lj_|3LP8sytYi%}P+Oy}jLfEDxbX=UW^ByT0lD&3T!7 z^j8BJ8F;XzPlSbq$%I@Zm|Oksg+QFadwYAI?To=yR#rY&QVNlwkpx>iR;WyB*z`{A zH(zNK7Sf;+R-{ZsY-~3;vd0JCm)FjACytMgNA)kk)~5=4aJB&XF5g#CpE+*0lGj+6CXF8>Fg!Z=M9S#W8pkNp+?kA7^r6%Zb)p*C>b zbTv$qGI658Ws1~3JMN59hlGT*EiRIadY!dJ6LJoS9u+EodSf@z+l%CJxDcqs00)tg zlgk6y!g{`ros673&1MD(lY&ZE_>)sbP43`%|Hz0u^h!wz*f=;r2xtVRH)p#fCm5@% zt3>>7iNLdmSoFr3?M)WzHoCJHDy6|fij13unwp!PuK!qWrGg#L^*Y}pu@v>bm^|6T zLGH*?FV4|qNz=i`q!5vKe7INpW-NMly`6n^*mzXouq@_tG5Z?7PVYvK7AF!58#^%D z|AE+cs;nJ6ds=bdhx1q5#NZZ!K{#1}GbG@3#?~^NVGw;YERZ3^5NPF5^pRo?AIMKu>rMU#uAw ziOXodHI#;of+A^XNG2CY7MUv@o(B>UGCF$br%$9}xBEOB+uJ(tI7{Jq+c*09`*US! zuz zlqMmL zI45|OC#1tM3SN(7fnQX{BolaV;{noE&2zdu4GoRK$-=?&*(OgOI|~bGMmoBume|4i2zw6wu> zRaJQ^St5F?Zd6zyuTIvw!8OL;WeR)5722dfQ)}0zUWi zXDo%VJ6q|}M*l|}d3kw#hL2;sMm|Sz9-OIxr(6(Q1OxkUm1M>kDm+Dw4<|gP;4yv!r~$f1U$akUW5#d52!;}(b0_( zAXB{0$QV*GTC8`1%M@~x3Pwav6#{D_2eO!;V77cbh1u~+JE)+cwY7XDTBTvV@s!oB zus^arwudv#?CgFj;eyhF4+;e&;Bv`3UhCmv-r=+R!JK7X!QeSF35SP^i^ZPn!5sei z-Y<)#QcaeH7GDDkRJWbc+#Dvg(lCB=PXhEKR^T`Vp0r|n2~V5LI&#zA|m%YJ16HiRGlYTqTU2x;jQOt zn0fN#CV0Ir_9H(%bEw+GAbKj{fL_4dAW)2KlR!b6R9Ve-n(%EFL1 zNR5=CF>LW626RpXc@}Cd!~<)`)sQo}P={9ZT=eRwI!< zv+oK+Ck5y*K<%@f8QAiQ@873O7`eFAO23YeP(vKu-NR~Y3vE@at(41uDD1J9*OmZe zj7H1LiwA0XFF+V+Rp6$yOUgldTmc7!WD^{=vP*Y&ccldlY+_>K%{p>o;>eH?_zYnW z42RZxevPcOG(F~WS=ZlF=>B&nNHnyxV>LD!J+mhl7jiFOVsIEYMS%2TcDT@hI14j5 z&B(wY_qpknl01laQwxjFPCuje0Mvu1KCU@TeF(|7eFOR z;D)TbM8P4=FDT$z4hKjO4zl4aIA}9^VK_$rPz{29XR;W#&ThW&L~j@0*)U(1c`;0E zL|8vODG~s?9sns4l9R(|<>R20u}H6)LHny=ajoANn~dxTwcQ*_QaHeZ*gsqWa`cHc zp)C0Lc25BuGbeyOwf_wNp8jU*^MaJ`P5!ENNO-tT5iZk1H(Zf?a*bf{jn8VM`#}`g z_`5%YoI^YKImYK8Ymt`eRU<;4Y1qk5f7x(+2M~$6x_auRwzl^1pFdE7A1G36Gs92!O(SZ4mt!7C}wmsMH#dq*o1`PAhoW8`_ip@OYC=l zKCMu@d2|#HF4;LfP3V7rPADZM_4Lgo(`30p3jQbFcgLn8o`2w>C}+0%^$EaFZ|I2G zV&mg6$;il_nsvZ~wS(4ZA~7EU68^VExypp)vUq}8UtVUK3v$Zxt7Hlx5E2qLoem3c z)_wkcUaav20}+EP0_>nUxFI4Qt0&ORFZ2NJwiCdGr|$c@0H`5?RNI{=8~qdjQSCcz zkm4P0FYEvcwel(ej|z%^ka{s41_4;h3R4(!3kxpmQK}3PPn`Mh4!vEGxMQvU{;s>@ z2mx~_wlfvfuyAlrdy~v}54T6q1p^tQ`^J8ZxBN6BK0F)=z@$)slAvf_zclWNLH&M$Tz<;psodALR`T6PoaAlq<(}M%~PMd8s00yqYyc?BF%!f!i z>`6O7pA}}Ei0GvB%hRPgnBf@YQtIkBP&^b%`fP1G+g}c37<2`gu?jP3X!8K65z?I- zP_&)6Fg9q&%p@Pq5DX^adt*9NVfHsDZgv@YZN@{ijy}hWb$}G&bh@SdoDS#hLOpi1 z)lVr$$ET$SG)M!zz2Q!#qN1X!pm_z$^dUCQltA+=(+=Q!(ENpNY`pTh+5Mth??^2d zLloE+Tp_U&6N5)xF<>W+xrJ^_fo6P$(@S@Qv%yFsoxp_Ql3R;7&?G;-Hh2jL7D-8)rb4jlg1eyx`sa4c}WkyRqumDrH8E$pSimX^TWS;{PEya5i54KWUFt z7VO}p)YLwdhoEVMC)mmCm7o1<}_ZHhvjaHxUhkyWJ{jg zyf*6s1QHbEiy%={Zo%=u8&JEYPG?Q;F3hQ`L?Rj`lz`Y@*xXsOv& zE)$AR$;7htd@eb~4ia2wFG7Og8*u-Js8MS51Pi4;brj}=hILZ}B&5#(NkKamfI1BAjY6dYnKcUAy1NlThb{+jye132ib5y(I}-p&F%OBkZ&(wK2CPVXhq%~L!f0i z3ZM0zYCk5uhwL+?|Bt)*t~LCDUhx> zpm{bUgA69=Ulyx~OBjju*)zfbuWKvpxVSioDHq1n*k}3Bm%lFYL-Wv-MqPb6nkp|n_VIc35lDZQut*l@nZ$Kv4a)LvA5+Flk*6ics z!)tH)?wtS~PGrvs3=GUntu0|Gn~)F*P9%JBadFun5ZcfzQ&t7|C2)>o_lH0;BBiQ| zRl5Iu*)Ldzrag&OM|TtSV4#_w9^KmBX58@v?W?sYX0fLJ?kN$s4S`?k2|i+6hH2vI zOoPjp;mgkVr%WBAx1cpOcWmYEf&RDzO+s5|CmdJ;0M=6Z?b788;3teTaKVpM{@u4A z$xqPKn2Ur?qG?a=;o))9mq0so3vCaS(|O1AFF`Wkh1`ID4F~9m5^hy2Y-VQ0D=G`R zF@{|goMiOFXj(n-4ln-*yE@*|`OwvxrORNxD_--p%!7CQ1 z=jP-lJu-g<4OHGpC7-^U327>%>T9#_c7N(Mg64BMxMx2vB zd$soIT1t@y>xtD!mMFBm>`i~e)X~ua-M)my*#`+KT?`P(fJ}RGadDwrX^zm*(E(*^ zK&pg;v^?C{Dy8#A7B@PsN?l!Dtqpe7y%h-tj)>q%CTV zAEp1%a%^wqiIgHO77MywhBTF?s8Ub6e$^`!01ujnv&AtXGj(>Pb@qz`leKkqav%YZ zPfR3aWyJy3LV}GyO@o@KE#>1E8231d`K7&}I5?P5B7V=XoFB;W(nYAzF316F4?>!r z%u<6?smNWdT#_{iWS_VVcNbmGc-KC2!eolUl$92vl?p=+ZdZrX#GOuhS~F%~N)GuL zkfkjNwvQX0_D)QEoJPO{K);E7==$z-#8VIX18y_nH~+&ROriR9ToaSw5$XARDD~-P z7T4M;{tc}Kv`&^wXf6V=JKT~(T&Av|-b*QtqH&y+XtMm6GO)9$uOB07P3q~&jyAD) zHdf)0N9u9d9jMQzbwY9)D(JU;B{AJCC}k-A_vl|$$g1IyUuwAAT*Hq3MhYBmIBguW z8?UikekOLH=ks1TH+8i$9GZRVM;l##bN2c9Q65vkCT1PhenoV>fp>bwu{LetPx=XiatA!w zb7+E?C0Z64p6>>TY4l<>@9GI&@m9n9Y6^X+^`i$~O7?iM z^{S>foL$eHSE4xk#j~|Z%Rl*za^G$HFO`wK4M9JE`wFxU(3O`y^W(ZVP#u5U=Iz*> zBukSsh}HLD9joH(i^4p7$rK+07E93IC9#;#vmuGvkRQ4|NPx(gGpSP~`C`fI{_(}_ zpqK`D5`MPTzZK-?yehBkCZ)DCad+5qFWB0>J$Kdn?~wSonPxL|ZWm`(OTr={kk3XD z{_bFmUG#^-z+}R0?s)y%aKfmZ9)>Z_hVa)V@w_oX zv%bW(&lCuW5XL+cTNwYw6_|thw_Xy@C$q3pVBtTq8hO?oV=BNXq?g=N#Z_$l8)?ZX z0APw!9mhMMj400?pvKVF0@4lxz#}mrrlbg)(*zr48vOW1Yl6MtqZQNL zA$zT%-Ds_$>c!)`Sc**`>Md8Huwo>&j;R5!rYb<|A|}$7P*N-Tv>W@scg!sb5;!}E zr%~CXKH}({fqqC6k`t!{9qbREsGu&wE2~Y!OpMAdTO}$@f`0N)~Z)iCNaX;vl7mE-WQzn?eN$EgbzIiNHX6fb2h-ZUyNKdd_@;TJ z1pkB1*xUHQGAm|q$X8(hvxZ68UlX2s&i`6#wbA3Sj^a-DRcvvPnpr1-zNETMx8z)* zjGh;xFAuI`8CuO=`?s8ndkCpP4AW~ga>a%ocv!-J!cobx0eb8V!R z)8aUM@#?!tDD|>nC)M}wHl)Zb<&^K8fcEA2tTOMat2lG%P2N!G-W~RSjiwR<);>3C0-E? zX=Hag>mDH4xf9P9X$nMdrNowl%T;h(>q0r(pT*_i;3zU_6%zv_FC86S#h#3eOpfpE z1yBqSgIaQRfb+vO#s%!9loKEAWKi^U~t#mt3$ax{0 zBZ&s11@mqYt!N(abo(cq7R%^l#y76^8bBQMG`Kx)f96frcU1P>422D*Jcy~5M@lz$TbT;3lZ-hpw&P;@o!^~Kl!Yr}xbfDes8Xzz_uk%=H+jyJv;w>1zE$mPvMLqc=|J-X84Z+J}#nr zulM(2qsQv*Zhy)8?rtPd@ldH%SpRb|_K{yqw}p(iq6GyQI{&MzaeYscmh}o(Nki9X zh7-q6^nAW_WL)Ghs(|_EiFR`jP28mJ=o^R%Jx=0i7t!d*-_5P`)sek3rrp-Q=8|xx+BXqkg#k&I66d@#!hgZG*nc_Es=s{mrykjvZxASoNs$Og%5W z{?$s~uxVE-^?gW=FjPrVR{nLP@Zv>aW23OfB_JY*o|;1V9hPD+#qZuTdpJ6dMtmRW z>ys$guN|lrHT8^~(f^MZpbY{VgsDLwQh^@P5~y>S;W9L10%BBD5I~5TC&|joOVcD+ zi*SP2DodE;;^4plT?S}2%l-hJm6|D{StCf3CG`KLrFea@Ejhtk7_vdrq$##OI1+$@ zgt{EjHH4R2oP}gcd?`JB3e=cD8Ja*q$cTuFQYSozcK@+;^=+^In6eu;5t*5p3ApXy zS*P~^(LIJ*I?QynBb3A~9Gw)g!7y&w|B)O|)(i+k(9;Ao%`a*x8;Pso$<00|%eB#M z6-U1e)>BCuh}!}~cTRS#4TUN+toaC(weE&NoM2M=o?&Z$GmZs4S+&@Fv-h*9Zzdz@ zIBx-R4+;u8-b^uhQ=^6dEn85E8THuN24O^~68+6Je4hJb?OguC&w9l4uq_X?)f2yk;5+FXf|=5Q;02 zb^0(qL_h$}&U^h&cI)c!`fDqX*q_P{T5Et;1cC%+%j1JL`Zg*Aw9PSBSMTXy}-xf6sA9=3D*BkH{HnAE+ z0*+LG?t7n^84I$4Ro{quL|hyJIPCj#&1jBA=rxCWPL|Xs`CgM$gGod^=IdOEc z`1X8ocvudImQDwAd=LqsBSV$=$ud2S&vx7k_J;iT*imf>(g92B0b5H_gT<+h{&3R| zU6KAYTsh&yJABf89>Y)j&Ur95nBx;FPBKy7M4B#dX-rbOM-VE94492+gaEk&(uNBE zb7%-2QvOl{G%Fw&FgG?gsX^mz)aWh?zA{)V2`kqijv18)*gUcvdt54UqzIuQ{=!7K zFVEm!sLXDve1jvrd}G}?dmyn-?yh$y?M7PAP=y&`WQ8AufrogA0Hzq6916TfUKw_odTmXiGnFQ~k~usYgyflh3?j~ z$Z)MUJXuxz4{?$0sfy)I9NEZ%5nW-ofNqcbRZ};fii|)s2m$i&@NflK(vz*9q;w1n z=G{^FMB;u;a?Mar2Vj?xF)+fE7^Ed7VL(E80(yA#?I=@`d<5>dhP7ml4i?5Big}No zOgi=M3%c;ujZy>nY?R~r#Q2$2o?4KN1vkc2M=09r`g$;^Ot~Ou?95g(ZjEGPLZF&2 z*#54`d*GvR1iM_{(9r(#C;BsorLIbg-jEN&f?HS>;_ipeOgu|P&s?YYLthU>bOp?i zG;mrxby#%I{k0P!c!m;m-2r8@vMS#u)e61u57g3Bv2W4}`S%=5Nue=y%*~MilL2^? zsjIhuge<4r;zn3%AfPnOh%EW;-#7C>8GLX<)Xt44e}BEL^!v`0KL` z3Bt&5N8JmL2)p_huPy$;Zx-CNx*_H)s56A=oUq%0$uEArM1}CaVK2)W7%k!h4I`dS zB+=yoYVI%1h~i6>AkWUf9Gy;GQPA6_rM1WqWjo0U*E9_6c&q(O+4Db#2lC^bz6sYO zml-=SY`6cGPubDv#z)a!@o;~HxeG+HA(5@}IN{L+QtJ6BT4p|JYd(0`_7LWXXh@E+ zXpxe?{*FK-i%6VyKLm2EGRc}&z-1;k@%$+}06C-4s~=xOw6*v0Gm|A=qwL&?_?-L{ z$xQS8Q2y&&E zqB_cQ^!5^;u(P>7w$xEV+~bIZEq{zEfX)deZ-9BBJ53v4`(pVD*99b5z42Te1L-vK zk!FdYen~PmsZh3=3S2=>i^&(VZpeGQie;rRnRF5HEg?p^UqZB{0oq4aNWj!j8)?$n zqz~5acM~&C`lPv*iCofzhw%$r2_Nm=;l)Z6Y#Nzu>*LF2@;Je*#_@21BNz^JE(AaYDG6ny?Ix}K1 zpZZZ82*kvPhtk!LV$@Uh8voNLvlVZ>^nW-LjkS|sdB5)7upnvd;=&Jx{DsgQZ4HK)^I8%55~TZwWZslnBhAIgE> zg;u9mC==4t*lS2&1F4>BN)}q_`TaQTO({{te+7Cr(-(kG%1HueuteTP=n0m7)YEsE zu=GEBB5amX%R?lnj-M1T1?-<6tX(3cl`)G+wAzTpGRx_`bP~QMe*Y!$<>Si=cqp6( zHUL@G+wHH5J`K5ZjmIRN*u6`n}h7dl>35`EQ+S`jr!};;X z?@~JF2b`R8mX)C>K9y!R)ecmcu5>@phZ^Vda0J%@)w}xpcXV5u1k~06fOtzwOWy!@ zro`+2+|+Yc3`*7f5{u8)dS9m!pUhQM7uRDzM*kQxac8AeoSO*N*8W>`psv}@D!W#i z-`Bow>S|oOBoNSdxBbTLGHMN2j?c!|M{f^7K}K!^%InJh{tyc$utR|^0vIrmNJ&X! zD8(tzNq8l!t&1cSP&U<^Ew=KM)0p+@hB1=As#axmpy&S@t_OMa%K>gk_#GiV1oAI; zYJFTMG|P~inrs9(Eu~Dck8g9OM+X31oABvVG>}@}__$vl8ol%Ksu}>;-|6ye#l$fD zK~WWMYJj_b7Mg+@M4{*2nwEYfkoy@5mO<(Ab~8*lJ&#Dc4>d+I3h<|PHU9iY_$`p_ z8h^APgWYL>X0O6Yh8WkEH@Y13t9?FRgsgRsW+KX)R5ICrmk`)CEn9#C?R`Q*AP}}K zfV`+b5A2x1pFh)<4gs4Q@Q>*KMuYhqjW=`Zn+%oe+^U}aIUAlrl&$K$f%f_dm|BOV zc->ZG1F)m|LA!&U|6tj_RMvV=tY>XXR#LD@kU#v=AJ{$5o#lXFw9CAh*p~E9HAse^ zEUrI<2EEPg^vI?v0`*Gr&6_uAoB&Y4BOsV9)H^}_G?0Koo2lm^!NI|DOd(fJdcDc#egO+sU$AWAm{`T&!3V zHBu(8vP_JQPUg3tGjK-m40y5`H|eg60A2G;D4vrbWJN9`idkc`V8wcpwA}m2%X?g8 z&ZG_&6Bs1{)he0$V7D>m6$z&|?4zj6ha&HyP^cC1718WA8In9H<__h-A_z860{L2K zF&=~vsgL;CuFlu5Uo*1hpFc;Y!$A=d5n0*YMTwLNvYCj%wP4LdC;NB@6dK3l6-g-i z1iE!ZWMrTrhdk&$>&LCJ{_mVmDjj9~J>r*3L^$bIII`a;?7gm@r%$eI`54#?Lk|>R zqm}o(Dt@yp2=wV!uU|_W5`9liN5I>?xppBT6y9!L!zG{0|d z8cO6#b;SQgT1rU7Ty4@>B4-p)<{FdEs7V6!s3#=tsAy=xz%6ZVZhj2>05GbL+`!sK zz_^Cov)w-kpYdRUH2Q$iM)Hdw!U8JeQDpf>AXKv2?y*w;ENS)11JOg(-8&-VTsbTsoYw| z+@%;v*(myYZDOkVBdss(s0nFC2Yi0AyP_@+YEZ2IN)LMuxa4s1pklN4!F<4~kRjj% z+xqw*Tvj&eGDR-z-UZCytO6fQS)QP!7C*TCa>^N1AA&n-SKWMNx)CEQ#Y&xu3}l92 z;GTqP<-lt&GdI@(4iX2)o{jbE*RO$n+zhC;z(CglJabdy0bTujRxYMcJWVlSJsUOmce@tnKCccJv2d>Jdx$nn>FMZlfbqSI z+t${$YJ5OKVMLiUnsw|F+jkFnv?W`}#oMx9Y3(JRiKce7hHs7*X2#XQ>!yh9qo(UU zT=>og$5mKh3DL6zUgl2lkZ8aX3TT}5=F27A=b7$*!5ZsG@|_#+sfQW`OY#J5mAbGl zOWN>7RzWhy*qT|SOb-5a&IUdIN5aV50i7cU-9*>K>kGh5I|8i%+WM#m=3^kRM zRRMweGdPA}yatsWg@U zy$$XDjjVwoLWqK0G%Zf)C3SvcK^&WR!8zu(s~SCQ`THR4jTELT^?BHw=icgNT+REw z8voK~Pn;{D8Yh%00dCs&{jXA=IZ%M!Jj`-1*{R^00uCA3TUS#541w?&+wFk3?4pl0 zBvO~F3lVF$(!BH2uyej4q`jC_e-3F6d6VEMeLFH5fT^#a)e`a-nJ2w2o$@S`f$45M>XLg&NQpDpB@?DgY!raivS*NBq$g}J;3Vz4E%78`Vaprf=Q znMpMK(d>C@1@%FKEI0#P_H6@0xPR;6K$Yx-s~H6g#N4*X`J+TXuggcZao5up_As+O%B)#(3jm#s9ZHSnu}wst6#09K`J;RK00YXRowr8?sn z-HuA_UO)h}w3S2XJ8JJ}Vtxd4ei=hzxp&o?XBU`L#wws5rmk6gkhsNI9fNmJlv}-V($c z!$1l`Sf1&*z(*HK;9JM>+IXa&1r+A8hz7})PTt~f6;uo;?D?F|O91(=qo;uQEvY-V z5?uSvj;L(C{i4v6Ehtk^7t}x!E0kAuTJJFfMkp^dH72}NZc9`!1EJdJZU>KuC{>`I zr1uC08II4+KsAE{KC!-yj7oTg*DaeLiU+52shA0N{uDnLtR;45PAt@PG8$SF7{FvA zQL{6U*t$~|)wjS9RjZ`Z0>-ifT&>b|SN-hwG-kkDgR`R}mTsEo-KsIIgCwWd%nrzS2&4IwQ@p~q(o?_)}|`i6AE zA1uiGN2O-U_f9VBCee&Va?16c~rPfGzEPdU_OKMycuP5nQ&1yMYnwjG)Tr z+Sw48c7aE&19%x6f&0Epw^GV>5!knA0bTcd6`KJ7_X@fZ^K-z(m=i!dcwK2kl!vp7d_DPT&O1m5<_cPHkM zHeh5Q{qA5An3Tr&_ZR|?J5%+V1nVUKX5=I6Xz~#YT%wfAh-3+s#%HC*iF^i=l*}4y zecQ$wniRbkx*-ZB!?!ulnP2Kr`MY2S0QDRwhOv?co0G*FNWh?5QOW#rlQU&-b!)4z zl2J_->KFr%wD&!=Gz20aN7fF^_1L7OrE1^73S8}#etYV7cZE7nKmQW%v|oqE$*0$A z<+MBCW9g_fTX+|v#Eia@-VH84dj>}hN&7zvOuTo#O6SQI;ra6~wTdqFD*izk^Q|=J zBV0=4rRnHu^UsL-sSCLG4Yc4i3;fNTR~fii)*t~;d&J6lOQekLwR zpasa|K*@~tyT5_jwyekVQGnqQI(oK~30y@wqDKcC8(oR`cC>19?SR;aC{J8LIkby? zJs>KqfDe0s(Z&gUD*duIkC>kxS(O>WX_8x@%dixjE7XK%VZ|xAll)7nwDP&a1B4Z` z>Z8mrxuldJ9dW*3UqzEi(jS-sUL%qbf^4XEc7zkQ>yu*l6PU+j{9P09KQZon&N@6l81+h%o@i)sG$)T zrzGSs3;=Zi>b7#*(`=q&MpgPbz>?)XJfu0IlH&SRVG11xBA^9%Gg%aP)N+dfenG;| zPmB%5_lpke+FUacaEF*=Huf(e-)7^duJ-gA+Pb^L>AQEDc-PTzFc+-dcy+jCKb2>S z8sC*2rEIxP;W~KnD2$+=72M=aouh@CUsy;`D8ytu`T0X(00;hW&5in?k1S9$5$B%MLwJSS0hr*^76;_LB>4V*d5z5k1Lzkj z2jt=@ek$ba$^ck>cYn`UbA5fC3EVy&o?M>b%~0k=NJjtcy6h3uAeQ8xcIJF3QXc)t$JxnB{O{%M-6*6fKR zx8=In_!g~E$fRx#_4&Hx2pDDX8D)N;CN21KqdAUu&+k0dkx?-~smO{ZVT)FiVp%%D zm)LjrbwIHjT#1@>;rsgm6(k^oOPjyA55atb>deW>nB3x7LBMGo1D*Z>a~@z00(j%v zfXy=sOifu&8UcqE2BjGE+nuws#3ugtd>=l1aNHUqj{UJk^pu30{Abz}?dxwV)$v}i zi+2J1asw6FBuJB7)iI4d<39IHjz?@?mm9)|?F^i!*iw8uW{yuX6_u2_faw;DNx^__ z0*C~wZGmtC-WP1ZKx&5f$%nd&C;I5YQpr2gTU<)gj3n5r^Ra6^h`}*73uAUrQ7QB0 z^GmABVTZm(|M&O-hoh^C8xObPQ&&tz4cW*wcB-ED800;8Z%w|F`NZPQ^1^T6wXo zg!JYYg8unNnKJs&&j#3-FSFH@SS~0arh5HTUOG>eH3;bVoSaXabk5I{z8=m1itgT{ zmG8}2J200cXj^3g#izbX2g?Ir@R^^S-Lh$f-LGo$fwS#BOj*F>u8oepe!9k4jXSE2 zptqNwSD<~V=C#!{9%ek8l#Cyi$ZJ%sl=US+TMFc%IC5{=i&suK{i98UuJFB-_P>%CiS^;K_#i$$`oJ zyiOxY_wlzGQ0Gz3(w^6Ktwz6bmykCRX>i8!H>!_%^c~f*mL+%ry!~-xg0Qx~*W`7< zuv5&VP!(o!K|t-jGjaht=$(c;(lj9 zCW;-&OF&ohhmV|1KjBLPW%R%Zq<{&+&dNrpCz*!-?W@Yf^QA{YiBi?izf5QNCd6IC zO$9Q(?PhRUAoANU^tn~Ti>k3`rHcD=sD5K!FwaMKH8&3{vOGz_yRL#{o9@iJI72X1jiB@Ac-!tqZypvAN zUn25TfJHy6cZyX#8%`^A)5wS+jD=BhQO;fDfzu>qDp+D&+G%*Ers^0sO z1BkW$kN4r;|y&LXQz(?>ZRS+Ue^rCFd6m}2LPBt_% z76{VvG9bgVw0riJekpJsf10^iRwH<8DLj#_F=QQCf)0^>uwyurehu9Qe?6oBBjwWr z(85;Ealb0a6+FY$=%7+50zz4t&R{b8378Nz1qOG+-O>*-@}$YR&veq`*=<{pNLtbA zn|~&hI6sdqt&b}XO-;wX3vl@s--sS=xZGLD3+XC z!n>JO91B~5$Uu&4nxiWfWc_M<@nMvwMS{||AqCLZ09d7=hG38(z@!u!vEVZZD2pu@ zs9D*Bb9C1K@d5m+kq*$w%s;aA zg;AAGJizYru&O0v#l@)x;HXfokbsyrTBWWL$g2f{ZXkjAYg>V9Cw3&Ce1)|Z;QJYL z=lAela+Z*ncU0Xy8C4*bnW`Id(=2>I0_sU+tl#_`_E~36wvUPLdD7IMyc$&a2VyZx ze(TUD$2T(8%DGWmR0SuB0p*$$ETYqG4gpz0CC?K6Lfx;V|x zeeOHtljXhbk)N4if2MS-6{{eq3+D2M>iEIGWjX)`gJ!Cy_<4~1g3mF$&f^?X%{`W* zBY{2!3h*ZBbWn$Kz4U`kI&C#f#n}OJvJ5=#Kgp8YRWXkoO{?Wx~cK@y^9N@ zM&Wbsyq88)b=uorQn|re-d$|#sv%g<5A;l~YPc|>Sf}B2e$f^~ zv9bgw>d)P^3m8KP)Y<~}^6ioA)34B0>fQMsKEK^u*IQj+8}Me3*8$UBU}%)fenDVn zX2$gH>co092S!R-+H+c;0OZ7~{W0Fypp-qez7M+yn)GYsiLx*Uf{{#unev-o2!+Vr ziaowbkV{jTPv#~Q)WyZv(J=UqO{-wAhp30qqFZnBSyUmpJYi2VDaCN-J%Lpb;H8Gt zg-g0wF<4*jnzGz?85U{Adp+C?e;m;-LWRn8Xkbp#Vt+IoO}2IF^~ zjmiEG*GOPSsu%PcYF}QG*VxSyv_9Oho?id!!xHKN1D9aDlYKGK6kr4B#Cm$(F~bH= z{^LEn#@C@Ji;K07j3uY!qY9o*d$0=qG0!VAkQ21b{*1CEy6g8`*G*TL2-&xvr)Q@5%e)KurQOZV&DzF`0|N>$G%5n#<^V)}JzyV!oAd-UW@}*H1Uj_@(!%IJW)mCu zWnnN($|%n>R-g)&>E_}fI;j5nbIOv_E1zR1`vUAYLdp9DDG-wIwnpdeC(Gjuz4Z7D~Z8C}Us^0r` zE`A!AG9@l+R=K~s1Kxt`>mq~i9AGXpN40?%VgUgG==&yoWK=emi&8R; z*kFwcbzuM9`H~MklY(9jmx9K76os!1tvM{5vMwXyNio6IYp~>GZ0X}lix8+^;TudN zx|Ti8tzozJo}HUQp;u0#>Ly-+>$`ru6jw(y^IxAV2662J3N?rP!UoE8fC^NkeVG|m ztK)Q*MZ-p%ng(Q~n3yL(qT~LS#|xN1KRA*djZP7>GrU3 zbB^NwXcK2?bHF=6=v!!k_w(T8HUksgpk<`uOCtYV4OpTw{n{uk0zyJ0Ag!dP5@}z9 zH=h9NMijFeO%Q9t?b&^Z-nZhQum^&r15F&cv14@XvRf>^cp(uv!81qlIqf3*CR7VK? zu=LL#{;EC{p~LD@$v+nJPIHpDRS{-MwsVJGGPzOEoa34K3B`$Gac!&d<`oPmv`uy@ zgotbRR*og!$PdhxdOTeX5aLYXUiZw!L$$cAZBuzD*Jb7k+0!XFIXx{shR!L$M#{hf z83<^MQZxysbG+*!A7yJozYWIx&u|yNunVQA7aoYX@nZYYbaq`~VF-}`y7kOJ-@pLX z1;(i6X?DyD+QcnJwYE^v8TVtNmK(!MO_G~4lw7VU4-Q5~B(Rh4ImXR~1OUUV4Ha^{ zvb4;VjV6H3RrbVF#)IMcJcs2LXqf~9=TRVHcx|ViKzkFAHBZ3XOPGT8?A^^-@l=zM zlt^{`z6q}~951?BWY@P7Qhmt;61YZ!`&FSEyf_L`)T3vmRMGLUUHnDAu30B97XPo# z-a4r2wR`{HfTW0oAfX_sbc=+5lnRKnGziilARr+rU4qgLN()GbbW5WGN_UAgO2c=( z^*rZ!{`k!NW_~kg=A7}cW$*XC?{%+xt!uq5-@zi({P=j&Iiqh>r`OFg@tFhsORcK< zJ;ENnms>m_X{sN(cH>K`gc98=98@1;^_*m7#w=>1PH%ajYDonvtYvsdyeVVYEvl9O+I?(pygkp z=i3G2VQ*D#n|tH0dTM07x=!3xjqa8GjkVCo0w=S&T0n8^>YY2KzeXkQeiMD%SyfGX zCdppsTD`pRv&Ykw7*;*frh?|+OK98HA8*T9jg(+Q$pkYkMF#);d1yCLP0!E!!mQRc zHnyIOH*cQ}HJAlAuUb#u8=nZly_!C+%v5og%+seh`^FVgv`p0wv~*k7ko$GlSmf&> zUVTAZ&dcBb5;Ju4tCMeT^z>uOTP1aW*Q=}155M~aud3p)zv!Z_OOLNlcL>HBxO7AF zRXcTnw4H9|R}E-B#Ho_|`KPnhiXH7;{!Q|lbpFg{qkb;onCeit@K7%sJ5?wWJl_538@{ysyyuHCJgk;b>W){a%@rs9fRF2)Z{H zy5`behoAU8!UzTdf!fsYyVMt}1gkgpyd#blC++*0^~|VHPr@J0tVrCQpPJMjnp6!M z5ZlYUjrD&1=KN)%ASo=G^Gb=wr!R)qcP^yV;dJHmO^4aZ^yLtrh%E`6c2BdKz9wH5 zy;6Nw{c&Gj*#6CY3m4U2w;y*to#MFtY>WG9O9&F0AAL~QDcb{x4`}rG9?J|FFQ3yPW*$?%7OV- ziWPxRq^k`q_ywFaD1N-e?=_Y{d41PXDLdg=yD+cw5jQ6~!Zb>6Hd0UbV%Dd-eN(KD z_wr^uP!eb3@1J`cKc%rMSF`Xx*I=W8S+-8VXV&f{lpz${f9tf!Oq5QtJxxzHA+D08 zwzif}KX$xRexE)>EM%!hrpdZhebA=!LAa8J$+YWCX@gS7*NeeD0a_nap4|42_Q_Ts zQsFW7dTw{^rEH9v+oDWO)eYuPUCNhbe-+MkMEPmWePm&1{mip~y&96wqCqZ{$Xe8O zDHjE;ljs_@ysrKmd-hKh$)>7vKe?*9#VfxcT6u%EB^~srQ56q9btAdtXhjoreMLpz zI{L_3E3L`-Ldx~+2-r?v4^zm$ePY)4CM8AbPAv$NZu5M+7(mBMF}LW}?Zotw57LpO z+b`5D#{dAZ1rpDlsMp3u7W{FtN8(BLkC`iNHxDustb@yL6~+;bnJqq2mQ6K(Fq`{^ z$)dmWoULgdIcp^Y{v4L>YP|BDB_8rBW4m2ajqymeM1nh;Qgjo3=}o;yYNzs~ib9Dm z;;)Lcch7r>G4js7k4~71Ro>Q6A%C9{WDt6XvD^P+Mvh7+e=NxYv%s|%wwFq#1`dLIHPa&~rxchuRo)c1~ylNTF5kxR~y6gINU zR;5*EFnD?d2+O@y7uTyDPUczCj9;*nQe0$`OxAy@b4kUU^nQ7^o+4}d8)Li3moZf1 z1#F^ondRrD(b7um({eIM)K<(Etb0;$^3W%^O)XqF2Blr`@=LxNqQ(~*U&`m4=P3)B zowd`FHDBpZWkCF+zn>hg}K^LE(n?TsJR>|dJ)a~cm} zwDNUSlrQvFXaxl+K$IaVD;wNLOqSugJK>=<{3*hGJkHn4Nj^%6vCnEo*yjgtKvGTZ zd8Y0T`0gK#mX+*DSsNJ}V`eJnmKPlFW&Dtx)R!-iX*zkQ*e%z=L&~V@C&EnGm4Ek! zv+Wh}tHnxn%1f%}KN6 z7v<)n^Nnv=2mFq|o5@!_uF3V<#4SmLKSo#~sBQdP+cOS!>_ORkXL{-7AKuPW3B^YG zomM}3hrSA|7UZ%B6A)MrA3j8QB%pS0gk11Kw0^v7`)_EuJ^(Dlyfk+c~*#DiEfN$sP=1*ntiJ*ykNex$RDK3ggZXYV#(^|zY7tt^EO!r(L%4yu*+=TYY*HNa(PKzP@wsx*( z^PliikJubo43=7TsrfD!eB4UD^DADi?bEXSI4Ca4g@MK2|O4r(IpC`l(xQ&hfXnK z+Yv>LJalN6Hs_#?o+yM$?b($UeVcUqn9QFeoDX)IttPlN7%{5b)P>d@xz9P~Nbq9t z=!s0A%L%C8Pke?MBZ0;At*)iy$q4O*^58_GhH*xntQ%e|0@Bx0SUAHHdEU)Cbq*#> zIgPxmenLs$a=7+ILW42j?Y=_m@z@JKml)k6TQjGJ8PZ_`wR&b2ss?_(zSlq;52HRZ zP``lm*$<#zFxr0!@{hut1-(B`xLv(nSuNGMyS=b69`uT%&d6)Al?T6ED{O_=6m=m1yCy&XdVB4wT<>F~JH~z91xFn=z#!752En6+vuIe`;)WC~UjH2ox zDCUPKqqrDdbB^u7ps1y$e0=1m)%Su#prhGw0^)7VndDyG*( zdtC$la6Rc4V$Z~_mR^~BO%b#keyf?(*O=)pMYX&tYXrd#*=J%0rtCny)!D}1KB ztv3}#8oHB{2NEoU?!=!&)g0ybXr7PeD|SB3e72)zJpPS2V~_{;^p#7FyJ^~YEnY>v znquM~bQ3=A(cD?3g=xj{JbG=#d5VvNT3tIzikq+0sTkehrTNjSa{0+K0q&@DmQMrv zR&uF!rlQ?RYN}5<>@i=&7aK5seQ}9N=S!>6w#ZZY%P8|*ERN?MLaOXjnb)YW6&=VZaKZ26;FR$a;E&VpD|mrk7{={5ieD$U z9Oe0N@Ht%EVNh5KuzElTJnu!dUvJbwkiyG*!EYY-%UhXiOS2aftV;r}M|iU0v6vGx zc>39|l~Bi4hv}Ug!6@|Nl&#HYwpDe>-CrM*9$!d!)9oRTE8Tv(Ztkt>7gNO;?L3+I zolw^HGN#>2=DojlC3K^F%CWm`F58*5?S8K2YiIdhIMwg@C^x<-jz`b0IB&OC-RXUvuX-fY6O0osy>ZU4rg@bl zcO2TAujCyKTq@4`eRSx12q6eC7aU_HV|%sJYg-57j1i^`cBUVC>~4;qq9?_)j6Wj_ z@qXGu>wYBwbG7n^h^{tXKpwn^9J)Y(z{0SsfZ0ttcJ?*Q55&~%0dZ@4}8 z8_QCs1>)wV@sR+CMoPA4H&EC1Qo|C^v!|yp6fesvk$Zl0Fs0+hBjJz$Jr>1yt?`QL z`m+a}nRM+{=yLtW!p6TLnM8R}`0-&t1rsF)Bg|Y)J8RFh>c-Xm^FotdoeR zA^^0*ub*vdJXh(+>IDL0(72VlqFJJG{4#tcD6op+``uKzL(g@z^Et;z#g$%iWbYgr z(8jWPH`Gi_n1B!0s3PMZ>+Zp={2L?Y^(1xWS(T@B+&tm_>Gl1>&h_>TdzEy= z^m|=Gw;U+KdlDDUnDF1pU=9O<2hl?dIreNC7}JU-2w_`jatt`$n93zIA_?+(N`GzY zjGak}qwPb8=}z+N5d;|s^Mgeuy_6{N$B$`E`jUd6@3NH?KLhv9u(vXd*ms_uR1R8u zw_(!}p|H3}gNZrlj(P+}jWSz@BW{ZjjLfNwV#HuGJc{&bLM1GD^1P0(96 z?kx4R8#5cTboEbow5UEseprzwrF|G8t)$F%t>W|O!_(wCEt2?`xe-n~XH})y37#B- ziVGr=Unuo^&s7;%aFr8(dB3U@m!Zu-DG|m&1UUrAx>iyq>HX~lMA5E09pja4bU%>2 z&zAbaemc7E`$t!bl4lOru^5sVHGHkF%~GcmaK%t{ZRcN*u^C8_lr5NEXBkwPx5dv{Zs()=-3(5v=ztNk)rS$d4OvdhPKNVhlEznOQ;yL<`{ z`{E%MTtF+xeSTp|ATQ}0M2B~gKJ~?q5BnM^WW(_CA?2gHb&K47v8M@{cn*5fGwUwx7%(1bE+%lGuYA*PE6c1K2C})!){^~?fO*w(Gi+$E^*G7Yvew{(#=&V5 zDHG1N1_As4_8$68tM6XZ(+1SMmkJ-&tOz=*K|b?ahji3qwEPF>2WFLa9!HVLHWm)I z_m@5hwJAu)UJ1djvuxx2O7}w=lIH5_>U1DSW8HrOO&|nfw?0`1u^8cs!7Knd+(QdC z`2mw@Hd!lJQsR*P>vBnf=ast>yA)}A9g8lNRNVFO?$5NVJRAi2bVH;!`aIlBTWiuK zapQ!AIfkQL}$?H>1mGOP*j z;>8YMToPcUV%*DAF1YIm>QxG;a|5s0D?9sob4yDx(9H10^lP490CZ{x02%orgEi41 zgXPTXMd?sth~7R>2-xhb6jSV{PWAQON)8%v0gP^KU3oS;5G>p4Ubrsm()kin!(|%? zsqFyf8vwH7En#6xY8X2n8Vv`AG{B*dvicZksa{7!c#Z2!2@1TFSZk0z3K75<$&Ie$7LfQflEpFLf|H_m+b>hhdQq6F^b4Rb<&$m!Y;*kD zPv{RKa89_6P7CrrcFizltI2GASFa>e5AVnIc)xgxkVAXv1jnwk~>g#*8-$`kbN{jQP0><8V?=9H#xQ0v1o0LA|m7#;Xt?=B2u z@CEU29}i8Zn@Ide3y`NaH2Sc!U`S9fGsM-z)D!~+BO+y^qHg@&IckI-DU%M5b_2?z2YP5hj*6wiET*UqG z5vSc5W+%ttV?Gp-42%4Q;{E3ZF6!E z(csj23m*!Ep6$T?MaUO0etwO=+%;Ao;jja|6okr>rR)HWhs`g9ISiP|h?p2G8X6jy zF@6kVUJOJ0xRqZ=V_=oQi;j;z{Qg1&8YGb0^F~BRhV;IVMPnDTJ1<0|eLf>55%XY1 zZMuhSqA&TF!kPG89s3qRW%xFS74~UeS{*yRx21MmcHK!ThIkM6nzf6j&R9w8_n*4n zRVp9t<(L;1$1aLj(s;T2ZW*K5vw|+1l)#QQOp1qk%1mIj7^lErq64WkUTUt=yDzu2=2H{^u7sa3+A~W z!@v*1+=ZtOvqrY_pNJ5AIzRw&hwA|&YX?;HD}v9?S(H;EO}#)OdO~XI!XX8wNb?7s zw&1-GN+m#mz|j*E6Z_)wL3I2M!cW!nwFFI0iIcwXoVHfol+!0JUXj>SD}?ruzj5~G zUK}?BZft%%j(0QNk&rk@i80NtGq++hA#U3`{Q}blarIT>=*H$PSu|@civ$&w6+d|?$b5+ zYd~dzeA-q+jyi-KfQa(-zrkl|>i6M)_Qm1pWO$!X^@T$Y(|F$Wh4VO^OL0cxm5&t{ zWH+~V@70&)^23)5@14~RbTp0d>sTKZN`XuRp}j*z#Sr+HKy$%H0U+J-=_oT0A~W`| zsOyg%I{zC_9>j5>Ju0+4@2LQg2%EZJWN@PijEErgVR%HPQ$-zMdG|SnhYi}VtJR=9 zX}GTzJFt!E8(XSOaO(xSwSG%_0Tp5D4KH&hHgQU^$Io#)r-x{u2xJ~^lY<4RX^4eH^k04kE7%0)hWqJ{h zV{)hn#gxCTd+mOO5^Jy=^Y}3K>Cd;qpCWppM6Iq81>+U|vnjV$8Ze}A!h}|>5 zYdCM0Wp`LSoBvd7@*7}Lhk+TGZ!_LYG8sk<69bH5~~Ht6%i?GDkFH#&UJ$m?ma zXR9{!^)KL`#vdIfl6*Yk@uj@*>@vDNwe8VS@z?#$WRLMEw6&wnWCEgKPXd%c_}rIY z8oRsi4}{YEP5M0T+lxR;+>2*Q3 zE5!Z*m(Sgd=|KksC_ODL8jMOn8y^edb3^*lJ9rre18+HRB0Lf<^DhjrL$SaQVNhF# zNP-QUR6|6w#7?+meFB9gKcGvT&2=%ywEv+Yr=-~ddUJQRRlVa6v}CNg&uvr1NB#Eu z8(x#gi!zXZA73U2TON9U`RQb>d2?vq_4g>-^75WPVJjB$4LZ)dFiztkRkb+$;;Lyy zf@p36p;o=u<};2|o0>_lNGc}zgEy&efzt7g1OF`UoW|LvC}~!uIO2;AG=-k`lQ<;h zQ&k_}{9QQxkE}2qt_JsbewYbh7e43=(g@|0cOxE*+RCxH6waw?5Ff|)0VT$%-<({% zZI#)eB+2=eNlitf`!b@ag{z?@4lTNDDn+><4AhvjfNPpb~GN}5gNa}T=0 zgKcc(Y)0J`NSj!7%a&l%Y#j3{zaWZtVNRGt0w?zuJeZQ`v$3!xr&F|#7cle9jOh%e z9DiV3#hyE@roUyoXj5Mb4^S%%kBwd#bho@mtc`Vo0?bd;u4-GBC51}-U1)g+6&n}6 z3pDO`I*^5^PV7&y3;c;Pe(#=*P>>d?eO#Lndgni{(;7kP2GHGJ z#~ZeqvKfbmv!4;N(Eca&Q}i}waKYrs*ZzpJxs6GAq`J}eV2tWIv(ex0q)?%rT^Qr( zBn;bVD!w0T?}kP7P5Yk&=sc;043Eh$I#h%)Cj57j@p7%D4AaJJ=f!HmHN}owoo@OP zDzhqi8=ng@r}$oDeqnltrm>uRY9(Sc=e~B@$a{_Id8$j7$hQ7GMxlmZ8G>t4h47on7)oHr-l*o8+gSwH`0 zZScd|h;S4>71#PXCH^ga{7JZPT*$}g;vILY?0;N)r56ZUeAE3052OK^o4{v|4)|)& z%_ZMNW*7l<3e|B0KnHaDYOQ_}3AT&yEYj59a|1m%4ce1mP%8Z?Bg#AOasRCL2RqgP zrL>pfVoz7G24y;u3!R=Enzr|MbxFW63&OaUh})9CKOGkrA&jA`xlUf`xn)rOpAbUP z_^4ap+S!QvQ)Q*sXdW3cBHdNhgj#+4z28Q$mbW>S4LN3)bOSC#s<0#2cw7>uG`KFi zrcPDb&>%@4gj=MFh76g*R0_Zj9%U}|Hm!cp`1iZyS5?&Tjh^yoxr$1x;O$Y+cI55x zkeB|mlt%~;D%P?1%z~k1FhDGXb%}~9#LEj+U=4!L1i7H-+cpF*h5qNSx+L;~=#pk` z83O?~z?cTy&pk^WZyO&d_bn4ambtyXjY7G& zxVWbF_xAGZ+{K|a>Bz@p17I|GIW!FokphaK5k`qXL*_8rx$q336MU%M#se=MBssI1 ztR0y@$TGRNdE~DqzPc{nV!{;u@LoB;^+i7OfXt~L$;YpgHmsa4lal&@4yO*0Zobnj z?K&%5TU&E%SGmtX4Cg)yx#;YQZa~IS^@>oU=anX@2I#_Dfj4Xm!~$eA5MI{-b2*Nq z%+a!l{10L1LmFLSH3C!ZSu-%F&1YUupY4XJu7PsE<@xzag@*FEXFF4w8`{i*)hD-_ zKVACNeqF;Vcf2T2caza_`4)qdDWbT_mj#YH~gq*gFbT;8GAnot#u{ ze&0N~McCnfc4%R+*+2emDrsOMmNt;wsOMHqp(rOOXTDA~aZ7-G?AY(0BR_M^l(6{s?j8zF{Xz^sNx5(3>}2?k;qhf0H? zW<+KMqflW`<1&Od1HN3eFebs`USInrb;{cyO_KiWuFdgABPUH9Q%o*$tm+xhDto0p z;MzBh8A)n-bC2Pky!;>~{tuAGfhbY7gXIz#*(_9xfom-AJb{u%Aqgw6z{|@CzhUQe z?n1*uBjP{D=1qrs-AO;Kn1JxCKJK3$>`}n7hq!Q!I0!MmfI;|K#107sMhI8HAOh-b zFH1{#p|~FaS&&6=c2|l3wpfBQPVPTPeg1($npCHkqUq9?vn)ALuV?o5{BsmtMxh89 zVsmTjbJ77%#a!%Z)0&DWO4lF#*H3(!#*eRN_RQt~Mr!}QZ^g6&^%oV^_eq_W`Xy?} z^!z6p{BnO5c7vE=226=KPei7EBsxERqVo3)itD9dgW?@(fD%6;Ir##LldTXkF(}0N z;9W=K^DmBUMrmJ9Ye{4ZxgXf%NO>+$YJ5aP0*4mM_$FoyOc5XsYE%NUcl)6Z*4W)G zXXD*QE(!NV7+tf2ixFN(5itZhppHijzXSUaA-@s0G7(;a4*hMYhX8z`x&8Lt2)8-( zfd$=b!hTd#DskBp`~Pzl{;9gQW|!^NT~l7kya0JKMk0;gkl4Gu&Q$N~qdsP@odd@1 zx?HZnIMW-LjCn17k`s24V|wu%zSPg4+vMaqM=591J{MU|j=(wsVV`T2nR!FlLTDS{ zKk@N|0nYyRe5~rbaHR)GA7aUXT>GFHKtwNd4K9?~*6?W%^Ao_=L-Z+ccBg#SSfEwl zFUP76wJL+}SH!I{4tKYowp>gS_-73pExeDyXy}*zT5;<9yPv}6=NDq^>O^81pYev< zxNZ|ue8uutk>rTQXv=3D%>X2}2o4R#lmZ^5093v&XrO6!ra)4p#!>-;HFnV5y6}e8u-~1GBhm zj3i|6d+XfVgTHy1p}J{NXp`dEgUe){GlOM}^Nv5gn^e46dcq|GujLFirr1o+&j#mX z3(ywMh+TBRdT+9S3#MlBV$>-<30*9u02#ONLM(gnziBCp-Nt>o)A21uC>-^#9 zy{~ON=l(u6m4z|K8-p$*`#0pBEG`*VFJ377H(Sx0mfwBQdS&YTenanrE;ANBKd&uZ zc2kvw)p>%ljWDBzlb6^JBjrgVHw%M;Yyt#~|J14=N#fizUsg>^!I-vejvvCA(avr{ zsTj>?thkxNR(dIosg8N-H2si4Di!S`KG9@zqwk=U``yMfYaQVds>_8Pf08sDVp0v) zoFNPOgJ+b}UvM^8%n#3XeGo8R_l@DaR-5!7`GbvTS_-=;LAoQRrB5oye^WG9w(+71 zg5gg|IfIOh56?wC``VF@`_HFcvQ)XeEOPE);jv#1eYQMRVq8*Bt4eA-&p+vH{xd-N zkoAY4M6@yVJdPPYGX|zsamxKk3F5=u#&*$@ZWB@7?AhaNLe85xN_y2GOsfR;K@m9aGC;mPwZ(qhjYbwuqMD9jRMU zvZPZZgc5-Uir#AN%jWj!eUYdM|Lw$kkEt8B-v9IKKe&a&4lv8Rzq~(n-1v~7b-4tO z?LTRGzF~L@K70GuUg;h$Vj6+4nhfM#ntoq$sPT4ryG&uO)Xi}a1k@Cw@OAepjEk=VHb`c z#%@^2w}Wt>gh?L3bN&()dp3U$vHbztEk8I8>-T5RBOYW>{X-#cgGfCGijGCa#ZABk z9>`Im|2bCKA2o5!H%_B~vq&P#1NT-nhPD2jaJ2H7-ZWH`|K5gf#=4!rqx~`WE`qC` zms=YS6KSYd(308He1NhJINlCekPs54+2_7~*f3S}chAOZ=^D5IZ&v-|fH12iH>q*uqzXZTP6Dqy)~vUmoiMPshb!fD3>s=_4_z zsTW~fsNlJRg2E?APVdp+&r}2j4E?iP+7dR-?Y~vcBNTKZnBKx0sHLA;F?(TsX^KKM z{BKATTo_w-ytaOcNglT)Q24W(sR}(zNh{u9(cA24Ys>69g0zMVIed`Xwtj<0!Wxha zK!pCqg7x)1KqhIP_=Y`c5{{wzbG25s>^FFzSw`%~ye00WMH~=eQCx!c@q%>wglCc4 zDhE{bh_JD-!8cKTpHOY9bOsU+1PNS!v@RS9^bZ|fmE?$<}U}*!8X#J}GVx`9Y zsdAbY0gUVG=9R){fiDVc1K)j&)tFVTs#v>7AvDzw+jHMaGrFwRiYyYufL;Z5~?K zL^4-Zd|pu4zZ!^Pa%G^`dRy-Y_Aj4Qk1K(5^`7qhkY%l}+9)iqcN0p5a+@USCIf~NgK<}?Q@`$W8=4(L`#d_O1{}aJXl%$a<}ughIXu;;kP}3>kfW?eg>gU z&=de?CT}prMYJZb@A)DSpI(8fR@f>;SFYsmXu6xg^jKWN&Xnx!x+|j7&<+>Q!Pg5!Ulg!kcme1VQwfJF{CH} zwSoFlZkBSb3-C|0Z05m7C!41UE1>+9%vGfm5O}l$4Gb{p%U%3c=lZJ|{#;2>(F?dE zX#lWrbCl+EkA2C;__rE5OsYZS+lNwN+1GcShZ;rGzro3>@ZdQWmT{lGn~*))RD1;G ze+v|YdFZSEDg9K637EgFwir!;ZqwbrH$t5qM|1rBW9%ajU7yynP{=S!cU|SW!~WdS zbJZ}1mm|0uvpA;D$l&&EDEJW{P36)|%YEL1K3}*}$rLabDd>hZwS;tdL)%m67c}q) zyB{M(o`8@e&z4^`7Z3XU;Rhw)2g%&eFs26xp^m*FagSQ4lG>w%mz>}-{&oxnP)dfJhJ8FRVZ0~pSV}Ihr6CP zhx%`Lx9_i1a8H}NHZJ^v-1Lk)mH+p>p{P{+FK=f5bjy0O8nvLY2_{s&AT^wY`!lix z06?HKS3%hq=JiO3iq@V!XQ}buC(GvRHnxZMQ@7Z5GY>HC=@CiWo7KyJ-Hb)(f8c6g;P;bgqDH{wTPzLahDu)>8(w~Gtq97iw6xSjyOW1_AtdWZ8KZAp z+&z2;sxBuoU&3bLQt~Zp8=H$LM7aRuY805zQlTnGPr56H=~l{GM` z0T;xLA%4+yphXIy!Igrsl%$8$ASGf*XgyX@dq%KwCwz1l8h`~&VxsQE3>X<1i{Z@Sg~eI{w!FQ+0r|BEcu9nF0G5wOKv&m*%b~{e=fCCJI(vGa z01q)?155?GTMfD89zGOst_Ap9DL6eAft4d1zWu-w=|0^_JtHr$F*i52g7U&oxTMQ7 z5EsLQ)sNkYh%Iv7$t@GeqJDy~#Q=b$c=u2-yR&de#y7& zn$5W3hr99c@W_oqfs|e(FzM{$=kHI+PHhwl!>-BDNDHs7sxqsf5Q>q$tg&#NVFGUU zek5;*C0LN&8uGV7gdYKnG`UgqWerAX0k>o+BwGaYVOm2S1k~2n$imUW9EQao|KY<2 z^)Z60LPf~<#;q*bOhZV`OrJh|$|fN2ZFvjY6`lF{`EjzDJg#i+m@(Y56i3b^6co8C z2Jt`01>E*+$tWl~$&K=%+#(YepOVrC%eez$kX4!U+40WBonOC

1sd5(nhL`okZr^(w`;5Vf?P9yL@=3U+jm^mB;)%85`)FGAgl}oF53It=vaik0#y3xxZDJA zmV<%KER3q`!^YiLxJ&pyJxRb{8h}aT{z`W0?mE4gnEnohdNY&_HhzG_wOj6QHV#zz{G(nnI2D zM@GkEkAh_eym_q;k@pw?l;)P}Y&--!0I)syUS>`LG?JGOMGrE%w(>}T@uH0cL!?s= zP^kC}n?u#>dTN_?wcO9;+sXgdZ7_gA^o4;NEe&ET4#;Xik$@nUeA*F{V?;Cr`N6V? zX=uIXPYO0DOo1>%brqX}gaiYk14KV=q%8;6Ok^$q6eH7 zw^B)W3@Q_idN)A;KO!dGpj3et{1qf>p&DTofIo!RQ5b+cKYZAbYx{aPUD1qyPc7`x z5O`)xGy_C!bHZurF{nDVj<}?!^ls{@#mzda)a$p0cK%Nd3AguD-ccvIdjWhDkmKym zoNkpZ8fv@lzU(H5s;$Z@Dw=QxoyWm3Ku8)> z=0tQF;JjiCt?fa#25@c|PP^lCnc&%@8<*snB8w3l2&iI~HaAN!#dbf4E-?U?+G6nW zjfskCB_<)!1O>nd#6k^FkQ#*+!_8@NcD5Or_YV#Z{thMI9$?(x~jd)n|T!N1DO$r*kEDgrj}kIx;lAfuw{ zhK<<|lMH5?&X|~(2@S}*uK+aFBCx{XEQ!HWfpbHdu@`Dr-f+_O+;BGv zHJ`LJEY{fh1(|smS-(a{BAC15%YZZYFd6>({nq9b~*-%vliVF{H)nV@($0I=}M z8xiR)ROdCIc=+9E$~}7|n>&0i`(f&ELRQm2u~{)vQ$Y^y@7x;5nNADsq7dZYt&KU2 zpaAuki6|%RnH7 z0GU7sVfOhgCag$bxFA1n^g;v424YJBVRUPEMWxpg&L5)J6eMYI zra+2?BuM;gOTCE!fSyKzBiwair!|4gA#2YHY!Gq~|HAi+z-ZXC%I>LnwRLp7105Ea zQp><;j_B0Dc#&cQTMCCrq@5gjD>*qg0Jqi*c?WRLhR1$GQ4_H?DJdx_0=GoumjD_7 z@+h7!V#EC!8edHQUN}b5vVYqtL;tksYS?P-?o72k`S6cMMYcyWYGC~3Ug-5lXb#? zV2k0@e+6n1I*{O9&jy2W1TqOMF~ng7qIx(8&KdYm}xZA+` zK?@-+Eh+5YIPez+Pfo=K4|lh-lf7H+hkUTz97dn7k5inTYung0uVV2^0rxkW{~q+TmmUCGPEt~eh7+cOVKB={b8N)9~DC( z2@V~&>c1}sgZhU%b+NIr>Q**3Ht_yd=Xd8j5qsXIwl?XVI8k?@FJHc(P>5&}vcG`_ zPtP*5&*(c#{n=aEa9-bb*_PUIbafSicn!Y3krrzR-|0IT{~#zHr*)lBN`Bc@`P;=J zss^uPe^0*Hn1Q=JeA@xg@f8J+D@6Um6OWHng#hl^mvZ}jyG9sZe2Gz&Y(3|JE$a*H zbg+y?_BBAZT>!FT*5?oB=VO40Id9M75>Sf-rg|LW!b5toQj0?y6V8p8=1jxxLO8vU zpaE1xr2t(nUC{tCM_@LfP!J%X>_GX}U85tSV;2`^TY+YKV9n_lX zFh6Gt#^aau-Mk@TZ-GNDE|(-l=|EmD7#QAAJ%bQ~0kI9Pu2vHtNpIe~xepMLnc3Nw zCcO!?AezP|xH3?yFcmNmA{qr>JEFRSI;YWSnK=@XfKTTS)tnzc`=Rgy_n1~dkru-; zhaTNq=>EkC+B2c1A#8#9EG6t_kRfsbZU>)&r*%M#QX&wOxjKCjpew^J3I*zx^{+xh z2VLstLrGB}N^Hd$A+l#O + + triangular + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/tukey.png b/docs/plots/tukey.png deleted file mode 100644 index bc7602ae729b4fd1f6f70458c864f627f81768e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36118 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 Od-72BLB6=2&;JENq(QX+ diff --git a/docs/plots/tukey.svg b/docs/plots/tukey.svg new file mode 100644 index 0000000..c1b6b19 --- /dev/null +++ b/docs/plots/tukey.svg @@ -0,0 +1,45 @@ + + + tukey + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/ultraspherical.svg b/docs/plots/ultraspherical.svg new file mode 100644 index 0000000..cdedb37 --- /dev/null +++ b/docs/plots/ultraspherical.svg @@ -0,0 +1,45 @@ + + + ultraspherical + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/docs/plots/welch.png b/docs/plots/welch.png deleted file mode 100644 index 6ac5284105afc0a41977fe731dbafe7c5a57120a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33843 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 g|2+@>E&#fXg6#R2bMnO6cZTR?q0e&C4Qvd(} diff --git a/docs/plots/welch.svg b/docs/plots/welch.svg new file mode 100644 index 0000000..32b5145 --- /dev/null +++ b/docs/plots/welch.svg @@ -0,0 +1,45 @@ + + + welch + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) + diff --git a/exact-blackman.js b/exact-blackman.js deleted file mode 100644 index 9165a49..0000000 --- a/exact-blackman.js +++ /dev/null @@ -1,12 +0,0 @@ -'use strict' - -function exactBlackman (i,N) { - var a0 = 0.42659, - a1 = 0.49656, - a2 = 0.076849, - f = 6.283185307179586*i/(N-1) - - return a0 - a1 * Math.cos(f) + a2*Math.cos(2*f) -} - -module.exports = exactBlackman diff --git a/flat-top.js b/flat-top.js deleted file mode 100644 index 2827a07..0000000 --- a/flat-top.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict' - -function flatTop (i,N) { - var a0 = 1, - a1 = 1.93, - a2 = 1.29, - a3 = 0.388, - a4 = 0.028, - f = 6.283185307179586*i/(N-1) - - return a0 - a1*Math.cos(f) +a2*Math.cos(2*f) - a3*Math.cos(3*f) + a4 * Math.cos(4*f) -} - -module.exports = flatTop diff --git a/gaussian.js b/gaussian.js deleted file mode 100644 index fa85e1d..0000000 --- a/gaussian.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict' - -function gaussian (i,N,sigma) { - var nm12 = 0.5*(N-1), - f = (i-nm12)/sigma/nm12 - - return Math.exp(-0.5*f*f) -} - -module.exports = gaussian diff --git a/generate.js b/generate.js deleted file mode 100644 index e40dac1..0000000 --- a/generate.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict' - -module.exports = function generate(func, N) { - var i, args=[0,N] - var signal = new Array(N); - - for(i=2; i=0; i--) { - args[0] = i - signal[i] = func.apply(null,args) - } - return signal; -} - diff --git a/hamming.js b/hamming.js deleted file mode 100644 index c49f7d1..0000000 --- a/hamming.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict' - -function hamming (i,N) { - return 0.54 - 0.46 * Math.cos(6.283185307179586*i/(N-1)) -} - -module.exports = hamming diff --git a/hann.js b/hann.js deleted file mode 100644 index 55398ff..0000000 --- a/hann.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict' - -function hann (i,N) { - return 0.5*(1 - Math.cos(6.283185307179586*i/(N-1))) -} - -module.exports = hann diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..f192a68 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,56 @@ +/** Window function: returns sample value at index `i` of window length `N`. */ +export type WindowFunction = (i: number, N: number, ...params: number[]) => number; + +// Simple windows — no parameters +export function rectangular(i: number, N: number): number; +export function triangular(i: number, N: number): number; +export function bartlett(i: number, N: number): number; +export function welch(i: number, N: number): number; +export function connes(i: number, N: number): number; +export function hann(i: number, N: number): number; +export function hamming(i: number, N: number): number; +export function cosine(i: number, N: number): number; +export function blackman(i: number, N: number): number; +export function exactBlackman(i: number, N: number): number; +export function nuttall(i: number, N: number): number; +export function blackmanNuttall(i: number, N: number): number; +export function blackmanHarris(i: number, N: number): number; +export function flatTop(i: number, N: number): number; +export function bartlettHann(i: number, N: number): number; +export function lanczos(i: number, N: number): number; +export function parzen(i: number, N: number): number; +export function bohman(i: number, N: number): number; + +// Parameterized windows +export function powerOfSine(i: number, N: number, alpha?: number): number; +export function kaiser(i: number, N: number, beta?: number): number; +export function gaussian(i: number, N: number, sigma?: number): number; +export function generalizedNormal(i: number, N: number, sigma?: number, p?: number): number; +export function tukey(i: number, N: number, alpha?: number): number; +export function planckTaper(i: number, N: number, epsilon?: number): number; +export function exponential(i: number, N: number, tau?: number): number; +export function hannPoisson(i: number, N: number, alpha?: number): number; +export function cauchy(i: number, N: number, alpha?: number): number; +export function rifeVincent(i: number, N: number, order?: number): number; +export function confinedGaussian(i: number, N: number, sigmaT?: number): number; + +// Array-computed windows (cached) +export function kaiserBesselDerived(i: number, N: number, beta?: number): number; +export function dolphChebyshev(i: number, N: number, attenuation?: number): number; +export function taylor(i: number, N: number, nbar?: number, sll?: number): number; +export function dpss(i: number, N: number, W?: number): number; +export function ultraspherical(i: number, N: number, mu?: number, xmu?: number): number; + +// Utilities +/** Generate a full window as Float64Array. */ +export function generate(fn: WindowFunction, N: number, ...params: number[]): Float64Array; +/** Apply window to a signal in-place, returns the same array. */ +export function apply(signal: T, fn: WindowFunction, ...params: number[]): T; + +// Metrics +/** Equivalent noise bandwidth in frequency bins. Rectangular = 1.0, Hann ≈ 1.5. */ +export function enbw(fn: WindowFunction, N: number, ...params: number[]): number; +/** Worst-case amplitude error in dB when a tone falls between DFT bins. */ +export function scallopLoss(fn: WindowFunction, N: number, ...params: number[]): number; +/** COLA deviation at given hop size. Returns 0 for perfect constant overlap-add. */ +export function cola(fn: WindowFunction, N: number, hop: number, ...params: number[]): number; diff --git a/index.js b/index.js index b70259b..ff66cad 100644 --- a/index.js +++ b/index.js @@ -1,21 +1,348 @@ -'use strict' - -module.exports = { - lanczos: require('./lanczos'), - rectangular: require('./rectangular'), - triangular: require('./triangular'), - bartlett: require('./bartlett'), - bartlettHann: require('./bartlett-hann'), - welch: require('./welch'), - hann: require('./hann'), - hamming: require('./hamming'), - blackman: require('./blackman'), - nuttall: require('./nuttall'), - blackmanNuttall: require('./blackman-nuttall'), - blackmanHarris: require('./blackman-harris'), - exactBlackman: require('./exact-blackman'), - flatTop: require('./flat-top'), - cosine: require('./cosine'), - gaussian: require('./gaussian'), - tukey: require('./tukey') +/** + * Window functions for signal processing and spectral analysis. + * + * Per-sample: fn(i, N, ...params) → number, where i ∈ [0, N-1]. + * Full array: generate(fn, N, ...params) → Float64Array. + * In-place: apply(signal, fn, ...params) → signal. + * + * @module window-function + */ + +let {cos, sin, abs, exp, sqrt, PI, cosh, acosh, acos, pow} = Math +let PI2 = 2 * PI + +// ────── Rectangular family ────── + +export function rectangular () { return 1 } + +export function triangular (i, N) { return 1 - abs((2 * i - N + 1) / N) } + +export function bartlett (i, N) { return 1 - abs((2 * i - N + 1) / (N - 1)) } + +export function welch (i, N) { let x = (2 * i - N + 1) / (N - 1); return 1 - x * x } + +export function connes (i, N) { let x = (2 * i - N + 1) / (N - 1); return (1 - x * x) * (1 - x * x) } + +// ────── Cosine-sum: w(i) = Σ aₖ cos(2πki/(N-1)) ────── + +function _cosineSum (i, N, a) { + let f = PI2 * i / (N - 1), v = a[0] + for (let k = 1; k < a.length; k++) v += (k % 2 ? -1 : 1) * a[k] * cos(k * f) + return v +} + +export function hann (i, N) { return 0.5 - 0.5 * cos(PI2 * i / (N - 1)) } +export function hamming (i, N) { return 0.54 - 0.46 * cos(PI2 * i / (N - 1)) } +export function cosine (i, N) { return sin(PI * i / (N - 1)) } + +/** Power-of-sine (α=0: rectangular, α=1: cosine, α=2: hann). */ +export function powerOfSine (i, N, alpha) { + if (alpha == null) alpha = 2 + return pow(sin(PI * i / (N - 1)), alpha) +} +export function blackman (i, N) { return _cosineSum(i, N, [0.42, 0.5, 0.08]) } +export function exactBlackman (i, N) { return _cosineSum(i, N, [0.42659, 0.49656, 0.076849]) } +export function nuttall (i, N) { return _cosineSum(i, N, [0.355768, 0.487396, 0.144232, 0.012604]) } +export function blackmanNuttall (i, N) { return _cosineSum(i, N, [0.3635819, 0.4891775, 0.1365995, 0.0106411]) } +export function blackmanHarris (i, N) { return _cosineSum(i, N, [0.35875, 0.48829, 0.14128, 0.01168]) } +export function flatTop (i, N) { return _cosineSum(i, N, [1, 1.93, 1.29, 0.388, 0.028]) } + +export function bartlettHann (i, N) { + let x = i / (N - 1) + return 0.62 - 0.48 * abs(x - 0.5) - 0.38 * cos(PI2 * x) +} + +// ────── Parameterized ────── + +export function kaiser (i, N, beta) { + if (beta == null) beta = 8.6 + let x = (2 * i - N + 1) / (N - 1) + return _i0(beta * sqrt(1 - x * x)) / _i0(beta) +} + +export function gaussian (i, N, sigma) { + if (sigma == null) sigma = 0.4 + let x = (2 * i - N + 1) / (sigma * (N - 1)) + return exp(-0.5 * x * x) +} + +/** Generalized normal (p=2: Gaussian, p→∞: rectangular). */ +export function generalizedNormal (i, N, sigma, p) { + if (sigma == null) sigma = 0.4 + if (p == null) p = 2 + let x = abs((2 * i - N + 1) / (sigma * (N - 1))) + return exp(-0.5 * pow(x, p)) +} + +export function tukey (i, N, alpha) { + if (alpha == null) alpha = 0.5 + let half = 0.5 * alpha * (N - 1) + if (half < 1e-12) return 1 + if (i <= half) return 0.5 * (1 + cos(PI * (i / half - 1))) + if (i >= (N - 1) - half) return 0.5 * (1 + cos(PI * ((N - 1 - i) / half - 1))) + return 1 +} + +export function planckTaper (i, N, epsilon) { + if (epsilon == null) epsilon = 0.1 + let eN = epsilon * (N - 1) + if (i <= 0 || i >= N - 1) return 0 + if (i < eN) return 1 / (1 + exp(eN / i - eN / (eN - i))) + if (i > (N - 1) - eN) return 1 / (1 + exp(eN / (N - 1 - i) - eN / (eN - N + 1 + i))) + return 1 +} + +// ────── Exponential family ────── + +export function exponential (i, N, tau) { + if (tau == null) tau = 1 + return exp(-abs(2 * i - N + 1) / (tau * (N - 1))) +} + +export function hannPoisson (i, N, alpha) { + if (alpha == null) alpha = 2 + return 0.5 * (1 - cos(PI2 * i / (N - 1))) * exp(-alpha * abs(2 * i - N + 1) / (N - 1)) +} + +export function cauchy (i, N, alpha) { + if (alpha == null) alpha = 3 + let x = alpha * (2 * i - N + 1) / (N - 1) + return 1 / (1 + x * x) +} + +// ────── Sinc-based ────── + +export function lanczos (i, N) { + let x = 2 * i / (N - 1) - 1 + return x === 0 ? 1 : sin(PI * x) / (PI * x) +} + +// ────── B-spline / polynomial ────── + +export function parzen (i, N) { + let a = abs((2 * i - N + 1) / (N - 1)) + if (a <= 0.5) return 1 - 6 * a * a * (1 - a) + let b = 1 - a + return 2 * b * b * b +} + +export function bohman (i, N) { + let a = abs((2 * i - N + 1) / (N - 1)) + if (a >= 1) return 0 + return (1 - a) * cos(PI * a) + sin(PI * a) / PI +} + +/** Rife-Vincent class I (order=1: Hann, order=2: 4th power-of-sine, order=3+: minimum high-order sidelobes). */ +export function rifeVincent (i, N, order) { + if (order == null) order = 1 + // Class I coefficients scaled for unity average (Rife & Vincent 1970) + let a + if (order === 1) a = [1, 1] + else if (order === 2) a = [1, 4 / 3, 1 / 3] + else if (order === 3) a = [1, 1.5, 0.6, 0.1] + else throw new RangeError('rifeVincent: order must be 1, 2, or 3') + let f = PI2 * i / (N - 1), v = a[0] + for (let k = 1; k < a.length; k++) v += (k % 2 ? -1 : 1) * a[k] * cos(k * f) + // Scale to peak=1 + let peak = a.reduce((s, c) => s + c, 0) + return v / peak +} + +/** Approximate confined Gaussian (optimal RMS time-frequency, Starosielec 2014). */ +export function confinedGaussian (i, N, sigmaT) { + if (sigmaT == null) sigmaT = 0.1 + let L = N + 1, half = (N - 1) / 2 + function G (x) { let t = (x - half) / (2 * L * sigmaT); return exp(-t * t) } + let gn = G(i), gh = G(-0.5) + return gn - gh * (G(i + L) + G(i - L)) / (G(-0.5 + L) + G(-0.5 - L)) +} + +// ────── Array-computed (IDFT / eigenvalue) ────── +// These compute the full window array, cache last result per function. +// Same export shape as simple windows — plain `export function`. + +/** Kaiser-Bessel derived (MDCT/AAC/Vorbis, Princen-Bradley). N must be even. */ +export function kaiserBesselDerived (i, N, beta) { + if (beta == null) beta = 8.6 + let c = kaiserBesselDerived + if (c._N !== N || c._b !== beta) { + let h = N / 2, k = new Float64Array(h + 1), s = 0 + for (let j = 0; j <= h; j++) { let x = (2 * j - h) / h; s += _i0(beta * sqrt(abs(1 - x * x))); k[j] = s } + for (let j = 0; j <= h; j++) k[j] /= k[h] + let w = new Float64Array(N) + for (let j = 0; j < h; j++) w[j] = sqrt(k[j]) + for (let j = h; j < N; j++) w[j] = sqrt(k[N - 1 - j]) + c._w = w; c._N = N; c._b = beta + } + return c._w[i] +} + +/** Dolph-Chebyshev (equiripple sidelobes at specified attenuation dB). */ +export function dolphChebyshev (i, N, attenuation) { + if (attenuation == null) attenuation = 100 + let c = dolphChebyshev + if (c._N !== N || c._a !== attenuation) { + let ord = N - 1, b = cosh(acosh(pow(10, attenuation / 20)) / ord) + let W = new Float64Array(N) + for (let k = 0; k < N; k++) { + let x = b * cos(PI * k / N) + let T = abs(x) <= 1 ? cos(ord * acos(x)) : cosh(ord * acosh(abs(x))) + W[k] = (k % 2 ? -1 : 1) * T + } + let w = new Float64Array(N) + for (let n = 0; n < N; n++) { + let s = W[0] + for (let k = 1; k < N; k++) s += 2 * W[k] * cos(PI2 * k * n / N) + w[n] = s + } + c._w = _normalize(w); c._N = N; c._a = attenuation + } + return c._w[i] +} + +/** Taylor (monotonically decreasing sidelobes, radar/SAR standard). */ +export function taylor (i, N, nbar, sll) { + if (nbar == null) nbar = 4 + if (sll == null) sll = 30 + let c = taylor + if (c._N !== N || c._nb !== nbar || c._s !== sll) { + let A = acosh(pow(10, sll / 20)) / PI + let s2 = nbar * nbar / (A * A + (nbar - 0.5) * (nbar - 0.5)) + let Fm = new Float64Array(nbar - 1) + for (let m = 1; m < nbar; m++) { + let num = 1, den = 1 + for (let n = 1; n < nbar; n++) { + num *= 1 - m * m * s2 / (A * A + (n - 0.5) * (n - 0.5)) + if (n !== m) den *= 1 - m * m / (n * n) + } + Fm[m - 1] = (m % 2 ? 1 : -1) * num / (2 * den) + } + let w = new Float64Array(N) + for (let n = 0; n < N; n++) { + let v = 1 + for (let m = 1; m < nbar; m++) v += 2 * Fm[m - 1] * cos(PI2 * m * (n - (N - 1) / 2) / N) + w[n] = v + } + c._w = _normalize(w); c._N = N; c._nb = nbar; c._s = sll + } + return c._w[i] +} + +/** DPSS / Slepian (optimal energy concentration). W = half-bandwidth [0, 0.5]. */ +export function dpss (i, N, W) { + if (W == null) W = 0.1 + let c = dpss + if (c._N !== N || c._W !== W) { + let v = new Float64Array(N), m = 0 + for (let j = 0; j < N; j++) { let x = (2 * j - N + 1) / (N - 1); v[j] = exp(-5 * x * x); m += v[j] * v[j] } + m = sqrt(m) + for (let j = 0; j < N; j++) v[j] /= m + for (let iter = 0; iter < 50; iter++) { + let u = new Float64Array(N) + for (let j = 0; j < N; j++) { + let s = 2 * W * v[j] + for (let k = 0; k < N; k++) if (k !== j) s += sin(PI2 * W * (j - k)) / (PI * (j - k)) * v[k] + u[j] = s + } + m = 0 + for (let j = 0; j < N; j++) m += u[j] * u[j] + m = sqrt(m) + for (let j = 0; j < N; j++) v[j] = u[j] / m + } + let maxIdx = 0 + for (let j = 1; j < N; j++) if (abs(v[j]) > abs(v[maxIdx])) maxIdx = j + if (v[maxIdx] < 0) for (let j = 0; j < N; j++) v[j] = -v[j] + c._w = _normalize(v); c._N = N; c._W = W + } + return c._w[i] +} + +/** Ultraspherical / Gegenbauer (mu=0: Dolph-Chebyshev, mu=1: Saramaki). Streit 1984. */ +export function ultraspherical (i, N, mu, xmu) { + if (mu == null) mu = 1 + if (xmu == null) xmu = 1 + let c = ultraspherical + if (c._N !== N || c._mu !== mu || c._xmu !== xmu) { + let ord = N - 1, w = new Float64Array(N) + for (let n = 0; n < N; n++) { + let s = _gegen(ord, mu, xmu) + for (let k = 1; k < N; k++) { + let x = xmu * cos(PI * k / N) + s += 2 * (k % 2 ? -1 : 1) * _gegen(ord, mu, x) * cos(PI2 * k * n / N) + } + w[n] = s + } + c._w = _normalize(w); c._N = N; c._mu = mu; c._xmu = xmu + } + return c._w[i] +} + +// ────── Utilities ────── + +/** Generate a full window as Float64Array. */ +export function generate (fn, N, ...params) { + let w = new Float64Array(N) + for (let i = 0; i < N; i++) w[i] = fn(i, N, ...params) + return w +} + +/** Apply window to a signal in-place. */ +export function apply (signal, fn, ...params) { + for (let i = 0, N = signal.length; i < N; i++) signal[i] *= fn(i, N, ...params) + return signal +} + +/** Equivalent noise bandwidth in frequency bins. Lower = less noise leakage. Rectangular = 1.0, Hann ≈ 1.5. */ +export function enbw (fn, N, ...params) { + let s = 0, s2 = 0 + for (let i = 0; i < N; i++) { let v = fn(i, N, ...params); s += v; s2 += v * v } + return N * s2 / (s * s) +} + +/** Worst-case amplitude error in dB when a tone falls between DFT bins. Rectangular ≈ 3.92, flat-top ≈ 0. */ +export function scallopLoss (fn, N, ...params) { + let s = 0, re = 0, im = 0 + for (let i = 0; i < N; i++) { let v = fn(i, N, ...params); s += v; re += v * cos(PI * i / N); im -= v * sin(PI * i / N) } + return s === 0 ? Infinity : -20 * Math.log10(sqrt(re * re + im * im) / abs(s)) +} + +/** COLA (Constant Overlap-Add) deviation. Returns max relative deviation from constant sum; 0 = perfect reconstruction. */ +export function cola (fn, N, hop, ...params) { + let win = generate(fn, N, ...params) + let sums = new Float64Array(hop) + for (let t = 0; t < hop; t++) for (let k = t; k < N; k += hop) sums[t] += win[k] + let mean = 0 + for (let t = 0; t < hop; t++) mean += sums[t] + mean /= hop + if (mean === 0) return Infinity + let maxDev = 0 + for (let t = 0; t < hop; t++) { let d = abs(sums[t] - mean) / mean; if (d > maxDev) maxDev = d } + return maxDev +} + +// ────── Private ────── + +function _i0 (x) { + let s = 1, t = 1 + for (let k = 1; k <= 25; k++) { t *= (x / (2 * k)) * (x / (2 * k)); s += t; if (t < 1e-15 * s) break } + return s +} + +/** Gegenbauer (ultraspherical) polynomial C_n^mu(x) via recurrence. */ +function _gegen (n, mu, x) { + if (n === 0) return 1 + if (n === 1) return 2 * mu * x + let c0 = 1, c1 = 2 * mu * x + for (let k = 2; k <= n; k++) { + let c2 = (2 * x * (k + mu - 1) * c1 - (k + 2 * mu - 2) * c0) / k + c0 = c1; c1 = c2 + } + return c1 +} + +function _normalize (w) { + let peak = 0 + for (let i = 0; i < w.length; i++) if (abs(w[i]) > peak) peak = abs(w[i]) + if (peak > 0) for (let i = 0; i < w.length; i++) w[i] /= peak + return w } diff --git a/lanczos.js b/lanczos.js deleted file mode 100644 index d95d0bc..0000000 --- a/lanczos.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict' - -function sinc (x) { - return x === 0 ? 1 : 0.3183098861837907 * Math.sin(Math.PI*x) / x -} - -function lanczos (i, N) { - return sinc(2*i/(N-1)-1) -} - -module.exports = lanczos diff --git a/nuttall.js b/nuttall.js deleted file mode 100644 index 8bdab5f..0000000 --- a/nuttall.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict' - -var TWOPI = Math.PI * 2 - -function nuttall (i,N) { - var a0 = 0.355768, - a1 = 0.487396, - a2 = 0.144232, - a3 = 0.012604, - f = TWOPI*i/(N-1) - - return a0 - a1*Math.cos(f) +a2*Math.cos(2*f) - a3*Math.cos(3*f) -} - -module.exports = nuttall diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..1fc0231 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,26 @@ +{ + "name": "window-function", + "version": "3.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "window-function", + "version": "3.0.0", + "license": "MIT", + "devDependencies": { + "fourier-transform": "^2.2.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/fourier-transform": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/fourier-transform/-/fourier-transform-2.2.0.tgz", + "integrity": "sha512-IMFCIoZycT+8btxocpqAfVBpLtfLgQ/Y0w8KvX4iPT8C2Y+aAXz49kK4Gvaogfqz0lZiBpwAk2z10Grfyg5Buw==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/package.json b/package.json index ec67c26..dddf804 100644 --- a/package.json +++ b/package.json @@ -1,30 +1,43 @@ { "name": "window-function", - "version": "2.0.2", - "description": "Window functions for spectral analysis", - "main": "index.js", + "version": "3.0.0", + "type": "module", + "description": "Window functions for signal processing and spectral analysis", + "exports": "./index.js", + "types": "./index.d.ts", "scripts": { - "test": "node test", - "generate-plots": "node docs/generate.js" + "test": "node --test test.js" + }, + "engines": { + "node": ">=18" }, "repository": { "type": "git", "url": "git://github.com/scijs/window-function.git" }, + "homepage": "https://github.com/scijs/window-function", "keywords": [ - "complex", - "scijs", "window-function", - "math", - "fourier", + "window", + "apodization", + "hann", + "hamming", + "blackman", + "kaiser", + "dpss", "spectral-analysis", - "dsp" + "fft", + "fft-window", + "dsp", + "signal-processing", + "stft" ], "author": "Ricky Reusser", + "contributors": [ + "Dmitry Iv. " + ], "license": "MIT", "devDependencies": { - "jshint": "^2.7.0", - "plotly": "^1.0.2", - "tape": "^4.9.1" + "fourier-transform": "^2.2.0" } } diff --git a/rectangular.js b/rectangular.js deleted file mode 100644 index ab6d0cd..0000000 --- a/rectangular.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict' - -function rectangular (i,N) { - return 1 -} - -module.exports = rectangular diff --git a/sinc.js b/sinc.js deleted file mode 100644 index e69de29..0000000 diff --git a/test.js b/test.js index f0370b8..ba3d29b 100644 --- a/test.js +++ b/test.js @@ -1,68 +1,246 @@ -'use strict' - -var wfunc = require('./') -var applyWindow = require('./apply') -var generateWindow = require('./generate') -var t = require('tape') - - -var windows = [ - 'rectangular', - 'triangular', - 'bartlett', - 'welch', - 'hann', - 'hamming', - 'blackman', - 'exactBlackman', - 'nuttall', - 'blackmanNuttall', - 'blackmanHarris', - 'flatTop', - 'bartlettHann', - 'cosine', - 'lanczos' +import { test } from 'node:test' +import assert from 'node:assert' +import * as w from './index.js' + +let N = 101, eps = 1e-10 + +function near (a, b, tol) { return Math.abs(a - b) < (tol || eps) } + +let simple = [ + 'rectangular', 'triangular', 'bartlett', 'welch', 'connes', + 'hann', 'hamming', 'cosine', 'blackman', 'exactBlackman', + 'nuttall', 'blackmanNuttall', 'blackmanHarris', 'flatTop', 'bartlettHann', + 'lanczos', 'parzen', 'bohman' ] -t('window functions return a finite number',function(t) { +// ────── Finite values ────── + +test('simple windows — finite at multiple positions', () => { + for (let name of simple) + for (let i of [0, 1, Math.floor(N / 2), N - 2, N - 1]) + assert.ok(isFinite(w[name](i, N)), `${name}(${i}, ${N})`) +}) + +test('parameterized windows — finite', () => { + let cases = [ + ['kaiser', 8], ['gaussian', 0.4], ['tukey', 0.5], ['planckTaper', 0.1], + ['exponential', 1], ['hannPoisson', 2], ['cauchy', 3], + ['powerOfSine', 2], ['rifeVincent', 2], ['confinedGaussian', 0.1] + ] + for (let [name, ...p] of cases) + for (let i of [0, 1, Math.floor(N / 2), N - 2, N - 1]) + assert.ok(isFinite(w[name](i, N, ...p)), `${name}(${i})`) +}) + +test('array-based windows — finite', () => { + assert.ok(isFinite(w.kaiserBesselDerived(50, 100, 8))) + assert.ok(isFinite(w.dolphChebyshev(50, N, 80))) + assert.ok(isFinite(w.taylor(50, N, 4, 30))) + assert.ok(isFinite(w.dpss(50, N, 0.1))) + assert.ok(isFinite(w.ultraspherical(10, 31, 0.01, 1.5))) +}) + +// ────── Edge cases ────── + +test('N=1', () => { + assert.strictEqual(w.rectangular(0, 1), 1) + assert.ok(isFinite(w.triangular(0, 1))) +}) + +test('N=2 — minimal', () => { + assert.strictEqual(w.rectangular(0, 2), 1) + assert.ok(near(w.hann(0, 2), 0)) + assert.ok(near(w.hann(1, 2), 0)) + assert.ok(w.hamming(0, 2) > 0.05, 'hamming nonzero at endpoints') +}) + +test('boundary i=0 and i=N-1', () => { + assert.ok(near(w.hann(0, N), 0), 'hann start') + assert.ok(near(w.hann(N - 1, N), 0), 'hann end') + assert.ok(w.hamming(0, N) > 0.05, 'hamming nonzero start') + assert.ok(near(w.bartlett(0, N), 0), 'bartlett start') + assert.ok(near(w.bartlett(N - 1, N), 0), 'bartlett end') +}) + +// ────── Shape tests ────── + +test('hann — zero endpoints, unit center', () => { + assert.ok(near(w.hann(0, 5), 0)) + assert.ok(near(w.hann(2, 5), 1)) + assert.ok(near(w.hann(4, 5), 0)) +}) + +test('kaiser — unit center, near-zero edges', () => { + assert.ok(near(w.kaiser(50, N, 8), 1)) + assert.ok(w.kaiser(0, N, 8) < 0.1) +}) + +test('rectangular — constant 1', () => { + for (let i = 0; i < 10; i++) assert.strictEqual(w.rectangular(i, 10), 1) +}) + +test('connes = welch\u00b2', () => { + for (let i of [0, 25, 50, 75, 100]) { + let v = w.welch(i, N) + assert.ok(near(w.connes(i, N), v * v), `i=${i}`) + } +}) + +test('exponential — unit center, decays', () => { + assert.ok(near(w.exponential(50, N, 1), 1)) + assert.ok(w.exponential(0, N, 1) < 0.5) +}) + +test('flatTop — center exceeds 1.0 (by design)', () => { + let center = w.flatTop(50, N) + assert.ok(center > 4 && center < 5, `flatTop center = ${center}`) +}) + +test('dolphChebyshev — symmetric, unit center', () => { + assert.ok(near(w.dolphChebyshev(50, N, 80), 1, 1e-8)) + assert.ok(near(w.dolphChebyshev(10, N, 80), w.dolphChebyshev(90, N, 80), 1e-8)) +}) + +test('taylor — symmetric, normalized to 1', () => { + let win = w.generate(w.taylor, N, 4, 30) + let peak = Math.max(...win) + assert.ok(near(peak, 1, 1e-8), 'normalized peak = 1') + assert.ok(near(w.taylor(10, N, 4, 30), w.taylor(90, N, 4, 30), 1e-8), 'symmetric') +}) + +test('dpss — symmetric, unit peak, bell-shaped', () => { + assert.ok(near(w.dpss(50, N, 0.1), 1, 1e-6), 'center \u2248 1') + assert.ok(w.dpss(0, N, 0.1) < 0.5, 'edge < center') + assert.ok(near(w.dpss(10, N, 0.1), w.dpss(90, N, 0.1), 1e-8), 'symmetric') +}) + +test('kaiserBesselDerived — symmetric', () => { + let M = 100 + assert.ok(near(w.kaiserBesselDerived(25, M, 8), w.kaiserBesselDerived(74, M, 8), 1e-8)) + assert.ok(isFinite(w.kaiserBesselDerived(0, M, 8))) +}) + +// ────── Relationships ────── + +test('powerOfSine — generalizes cosine and hann', () => { + assert.ok(near(w.powerOfSine(25, N, 1), w.cosine(25, N)), 'alpha=1 \u2248 cosine') + assert.ok(near(w.powerOfSine(25, N, 2), w.hann(25, N)), 'alpha=2 \u2248 hann') +}) + +test('generalizedNormal — p=2 equals gaussian', () => { + for (let i of [0, 25, 50, 75, 100]) + assert.ok(near(w.generalizedNormal(i, N, 0.4, 2), w.gaussian(i, N, 0.4)), `i=${i}`) +}) + +test('rifeVincent — order 1 matches hann', () => { + for (let i of [0, 25, 50, 75, 100]) + assert.ok(near(w.rifeVincent(i, N, 1), w.hann(i, N)), `i=${i}`) +}) + +test('rifeVincent — throws for unsupported order', () => { + assert.throws(() => w.rifeVincent(50, N, 0), /order/) + assert.throws(() => w.rifeVincent(50, N, 4), /order/) + assert.throws(() => w.rifeVincent(50, N, -1), /order/) +}) + +test('confinedGaussian — bell-shaped', () => { + let center = w.confinedGaussian(50, N, 0.1) + let edge = w.confinedGaussian(0, N, 0.1) + assert.ok(isFinite(center) && center > edge) +}) + +test('ultraspherical — symmetric', () => { + let left = w.ultraspherical(10, 31, 0.01, 1.5) + let right = w.ultraspherical(20, 31, 0.01, 1.5) + assert.ok(isFinite(left) && near(left, right, 1e-8)) +}) + +// ────── Symmetry ────── - var i +test('symmetry — simple windows', () => { + let all = [ + 'hann', 'hamming', 'blackman', 'exactBlackman', 'nuttall', 'blackmanNuttall', + 'blackmanHarris', 'bartlett', 'bartlettHann', 'welch', 'connes', + 'cosine', 'lanczos', 'parzen', 'bohman', 'triangular', 'flatTop' + ] + for (let name of all) { + assert.ok(near(w[name](10, N), w[name](90, N)), `${name} inner`) + assert.ok(near(w[name](0, N), w[name](N - 1, N), 1e-8), `${name} endpoints`) + } +}) - for(i=0; i { + let cases = [['kaiser'], ['gaussian'], ['tukey'], ['exponential'], ['cauchy'], ['hannPoisson']] + for (let [name] of cases) + assert.ok(near(w[name](10, N), w[name](90, N)), name) +}) - t.ok( isFinite(wfunc[windows[j]](50,101)), windows[j] ) +// ────── Utilities ────── - })(i) +test('generate — basic', () => { + let win = w.generate(w.hamming, 100) + assert.ok(win instanceof Float64Array) + assert.strictEqual(win.length, 100) +}) - } +test('generate — parameterized', () => { + let win = w.generate(w.kaiser, 100, 8) + assert.ok(win instanceof Float64Array) + assert.ok(near(win[50], w.kaiser(50, 100, 8))) +}) - t.ok( isFinite(wfunc.gaussian(50,100,0.4)), 'gaussian (alpha = 0.4)') - t.ok( isFinite(wfunc.tukey(50,100,0.4)), 'tukey (alpha = 0.5)') - t.end() +test('apply — returns same array, modifies in-place', () => { + let sig = new Float64Array(5).fill(1) + let ret = w.apply(sig, w.hann) + assert.strictEqual(ret, sig) + assert.ok(sig[0] < 0.01) + assert.ok(sig[2] > 0.99) }) -t('applies a window to a signal',function(t) { - var x = [1, 1, 1, 1, 1] - var y = applyWindow( x, wfunc.hamming ) - t.equal(y.length, x.length) - t.end() +test('apply — parameterized', () => { + let sig = new Float64Array(100).fill(1) + w.apply(sig, w.kaiser, 8) + assert.ok(near(sig[50], w.kaiser(50, 100, 8))) }) -t('passes extra arguments to a window function',function(t) { - var x = [1, 1, 1, 1, 1] - applyWindow( x, wfunc.gaussian, 0.1 ) - t.ok( isFinite(x[0]), 'samples are finite' ) - t.ok( x[0] < 1e-8, 'samples have been windowed' ) - t.end() +// ────── Metrics ────── + +test('enbw — rectangular = 1.0', () => { + assert.ok(near(w.enbw(w.rectangular, 1024), 1.0, 0.001)) }) -t('constructs an window function array',function(t) { - var x = generateWindow( wfunc.hamming, 100 ) - t.ok( Array.isArray(x) ) - t.equal( x.length, 100 ) - t.ok( isFinite(x[50]) ) - t.end() +test('enbw — hann \u2248 1.5', () => { + let e = w.enbw(w.hann, 1024) + assert.ok(near(e, 1.5, 0.01), `got ${e}`) }) +test('enbw — blackmanHarris > hann', () => { + assert.ok(w.enbw(w.blackmanHarris, 1024) > w.enbw(w.hann, 1024)) +}) + +test('scallopLoss — rectangular \u2248 3.92 dB', () => { + let s = w.scallopLoss(w.rectangular, 4096) + assert.ok(near(s, 3.92, 0.1), `got ${s}`) +}) +test('scallopLoss — flatTop \u2248 0 dB', () => { + assert.ok(w.scallopLoss(w.flatTop, 4096) < 0.1) +}) + +test('scallopLoss — hann < rectangular', () => { + assert.ok(w.scallopLoss(w.hann, 1024) < w.scallopLoss(w.rectangular, 1024)) +}) + +test('cola — hann 50% overlap \u2248 perfect', () => { + // Odd N for exact COLA with symmetric convention + assert.ok(w.cola(w.hann, 1025, 512) < 1e-10) +}) + +test('cola — rectangular hop=N = perfect', () => { + assert.ok(w.cola(w.rectangular, 100, 100) < 1e-10) +}) + +test('cola — non-COLA detection', () => { + // Blackman at 50% overlap is not COLA + assert.ok(w.cola(w.blackman, 1024, 512) > 0.01) +}) diff --git a/triangular.js b/triangular.js deleted file mode 100644 index e874661..0000000 --- a/triangular.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict' - -function triangular (i,N) { - return 1 - Math.abs( 2 * (i - 0.5*(N-1)) / N ) -} - -module.exports = triangular diff --git a/tukey.js b/tukey.js deleted file mode 100644 index 111a1bc..0000000 --- a/tukey.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict' - -function tukey (i,N, alpha) { - var anm12 = 0.5*alpha*(N-1) - - if( i <= anm12 ) { - return 0.5*(1+Math.cos(Math.PI*(i/anm12 - 1))) - } else if ( i < (N-1)*(1-0.5*alpha) ) { - return 1 - } else { - return 0.5*(1+Math.cos(Math.PI*(i/anm12 - 2/alpha + 1))) - } -} - -module.exports = tukey diff --git a/welch.js b/welch.js deleted file mode 100644 index 0ff7261..0000000 --- a/welch.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict' - -function welch (i,N) { - var nm12 = 0.5*(N-1), - f = (i - nm12)/nm12 - return 1 - f*f -} - -module.exports = welch From 950a2985b8600c8b38281a7cf4d40b0723726644 Mon Sep 17 00:00:00 2001 From: "Dmitry Iv." Date: Wed, 25 Mar 2026 23:42:33 -0400 Subject: [PATCH 02/26] Split by files --- README.md | 557 +++++++++++++++++++++++++++++------------ _util.js | 32 +++ apply.js | 4 + bartlett.js | 2 + bartlettHann.js | 5 + blackman.js | 2 + blackmanHarris.js | 2 + blackmanNuttall.js | 2 + bohman.js | 6 + cauchy.js | 5 + cola.js | 14 ++ confinedGaussian.js | 8 + connes.js | 1 + cosine.js | 2 + dolphChebyshev.js | 22 ++ dpss.js | 28 +++ enbw.js | 5 + exactBlackman.js | 2 + exponential.js | 5 + flatTop.js | 2 + gaussian.js | 6 + generalizedNormal.js | 7 + generate.js | 5 + hamming.js | 2 + hann.js | 2 + hannPoisson.js | 5 + index.js | 384 ++++------------------------ kaiser.js | 6 + kaiserBesselDerived.js | 15 ++ lanczos.js | 5 + nuttall.js | 2 + package.json | 44 +++- parzen.js | 7 + planckTaper.js | 9 + powerOfSine.js | 5 + rectangular.js | 1 + rifeVincent.js | 13 + scallopLoss.js | 6 + taylor.js | 27 ++ triangular.js | 2 + tukey.js | 9 + ultraspherical.js | 19 ++ welch.js | 1 + 43 files changed, 782 insertions(+), 506 deletions(-) create mode 100644 _util.js create mode 100644 apply.js create mode 100644 bartlett.js create mode 100644 bartlettHann.js create mode 100644 blackman.js create mode 100644 blackmanHarris.js create mode 100644 blackmanNuttall.js create mode 100644 bohman.js create mode 100644 cauchy.js create mode 100644 cola.js create mode 100644 confinedGaussian.js create mode 100644 connes.js create mode 100644 cosine.js create mode 100644 dolphChebyshev.js create mode 100644 dpss.js create mode 100644 enbw.js create mode 100644 exactBlackman.js create mode 100644 exponential.js create mode 100644 flatTop.js create mode 100644 gaussian.js create mode 100644 generalizedNormal.js create mode 100644 generate.js create mode 100644 hamming.js create mode 100644 hann.js create mode 100644 hannPoisson.js create mode 100644 kaiser.js create mode 100644 kaiserBesselDerived.js create mode 100644 lanczos.js create mode 100644 nuttall.js create mode 100644 parzen.js create mode 100644 planckTaper.js create mode 100644 powerOfSine.js create mode 100644 rectangular.js create mode 100644 rifeVincent.js create mode 100644 scallopLoss.js create mode 100644 taylor.js create mode 100644 triangular.js create mode 100644 tukey.js create mode 100644 ultraspherical.js create mode 100644 welch.js diff --git a/README.md b/README.md index 1d15e22..c9c9e30 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,12 @@ # window-function -Complete, encyclopedic collection of window functions for signal processing and spectral analysis. +Complete collection of window functions for signal processing and spectral analysis. -**34 window functions** in a single file. Zero dependencies. Pure ESM. - -Covers every window function in scipy.signal.windows, MATLAB Signal Processing Toolbox, Harris (1978) "On the Use of Windows for Harmonic Analysis with the DFT", and the Wikipedia "Window function" article — plus scientific niche windows used in FTIR spectroscopy (Connes), gravitational wave detection (Planck-taper), audio codecs (KBD), multitaper estimation (DPSS), antenna design (Ultraspherical), and time-frequency optimization (Confined Gaussian). - - -## Why so many windows? - -Every window is a tradeoff. You can optimize for: - -- **Frequency resolution** — narrow main lobe, distinguish close frequencies -- **Spectral leakage** — low sidelobes, weak signals aren't masked by strong ones -- **Amplitude accuracy** — flat main lobe top, measured amplitudes are correct - -No single window optimizes all three. The rectangular window has the best resolution but worst leakage (-13 dB). The flat-top has the best amplitude accuracy but worst resolution. The Hann window is the go-to general-purpose compromise. Everything else exists because different measurement contexts shift the tradeoff. - -## Usage +**34 window functions** · Zero dependencies · Pure ESM · Individual imports ```js import { hann, kaiser, generate, apply } from 'window-function' +import { hann } from 'window-function/hann' hann(50, 101) // → 1.0 (single sample) generate(hann, 1024) // → Float64Array(1024) @@ -28,15 +14,25 @@ generate(kaiser, 1024, 8.6) // → parameterized window apply(signal, kaiser, 8.6) // → signal, windowed in-place ``` -Compare windows quantitatively: +## Which window should I pick? -```js -import { hann, blackmanHarris, enbw, scallopLoss, cola } from 'window-function' +Every window is a tradeoff between **frequency resolution** (narrow main lobe), **spectral leakage** (low sidelobes), and **amplitude accuracy** (flat main lobe top). No single window optimizes all three. -enbw(hann, 1024) // → 1.5 (noise bandwidth in bins) -scallopLoss(hann, 1024) // → 1.42 dB (worst-case amplitude error) -cola(hann, 1024, 512) // → 0 (perfect STFT reconstruction at 50% overlap) -``` +| I need to... | Use | Why | +|---|---|---| +| Just get started | `hann` | Good all-round, zero edges, 50% COLA | +| Design FIR filters | `kaiser` or `hamming` | Kaiser is tunable, Hamming is the classic | +| Measure amplitudes accurately | `flatTop` | < 0.01 dB scalloping loss | +| High dynamic range (>80 dB) | `blackmanHarris` | -92 dB equiripple sidelobes | +| Audio codec (MDCT) | `kaiserBesselDerived` or `cosine` | Princen-Bradley perfect reconstruction | +| Preserve center, taper edges | `tukey` | Adjustable flat-top fraction | +| Robust spectral estimation | `dpss` | Optimal for multitaper method | +| Radar / SAR | `taylor` | Monotonic sidelobes, radar standard | +| Antenna array design | `dolphChebyshev` or `ultraspherical` | Optimal equiripple or tunable taper | +| Tune resolution/leakage | `kaiser` or `gaussian` | Single-parameter adjustment | +| Modal / impact analysis | `exponential` | Controlled decay for underdamped systems | +| FTIR spectroscopy | `connes` | Smooth apodization for interferograms | +| Gravitational waves | `planckTaper` | C∞ smooth, no spectral artifacts | ## API @@ -50,206 +46,433 @@ Every window: `fn(i, N, ...params) → number` — sample `i` of window length ` | `scallopLoss(fn, N, ...params)` | window function, length, params | Worst-case amplitude error (dB) | | `cola(fn, N, hop, ...params)` | window function, length, hop, params | COLA deviation (0 = perfect) | -## Complete Window Reference +```js +import { hann, enbw, scallopLoss, cola } from 'window-function' + +enbw(hann, 1024) // → 1.5 (noise bandwidth in bins) +scallopLoss(hann, 1024) // → 1.42 dB (worst-case amplitude error) +cola(hann, 1024, 512) // → 0 (perfect STFT reconstruction at 50% overlap) +``` + +## Window Reference ### Simple — no parameters -| Window | Peak sidelobe | Rolloff | What it does | When to use | -|---|---|---|---|---| -| `rectangular` | -13 dB | -6 dB/oct | No windowing at all | Transient signals already zero at edges; harmonic analysis with integer cycles | -| `triangular` | -27 dB | -12 dB/oct | Linear taper, nonzero endpoints | Simple smoothing, 2nd-order B-spline | -| `bartlett` | -27 dB | -12 dB/oct | Linear taper, zero endpoints | Bartlett's method PSD estimation. Bartlett 1950 | -| `welch` | -21 dB | -12 dB/oct | Parabolic taper | Welch's method PSD estimation. Welch 1967 | -| `connes` | — | -24 dB/oct | Welch squared (4th power parabolic) | FTIR spectroscopy, interferogram apodization. Connes 1961 | -| `hann` | -32 dB | -18 dB/oct | Raised cosine, zero endpoints | General-purpose spectral analysis, STFT with 50% overlap (COLA). The default choice. Blackman & Tukey 1958 | -| `hamming` | -43 dB | -6 dB/oct | Raised cosine, nonzero endpoints | FIR filter design (window method), speech processing. Hamming 1977 | -| `cosine` | -23 dB | -12 dB/oct | Half-period sine | MDCT audio codecs: MP3, AAC, Vorbis. Princen & Bradley 1987 | -| `blackman` | -58 dB | -18 dB/oct | 3-term cosine sum | Spectral analysis needing better leakage than Hann. Blackman & Tukey 1958 | -| `exactBlackman` | -69 dB | -6 dB/oct | Blackman with exact zero placement | Precision analysis, zeros at 3rd/4th sidelobes. Harris 1978 | -| `nuttall` | -93 dB | -18 dB/oct | 4-term, continuous 1st derivative | High-dynamic-range analysis without edge discontinuity. Nuttall 1981 | -| `blackmanNuttall` | -98 dB | -6 dB/oct | 4-term, lowest sidelobes | Maximum sidelobe suppression among 4-term windows. Nuttall 1981 | -| `blackmanHarris` | -92 dB | -6 dB/oct | 4-term, minimum sidelobe | ADC testing, measurement instrumentation, >80 dB dynamic range. Harris 1978 | -| `flatTop` | -93 dB | -6 dB/oct | 5-term, near-zero scalloping. Peak ~4.64 | Amplitude calibration, transducer calibration (~0.01 dB accuracy). ISO 18431. Heinzel 2002 | -| `bartlettHann` | -36 dB | — | Bartlett × Hann hybrid | Balanced near/far sidelobe levels. Ha & Pearce 1989 | -| `lanczos` | -26 dB | — | Sinc main lobe | Image resampling, interpolation (FFmpeg, ImageMagick). Duchon 1979 | -| `parzen` | -53 dB | -24 dB/oct | 4th-order B-spline | Kernel density estimation, always-positive spectrum. Parzen 1961 | -| `bohman` | -46 dB | -24 dB/oct | Autocorrelation of cosine window | Fast sidelobe decay, spectral estimation | +--- + +#### `rectangular(i, N)` + +$$w(n) = 1$$ + + + +No windowing at all. Best frequency resolution, worst spectral leakage. Use for transient signals already zero at edges, or harmonic analysis with integer cycles. +**-13 dB** peak sidelobe · **-6 dB/oct** rolloff + +--- + +#### `triangular(i, N)` + +$$w(n) = 1 - \left|\frac{2n - N + 1}{N}\right|$$ + + + +Linear taper, nonzero endpoints. Simple smoothing, 2nd-order B-spline. +**-27 dB** peak sidelobe · **-12 dB/oct** rolloff + +--- + +#### `bartlett(i, N)` + +$$w(n) = 1 - \left|\frac{2n - N + 1}{N - 1}\right|$$ + + + +Linear taper, zero endpoints. Bartlett's method PSD estimation. Bartlett 1950. +**-27 dB** peak sidelobe · **-12 dB/oct** rolloff + +--- + +#### `welch(i, N)` + +$$w(n) = 1 - \left(\frac{2n - N + 1}{N - 1}\right)^2$$ + + + +Parabolic taper. Welch's method PSD estimation. Welch 1967. +**-21 dB** peak sidelobe · **-12 dB/oct** rolloff + +--- + +#### `connes(i, N)` + +$$w(n) = \left[1 - \left(\frac{2n - N + 1}{N - 1}\right)^2\right]^2$$ + + + +Welch squared (4th power parabolic). FTIR spectroscopy, interferogram apodization. Connes 1961. +**-24 dB/oct** rolloff + +--- + +#### `hann(i, N)` + +$$w(n) = 0.5 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right)$$ + + + +Raised cosine, zero endpoints. The default general-purpose choice. STFT with 50% overlap (COLA). Also called "Hanning" (misnomer). Blackman & Tukey 1958. +**-32 dB** peak sidelobe · **-18 dB/oct** rolloff + +--- + +#### `hamming(i, N)` + +$$w(n) = 0.54 - 0.46\cos\!\left(\frac{2\pi n}{N-1}\right)$$ + + + +Raised cosine, nonzero endpoints. Optimized for first sidelobe cancellation. FIR filter design, speech processing. Hamming 1977. +**-43 dB** peak sidelobe · **-6 dB/oct** rolloff + +--- + +#### `cosine(i, N)` + +$$w(n) = \sin\!\left(\frac{\pi n}{N-1}\right)$$ + + + +Half-period sine. MDCT audio codecs: MP3, AAC, Vorbis. Princen & Bradley 1987. +**-23 dB** peak sidelobe · **-12 dB/oct** rolloff + +--- + +#### `blackman(i, N)` + +$$w(n) = 0.42 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.08\cos\!\left(\frac{4\pi n}{N-1}\right)$$ + + + +3-term cosine sum. Better leakage than Hann at the cost of wider main lobe. Blackman & Tukey 1958. +**-58 dB** peak sidelobe · **-18 dB/oct** rolloff + +--- + +#### `exactBlackman(i, N)` + +$$w(n) = 0.42659 - 0.49656\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.076849\cos\!\left(\frac{4\pi n}{N-1}\right)$$ + + + +Blackman with exact zero placement at 3rd and 4th sidelobes. Harris 1978. +**-69 dB** peak sidelobe · **-6 dB/oct** rolloff + +--- + +#### `nuttall(i, N)` + +$$w(n) = 0.355768 - 0.487396\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.144232\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.012604\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$$ + + + +4-term cosine sum, continuous 1st derivative. High-dynamic-range analysis without edge discontinuity. Nuttall 1981. +**-93 dB** peak sidelobe · **-18 dB/oct** rolloff + +--- + +#### `blackmanNuttall(i, N)` + +$$w(n) = 0.3635819 - 0.4891775\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.1365995\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.0106411\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$$ + + + +4-term cosine sum, lowest sidelobes among 4-term windows. Nuttall 1981. +**-98 dB** peak sidelobe · **-6 dB/oct** rolloff + +--- + +#### `blackmanHarris(i, N)` + +$$w(n) = 0.35875 - 0.48829\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.14128\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.01168\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$$ + + + +4-term minimum sidelobe. ADC testing, measurement instrumentation, >80 dB dynamic range. Harris 1978. +**-92 dB** peak sidelobe · **-6 dB/oct** rolloff -

-rectangular -triangular -bartlett -welch -connes -hann -hamming -cosine -blackman -exactBlackman -nuttall -blackmanNuttall -blackmanHarris -flatTop -bartlettHann -lanczos -parzen -bohman -

+--- -
-Formulas +#### `flatTop(i, N)` -$$w(n) = 1 \quad \text{(rectangular)}$$ +$$w(n) = 1 - 1.93\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 1.29\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.388\cos\!\left(\frac{6\pi n}{N\!-\!1}\right) + 0.028\cos\!\left(\frac{8\pi n}{N\!-\!1}\right)$$ -$$w(n) = 1 - \left|\frac{2n - N + 1}{N}\right| \quad \text{(triangular)}$$ + -$$w(n) = 1 - \left|\frac{2n - N + 1}{N - 1}\right| \quad \text{(bartlett)}$$ +5-term cosine sum with near-zero scalloping. Peak value ~4.64 (by design — optimized for amplitude accuracy, not energy). Amplitude calibration, transducer calibration (~0.01 dB accuracy). ISO 18431. Heinzel 2002. +**-93 dB** peak sidelobe · **-6 dB/oct** rolloff -$$w(n) = 1 - \left(\frac{2n - N + 1}{N - 1}\right)^2 \quad \text{(welch)}$$ +--- -$$w(n) = \left[1 - \left(\frac{2n - N + 1}{N - 1}\right)^2\right]^2 \quad \text{(connes)}$$ +#### `bartlettHann(i, N)` -$$w(n) = 0.5 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right) \quad \text{(hann)}$$ +$$w(n) = 0.62 - 0.48\left|\frac{n}{N\!-\!1} - 0.5\right| - 0.38\cos\!\left(\frac{2\pi n}{N-1}\right)$$ -$$w(n) = 0.54 - 0.46\cos\!\left(\frac{2\pi n}{N-1}\right) \quad \text{(hamming)}$$ + -$$w(n) = \sin\!\left(\frac{\pi n}{N-1}\right) \quad \text{(cosine)}$$ +Bartlett-Hann hybrid. Balanced near/far sidelobe levels. Ha & Pearce 1989. +**-36 dB** peak sidelobe -$$w(n) = \sum_{k=0}^{K} (-1)^k\, a_k \cos\!\left(\frac{2\pi k n}{N-1}\right) \quad \text{(cosine-sum family: blackman, nuttall, etc.)}$$ +--- -$$w(n) = 0.62 - 0.48\left|\frac{n}{N-1} - 0.5\right| - 0.38\cos\!\left(\frac{2\pi n}{N-1}\right) \quad \text{(bartlettHann)}$$ +#### `lanczos(i, N)` -$$w(n) = \operatorname{sinc}\!\left(\frac{2n}{N-1} - 1\right) \quad \text{(lanczos)}$$ +$$w(n) = \operatorname{sinc}\!\left(\frac{2n}{N-1} - 1\right)$$ -$$w(n) = \begin{cases} 1 - 6a^2(1-a) & |a| \le 0.5 \\ 2(1-a)^3 & |a| > 0.5 \end{cases},\quad a = \left|\frac{2n-N+1}{N-1}\right| \quad \text{(parzen)}$$ + -$$w(n) = (1-|a|)\cos(\pi|a|) + \frac{\sin(\pi|a|)}{\pi},\quad a = \frac{2n-N+1}{N-1} \quad \text{(bohman)}$$ +Sinc main lobe. Image resampling, interpolation (FFmpeg, ImageMagick). Duchon 1979. +**-26 dB** peak sidelobe -
+--- + +#### `parzen(i, N)` + +$$w(n) = \begin{cases} 1 - 6a^2(1-a) & |a| \le 0.5 \\\ 2(1-a)^3 & |a| > 0.5 \end{cases} \quad a = \left|\frac{2n-N+1}{N-1}\right|$$ + + + +4th-order B-spline. Always-positive spectrum. Kernel density estimation. Parzen 1961. +**-53 dB** peak sidelobe · **-24 dB/oct** rolloff + +--- + +#### `bohman(i, N)` + +$$w(n) = (1-|a|)\cos(\pi|a|) + \frac{\sin(\pi|a|)}{\pi} \quad a = \frac{2n-N+1}{N-1}$$ + + + +Autocorrelation of cosine window. Fast sidelobe decay, spectral estimation. +**-46 dB** peak sidelobe · **-24 dB/oct** rolloff + +--- ### Parameterized — adjustable tradeoff -| Window | Parameters | What it does | When to use | -|---|---|---|---| -| `kaiser(i, N, beta)` | `beta`: 0→rect, 5.4→Hamming, 8.6→Blackman | Near-optimal DPSS approximation via Bessel I0 | FIR filter design — the standard parameterized window. Kaiser 1974 | -| `gaussian(i, N, sigma)` | `sigma`: width, default 0.4 | Gaussian bell, minimum time-bandwidth product | STFT/Gabor transform, frequency estimation via parabolic interpolation. Gabor 1946 | -| `generalizedNormal(i, N, sigma, p)` | `sigma`, `p`: shape (2=Gaussian, →∞=rect) | Continuous family between Gaussian and rectangular | Adjustable time-frequency tradeoff, controllable flat-top width | -| `tukey(i, N, alpha)` | `alpha`: 0→rect, 1→Hann | Flat center with cosine-tapered edges | Preserving signal amplitude while tapering edges. Vibration analysis, LIGO | -| `planckTaper(i, N, epsilon)` | `epsilon`: taper fraction, default 0.1 | C∞-smooth bump function (infinitely differentiable) | Gravitational wave analysis (LIGO/Virgo). McKechan 2010 | -| `powerOfSine(i, N, alpha)` | `alpha`: 0→rect, 1→cosine, 2→Hann | `sin^α` family | Codec design, parameterized spectral analysis | -| `exponential(i, N, tau)` | `tau`: time constant, default 1 | Exponential decay from center | Modal analysis, impact testing (compensates underdamped responses). Harris 1978 | -| `hannPoisson(i, N, alpha)` | `alpha`: ≥2 → no sidelobes | Hann × exponential product | Frequency estimators using convex optimization — unique no-sidelobe property | -| `cauchy(i, N, alpha)` | `alpha`: width, default 3 | Lorentzian 1/(1+x²) shape | Spectroscopy (matches spectral line shapes). Harris 1978 | -| `rifeVincent(i, N, order)` | `order`: 1=Hann, 2, 3 | Cosine-sum optimized for sidelobe fall-off | Power grid harmonic analysis, interpolated DFT. Rife & Vincent 1970 | -| `confinedGaussian(i, N, sigmaT)` | `sigmaT`: temporal width, default 0.1 | Optimal RMS time-frequency bandwidth | Time-frequency analysis, audio coding (MP3/AAC). Starosielec 2014 | +--- -

-kaiser -gaussian -generalizedNormal -tukey -planckTaper -powerOfSine -exponential -hannPoisson -cauchy -rifeVincent -confinedGaussian -

+#### `kaiser(i, N, beta)` -
-Formulas +`beta`: shape — 0 → rectangular, 5.4 → Hamming, **8.6** (default) → Blackman. -$$w(n) = \frac{I_0\!\left(\beta\sqrt{1 - \left(\frac{2n-N+1}{N-1}\right)^2}\right)}{I_0(\beta)} \quad \text{(kaiser)}$$ +$$w(n) = \frac{I_0\!\left(\beta\sqrt{1 - \left(\frac{2n-N+1}{N-1}\right)^2}\right)}{I_0(\beta)}$$ -$$w(n) = \exp\!\left[-\frac{1}{2}\left(\frac{2n-N+1}{\sigma(N-1)}\right)^2\right] \quad \text{(gaussian)}$$ + -$$w(n) = \exp\!\left[-\frac{1}{2}\left|\frac{2n-N+1}{\sigma(N-1)}\right|^p\right] \quad \text{(generalizedNormal)}$$ +Near-optimal DPSS approximation via Bessel I₀. The standard parameterized window for FIR filter design. Kaiser 1974. -$$w(n) = \begin{cases} \frac{1}{2}\left[1+\cos\!\left(\pi\!\left(\frac{n}{\alpha(N-1)/2}-1\right)\right)\right] & n \le \frac{\alpha(N-1)}{2} \\ 1 & \text{center} \\ \text{symmetric} & n \ge N-1-\frac{\alpha(N-1)}{2} \end{cases} \quad \text{(tukey)}$$ +--- -$$w(n) = \sin^\alpha\!\left(\frac{\pi n}{N-1}\right) \quad \text{(powerOfSine)}$$ +#### `gaussian(i, N, sigma)` -$$w(n) = \exp\!\left(\frac{-|2n-N+1|}{\tau(N-1)}\right) \quad \text{(exponential)}$$ +`sigma`: width, default **0.4**. -$$w(n) = \frac{1}{2}\left(1-\cos\frac{2\pi n}{N-1}\right)\exp\!\left(\frac{-\alpha|2n-N+1|}{N-1}\right) \quad \text{(hannPoisson)}$$ +$$w(n) = \exp\!\left[-\frac{1}{2}\left(\frac{2n-N+1}{\sigma(N-1)}\right)^2\right]$$ -$$w(n) = \frac{1}{1+\left(\frac{\alpha(2n-N+1)}{N-1}\right)^2} \quad \text{(cauchy)}$$ + -$$w(n) = \frac{1}{Z}\sum_{k=0}^{K}(-1)^k a_k\cos\frac{2\pi kn}{N-1} \quad \text{(rifeVincent, class I)}$$ +Gaussian bell, minimum time-bandwidth product. STFT/Gabor transform, frequency estimation via parabolic interpolation. Gabor 1946. -
+--- -### Array-computed — require full-window computation (cached) +#### `generalizedNormal(i, N, sigma, p)` -| Window | Parameters | What it does | When to use | -|---|---|---|---| -| `dolphChebyshev(i, N, dB)` | `dB`: sidelobe attenuation, default 100 | Optimal: narrowest main lobe for given equiripple sidelobe level | Antenna array design, radar beam patterns. Dolph 1946 | -| `taylor(i, N, nbar, sll)` | `nbar`: constant lobes (4), `sll`: level dB (30) | Dolph-Chebyshev variant with monotonically decreasing sidelobes | Radar, SAR image formation — the radar community standard. Taylor 1955 | -| `kaiserBesselDerived(i, N, beta)` | `beta`: shape, default 8.6. N must be even | Princen-Bradley condition for perfect MDCT reconstruction | AAC, Vorbis, Opus audio codecs (long blocks). Princen & Bradley 1987 | -| `dpss(i, N, W)` | `W`: half-bandwidth [0, 0.5], default 0.1 | Provably optimal energy concentration in frequency band | Thomson multitaper spectral estimation, neuroscience (EEG/MEG), climate science. Slepian 1978 | -| `ultraspherical(i, N, mu, xmu)` | `mu`: 0→Dolph-Cheb, 1→Saramaki; `xmu`: sidelobe control | Gegenbauer polynomial window — independent sidelobe level and taper rate | Advanced antenna design, beamforming. Streit 1984 | +`sigma`: width (default **0.4**), `p`: shape — 2 = Gaussian, →∞ = rectangular. -

-dolphChebyshev -taylor -kaiserBesselDerived -dpss -ultraspherical -

+$$w(n) = \exp\!\left[-\frac{1}{2}\left|\frac{2n-N+1}{\sigma(N-1)}\right|^p\right]$$ -
-Formulas + -$$W(k) = (-1)^k T_{N-1}\!\left(\beta\cos\frac{\pi k}{N}\right),\quad w = \operatorname{IDFT}(W) \quad \text{(dolphChebyshev)}$$ +Continuous family between Gaussian and rectangular. Adjustable time-frequency tradeoff. -$$w(n) = 1 + 2\sum_{m=1}^{\bar{n}-1} F_m \cos\frac{2\pi m(n-(N-1)/2)}{N} \quad \text{(taylor)}$$ +--- -$$w(n) = \sqrt{\frac{\sum_{j=0}^{n} K(j)}{\sum_{j=0}^{N/2} K(j)}},\quad K(j) = I_0\!\left(\beta\sqrt{1-\left(\frac{2j-N/2}{N/2}\right)^2}\right) \quad \text{(kaiserBesselDerived)}$$ +#### `tukey(i, N, alpha)` -$$\mathbf{T}\mathbf{v} = \lambda\mathbf{v},\quad T_{jk} = \frac{\sin 2\pi W(j-k)}{\pi(j-k)} \quad \text{(dpss — dominant eigenvector)}$$ +`alpha`: taper fraction — 0 → rectangular, **0.5** (default), 1 → Hann. -$$W(k) = C_n^\mu\!\left(x_\mu\cos\frac{\pi k}{N}\right),\quad w = \operatorname{IDFT}(W) \quad \text{(ultraspherical)}$$ +$$w(n) = \begin{cases} \frac{1}{2}\left[1+\cos\!\left(\pi\!\left(\frac{n}{\alpha(N\!-\!1)/2}-1\right)\right)\right] & n \le \frac{\alpha(N\!-\!1)}{2} \\\ 1 & \text{center} \\\ \text{symmetric} & n \ge N\!-\!1\!-\!\frac{\alpha(N\!-\!1)}{2} \end{cases}$$ -
+ -## Which window should I pick? +Flat center with cosine-tapered edges. Preserves signal amplitude while tapering. Vibration analysis, LIGO. -| I need to... | Use | Why | -|---|---|---| -| Just get started | `hann` | Good all-round, zero edges, 50% COLA | -| Design FIR filters | `kaiser` or `hamming` | Kaiser is tunable, Hamming is the classic | -| Measure amplitudes accurately | `flatTop` | < 0.01 dB scalloping loss | -| High dynamic range (>80 dB) | `blackmanHarris` | -92 dB equiripple sidelobes | -| Audio codec (MDCT) | `kaiserBesselDerived` or `cosine` | Princen-Bradley perfect reconstruction | -| Preserve center, taper edges | `tukey` | Adjustable flat-top fraction | -| Robust spectral estimation | `dpss` | Optimal for multitaper method | -| Frequency estimation via optimization | `hannPoisson` | Monotonically decreasing (convex) transform | -| Radar / SAR | `taylor` | Monotonic sidelobes, radar standard | -| Antenna array design | `dolphChebyshev` or `ultraspherical` | Optimal equiripple or tunable taper | -| Tune resolution/leakage continuously | `kaiser` or `gaussian` | Single-parameter adjustment | -| Modal / impact analysis | `exponential` | Controlled decay for underdamped systems | -| FTIR spectroscopy | `connes` | Smooth apodization for interferograms | -| Gravitational waves | `planckTaper` | C∞ smooth, no spectral artifacts | +--- -## Quantitative metrics +#### `planckTaper(i, N, epsilon)` + +`epsilon`: taper fraction, default **0.1**. + + + +C∞-smooth bump function (infinitely differentiable). Gravitational wave analysis (LIGO/Virgo). McKechan 2010. + +--- + +#### `powerOfSine(i, N, alpha)` + +`alpha`: exponent — 0 → rectangular, 1 → cosine, **2** (default) → Hann. + +$$w(n) = \sin^\alpha\!\left(\frac{\pi n}{N-1}\right)$$ + + + +sin^α family. Codec design, parameterized spectral analysis. + +--- + +#### `exponential(i, N, tau)` + +`tau`: time constant, default **1**. + +$$w(n) = \exp\!\left(\frac{-|2n-N+1|}{\tau(N-1)}\right)$$ + + + +Exponential decay from center. Modal analysis, impact testing (compensates underdamped responses). Harris 1978. + +--- + +#### `hannPoisson(i, N, alpha)` + +`alpha`: decay — default **2**. At α ≥ 2, the transform has no sidelobes. + +$$w(n) = \frac{1}{2}\left(1-\cos\frac{2\pi n}{N\!-\!1}\right)\exp\!\left(\frac{-\alpha|2n\!-\!N\!+\!1|}{N-1}\right)$$ + + + +Hann × exponential product. Unique no-sidelobe property enables frequency estimators using convex optimization. + +--- + +#### `cauchy(i, N, alpha)` -The decision tables above give qualitative guidance. These three functions let you verify and compare numerically: +`alpha`: width, default **3**. -- **ENBW** (Equivalent Noise Bandwidth) — how many frequency bins of noise power leak through the window. Rectangular = 1.0 (theoretical minimum), Hann = 1.5, Blackman-Harris = 2.0. Lower means less noise contaminates your measurement. +$$w(n) = \frac{1}{1+\left(\frac{\alpha(2n-N+1)}{N-1}\right)^2}$$ + + + +Lorentzian 1/(1+x²) shape. Matches spectral line shapes in spectroscopy. Harris 1978. + +--- + +#### `rifeVincent(i, N, order)` + +`order`: **1** (default) = Hann, 2, 3. Throws for other values. + +$$w(n) = \frac{1}{Z}\sum_{k=0}^{K}(-1)^k a_k\cos\frac{2\pi kn}{N-1}$$ + + + +Class I cosine-sum optimized for sidelobe fall-off. Power grid harmonic analysis, interpolated DFT. Rife & Vincent 1970. + +--- + +#### `confinedGaussian(i, N, sigmaT)` + +`sigmaT`: temporal width, default **0.1**. + + + +Approximate confined Gaussian — optimal RMS time-frequency bandwidth. Time-frequency analysis, audio coding (MP3/AAC). Starosielec 2014. + +--- + +### Array-computed — cached + +These compute the full window on first call and cache the result. Recomputed when N or parameters change. + +--- + +#### `dolphChebyshev(i, N, dB)` + +`dB`: sidelobe attenuation, default **100**. + +$$W(k) = (-1)^k T_{N-1}\!\left(\beta\cos\frac{\pi k}{N}\right),\quad w = \operatorname{IDFT}(W)$$ + + + +Optimal: narrowest main lobe for a given equiripple sidelobe level. Antenna array design, radar beam patterns. Dolph 1946. + +--- + +#### `taylor(i, N, nbar, sll)` + +`nbar`: number of constant-level sidelobes (default **4**), `sll`: sidelobe level in dB (default **30**). + +$$w(n) = 1 + 2\sum_{m=1}^{\bar{n}-1} F_m \cos\frac{2\pi m(n-(N\!-\!1)/2)}{N}$$ + + + +Dolph-Chebyshev variant with monotonically decreasing sidelobes. The radar community standard for SAR image formation. Taylor 1955. + +--- + +#### `kaiserBesselDerived(i, N, beta)` + +`beta`: shape, default **8.6**. N must be even. + +$$w(n) = \sqrt{\frac{\sum_{j=0}^{n} K(j)}{\sum_{j=0}^{N/2} K(j)}} \quad K(j) = I_0\!\left(\beta\sqrt{1-\left(\frac{2j-N/2}{N/2}\right)^2}\right)$$ + + + +Satisfies the Princen-Bradley condition for perfect MDCT reconstruction. AAC, Vorbis, Opus audio codecs (long blocks). Princen & Bradley 1987. + +--- + +#### `dpss(i, N, W)` + +`W`: half-bandwidth [0, 0.5], default **0.1**. + +$$\mathbf{T}\mathbf{v} = \lambda\mathbf{v} \quad T_{jk} = \frac{\sin 2\pi W(j-k)}{\pi(j-k)}$$ + + + +Dominant eigenvector of the sinc Toeplitz matrix — provably optimal energy concentration in a frequency band. Also called Slepian window. Thomson multitaper spectral estimation, neuroscience (EEG/MEG), climate science. Slepian 1978. + +--- + +#### `ultraspherical(i, N, mu, xmu)` + +`mu`: 0 → Dolph-Chebyshev, **1** (default) → Saramaki. `xmu`: sidelobe control (default **1**). + +$$W(k) = C_n^\mu\!\left(x_\mu\cos\frac{\pi k}{N}\right),\quad w = \operatorname{IDFT}(W)$$ + + + +Gegenbauer polynomial window. Independent control of sidelobe level and taper rate. Advanced antenna design, beamforming. Streit 1984. + +--- + +## Quantitative metrics -- **Scallop loss** — the worst-case amplitude error when a tone falls exactly between two DFT bins. Rectangular = 3.92 dB (worst), Hann = 1.42 dB, flat-top ≈ 0 dB (best). This is why flat-top windows exist: amplitude accuracy at the cost of frequency resolution. +Three functions to verify and compare windows numerically: -- **COLA** (Constant Overlap-Add) — whether overlapping windows sum to a constant, which guarantees perfect STFT reconstruction. Returns 0 for perfect COLA. Hann at 50% overlap is the classic COLA pair. +- **ENBW** (Equivalent Noise Bandwidth) — frequency bins of noise power leaking through. Rectangular = 1.0, Hann = 1.5, Blackman-Harris = 2.0. Lower = less noise. +- **Scallop loss** — worst-case amplitude error between DFT bins. Rectangular = 3.92 dB, Hann = 1.42 dB, flat-top ≈ 0 dB. +- **COLA** (Constant Overlap-Add) — 0 = perfect STFT reconstruction at the given hop size. ## Migrating from v2 -v3 is a complete rewrite: CJS → ESM, 20 files → 1, 18 → 34 windows. +v3 is a complete rewrite: CJS → ESM, 18 → 34 windows, subpath imports preserved. ```diff - const hann = require('window-function/hann') - const apply = require('window-function/apply') -+ import { hann, apply } from 'window-function' ++ import { hann } from 'window-function/hann' ++ import { apply } from 'window-function/apply' ``` The per-sample API (`fn(i, N, ...params) → number`) is unchanged. @@ -282,4 +505,4 @@ The per-sample API (`fn(i, N, ...params) → number`) is unchanged. ## License -MIT •
+MIT · diff --git a/_util.js b/_util.js new file mode 100644 index 0000000..6d539e8 --- /dev/null +++ b/_util.js @@ -0,0 +1,32 @@ +export let { cos, sin, abs, exp, sqrt, PI, cosh, acosh, acos, pow, log10 } = Math +export let PI2 = 2 * PI + +export function cosineSum (i, N, a) { + let f = PI2 * i / (N - 1), v = a[0] + for (let k = 1; k < a.length; k++) v += (k % 2 ? -1 : 1) * a[k] * cos(k * f) + return v +} + +export function i0 (x) { + let s = 1, t = 1 + for (let k = 1; k <= 25; k++) { t *= (x / (2 * k)) * (x / (2 * k)); s += t; if (t < 1e-15 * s) break } + return s +} + +export function gegen (n, mu, x) { + if (n === 0) return 1 + if (n === 1) return 2 * mu * x + let c0 = 1, c1 = 2 * mu * x + for (let k = 2; k <= n; k++) { + let c2 = (2 * x * (k + mu - 1) * c1 - (k + 2 * mu - 2) * c0) / k + c0 = c1; c1 = c2 + } + return c1 +} + +export function normalize (w) { + let peak = 0 + for (let i = 0; i < w.length; i++) if (abs(w[i]) > peak) peak = abs(w[i]) + if (peak > 0) for (let i = 0; i < w.length; i++) w[i] /= peak + return w +} diff --git a/apply.js b/apply.js new file mode 100644 index 0000000..d9b772a --- /dev/null +++ b/apply.js @@ -0,0 +1,4 @@ +export function apply (signal, fn, ...params) { + for (let i = 0, N = signal.length; i < N; i++) signal[i] *= fn(i, N, ...params) + return signal +} diff --git a/bartlett.js b/bartlett.js new file mode 100644 index 0000000..b9fb42a --- /dev/null +++ b/bartlett.js @@ -0,0 +1,2 @@ +let { abs } = Math +export function bartlett (i, N) { return 1 - abs((2 * i - N + 1) / (N - 1)) } diff --git a/bartlettHann.js b/bartlettHann.js new file mode 100644 index 0000000..0c58e5c --- /dev/null +++ b/bartlettHann.js @@ -0,0 +1,5 @@ +import { abs, cos, PI2 } from './_util.js' +export function bartlettHann (i, N) { + let x = i / (N - 1) + return 0.62 - 0.48 * abs(x - 0.5) - 0.38 * cos(PI2 * x) +} diff --git a/blackman.js b/blackman.js new file mode 100644 index 0000000..b5a89f0 --- /dev/null +++ b/blackman.js @@ -0,0 +1,2 @@ +import { cosineSum } from './_util.js' +export function blackman (i, N) { return cosineSum(i, N, [0.42, 0.5, 0.08]) } diff --git a/blackmanHarris.js b/blackmanHarris.js new file mode 100644 index 0000000..10f978a --- /dev/null +++ b/blackmanHarris.js @@ -0,0 +1,2 @@ +import { cosineSum } from './_util.js' +export function blackmanHarris (i, N) { return cosineSum(i, N, [0.35875, 0.48829, 0.14128, 0.01168]) } diff --git a/blackmanNuttall.js b/blackmanNuttall.js new file mode 100644 index 0000000..140912b --- /dev/null +++ b/blackmanNuttall.js @@ -0,0 +1,2 @@ +import { cosineSum } from './_util.js' +export function blackmanNuttall (i, N) { return cosineSum(i, N, [0.3635819, 0.4891775, 0.1365995, 0.0106411]) } diff --git a/bohman.js b/bohman.js new file mode 100644 index 0000000..c939c59 --- /dev/null +++ b/bohman.js @@ -0,0 +1,6 @@ +import { abs, cos, sin, PI } from './_util.js' +export function bohman (i, N) { + let a = abs((2 * i - N + 1) / (N - 1)) + if (a >= 1) return 0 + return (1 - a) * cos(PI * a) + sin(PI * a) / PI +} diff --git a/cauchy.js b/cauchy.js new file mode 100644 index 0000000..de8adde --- /dev/null +++ b/cauchy.js @@ -0,0 +1,5 @@ +export function cauchy (i, N, alpha) { + if (alpha == null) alpha = 3 + let x = alpha * (2 * i - N + 1) / (N - 1) + return 1 / (1 + x * x) +} diff --git a/cola.js b/cola.js new file mode 100644 index 0000000..e20dd22 --- /dev/null +++ b/cola.js @@ -0,0 +1,14 @@ +import { abs } from './_util.js' +import { generate } from './generate.js' +export function cola (fn, N, hop, ...params) { + let win = generate(fn, N, ...params) + let sums = new Float64Array(hop) + for (let t = 0; t < hop; t++) for (let k = t; k < N; k += hop) sums[t] += win[k] + let mean = 0 + for (let t = 0; t < hop; t++) mean += sums[t] + mean /= hop + if (mean === 0) return Infinity + let maxDev = 0 + for (let t = 0; t < hop; t++) { let d = abs(sums[t] - mean) / mean; if (d > maxDev) maxDev = d } + return maxDev +} diff --git a/confinedGaussian.js b/confinedGaussian.js new file mode 100644 index 0000000..fa4c421 --- /dev/null +++ b/confinedGaussian.js @@ -0,0 +1,8 @@ +import { exp } from './_util.js' +export function confinedGaussian (i, N, sigmaT) { + if (sigmaT == null) sigmaT = 0.1 + let L = N + 1, half = (N - 1) / 2 + function G (x) { let t = (x - half) / (2 * L * sigmaT); return exp(-t * t) } + let gn = G(i), gh = G(-0.5) + return gn - gh * (G(i + L) + G(i - L)) / (G(-0.5 + L) + G(-0.5 - L)) +} diff --git a/connes.js b/connes.js new file mode 100644 index 0000000..e4f3e80 --- /dev/null +++ b/connes.js @@ -0,0 +1 @@ +export function connes (i, N) { let x = (2 * i - N + 1) / (N - 1); return (1 - x * x) * (1 - x * x) } diff --git a/cosine.js b/cosine.js new file mode 100644 index 0000000..56533cf --- /dev/null +++ b/cosine.js @@ -0,0 +1,2 @@ +import { sin, PI } from './_util.js' +export function cosine (i, N) { return sin(PI * i / (N - 1)) } diff --git a/dolphChebyshev.js b/dolphChebyshev.js new file mode 100644 index 0000000..a46bf89 --- /dev/null +++ b/dolphChebyshev.js @@ -0,0 +1,22 @@ +import { cos, abs, cosh, acosh, acos, pow, PI, PI2, normalize } from './_util.js' +export function dolphChebyshev (i, N, attenuation) { + if (attenuation == null) attenuation = 100 + let c = dolphChebyshev + if (c._N !== N || c._a !== attenuation) { + let ord = N - 1, b = cosh(acosh(pow(10, attenuation / 20)) / ord) + let W = new Float64Array(N) + for (let k = 0; k < N; k++) { + let x = b * cos(PI * k / N) + let T = abs(x) <= 1 ? cos(ord * acos(x)) : cosh(ord * acosh(abs(x))) + W[k] = (k % 2 ? -1 : 1) * T + } + let w = new Float64Array(N) + for (let n = 0; n < N; n++) { + let s = W[0] + for (let k = 1; k < N; k++) s += 2 * W[k] * cos(PI2 * k * n / N) + w[n] = s + } + c._w = normalize(w); c._N = N; c._a = attenuation + } + return c._w[i] +} diff --git a/dpss.js b/dpss.js new file mode 100644 index 0000000..fb05ec9 --- /dev/null +++ b/dpss.js @@ -0,0 +1,28 @@ +import { sin, exp, sqrt, abs, PI, PI2, normalize } from './_util.js' +export function dpss (i, N, W) { + if (W == null) W = 0.1 + let c = dpss + if (c._N !== N || c._W !== W) { + let v = new Float64Array(N), m = 0 + for (let j = 0; j < N; j++) { let x = (2 * j - N + 1) / (N - 1); v[j] = exp(-5 * x * x); m += v[j] * v[j] } + m = sqrt(m) + for (let j = 0; j < N; j++) v[j] /= m + for (let iter = 0; iter < 50; iter++) { + let u = new Float64Array(N) + for (let j = 0; j < N; j++) { + let s = 2 * W * v[j] + for (let k = 0; k < N; k++) if (k !== j) s += sin(PI2 * W * (j - k)) / (PI * (j - k)) * v[k] + u[j] = s + } + m = 0 + for (let j = 0; j < N; j++) m += u[j] * u[j] + m = sqrt(m) + for (let j = 0; j < N; j++) v[j] = u[j] / m + } + let maxIdx = 0 + for (let j = 1; j < N; j++) if (abs(v[j]) > abs(v[maxIdx])) maxIdx = j + if (v[maxIdx] < 0) for (let j = 0; j < N; j++) v[j] = -v[j] + c._w = normalize(v); c._N = N; c._W = W + } + return c._w[i] +} diff --git a/enbw.js b/enbw.js new file mode 100644 index 0000000..e71755e --- /dev/null +++ b/enbw.js @@ -0,0 +1,5 @@ +export function enbw (fn, N, ...params) { + let s = 0, s2 = 0 + for (let i = 0; i < N; i++) { let v = fn(i, N, ...params); s += v; s2 += v * v } + return N * s2 / (s * s) +} diff --git a/exactBlackman.js b/exactBlackman.js new file mode 100644 index 0000000..4aa3de2 --- /dev/null +++ b/exactBlackman.js @@ -0,0 +1,2 @@ +import { cosineSum } from './_util.js' +export function exactBlackman (i, N) { return cosineSum(i, N, [0.42659, 0.49656, 0.076849]) } diff --git a/exponential.js b/exponential.js new file mode 100644 index 0000000..5e2ed94 --- /dev/null +++ b/exponential.js @@ -0,0 +1,5 @@ +import { abs, exp } from './_util.js' +export function exponential (i, N, tau) { + if (tau == null) tau = 1 + return exp(-abs(2 * i - N + 1) / (tau * (N - 1))) +} diff --git a/flatTop.js b/flatTop.js new file mode 100644 index 0000000..764718a --- /dev/null +++ b/flatTop.js @@ -0,0 +1,2 @@ +import { cosineSum } from './_util.js' +export function flatTop (i, N) { return cosineSum(i, N, [1, 1.93, 1.29, 0.388, 0.028]) } diff --git a/gaussian.js b/gaussian.js new file mode 100644 index 0000000..45a52b8 --- /dev/null +++ b/gaussian.js @@ -0,0 +1,6 @@ +import { exp } from './_util.js' +export function gaussian (i, N, sigma) { + if (sigma == null) sigma = 0.4 + let x = (2 * i - N + 1) / (sigma * (N - 1)) + return exp(-0.5 * x * x) +} diff --git a/generalizedNormal.js b/generalizedNormal.js new file mode 100644 index 0000000..9e67335 --- /dev/null +++ b/generalizedNormal.js @@ -0,0 +1,7 @@ +import { abs, exp, pow } from './_util.js' +export function generalizedNormal (i, N, sigma, p) { + if (sigma == null) sigma = 0.4 + if (p == null) p = 2 + let x = abs((2 * i - N + 1) / (sigma * (N - 1))) + return exp(-0.5 * pow(x, p)) +} diff --git a/generate.js b/generate.js new file mode 100644 index 0000000..ddb2c87 --- /dev/null +++ b/generate.js @@ -0,0 +1,5 @@ +export function generate (fn, N, ...params) { + let w = new Float64Array(N) + for (let i = 0; i < N; i++) w[i] = fn(i, N, ...params) + return w +} diff --git a/hamming.js b/hamming.js new file mode 100644 index 0000000..9e73ad0 --- /dev/null +++ b/hamming.js @@ -0,0 +1,2 @@ +import { cos, PI2 } from './_util.js' +export function hamming (i, N) { return 0.54 - 0.46 * cos(PI2 * i / (N - 1)) } diff --git a/hann.js b/hann.js new file mode 100644 index 0000000..6de29fb --- /dev/null +++ b/hann.js @@ -0,0 +1,2 @@ +import { cos, PI2 } from './_util.js' +export function hann (i, N) { return 0.5 - 0.5 * cos(PI2 * i / (N - 1)) } diff --git a/hannPoisson.js b/hannPoisson.js new file mode 100644 index 0000000..33303cf --- /dev/null +++ b/hannPoisson.js @@ -0,0 +1,5 @@ +import { cos, exp, abs, PI2 } from './_util.js' +export function hannPoisson (i, N, alpha) { + if (alpha == null) alpha = 2 + return 0.5 * (1 - cos(PI2 * i / (N - 1))) * exp(-alpha * abs(2 * i - N + 1) / (N - 1)) +} diff --git a/index.js b/index.js index ff66cad..e3e6919 100644 --- a/index.js +++ b/index.js @@ -8,341 +8,49 @@ * @module window-function */ -let {cos, sin, abs, exp, sqrt, PI, cosh, acosh, acos, pow} = Math -let PI2 = 2 * PI - -// ────── Rectangular family ────── - -export function rectangular () { return 1 } - -export function triangular (i, N) { return 1 - abs((2 * i - N + 1) / N) } - -export function bartlett (i, N) { return 1 - abs((2 * i - N + 1) / (N - 1)) } - -export function welch (i, N) { let x = (2 * i - N + 1) / (N - 1); return 1 - x * x } - -export function connes (i, N) { let x = (2 * i - N + 1) / (N - 1); return (1 - x * x) * (1 - x * x) } - -// ────── Cosine-sum: w(i) = Σ aₖ cos(2πki/(N-1)) ────── - -function _cosineSum (i, N, a) { - let f = PI2 * i / (N - 1), v = a[0] - for (let k = 1; k < a.length; k++) v += (k % 2 ? -1 : 1) * a[k] * cos(k * f) - return v -} - -export function hann (i, N) { return 0.5 - 0.5 * cos(PI2 * i / (N - 1)) } -export function hamming (i, N) { return 0.54 - 0.46 * cos(PI2 * i / (N - 1)) } -export function cosine (i, N) { return sin(PI * i / (N - 1)) } - -/** Power-of-sine (α=0: rectangular, α=1: cosine, α=2: hann). */ -export function powerOfSine (i, N, alpha) { - if (alpha == null) alpha = 2 - return pow(sin(PI * i / (N - 1)), alpha) -} -export function blackman (i, N) { return _cosineSum(i, N, [0.42, 0.5, 0.08]) } -export function exactBlackman (i, N) { return _cosineSum(i, N, [0.42659, 0.49656, 0.076849]) } -export function nuttall (i, N) { return _cosineSum(i, N, [0.355768, 0.487396, 0.144232, 0.012604]) } -export function blackmanNuttall (i, N) { return _cosineSum(i, N, [0.3635819, 0.4891775, 0.1365995, 0.0106411]) } -export function blackmanHarris (i, N) { return _cosineSum(i, N, [0.35875, 0.48829, 0.14128, 0.01168]) } -export function flatTop (i, N) { return _cosineSum(i, N, [1, 1.93, 1.29, 0.388, 0.028]) } - -export function bartlettHann (i, N) { - let x = i / (N - 1) - return 0.62 - 0.48 * abs(x - 0.5) - 0.38 * cos(PI2 * x) -} - -// ────── Parameterized ────── - -export function kaiser (i, N, beta) { - if (beta == null) beta = 8.6 - let x = (2 * i - N + 1) / (N - 1) - return _i0(beta * sqrt(1 - x * x)) / _i0(beta) -} - -export function gaussian (i, N, sigma) { - if (sigma == null) sigma = 0.4 - let x = (2 * i - N + 1) / (sigma * (N - 1)) - return exp(-0.5 * x * x) -} - -/** Generalized normal (p=2: Gaussian, p→∞: rectangular). */ -export function generalizedNormal (i, N, sigma, p) { - if (sigma == null) sigma = 0.4 - if (p == null) p = 2 - let x = abs((2 * i - N + 1) / (sigma * (N - 1))) - return exp(-0.5 * pow(x, p)) -} - -export function tukey (i, N, alpha) { - if (alpha == null) alpha = 0.5 - let half = 0.5 * alpha * (N - 1) - if (half < 1e-12) return 1 - if (i <= half) return 0.5 * (1 + cos(PI * (i / half - 1))) - if (i >= (N - 1) - half) return 0.5 * (1 + cos(PI * ((N - 1 - i) / half - 1))) - return 1 -} - -export function planckTaper (i, N, epsilon) { - if (epsilon == null) epsilon = 0.1 - let eN = epsilon * (N - 1) - if (i <= 0 || i >= N - 1) return 0 - if (i < eN) return 1 / (1 + exp(eN / i - eN / (eN - i))) - if (i > (N - 1) - eN) return 1 / (1 + exp(eN / (N - 1 - i) - eN / (eN - N + 1 + i))) - return 1 -} - -// ────── Exponential family ────── - -export function exponential (i, N, tau) { - if (tau == null) tau = 1 - return exp(-abs(2 * i - N + 1) / (tau * (N - 1))) -} - -export function hannPoisson (i, N, alpha) { - if (alpha == null) alpha = 2 - return 0.5 * (1 - cos(PI2 * i / (N - 1))) * exp(-alpha * abs(2 * i - N + 1) / (N - 1)) -} - -export function cauchy (i, N, alpha) { - if (alpha == null) alpha = 3 - let x = alpha * (2 * i - N + 1) / (N - 1) - return 1 / (1 + x * x) -} - -// ────── Sinc-based ────── - -export function lanczos (i, N) { - let x = 2 * i / (N - 1) - 1 - return x === 0 ? 1 : sin(PI * x) / (PI * x) -} - -// ────── B-spline / polynomial ────── - -export function parzen (i, N) { - let a = abs((2 * i - N + 1) / (N - 1)) - if (a <= 0.5) return 1 - 6 * a * a * (1 - a) - let b = 1 - a - return 2 * b * b * b -} - -export function bohman (i, N) { - let a = abs((2 * i - N + 1) / (N - 1)) - if (a >= 1) return 0 - return (1 - a) * cos(PI * a) + sin(PI * a) / PI -} - -/** Rife-Vincent class I (order=1: Hann, order=2: 4th power-of-sine, order=3+: minimum high-order sidelobes). */ -export function rifeVincent (i, N, order) { - if (order == null) order = 1 - // Class I coefficients scaled for unity average (Rife & Vincent 1970) - let a - if (order === 1) a = [1, 1] - else if (order === 2) a = [1, 4 / 3, 1 / 3] - else if (order === 3) a = [1, 1.5, 0.6, 0.1] - else throw new RangeError('rifeVincent: order must be 1, 2, or 3') - let f = PI2 * i / (N - 1), v = a[0] - for (let k = 1; k < a.length; k++) v += (k % 2 ? -1 : 1) * a[k] * cos(k * f) - // Scale to peak=1 - let peak = a.reduce((s, c) => s + c, 0) - return v / peak -} - -/** Approximate confined Gaussian (optimal RMS time-frequency, Starosielec 2014). */ -export function confinedGaussian (i, N, sigmaT) { - if (sigmaT == null) sigmaT = 0.1 - let L = N + 1, half = (N - 1) / 2 - function G (x) { let t = (x - half) / (2 * L * sigmaT); return exp(-t * t) } - let gn = G(i), gh = G(-0.5) - return gn - gh * (G(i + L) + G(i - L)) / (G(-0.5 + L) + G(-0.5 - L)) -} - -// ────── Array-computed (IDFT / eigenvalue) ────── -// These compute the full window array, cache last result per function. -// Same export shape as simple windows — plain `export function`. - -/** Kaiser-Bessel derived (MDCT/AAC/Vorbis, Princen-Bradley). N must be even. */ -export function kaiserBesselDerived (i, N, beta) { - if (beta == null) beta = 8.6 - let c = kaiserBesselDerived - if (c._N !== N || c._b !== beta) { - let h = N / 2, k = new Float64Array(h + 1), s = 0 - for (let j = 0; j <= h; j++) { let x = (2 * j - h) / h; s += _i0(beta * sqrt(abs(1 - x * x))); k[j] = s } - for (let j = 0; j <= h; j++) k[j] /= k[h] - let w = new Float64Array(N) - for (let j = 0; j < h; j++) w[j] = sqrt(k[j]) - for (let j = h; j < N; j++) w[j] = sqrt(k[N - 1 - j]) - c._w = w; c._N = N; c._b = beta - } - return c._w[i] -} - -/** Dolph-Chebyshev (equiripple sidelobes at specified attenuation dB). */ -export function dolphChebyshev (i, N, attenuation) { - if (attenuation == null) attenuation = 100 - let c = dolphChebyshev - if (c._N !== N || c._a !== attenuation) { - let ord = N - 1, b = cosh(acosh(pow(10, attenuation / 20)) / ord) - let W = new Float64Array(N) - for (let k = 0; k < N; k++) { - let x = b * cos(PI * k / N) - let T = abs(x) <= 1 ? cos(ord * acos(x)) : cosh(ord * acosh(abs(x))) - W[k] = (k % 2 ? -1 : 1) * T - } - let w = new Float64Array(N) - for (let n = 0; n < N; n++) { - let s = W[0] - for (let k = 1; k < N; k++) s += 2 * W[k] * cos(PI2 * k * n / N) - w[n] = s - } - c._w = _normalize(w); c._N = N; c._a = attenuation - } - return c._w[i] -} - -/** Taylor (monotonically decreasing sidelobes, radar/SAR standard). */ -export function taylor (i, N, nbar, sll) { - if (nbar == null) nbar = 4 - if (sll == null) sll = 30 - let c = taylor - if (c._N !== N || c._nb !== nbar || c._s !== sll) { - let A = acosh(pow(10, sll / 20)) / PI - let s2 = nbar * nbar / (A * A + (nbar - 0.5) * (nbar - 0.5)) - let Fm = new Float64Array(nbar - 1) - for (let m = 1; m < nbar; m++) { - let num = 1, den = 1 - for (let n = 1; n < nbar; n++) { - num *= 1 - m * m * s2 / (A * A + (n - 0.5) * (n - 0.5)) - if (n !== m) den *= 1 - m * m / (n * n) - } - Fm[m - 1] = (m % 2 ? 1 : -1) * num / (2 * den) - } - let w = new Float64Array(N) - for (let n = 0; n < N; n++) { - let v = 1 - for (let m = 1; m < nbar; m++) v += 2 * Fm[m - 1] * cos(PI2 * m * (n - (N - 1) / 2) / N) - w[n] = v - } - c._w = _normalize(w); c._N = N; c._nb = nbar; c._s = sll - } - return c._w[i] -} - -/** DPSS / Slepian (optimal energy concentration). W = half-bandwidth [0, 0.5]. */ -export function dpss (i, N, W) { - if (W == null) W = 0.1 - let c = dpss - if (c._N !== N || c._W !== W) { - let v = new Float64Array(N), m = 0 - for (let j = 0; j < N; j++) { let x = (2 * j - N + 1) / (N - 1); v[j] = exp(-5 * x * x); m += v[j] * v[j] } - m = sqrt(m) - for (let j = 0; j < N; j++) v[j] /= m - for (let iter = 0; iter < 50; iter++) { - let u = new Float64Array(N) - for (let j = 0; j < N; j++) { - let s = 2 * W * v[j] - for (let k = 0; k < N; k++) if (k !== j) s += sin(PI2 * W * (j - k)) / (PI * (j - k)) * v[k] - u[j] = s - } - m = 0 - for (let j = 0; j < N; j++) m += u[j] * u[j] - m = sqrt(m) - for (let j = 0; j < N; j++) v[j] = u[j] / m - } - let maxIdx = 0 - for (let j = 1; j < N; j++) if (abs(v[j]) > abs(v[maxIdx])) maxIdx = j - if (v[maxIdx] < 0) for (let j = 0; j < N; j++) v[j] = -v[j] - c._w = _normalize(v); c._N = N; c._W = W - } - return c._w[i] -} - -/** Ultraspherical / Gegenbauer (mu=0: Dolph-Chebyshev, mu=1: Saramaki). Streit 1984. */ -export function ultraspherical (i, N, mu, xmu) { - if (mu == null) mu = 1 - if (xmu == null) xmu = 1 - let c = ultraspherical - if (c._N !== N || c._mu !== mu || c._xmu !== xmu) { - let ord = N - 1, w = new Float64Array(N) - for (let n = 0; n < N; n++) { - let s = _gegen(ord, mu, xmu) - for (let k = 1; k < N; k++) { - let x = xmu * cos(PI * k / N) - s += 2 * (k % 2 ? -1 : 1) * _gegen(ord, mu, x) * cos(PI2 * k * n / N) - } - w[n] = s - } - c._w = _normalize(w); c._N = N; c._mu = mu; c._xmu = xmu - } - return c._w[i] -} - -// ────── Utilities ────── - -/** Generate a full window as Float64Array. */ -export function generate (fn, N, ...params) { - let w = new Float64Array(N) - for (let i = 0; i < N; i++) w[i] = fn(i, N, ...params) - return w -} - -/** Apply window to a signal in-place. */ -export function apply (signal, fn, ...params) { - for (let i = 0, N = signal.length; i < N; i++) signal[i] *= fn(i, N, ...params) - return signal -} - -/** Equivalent noise bandwidth in frequency bins. Lower = less noise leakage. Rectangular = 1.0, Hann ≈ 1.5. */ -export function enbw (fn, N, ...params) { - let s = 0, s2 = 0 - for (let i = 0; i < N; i++) { let v = fn(i, N, ...params); s += v; s2 += v * v } - return N * s2 / (s * s) -} - -/** Worst-case amplitude error in dB when a tone falls between DFT bins. Rectangular ≈ 3.92, flat-top ≈ 0. */ -export function scallopLoss (fn, N, ...params) { - let s = 0, re = 0, im = 0 - for (let i = 0; i < N; i++) { let v = fn(i, N, ...params); s += v; re += v * cos(PI * i / N); im -= v * sin(PI * i / N) } - return s === 0 ? Infinity : -20 * Math.log10(sqrt(re * re + im * im) / abs(s)) -} - -/** COLA (Constant Overlap-Add) deviation. Returns max relative deviation from constant sum; 0 = perfect reconstruction. */ -export function cola (fn, N, hop, ...params) { - let win = generate(fn, N, ...params) - let sums = new Float64Array(hop) - for (let t = 0; t < hop; t++) for (let k = t; k < N; k += hop) sums[t] += win[k] - let mean = 0 - for (let t = 0; t < hop; t++) mean += sums[t] - mean /= hop - if (mean === 0) return Infinity - let maxDev = 0 - for (let t = 0; t < hop; t++) { let d = abs(sums[t] - mean) / mean; if (d > maxDev) maxDev = d } - return maxDev -} - -// ────── Private ────── - -function _i0 (x) { - let s = 1, t = 1 - for (let k = 1; k <= 25; k++) { t *= (x / (2 * k)) * (x / (2 * k)); s += t; if (t < 1e-15 * s) break } - return s -} - -/** Gegenbauer (ultraspherical) polynomial C_n^mu(x) via recurrence. */ -function _gegen (n, mu, x) { - if (n === 0) return 1 - if (n === 1) return 2 * mu * x - let c0 = 1, c1 = 2 * mu * x - for (let k = 2; k <= n; k++) { - let c2 = (2 * x * (k + mu - 1) * c1 - (k + 2 * mu - 2) * c0) / k - c0 = c1; c1 = c2 - } - return c1 -} - -function _normalize (w) { - let peak = 0 - for (let i = 0; i < w.length; i++) if (abs(w[i]) > peak) peak = abs(w[i]) - if (peak > 0) for (let i = 0; i < w.length; i++) w[i] /= peak - return w -} +// Simple windows +export { rectangular } from './rectangular.js' +export { triangular } from './triangular.js' +export { bartlett } from './bartlett.js' +export { welch } from './welch.js' +export { connes } from './connes.js' +export { hann } from './hann.js' +export { hamming } from './hamming.js' +export { cosine } from './cosine.js' +export { blackman } from './blackman.js' +export { exactBlackman } from './exactBlackman.js' +export { nuttall } from './nuttall.js' +export { blackmanNuttall } from './blackmanNuttall.js' +export { blackmanHarris } from './blackmanHarris.js' +export { flatTop } from './flatTop.js' +export { bartlettHann } from './bartlettHann.js' +export { lanczos } from './lanczos.js' +export { parzen } from './parzen.js' +export { bohman } from './bohman.js' + +// Parameterized windows +export { powerOfSine } from './powerOfSine.js' +export { kaiser } from './kaiser.js' +export { gaussian } from './gaussian.js' +export { generalizedNormal } from './generalizedNormal.js' +export { tukey } from './tukey.js' +export { planckTaper } from './planckTaper.js' +export { exponential } from './exponential.js' +export { hannPoisson } from './hannPoisson.js' +export { cauchy } from './cauchy.js' +export { rifeVincent } from './rifeVincent.js' +export { confinedGaussian } from './confinedGaussian.js' + +// Array-computed windows +export { kaiserBesselDerived } from './kaiserBesselDerived.js' +export { dolphChebyshev } from './dolphChebyshev.js' +export { taylor } from './taylor.js' +export { dpss } from './dpss.js' +export { ultraspherical } from './ultraspherical.js' + +// Utilities +export { generate } from './generate.js' +export { apply } from './apply.js' +export { enbw } from './enbw.js' +export { scallopLoss } from './scallopLoss.js' +export { cola } from './cola.js' diff --git a/kaiser.js b/kaiser.js new file mode 100644 index 0000000..46ba958 --- /dev/null +++ b/kaiser.js @@ -0,0 +1,6 @@ +import { sqrt, i0 } from './_util.js' +export function kaiser (i, N, beta) { + if (beta == null) beta = 8.6 + let x = (2 * i - N + 1) / (N - 1) + return i0(beta * sqrt(1 - x * x)) / i0(beta) +} diff --git a/kaiserBesselDerived.js b/kaiserBesselDerived.js new file mode 100644 index 0000000..28f82eb --- /dev/null +++ b/kaiserBesselDerived.js @@ -0,0 +1,15 @@ +import { sqrt, abs, i0 } from './_util.js' +export function kaiserBesselDerived (i, N, beta) { + if (beta == null) beta = 8.6 + let c = kaiserBesselDerived + if (c._N !== N || c._b !== beta) { + let h = N / 2, k = new Float64Array(h + 1), s = 0 + for (let j = 0; j <= h; j++) { let x = (2 * j - h) / h; s += i0(beta * sqrt(abs(1 - x * x))); k[j] = s } + for (let j = 0; j <= h; j++) k[j] /= k[h] + let w = new Float64Array(N) + for (let j = 0; j < h; j++) w[j] = sqrt(k[j]) + for (let j = h; j < N; j++) w[j] = sqrt(k[N - 1 - j]) + c._w = w; c._N = N; c._b = beta + } + return c._w[i] +} diff --git a/lanczos.js b/lanczos.js new file mode 100644 index 0000000..f2586ca --- /dev/null +++ b/lanczos.js @@ -0,0 +1,5 @@ +import { sin, PI } from './_util.js' +export function lanczos (i, N) { + let x = 2 * i / (N - 1) - 1 + return x === 0 ? 1 : sin(PI * x) / (PI * x) +} diff --git a/nuttall.js b/nuttall.js new file mode 100644 index 0000000..39d0374 --- /dev/null +++ b/nuttall.js @@ -0,0 +1,2 @@ +import { cosineSum } from './_util.js' +export function nuttall (i, N) { return cosineSum(i, N, [0.355768, 0.487396, 0.144232, 0.012604]) } diff --git a/package.json b/package.json index dddf804..f5ba5bd 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,50 @@ "version": "3.0.0", "type": "module", "description": "Window functions for signal processing and spectral analysis", - "exports": "./index.js", + "exports": { + ".": "./index.js", + "./rectangular": "./rectangular.js", + "./triangular": "./triangular.js", + "./bartlett": "./bartlett.js", + "./welch": "./welch.js", + "./connes": "./connes.js", + "./hann": "./hann.js", + "./hamming": "./hamming.js", + "./cosine": "./cosine.js", + "./blackman": "./blackman.js", + "./exactBlackman": "./exactBlackman.js", + "./nuttall": "./nuttall.js", + "./blackmanNuttall": "./blackmanNuttall.js", + "./blackmanHarris": "./blackmanHarris.js", + "./flatTop": "./flatTop.js", + "./bartlettHann": "./bartlettHann.js", + "./lanczos": "./lanczos.js", + "./parzen": "./parzen.js", + "./bohman": "./bohman.js", + "./powerOfSine": "./powerOfSine.js", + "./kaiser": "./kaiser.js", + "./gaussian": "./gaussian.js", + "./generalizedNormal": "./generalizedNormal.js", + "./tukey": "./tukey.js", + "./planckTaper": "./planckTaper.js", + "./exponential": "./exponential.js", + "./hannPoisson": "./hannPoisson.js", + "./cauchy": "./cauchy.js", + "./rifeVincent": "./rifeVincent.js", + "./confinedGaussian": "./confinedGaussian.js", + "./kaiserBesselDerived": "./kaiserBesselDerived.js", + "./dolphChebyshev": "./dolphChebyshev.js", + "./taylor": "./taylor.js", + "./dpss": "./dpss.js", + "./ultraspherical": "./ultraspherical.js", + "./generate": "./generate.js", + "./apply": "./apply.js", + "./enbw": "./enbw.js", + "./scallopLoss": "./scallopLoss.js", + "./cola": "./cola.js" + }, "types": "./index.d.ts", + "typesVersions": { "*": { "*": ["./index.d.ts"] } }, "scripts": { "test": "node --test test.js" }, diff --git a/parzen.js b/parzen.js new file mode 100644 index 0000000..690fc98 --- /dev/null +++ b/parzen.js @@ -0,0 +1,7 @@ +let { abs } = Math +export function parzen (i, N) { + let a = abs((2 * i - N + 1) / (N - 1)) + if (a <= 0.5) return 1 - 6 * a * a * (1 - a) + let b = 1 - a + return 2 * b * b * b +} diff --git a/planckTaper.js b/planckTaper.js new file mode 100644 index 0000000..0778a42 --- /dev/null +++ b/planckTaper.js @@ -0,0 +1,9 @@ +import { exp } from './_util.js' +export function planckTaper (i, N, epsilon) { + if (epsilon == null) epsilon = 0.1 + let eN = epsilon * (N - 1) + if (i <= 0 || i >= N - 1) return 0 + if (i < eN) return 1 / (1 + exp(eN / i - eN / (eN - i))) + if (i > (N - 1) - eN) return 1 / (1 + exp(eN / (N - 1 - i) - eN / (eN - N + 1 + i))) + return 1 +} diff --git a/powerOfSine.js b/powerOfSine.js new file mode 100644 index 0000000..86b7861 --- /dev/null +++ b/powerOfSine.js @@ -0,0 +1,5 @@ +import { sin, PI, pow } from './_util.js' +export function powerOfSine (i, N, alpha) { + if (alpha == null) alpha = 2 + return pow(sin(PI * i / (N - 1)), alpha) +} diff --git a/rectangular.js b/rectangular.js new file mode 100644 index 0000000..98a82c4 --- /dev/null +++ b/rectangular.js @@ -0,0 +1 @@ +export function rectangular () { return 1 } diff --git a/rifeVincent.js b/rifeVincent.js new file mode 100644 index 0000000..1c06b8f --- /dev/null +++ b/rifeVincent.js @@ -0,0 +1,13 @@ +import { cos, PI2 } from './_util.js' +export function rifeVincent (i, N, order) { + if (order == null) order = 1 + let a + if (order === 1) a = [1, 1] + else if (order === 2) a = [1, 4 / 3, 1 / 3] + else if (order === 3) a = [1, 1.5, 0.6, 0.1] + else throw new RangeError('rifeVincent: order must be 1, 2, or 3') + let f = PI2 * i / (N - 1), v = a[0] + for (let k = 1; k < a.length; k++) v += (k % 2 ? -1 : 1) * a[k] * cos(k * f) + let peak = a.reduce((s, c) => s + c, 0) + return v / peak +} diff --git a/scallopLoss.js b/scallopLoss.js new file mode 100644 index 0000000..5e04d49 --- /dev/null +++ b/scallopLoss.js @@ -0,0 +1,6 @@ +import { cos, sin, sqrt, abs, PI } from './_util.js' +export function scallopLoss (fn, N, ...params) { + let s = 0, re = 0, im = 0 + for (let i = 0; i < N; i++) { let v = fn(i, N, ...params); s += v; re += v * cos(PI * i / N); im -= v * sin(PI * i / N) } + return s === 0 ? Infinity : -20 * Math.log10(sqrt(re * re + im * im) / abs(s)) +} diff --git a/taylor.js b/taylor.js new file mode 100644 index 0000000..8e043a5 --- /dev/null +++ b/taylor.js @@ -0,0 +1,27 @@ +import { cos, acosh, pow, PI, PI2, normalize } from './_util.js' +export function taylor (i, N, nbar, sll) { + if (nbar == null) nbar = 4 + if (sll == null) sll = 30 + let c = taylor + if (c._N !== N || c._nb !== nbar || c._s !== sll) { + let A = acosh(pow(10, sll / 20)) / PI + let s2 = nbar * nbar / (A * A + (nbar - 0.5) * (nbar - 0.5)) + let Fm = new Float64Array(nbar - 1) + for (let m = 1; m < nbar; m++) { + let num = 1, den = 1 + for (let n = 1; n < nbar; n++) { + num *= 1 - m * m * s2 / (A * A + (n - 0.5) * (n - 0.5)) + if (n !== m) den *= 1 - m * m / (n * n) + } + Fm[m - 1] = (m % 2 ? 1 : -1) * num / (2 * den) + } + let w = new Float64Array(N) + for (let n = 0; n < N; n++) { + let v = 1 + for (let m = 1; m < nbar; m++) v += 2 * Fm[m - 1] * cos(PI2 * m * (n - (N - 1) / 2) / N) + w[n] = v + } + c._w = normalize(w); c._N = N; c._nb = nbar; c._s = sll + } + return c._w[i] +} diff --git a/triangular.js b/triangular.js new file mode 100644 index 0000000..2cd2115 --- /dev/null +++ b/triangular.js @@ -0,0 +1,2 @@ +let { abs } = Math +export function triangular (i, N) { return 1 - abs((2 * i - N + 1) / N) } diff --git a/tukey.js b/tukey.js new file mode 100644 index 0000000..574034d --- /dev/null +++ b/tukey.js @@ -0,0 +1,9 @@ +import { cos, PI } from './_util.js' +export function tukey (i, N, alpha) { + if (alpha == null) alpha = 0.5 + let half = 0.5 * alpha * (N - 1) + if (half < 1e-12) return 1 + if (i <= half) return 0.5 * (1 + cos(PI * (i / half - 1))) + if (i >= (N - 1) - half) return 0.5 * (1 + cos(PI * ((N - 1 - i) / half - 1))) + return 1 +} diff --git a/ultraspherical.js b/ultraspherical.js new file mode 100644 index 0000000..f7c42e8 --- /dev/null +++ b/ultraspherical.js @@ -0,0 +1,19 @@ +import { cos, PI, PI2, gegen, normalize } from './_util.js' +export function ultraspherical (i, N, mu, xmu) { + if (mu == null) mu = 1 + if (xmu == null) xmu = 1 + let c = ultraspherical + if (c._N !== N || c._mu !== mu || c._xmu !== xmu) { + let ord = N - 1, w = new Float64Array(N) + for (let n = 0; n < N; n++) { + let s = gegen(ord, mu, xmu) + for (let k = 1; k < N; k++) { + let x = xmu * cos(PI * k / N) + s += 2 * (k % 2 ? -1 : 1) * gegen(ord, mu, x) * cos(PI2 * k * n / N) + } + w[n] = s + } + c._w = normalize(w); c._N = N; c._mu = mu; c._xmu = xmu + } + return c._w[i] +} diff --git a/welch.js b/welch.js new file mode 100644 index 0000000..cbecec4 --- /dev/null +++ b/welch.js @@ -0,0 +1 @@ +export function welch (i, N) { let x = (2 * i - N + 1) / (N - 1); return 1 - x * x } From 8aab613d6cb45123d62cc137ca4c3ff088740b60 Mon Sep 17 00:00:00 2001 From: "Dmitry Iv." Date: Wed, 25 Mar 2026 23:47:51 -0400 Subject: [PATCH 03/26] Update README.md --- README.md | 64 +++++++++++++++++++++++++++---------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index c9c9e30..3402b2d 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ cola(hann, 1024, 512) // → 0 (perfect STFT reconstruction at 50% ove #### `rectangular(i, N)` -$$w(n) = 1$$ +$w(n) = 1$ @@ -73,7 +73,7 @@ No windowing at all. Best frequency resolution, worst spectral leakage. Use for #### `triangular(i, N)` -$$w(n) = 1 - \left|\frac{2n - N + 1}{N}\right|$$ +$w(n) = 1 - \left|\frac{2n - N + 1}{N}\right|$ @@ -84,7 +84,7 @@ Linear taper, nonzero endpoints. Simple smoothing, 2nd-order B-spline. #### `bartlett(i, N)` -$$w(n) = 1 - \left|\frac{2n - N + 1}{N - 1}\right|$$ +$w(n) = 1 - \left|\frac{2n - N + 1}{N - 1}\right|$ @@ -95,7 +95,7 @@ Linear taper, zero endpoints. Bartlett's method PSD estimation. Bartlett 1950. #### `welch(i, N)` -$$w(n) = 1 - \left(\frac{2n - N + 1}{N - 1}\right)^2$$ +$w(n) = 1 - \left(\frac{2n - N + 1}{N - 1}\right)^2$ @@ -106,7 +106,7 @@ Parabolic taper. Welch's method PSD estimation. Welch 1967. #### `connes(i, N)` -$$w(n) = \left[1 - \left(\frac{2n - N + 1}{N - 1}\right)^2\right]^2$$ +$w(n) = \left[1 - \left(\frac{2n - N + 1}{N - 1}\right)^2\right]^2$ @@ -117,7 +117,7 @@ Welch squared (4th power parabolic). FTIR spectroscopy, interferogram apodizatio #### `hann(i, N)` -$$w(n) = 0.5 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right)$$ +$w(n) = 0.5 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right)$ @@ -128,7 +128,7 @@ Raised cosine, zero endpoints. The default general-purpose choice. STFT with 50% #### `hamming(i, N)` -$$w(n) = 0.54 - 0.46\cos\!\left(\frac{2\pi n}{N-1}\right)$$ +$w(n) = 0.54 - 0.46\cos\!\left(\frac{2\pi n}{N-1}\right)$ @@ -139,7 +139,7 @@ Raised cosine, nonzero endpoints. Optimized for first sidelobe cancellation. FIR #### `cosine(i, N)` -$$w(n) = \sin\!\left(\frac{\pi n}{N-1}\right)$$ +$w(n) = \sin\!\left(\frac{\pi n}{N-1}\right)$ @@ -150,7 +150,7 @@ Half-period sine. MDCT audio codecs: MP3, AAC, Vorbis. Princen & Bradley 1987. #### `blackman(i, N)` -$$w(n) = 0.42 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.08\cos\!\left(\frac{4\pi n}{N-1}\right)$$ +$w(n) = 0.42 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.08\cos\!\left(\frac{4\pi n}{N-1}\right)$ @@ -161,7 +161,7 @@ $$w(n) = 0.42 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.08\cos\!\left(\frac #### `exactBlackman(i, N)` -$$w(n) = 0.42659 - 0.49656\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.076849\cos\!\left(\frac{4\pi n}{N-1}\right)$$ +$w(n) = 0.42659 - 0.49656\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.076849\cos\!\left(\frac{4\pi n}{N-1}\right)$ @@ -172,7 +172,7 @@ Blackman with exact zero placement at 3rd and 4th sidelobes. Harris 1978. #### `nuttall(i, N)` -$$w(n) = 0.355768 - 0.487396\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.144232\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.012604\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$$ +$w(n) = 0.355768 - 0.487396\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.144232\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.012604\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ @@ -183,7 +183,7 @@ $$w(n) = 0.355768 - 0.487396\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.144232 #### `blackmanNuttall(i, N)` -$$w(n) = 0.3635819 - 0.4891775\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.1365995\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.0106411\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$$ +$w(n) = 0.3635819 - 0.4891775\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.1365995\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.0106411\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ @@ -194,7 +194,7 @@ $$w(n) = 0.3635819 - 0.4891775\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.1365 #### `blackmanHarris(i, N)` -$$w(n) = 0.35875 - 0.48829\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.14128\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.01168\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$$ +$w(n) = 0.35875 - 0.48829\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.14128\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.01168\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ @@ -205,7 +205,7 @@ $$w(n) = 0.35875 - 0.48829\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.14128\co #### `flatTop(i, N)` -$$w(n) = 1 - 1.93\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 1.29\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.388\cos\!\left(\frac{6\pi n}{N\!-\!1}\right) + 0.028\cos\!\left(\frac{8\pi n}{N\!-\!1}\right)$$ +$w(n) = 1 - 1.93\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 1.29\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.388\cos\!\left(\frac{6\pi n}{N\!-\!1}\right) + 0.028\cos\!\left(\frac{8\pi n}{N\!-\!1}\right)$ @@ -216,7 +216,7 @@ $$w(n) = 1 - 1.93\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 1.29\cos\!\left(\fr #### `bartlettHann(i, N)` -$$w(n) = 0.62 - 0.48\left|\frac{n}{N\!-\!1} - 0.5\right| - 0.38\cos\!\left(\frac{2\pi n}{N-1}\right)$$ +$w(n) = 0.62 - 0.48\left|\frac{n}{N\!-\!1} - 0.5\right| - 0.38\cos\!\left(\frac{2\pi n}{N-1}\right)$ @@ -227,7 +227,7 @@ Bartlett-Hann hybrid. Balanced near/far sidelobe levels. Ha & Pearce 1989. #### `lanczos(i, N)` -$$w(n) = \operatorname{sinc}\!\left(\frac{2n}{N-1} - 1\right)$$ +$w(n) = \text{sinc}\!\left(\frac{2n}{N-1} - 1\right)$ @@ -238,7 +238,7 @@ Sinc main lobe. Image resampling, interpolation (FFmpeg, ImageMagick). Duchon 19 #### `parzen(i, N)` -$$w(n) = \begin{cases} 1 - 6a^2(1-a) & |a| \le 0.5 \\\ 2(1-a)^3 & |a| > 0.5 \end{cases} \quad a = \left|\frac{2n-N+1}{N-1}\right|$$ +$w(n) = 1 - 6a^2(1-a)$ for $|a| \le 0.5$,   $w(n) = 2(1-a)^3$ for $|a| > 0.5$,   where $a = |{(2n-N+1)}/{(N-1)}|$ @@ -249,7 +249,7 @@ $$w(n) = \begin{cases} 1 - 6a^2(1-a) & |a| \le 0.5 \\\ 2(1-a)^3 & |a| > 0.5 \end #### `bohman(i, N)` -$$w(n) = (1-|a|)\cos(\pi|a|) + \frac{\sin(\pi|a|)}{\pi} \quad a = \frac{2n-N+1}{N-1}$$ +$w(n) = (1-|a|)\cos(\pi|a|) + \frac{\sin(\pi|a|)}{\pi} \quad a = \frac{2n-N+1}{N-1}$ @@ -266,7 +266,7 @@ Autocorrelation of cosine window. Fast sidelobe decay, spectral estimation. `beta`: shape — 0 → rectangular, 5.4 → Hamming, **8.6** (default) → Blackman. -$$w(n) = \frac{I_0\!\left(\beta\sqrt{1 - \left(\frac{2n-N+1}{N-1}\right)^2}\right)}{I_0(\beta)}$$ +$w(n) = \frac{I_0\!\left(\beta\sqrt{1 - \left(\frac{2n-N+1}{N-1}\right)^2}\right)}{I_0(\beta)}$ @@ -278,7 +278,7 @@ Near-optimal DPSS approximation via Bessel I₀. The standard parameterized wind `sigma`: width, default **0.4**. -$$w(n) = \exp\!\left[-\frac{1}{2}\left(\frac{2n-N+1}{\sigma(N-1)}\right)^2\right]$$ +$w(n) = \exp\!\left[-\frac{1}{2}\left(\frac{2n-N+1}{\sigma(N-1)}\right)^2\right]$ @@ -290,7 +290,7 @@ Gaussian bell, minimum time-bandwidth product. STFT/Gabor transform, frequency e `sigma`: width (default **0.4**), `p`: shape — 2 = Gaussian, →∞ = rectangular. -$$w(n) = \exp\!\left[-\frac{1}{2}\left|\frac{2n-N+1}{\sigma(N-1)}\right|^p\right]$$ +$w(n) = \exp\!\left[-\frac{1}{2}\left|\frac{2n-N+1}{\sigma(N-1)}\right|^p\right]$ @@ -302,7 +302,7 @@ Continuous family between Gaussian and rectangular. Adjustable time-frequency tr `alpha`: taper fraction — 0 → rectangular, **0.5** (default), 1 → Hann. -$$w(n) = \begin{cases} \frac{1}{2}\left[1+\cos\!\left(\pi\!\left(\frac{n}{\alpha(N\!-\!1)/2}-1\right)\right)\right] & n \le \frac{\alpha(N\!-\!1)}{2} \\\ 1 & \text{center} \\\ \text{symmetric} & n \ge N\!-\!1\!-\!\frac{\alpha(N\!-\!1)}{2} \end{cases}$$ +$w(n) = \tfrac{1}{2}[1+\cos(\pi(n/(\alpha(N\!-\!1)/2)-1))]$ in the tapered edges, $w(n) = 1$ in the flat center. @@ -324,7 +324,7 @@ C∞-smooth bump function (infinitely differentiable). Gravitational wave analys `alpha`: exponent — 0 → rectangular, 1 → cosine, **2** (default) → Hann. -$$w(n) = \sin^\alpha\!\left(\frac{\pi n}{N-1}\right)$$ +$w(n) = \sin^\alpha\!\left(\frac{\pi n}{N-1}\right)$ @@ -336,7 +336,7 @@ sin^α family. Codec design, parameterized spectral analysis. `tau`: time constant, default **1**. -$$w(n) = \exp\!\left(\frac{-|2n-N+1|}{\tau(N-1)}\right)$$ +$w(n) = \exp\!\left(\frac{-|2n-N+1|}{\tau(N-1)}\right)$ @@ -348,7 +348,7 @@ Exponential decay from center. Modal analysis, impact testing (compensates under `alpha`: decay — default **2**. At α ≥ 2, the transform has no sidelobes. -$$w(n) = \frac{1}{2}\left(1-\cos\frac{2\pi n}{N\!-\!1}\right)\exp\!\left(\frac{-\alpha|2n\!-\!N\!+\!1|}{N-1}\right)$$ +$w(n) = \frac{1}{2}\left(1-\cos\frac{2\pi n}{N\!-\!1}\right)\exp\!\left(\frac{-\alpha|2n\!-\!N\!+\!1|}{N-1}\right)$ @@ -360,7 +360,7 @@ Hann × exponential product. Unique no-sidelobe property enables frequency estim `alpha`: width, default **3**. -$$w(n) = \frac{1}{1+\left(\frac{\alpha(2n-N+1)}{N-1}\right)^2}$$ +$w(n) = \frac{1}{1+\left(\frac{\alpha(2n-N+1)}{N-1}\right)^2}$ @@ -372,7 +372,7 @@ Lorentzian 1/(1+x²) shape. Matches spectral line shapes in spectroscopy. Harris `order`: **1** (default) = Hann, 2, 3. Throws for other values. -$$w(n) = \frac{1}{Z}\sum_{k=0}^{K}(-1)^k a_k\cos\frac{2\pi kn}{N-1}$$ +$w(n) = \frac{1}{Z}\sum_{k=0}^{K}(-1)^k a_k\cos\frac{2\pi kn}{N-1}$ @@ -400,7 +400,7 @@ These compute the full window on first call and cache the result. Recomputed whe `dB`: sidelobe attenuation, default **100**. -$$W(k) = (-1)^k T_{N-1}\!\left(\beta\cos\frac{\pi k}{N}\right),\quad w = \operatorname{IDFT}(W)$$ +$W(k) = (-1)^k T_{N-1}\!\left(\beta\cos\frac{\pi k}{N}\right),\quad w = \text{IDFT}(W)$ @@ -412,7 +412,7 @@ Optimal: narrowest main lobe for a given equiripple sidelobe level. Antenna arra `nbar`: number of constant-level sidelobes (default **4**), `sll`: sidelobe level in dB (default **30**). -$$w(n) = 1 + 2\sum_{m=1}^{\bar{n}-1} F_m \cos\frac{2\pi m(n-(N\!-\!1)/2)}{N}$$ +$w(n) = 1 + 2\sum_{m=1}^{\bar{n}-1} F_m \cos\frac{2\pi m(n-(N\!-\!1)/2)}{N}$ @@ -424,7 +424,7 @@ Dolph-Chebyshev variant with monotonically decreasing sidelobes. The radar commu `beta`: shape, default **8.6**. N must be even. -$$w(n) = \sqrt{\frac{\sum_{j=0}^{n} K(j)}{\sum_{j=0}^{N/2} K(j)}} \quad K(j) = I_0\!\left(\beta\sqrt{1-\left(\frac{2j-N/2}{N/2}\right)^2}\right)$$ +$w(n) = \sqrt{\frac{\sum_{j=0}^{n} K(j)}{\sum_{j=0}^{N/2} K(j)}} \quad K(j) = I_0\!\left(\beta\sqrt{1-\left(\frac{2j-N/2}{N/2}\right)^2}\right)$ @@ -436,7 +436,7 @@ Satisfies the Princen-Bradley condition for perfect MDCT reconstruction. AAC, Vo `W`: half-bandwidth [0, 0.5], default **0.1**. -$$\mathbf{T}\mathbf{v} = \lambda\mathbf{v} \quad T_{jk} = \frac{\sin 2\pi W(j-k)}{\pi(j-k)}$$ +$\mathbf{T}\mathbf{v} = \lambda\mathbf{v} \quad T_{jk} = \frac{\sin 2\pi W(j-k)}{\pi(j-k)}$ @@ -448,7 +448,7 @@ Dominant eigenvector of the sinc Toeplitz matrix — provably optimal energy con `mu`: 0 → Dolph-Chebyshev, **1** (default) → Saramaki. `xmu`: sidelobe control (default **1**). -$$W(k) = C_n^\mu\!\left(x_\mu\cos\frac{\pi k}{N}\right),\quad w = \operatorname{IDFT}(W)$$ +$W(k) = C_n^\mu\!\left(x_\mu\cos\frac{\pi k}{N}\right),\quad w = \text{IDFT}(W)$ From af83ea671e159ff4a4e56002ad10c4f9c24c994a Mon Sep 17 00:00:00 2001 From: "Dmitry Iv." Date: Wed, 25 Mar 2026 23:48:17 -0400 Subject: [PATCH 04/26] Update README.md --- README.md | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/README.md b/README.md index 3402b2d..bea6213 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,6 @@ cola(hann, 1024, 512) // → 0 (perfect STFT reconstruction at 50% ove ### Simple — no parameters ---- #### `rectangular(i, N)` @@ -69,7 +68,6 @@ $w(n) = 1$ No windowing at all. Best frequency resolution, worst spectral leakage. Use for transient signals already zero at edges, or harmonic analysis with integer cycles. **-13 dB** peak sidelobe · **-6 dB/oct** rolloff ---- #### `triangular(i, N)` @@ -80,7 +78,6 @@ $w(n) = 1 - \left|\frac{2n - N + 1}{N}\right|$ Linear taper, nonzero endpoints. Simple smoothing, 2nd-order B-spline. **-27 dB** peak sidelobe · **-12 dB/oct** rolloff ---- #### `bartlett(i, N)` @@ -91,7 +88,6 @@ $w(n) = 1 - \left|\frac{2n - N + 1}{N - 1}\right|$ Linear taper, zero endpoints. Bartlett's method PSD estimation. Bartlett 1950. **-27 dB** peak sidelobe · **-12 dB/oct** rolloff ---- #### `welch(i, N)` @@ -102,7 +98,6 @@ $w(n) = 1 - \left(\frac{2n - N + 1}{N - 1}\right)^2$ Parabolic taper. Welch's method PSD estimation. Welch 1967. **-21 dB** peak sidelobe · **-12 dB/oct** rolloff ---- #### `connes(i, N)` @@ -113,7 +108,6 @@ $w(n) = \left[1 - \left(\frac{2n - N + 1}{N - 1}\right)^2\right]^2$ Welch squared (4th power parabolic). FTIR spectroscopy, interferogram apodization. Connes 1961. **-24 dB/oct** rolloff ---- #### `hann(i, N)` @@ -124,7 +118,6 @@ $w(n) = 0.5 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right)$ Raised cosine, zero endpoints. The default general-purpose choice. STFT with 50% overlap (COLA). Also called "Hanning" (misnomer). Blackman & Tukey 1958. **-32 dB** peak sidelobe · **-18 dB/oct** rolloff ---- #### `hamming(i, N)` @@ -135,7 +128,6 @@ $w(n) = 0.54 - 0.46\cos\!\left(\frac{2\pi n}{N-1}\right)$ Raised cosine, nonzero endpoints. Optimized for first sidelobe cancellation. FIR filter design, speech processing. Hamming 1977. **-43 dB** peak sidelobe · **-6 dB/oct** rolloff ---- #### `cosine(i, N)` @@ -146,7 +138,6 @@ $w(n) = \sin\!\left(\frac{\pi n}{N-1}\right)$ Half-period sine. MDCT audio codecs: MP3, AAC, Vorbis. Princen & Bradley 1987. **-23 dB** peak sidelobe · **-12 dB/oct** rolloff ---- #### `blackman(i, N)` @@ -157,7 +148,6 @@ $w(n) = 0.42 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.08\cos\!\left(\frac{ 3-term cosine sum. Better leakage than Hann at the cost of wider main lobe. Blackman & Tukey 1958. **-58 dB** peak sidelobe · **-18 dB/oct** rolloff ---- #### `exactBlackman(i, N)` @@ -168,7 +158,6 @@ $w(n) = 0.42659 - 0.49656\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.076849\cos\!\ Blackman with exact zero placement at 3rd and 4th sidelobes. Harris 1978. **-69 dB** peak sidelobe · **-6 dB/oct** rolloff ---- #### `nuttall(i, N)` @@ -179,7 +168,6 @@ $w(n) = 0.355768 - 0.487396\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.144232\ 4-term cosine sum, continuous 1st derivative. High-dynamic-range analysis without edge discontinuity. Nuttall 1981. **-93 dB** peak sidelobe · **-18 dB/oct** rolloff ---- #### `blackmanNuttall(i, N)` @@ -190,7 +178,6 @@ $w(n) = 0.3635819 - 0.4891775\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.13659 4-term cosine sum, lowest sidelobes among 4-term windows. Nuttall 1981. **-98 dB** peak sidelobe · **-6 dB/oct** rolloff ---- #### `blackmanHarris(i, N)` @@ -201,7 +188,6 @@ $w(n) = 0.35875 - 0.48829\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.14128\cos 4-term minimum sidelobe. ADC testing, measurement instrumentation, >80 dB dynamic range. Harris 1978. **-92 dB** peak sidelobe · **-6 dB/oct** rolloff ---- #### `flatTop(i, N)` @@ -212,7 +198,6 @@ $w(n) = 1 - 1.93\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 1.29\cos\!\left(\fra 5-term cosine sum with near-zero scalloping. Peak value ~4.64 (by design — optimized for amplitude accuracy, not energy). Amplitude calibration, transducer calibration (~0.01 dB accuracy). ISO 18431. Heinzel 2002. **-93 dB** peak sidelobe · **-6 dB/oct** rolloff ---- #### `bartlettHann(i, N)` @@ -223,7 +208,6 @@ $w(n) = 0.62 - 0.48\left|\frac{n}{N\!-\!1} - 0.5\right| - 0.38\cos\!\left(\frac{ Bartlett-Hann hybrid. Balanced near/far sidelobe levels. Ha & Pearce 1989. **-36 dB** peak sidelobe ---- #### `lanczos(i, N)` @@ -234,7 +218,6 @@ $w(n) = \text{sinc}\!\left(\frac{2n}{N-1} - 1\right)$ Sinc main lobe. Image resampling, interpolation (FFmpeg, ImageMagick). Duchon 1979. **-26 dB** peak sidelobe ---- #### `parzen(i, N)` @@ -245,7 +228,6 @@ $w(n) = 1 - 6a^2(1-a)$ for $|a| \le 0.5$,   $w(n) = 2(1-a)^3$ for $|a| > 0. 4th-order B-spline. Always-positive spectrum. Kernel density estimation. Parzen 1961. **-53 dB** peak sidelobe · **-24 dB/oct** rolloff ---- #### `bohman(i, N)` @@ -256,11 +238,9 @@ $w(n) = (1-|a|)\cos(\pi|a|) + \frac{\sin(\pi|a|)}{\pi} \quad a = \frac{2n-N+1}{N Autocorrelation of cosine window. Fast sidelobe decay, spectral estimation. **-46 dB** peak sidelobe · **-24 dB/oct** rolloff ---- ### Parameterized — adjustable tradeoff ---- #### `kaiser(i, N, beta)` @@ -272,7 +252,6 @@ $w(n) = \frac{I_0\!\left(\beta\sqrt{1 - \left(\frac{2n-N+1}{N-1}\right)^2}\right Near-optimal DPSS approximation via Bessel I₀. The standard parameterized window for FIR filter design. Kaiser 1974. ---- #### `gaussian(i, N, sigma)` @@ -284,7 +263,6 @@ $w(n) = \exp\!\left[-\frac{1}{2}\left(\frac{2n-N+1}{\sigma(N-1)}\right)^2\right] Gaussian bell, minimum time-bandwidth product. STFT/Gabor transform, frequency estimation via parabolic interpolation. Gabor 1946. ---- #### `generalizedNormal(i, N, sigma, p)` @@ -296,7 +274,6 @@ $w(n) = \exp\!\left[-\frac{1}{2}\left|\frac{2n-N+1}{\sigma(N-1)}\right|^p\right] Continuous family between Gaussian and rectangular. Adjustable time-frequency tradeoff. ---- #### `tukey(i, N, alpha)` @@ -308,7 +285,6 @@ $w(n) = \tfrac{1}{2}[1+\cos(\pi(n/(\alpha(N\!-\!1)/2)-1))]$ in the tapered edges Flat center with cosine-tapered edges. Preserves signal amplitude while tapering. Vibration analysis, LIGO. ---- #### `planckTaper(i, N, epsilon)` @@ -318,7 +294,6 @@ Flat center with cosine-tapered edges. Preserves signal amplitude while tapering C∞-smooth bump function (infinitely differentiable). Gravitational wave analysis (LIGO/Virgo). McKechan 2010. ---- #### `powerOfSine(i, N, alpha)` @@ -330,7 +305,6 @@ $w(n) = \sin^\alpha\!\left(\frac{\pi n}{N-1}\right)$ sin^α family. Codec design, parameterized spectral analysis. ---- #### `exponential(i, N, tau)` @@ -342,7 +316,6 @@ $w(n) = \exp\!\left(\frac{-|2n-N+1|}{\tau(N-1)}\right)$ Exponential decay from center. Modal analysis, impact testing (compensates underdamped responses). Harris 1978. ---- #### `hannPoisson(i, N, alpha)` @@ -354,7 +327,6 @@ $w(n) = \frac{1}{2}\left(1-\cos\frac{2\pi n}{N\!-\!1}\right)\exp\!\left(\frac{-\ Hann × exponential product. Unique no-sidelobe property enables frequency estimators using convex optimization. ---- #### `cauchy(i, N, alpha)` @@ -366,7 +338,6 @@ $w(n) = \frac{1}{1+\left(\frac{\alpha(2n-N+1)}{N-1}\right)^2}$ Lorentzian 1/(1+x²) shape. Matches spectral line shapes in spectroscopy. Harris 1978. ---- #### `rifeVincent(i, N, order)` @@ -378,7 +349,6 @@ $w(n) = \frac{1}{Z}\sum_{k=0}^{K}(-1)^k a_k\cos\frac{2\pi kn}{N-1}$ Class I cosine-sum optimized for sidelobe fall-off. Power grid harmonic analysis, interpolated DFT. Rife & Vincent 1970. ---- #### `confinedGaussian(i, N, sigmaT)` @@ -388,13 +358,11 @@ Class I cosine-sum optimized for sidelobe fall-off. Power grid harmonic analysis Approximate confined Gaussian — optimal RMS time-frequency bandwidth. Time-frequency analysis, audio coding (MP3/AAC). Starosielec 2014. ---- ### Array-computed — cached These compute the full window on first call and cache the result. Recomputed when N or parameters change. ---- #### `dolphChebyshev(i, N, dB)` @@ -406,7 +374,6 @@ $W(k) = (-1)^k T_{N-1}\!\left(\beta\cos\frac{\pi k}{N}\right),\quad w = \text{ID Optimal: narrowest main lobe for a given equiripple sidelobe level. Antenna array design, radar beam patterns. Dolph 1946. ---- #### `taylor(i, N, nbar, sll)` @@ -418,7 +385,6 @@ $w(n) = 1 + 2\sum_{m=1}^{\bar{n}-1} F_m \cos\frac{2\pi m(n-(N\!-\!1)/2)}{N}$ Dolph-Chebyshev variant with monotonically decreasing sidelobes. The radar community standard for SAR image formation. Taylor 1955. ---- #### `kaiserBesselDerived(i, N, beta)` @@ -430,7 +396,6 @@ $w(n) = \sqrt{\frac{\sum_{j=0}^{n} K(j)}{\sum_{j=0}^{N/2} K(j)}} \quad K(j) = I_ Satisfies the Princen-Bradley condition for perfect MDCT reconstruction. AAC, Vorbis, Opus audio codecs (long blocks). Princen & Bradley 1987. ---- #### `dpss(i, N, W)` @@ -442,7 +407,6 @@ $\mathbf{T}\mathbf{v} = \lambda\mathbf{v} \quad T_{jk} = \frac{\sin 2\pi W(j-k)} Dominant eigenvector of the sinc Toeplitz matrix — provably optimal energy concentration in a frequency band. Also called Slepian window. Thomson multitaper spectral estimation, neuroscience (EEG/MEG), climate science. Slepian 1978. ---- #### `ultraspherical(i, N, mu, xmu)` @@ -454,7 +418,6 @@ $W(k) = C_n^\mu\!\left(x_\mu\cos\frac{\pi k}{N}\right),\quad w = \text{IDFT}(W)$ Gegenbauer polynomial window. Independent control of sidelobe level and taper rate. Advanced antenna design, beamforming. Streit 1984. ---- ## Quantitative metrics From 7a2b539f9ce70a2bc8b607204cb687725390d437 Mon Sep 17 00:00:00 2001 From: "Dmitry Iv." Date: Wed, 25 Mar 2026 23:49:01 -0400 Subject: [PATCH 05/26] Update README.md --- README.md | 68 +++++++++++++++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index bea6213..26b7e63 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ cola(hann, 1024, 512) // → 0 (perfect STFT reconstruction at 50% ove ### Simple — no parameters -#### `rectangular(i, N)` +### `rectangular(i, N)` $w(n) = 1$ @@ -69,7 +69,7 @@ No windowing at all. Best frequency resolution, worst spectral leakage. Use for **-13 dB** peak sidelobe · **-6 dB/oct** rolloff -#### `triangular(i, N)` +### `triangular(i, N)` $w(n) = 1 - \left|\frac{2n - N + 1}{N}\right|$ @@ -79,7 +79,7 @@ Linear taper, nonzero endpoints. Simple smoothing, 2nd-order B-spline. **-27 dB** peak sidelobe · **-12 dB/oct** rolloff -#### `bartlett(i, N)` +### `bartlett(i, N)` $w(n) = 1 - \left|\frac{2n - N + 1}{N - 1}\right|$ @@ -89,7 +89,7 @@ Linear taper, zero endpoints. Bartlett's method PSD estimation. Bartlett 1950. **-27 dB** peak sidelobe · **-12 dB/oct** rolloff -#### `welch(i, N)` +### `welch(i, N)` $w(n) = 1 - \left(\frac{2n - N + 1}{N - 1}\right)^2$ @@ -99,7 +99,7 @@ Parabolic taper. Welch's method PSD estimation. Welch 1967. **-21 dB** peak sidelobe · **-12 dB/oct** rolloff -#### `connes(i, N)` +### `connes(i, N)` $w(n) = \left[1 - \left(\frac{2n - N + 1}{N - 1}\right)^2\right]^2$ @@ -109,7 +109,7 @@ Welch squared (4th power parabolic). FTIR spectroscopy, interferogram apodizatio **-24 dB/oct** rolloff -#### `hann(i, N)` +### `hann(i, N)` $w(n) = 0.5 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right)$ @@ -119,7 +119,7 @@ Raised cosine, zero endpoints. The default general-purpose choice. STFT with 50% **-32 dB** peak sidelobe · **-18 dB/oct** rolloff -#### `hamming(i, N)` +### `hamming(i, N)` $w(n) = 0.54 - 0.46\cos\!\left(\frac{2\pi n}{N-1}\right)$ @@ -129,7 +129,7 @@ Raised cosine, nonzero endpoints. Optimized for first sidelobe cancellation. FIR **-43 dB** peak sidelobe · **-6 dB/oct** rolloff -#### `cosine(i, N)` +### `cosine(i, N)` $w(n) = \sin\!\left(\frac{\pi n}{N-1}\right)$ @@ -139,7 +139,7 @@ Half-period sine. MDCT audio codecs: MP3, AAC, Vorbis. Princen & Bradley 1987. **-23 dB** peak sidelobe · **-12 dB/oct** rolloff -#### `blackman(i, N)` +### `blackman(i, N)` $w(n) = 0.42 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.08\cos\!\left(\frac{4\pi n}{N-1}\right)$ @@ -149,7 +149,7 @@ $w(n) = 0.42 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.08\cos\!\left(\frac{ **-58 dB** peak sidelobe · **-18 dB/oct** rolloff -#### `exactBlackman(i, N)` +### `exactBlackman(i, N)` $w(n) = 0.42659 - 0.49656\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.076849\cos\!\left(\frac{4\pi n}{N-1}\right)$ @@ -159,7 +159,7 @@ Blackman with exact zero placement at 3rd and 4th sidelobes. Harris 1978. **-69 dB** peak sidelobe · **-6 dB/oct** rolloff -#### `nuttall(i, N)` +### `nuttall(i, N)` $w(n) = 0.355768 - 0.487396\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.144232\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.012604\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ @@ -169,7 +169,7 @@ $w(n) = 0.355768 - 0.487396\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.144232\ **-93 dB** peak sidelobe · **-18 dB/oct** rolloff -#### `blackmanNuttall(i, N)` +### `blackmanNuttall(i, N)` $w(n) = 0.3635819 - 0.4891775\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.1365995\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.0106411\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ @@ -179,7 +179,7 @@ $w(n) = 0.3635819 - 0.4891775\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.13659 **-98 dB** peak sidelobe · **-6 dB/oct** rolloff -#### `blackmanHarris(i, N)` +### `blackmanHarris(i, N)` $w(n) = 0.35875 - 0.48829\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.14128\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.01168\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ @@ -189,7 +189,7 @@ $w(n) = 0.35875 - 0.48829\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.14128\cos **-92 dB** peak sidelobe · **-6 dB/oct** rolloff -#### `flatTop(i, N)` +### `flatTop(i, N)` $w(n) = 1 - 1.93\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 1.29\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.388\cos\!\left(\frac{6\pi n}{N\!-\!1}\right) + 0.028\cos\!\left(\frac{8\pi n}{N\!-\!1}\right)$ @@ -199,7 +199,7 @@ $w(n) = 1 - 1.93\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 1.29\cos\!\left(\fra **-93 dB** peak sidelobe · **-6 dB/oct** rolloff -#### `bartlettHann(i, N)` +### `bartlettHann(i, N)` $w(n) = 0.62 - 0.48\left|\frac{n}{N\!-\!1} - 0.5\right| - 0.38\cos\!\left(\frac{2\pi n}{N-1}\right)$ @@ -209,7 +209,7 @@ Bartlett-Hann hybrid. Balanced near/far sidelobe levels. Ha & Pearce 1989. **-36 dB** peak sidelobe -#### `lanczos(i, N)` +### `lanczos(i, N)` $w(n) = \text{sinc}\!\left(\frac{2n}{N-1} - 1\right)$ @@ -219,7 +219,7 @@ Sinc main lobe. Image resampling, interpolation (FFmpeg, ImageMagick). Duchon 19 **-26 dB** peak sidelobe -#### `parzen(i, N)` +### `parzen(i, N)` $w(n) = 1 - 6a^2(1-a)$ for $|a| \le 0.5$,   $w(n) = 2(1-a)^3$ for $|a| > 0.5$,   where $a = |{(2n-N+1)}/{(N-1)}|$ @@ -229,7 +229,7 @@ $w(n) = 1 - 6a^2(1-a)$ for $|a| \le 0.5$,   $w(n) = 2(1-a)^3$ for $|a| > 0. **-53 dB** peak sidelobe · **-24 dB/oct** rolloff -#### `bohman(i, N)` +### `bohman(i, N)` $w(n) = (1-|a|)\cos(\pi|a|) + \frac{\sin(\pi|a|)}{\pi} \quad a = \frac{2n-N+1}{N-1}$ @@ -242,7 +242,7 @@ Autocorrelation of cosine window. Fast sidelobe decay, spectral estimation. ### Parameterized — adjustable tradeoff -#### `kaiser(i, N, beta)` +### `kaiser(i, N, beta)` `beta`: shape — 0 → rectangular, 5.4 → Hamming, **8.6** (default) → Blackman. @@ -253,7 +253,7 @@ $w(n) = \frac{I_0\!\left(\beta\sqrt{1 - \left(\frac{2n-N+1}{N-1}\right)^2}\right Near-optimal DPSS approximation via Bessel I₀. The standard parameterized window for FIR filter design. Kaiser 1974. -#### `gaussian(i, N, sigma)` +### `gaussian(i, N, sigma)` `sigma`: width, default **0.4**. @@ -264,7 +264,7 @@ $w(n) = \exp\!\left[-\frac{1}{2}\left(\frac{2n-N+1}{\sigma(N-1)}\right)^2\right] Gaussian bell, minimum time-bandwidth product. STFT/Gabor transform, frequency estimation via parabolic interpolation. Gabor 1946. -#### `generalizedNormal(i, N, sigma, p)` +### `generalizedNormal(i, N, sigma, p)` `sigma`: width (default **0.4**), `p`: shape — 2 = Gaussian, →∞ = rectangular. @@ -275,7 +275,7 @@ $w(n) = \exp\!\left[-\frac{1}{2}\left|\frac{2n-N+1}{\sigma(N-1)}\right|^p\right] Continuous family between Gaussian and rectangular. Adjustable time-frequency tradeoff. -#### `tukey(i, N, alpha)` +### `tukey(i, N, alpha)` `alpha`: taper fraction — 0 → rectangular, **0.5** (default), 1 → Hann. @@ -286,7 +286,7 @@ $w(n) = \tfrac{1}{2}[1+\cos(\pi(n/(\alpha(N\!-\!1)/2)-1))]$ in the tapered edges Flat center with cosine-tapered edges. Preserves signal amplitude while tapering. Vibration analysis, LIGO. -#### `planckTaper(i, N, epsilon)` +### `planckTaper(i, N, epsilon)` `epsilon`: taper fraction, default **0.1**. @@ -295,7 +295,7 @@ Flat center with cosine-tapered edges. Preserves signal amplitude while tapering C∞-smooth bump function (infinitely differentiable). Gravitational wave analysis (LIGO/Virgo). McKechan 2010. -#### `powerOfSine(i, N, alpha)` +### `powerOfSine(i, N, alpha)` `alpha`: exponent — 0 → rectangular, 1 → cosine, **2** (default) → Hann. @@ -306,7 +306,7 @@ $w(n) = \sin^\alpha\!\left(\frac{\pi n}{N-1}\right)$ sin^α family. Codec design, parameterized spectral analysis. -#### `exponential(i, N, tau)` +### `exponential(i, N, tau)` `tau`: time constant, default **1**. @@ -317,7 +317,7 @@ $w(n) = \exp\!\left(\frac{-|2n-N+1|}{\tau(N-1)}\right)$ Exponential decay from center. Modal analysis, impact testing (compensates underdamped responses). Harris 1978. -#### `hannPoisson(i, N, alpha)` +### `hannPoisson(i, N, alpha)` `alpha`: decay — default **2**. At α ≥ 2, the transform has no sidelobes. @@ -328,7 +328,7 @@ $w(n) = \frac{1}{2}\left(1-\cos\frac{2\pi n}{N\!-\!1}\right)\exp\!\left(\frac{-\ Hann × exponential product. Unique no-sidelobe property enables frequency estimators using convex optimization. -#### `cauchy(i, N, alpha)` +### `cauchy(i, N, alpha)` `alpha`: width, default **3**. @@ -339,7 +339,7 @@ $w(n) = \frac{1}{1+\left(\frac{\alpha(2n-N+1)}{N-1}\right)^2}$ Lorentzian 1/(1+x²) shape. Matches spectral line shapes in spectroscopy. Harris 1978. -#### `rifeVincent(i, N, order)` +### `rifeVincent(i, N, order)` `order`: **1** (default) = Hann, 2, 3. Throws for other values. @@ -350,7 +350,7 @@ $w(n) = \frac{1}{Z}\sum_{k=0}^{K}(-1)^k a_k\cos\frac{2\pi kn}{N-1}$ Class I cosine-sum optimized for sidelobe fall-off. Power grid harmonic analysis, interpolated DFT. Rife & Vincent 1970. -#### `confinedGaussian(i, N, sigmaT)` +### `confinedGaussian(i, N, sigmaT)` `sigmaT`: temporal width, default **0.1**. @@ -364,7 +364,7 @@ Approximate confined Gaussian — optimal RMS time-frequency bandwidth. Time-fre These compute the full window on first call and cache the result. Recomputed when N or parameters change. -#### `dolphChebyshev(i, N, dB)` +### `dolphChebyshev(i, N, dB)` `dB`: sidelobe attenuation, default **100**. @@ -375,7 +375,7 @@ $W(k) = (-1)^k T_{N-1}\!\left(\beta\cos\frac{\pi k}{N}\right),\quad w = \text{ID Optimal: narrowest main lobe for a given equiripple sidelobe level. Antenna array design, radar beam patterns. Dolph 1946. -#### `taylor(i, N, nbar, sll)` +### `taylor(i, N, nbar, sll)` `nbar`: number of constant-level sidelobes (default **4**), `sll`: sidelobe level in dB (default **30**). @@ -386,7 +386,7 @@ $w(n) = 1 + 2\sum_{m=1}^{\bar{n}-1} F_m \cos\frac{2\pi m(n-(N\!-\!1)/2)}{N}$ Dolph-Chebyshev variant with monotonically decreasing sidelobes. The radar community standard for SAR image formation. Taylor 1955. -#### `kaiserBesselDerived(i, N, beta)` +### `kaiserBesselDerived(i, N, beta)` `beta`: shape, default **8.6**. N must be even. @@ -397,7 +397,7 @@ $w(n) = \sqrt{\frac{\sum_{j=0}^{n} K(j)}{\sum_{j=0}^{N/2} K(j)}} \quad K(j) = I_ Satisfies the Princen-Bradley condition for perfect MDCT reconstruction. AAC, Vorbis, Opus audio codecs (long blocks). Princen & Bradley 1987. -#### `dpss(i, N, W)` +### `dpss(i, N, W)` `W`: half-bandwidth [0, 0.5], default **0.1**. @@ -408,7 +408,7 @@ $\mathbf{T}\mathbf{v} = \lambda\mathbf{v} \quad T_{jk} = \frac{\sin 2\pi W(j-k)} Dominant eigenvector of the sinc Toeplitz matrix — provably optimal energy concentration in a frequency band. Also called Slepian window. Thomson multitaper spectral estimation, neuroscience (EEG/MEG), climate science. Slepian 1978. -#### `ultraspherical(i, N, mu, xmu)` +### `ultraspherical(i, N, mu, xmu)` `mu`: 0 → Dolph-Chebyshev, **1** (default) → Saramaki. `xmu`: sidelobe control (default **1**). From 8035d1c038f6db7faa9c11ca46ef8aec4c55412c Mon Sep 17 00:00:00 2001 From: "Dmitry Iv." Date: Wed, 25 Mar 2026 23:51:10 -0400 Subject: [PATCH 06/26] Remove titles --- docs/generate.js | 9 ++- docs/plots/bartlett.svg | 87 +++++++++++++------------- docs/plots/bartlettHann.svg | 87 +++++++++++++------------- docs/plots/blackman.svg | 87 +++++++++++++------------- docs/plots/blackmanHarris.svg | 87 +++++++++++++------------- docs/plots/blackmanNuttall.svg | 87 +++++++++++++------------- docs/plots/bohman.svg | 87 +++++++++++++------------- docs/plots/cauchy.svg | 87 +++++++++++++------------- docs/plots/confinedGaussian.svg | 87 +++++++++++++------------- docs/plots/connes.svg | 87 +++++++++++++------------- docs/plots/cosine.svg | 87 +++++++++++++------------- docs/plots/dolphChebyshev.svg | 87 +++++++++++++------------- docs/plots/dpss.svg | 87 +++++++++++++------------- docs/plots/exactBlackman.svg | 87 +++++++++++++------------- docs/plots/exponential.svg | 87 +++++++++++++------------- docs/plots/flatTop.svg | 99 +++++++++++++++--------------- docs/plots/gaussian.svg | 87 +++++++++++++------------- docs/plots/generalizedNormal.svg | 87 +++++++++++++------------- docs/plots/hamming.svg | 87 +++++++++++++------------- docs/plots/hann.svg | 87 +++++++++++++------------- docs/plots/hannPoisson.svg | 87 +++++++++++++------------- docs/plots/kaiser.svg | 87 +++++++++++++------------- docs/plots/kaiserBesselDerived.svg | 87 +++++++++++++------------- docs/plots/lanczos.svg | 87 +++++++++++++------------- docs/plots/nuttall.svg | 87 +++++++++++++------------- docs/plots/parzen.svg | 87 +++++++++++++------------- docs/plots/planckTaper.svg | 87 +++++++++++++------------- docs/plots/powerOfSine.svg | 87 +++++++++++++------------- docs/plots/rectangular.svg | 87 +++++++++++++------------- docs/plots/rifeVincent.svg | 87 +++++++++++++------------- docs/plots/taylor.svg | 87 +++++++++++++------------- docs/plots/triangular.svg | 87 +++++++++++++------------- docs/plots/tukey.svg | 87 +++++++++++++------------- docs/plots/ultraspherical.svg | 87 +++++++++++++------------- docs/plots/welch.svg | 87 +++++++++++++------------- 35 files changed, 1472 insertions(+), 1507 deletions(-) diff --git a/docs/generate.js b/docs/generate.js index f84d413..527498b 100644 --- a/docs/generate.js +++ b/docs/generate.js @@ -9,12 +9,12 @@ import { writeFileSync, mkdirSync } from 'node:fs' let N = 256, NFFT = 4096 // Layout -let W = 560, H = 210 -let L = { x: 52, y: 28, w: 205, h: 138 } // left panel (time) -let R = { x: 322, y: 28, w: 205, h: 138 } // right panel (freq) +let W = 560, H = 190 +let L = { x: 52, y: 12, w: 205, h: 140 } // left panel (time) +let R = { x: 322, y: 12, w: 205, h: 140 } // right panel (freq) // Palette -let CLR = '#4a90d9', GRID = '#e5e7eb', AXIS = '#d1d5db', TXT = '#6b7280', TITLE = '#374151' +let CLR = '#4a90d9', GRID = '#e5e7eb', AXIS = '#d1d5db', TXT = '#6b7280' let wins = [ 'rectangular', 'triangular', 'bartlett', 'welch', 'connes', @@ -49,7 +49,6 @@ for (let name of wins) { let svg = `\n` svg += ` \n` - svg += ` ${name}\n` svg += panel(L, samples, 0, 1, 0, yTop, [0, 0.5, 1], yTicks, true) svg += panel(R, db, 0, 0.5, -120, 0, [0, 0.1, 0.2, 0.3, 0.4, 0.5], [0, -40, -80, -120], false) diff --git a/docs/plots/bartlett.svg b/docs/plots/bartlett.svg index 00081a9..9371f08 100644 --- a/docs/plots/bartlett.svg +++ b/docs/plots/bartlett.svg @@ -1,45 +1,44 @@ - - - bartlett - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/bartlettHann.svg b/docs/plots/bartlettHann.svg index a6f1c74..0ede530 100644 --- a/docs/plots/bartlettHann.svg +++ b/docs/plots/bartlettHann.svg @@ -1,45 +1,44 @@ - - - bartlettHann - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/blackman.svg b/docs/plots/blackman.svg index 185fb78..14a4415 100644 --- a/docs/plots/blackman.svg +++ b/docs/plots/blackman.svg @@ -1,45 +1,44 @@ - - - blackman - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/blackmanHarris.svg b/docs/plots/blackmanHarris.svg index 7e7bac6..06a2da6 100644 --- a/docs/plots/blackmanHarris.svg +++ b/docs/plots/blackmanHarris.svg @@ -1,45 +1,44 @@ - - - blackmanHarris - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/blackmanNuttall.svg b/docs/plots/blackmanNuttall.svg index 7cbf7cc..a4aaecd 100644 --- a/docs/plots/blackmanNuttall.svg +++ b/docs/plots/blackmanNuttall.svg @@ -1,45 +1,44 @@ - - - blackmanNuttall - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/bohman.svg b/docs/plots/bohman.svg index 5104feb..662b23e 100644 --- a/docs/plots/bohman.svg +++ b/docs/plots/bohman.svg @@ -1,45 +1,44 @@ - - - bohman - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/cauchy.svg b/docs/plots/cauchy.svg index 7f04b29..a247e58 100644 --- a/docs/plots/cauchy.svg +++ b/docs/plots/cauchy.svg @@ -1,45 +1,44 @@ - - - cauchy - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/confinedGaussian.svg b/docs/plots/confinedGaussian.svg index 16efa41..2ef5238 100644 --- a/docs/plots/confinedGaussian.svg +++ b/docs/plots/confinedGaussian.svg @@ -1,45 +1,44 @@ - - - confinedGaussian - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/connes.svg b/docs/plots/connes.svg index 3ccba20..2bea071 100644 --- a/docs/plots/connes.svg +++ b/docs/plots/connes.svg @@ -1,45 +1,44 @@ - - - connes - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/cosine.svg b/docs/plots/cosine.svg index 332c9e4..036afff 100644 --- a/docs/plots/cosine.svg +++ b/docs/plots/cosine.svg @@ -1,45 +1,44 @@ - - - cosine - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/dolphChebyshev.svg b/docs/plots/dolphChebyshev.svg index e1c8744..123af36 100644 --- a/docs/plots/dolphChebyshev.svg +++ b/docs/plots/dolphChebyshev.svg @@ -1,45 +1,44 @@ - - - dolphChebyshev - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/dpss.svg b/docs/plots/dpss.svg index 9496115..73c02b5 100644 --- a/docs/plots/dpss.svg +++ b/docs/plots/dpss.svg @@ -1,45 +1,44 @@ - - - dpss - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/exactBlackman.svg b/docs/plots/exactBlackman.svg index 1500d6d..7d32fe5 100644 --- a/docs/plots/exactBlackman.svg +++ b/docs/plots/exactBlackman.svg @@ -1,45 +1,44 @@ - - - exactBlackman - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/exponential.svg b/docs/plots/exponential.svg index 4f113bc..0fcf90f 100644 --- a/docs/plots/exponential.svg +++ b/docs/plots/exponential.svg @@ -1,45 +1,44 @@ - - - exponential - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/flatTop.svg b/docs/plots/flatTop.svg index ad56373..b15c891 100644 --- a/docs/plots/flatTop.svg +++ b/docs/plots/flatTop.svg @@ -1,51 +1,50 @@ - - - flatTop - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 1 + + 2 + + 3 + + 4 + + 5 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/gaussian.svg b/docs/plots/gaussian.svg index 23042ff..4c64c37 100644 --- a/docs/plots/gaussian.svg +++ b/docs/plots/gaussian.svg @@ -1,45 +1,44 @@ - - - gaussian - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/generalizedNormal.svg b/docs/plots/generalizedNormal.svg index cbfa389..4c64c37 100644 --- a/docs/plots/generalizedNormal.svg +++ b/docs/plots/generalizedNormal.svg @@ -1,45 +1,44 @@ - - - generalizedNormal - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/hamming.svg b/docs/plots/hamming.svg index 4a3eaa2..67cd074 100644 --- a/docs/plots/hamming.svg +++ b/docs/plots/hamming.svg @@ -1,45 +1,44 @@ - - - hamming - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/hann.svg b/docs/plots/hann.svg index b98aa44..493cd24 100644 --- a/docs/plots/hann.svg +++ b/docs/plots/hann.svg @@ -1,45 +1,44 @@ - - - hann - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/hannPoisson.svg b/docs/plots/hannPoisson.svg index 019cbc4..79eba30 100644 --- a/docs/plots/hannPoisson.svg +++ b/docs/plots/hannPoisson.svg @@ -1,45 +1,44 @@ - - - hannPoisson - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/kaiser.svg b/docs/plots/kaiser.svg index c15fbcb..ef99b50 100644 --- a/docs/plots/kaiser.svg +++ b/docs/plots/kaiser.svg @@ -1,45 +1,44 @@ - - - kaiser - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/kaiserBesselDerived.svg b/docs/plots/kaiserBesselDerived.svg index e89945e..1e30ba6 100644 --- a/docs/plots/kaiserBesselDerived.svg +++ b/docs/plots/kaiserBesselDerived.svg @@ -1,45 +1,44 @@ - - - kaiserBesselDerived - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/lanczos.svg b/docs/plots/lanczos.svg index 6544f6a..4351188 100644 --- a/docs/plots/lanczos.svg +++ b/docs/plots/lanczos.svg @@ -1,45 +1,44 @@ - - - lanczos - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/nuttall.svg b/docs/plots/nuttall.svg index 8e4cb46..1cb502c 100644 --- a/docs/plots/nuttall.svg +++ b/docs/plots/nuttall.svg @@ -1,45 +1,44 @@ - - - nuttall - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/parzen.svg b/docs/plots/parzen.svg index c5fd11d..41f634e 100644 --- a/docs/plots/parzen.svg +++ b/docs/plots/parzen.svg @@ -1,45 +1,44 @@ - - - parzen - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/planckTaper.svg b/docs/plots/planckTaper.svg index ed7e5c4..48b5e60 100644 --- a/docs/plots/planckTaper.svg +++ b/docs/plots/planckTaper.svg @@ -1,45 +1,44 @@ - - - planckTaper - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/powerOfSine.svg b/docs/plots/powerOfSine.svg index b3cf20a..493cd24 100644 --- a/docs/plots/powerOfSine.svg +++ b/docs/plots/powerOfSine.svg @@ -1,45 +1,44 @@ - - - powerOfSine - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/rectangular.svg b/docs/plots/rectangular.svg index 94a449b..33a4c7c 100644 --- a/docs/plots/rectangular.svg +++ b/docs/plots/rectangular.svg @@ -1,45 +1,44 @@ - - - rectangular - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/rifeVincent.svg b/docs/plots/rifeVincent.svg index dcc4262..493cd24 100644 --- a/docs/plots/rifeVincent.svg +++ b/docs/plots/rifeVincent.svg @@ -1,45 +1,44 @@ - - - rifeVincent - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/taylor.svg b/docs/plots/taylor.svg index 9eb9254..543979f 100644 --- a/docs/plots/taylor.svg +++ b/docs/plots/taylor.svg @@ -1,45 +1,44 @@ - - - taylor - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/triangular.svg b/docs/plots/triangular.svg index 549d716..e6e2563 100644 --- a/docs/plots/triangular.svg +++ b/docs/plots/triangular.svg @@ -1,45 +1,44 @@ - - - triangular - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/tukey.svg b/docs/plots/tukey.svg index c1b6b19..d059cd8 100644 --- a/docs/plots/tukey.svg +++ b/docs/plots/tukey.svg @@ -1,45 +1,44 @@ - - - tukey - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/ultraspherical.svg b/docs/plots/ultraspherical.svg index cdedb37..33a4c7c 100644 --- a/docs/plots/ultraspherical.svg +++ b/docs/plots/ultraspherical.svg @@ -1,45 +1,44 @@ - - - ultraspherical - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/welch.svg b/docs/plots/welch.svg index 32b5145..742dbb8 100644 --- a/docs/plots/welch.svg +++ b/docs/plots/welch.svg @@ -1,45 +1,44 @@ - - - welch - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) From 1ad7a4d73b8769b1d417289f10f2bfa6b999b2ad Mon Sep 17 00:00:00 2001 From: "Dmitry Iv." Date: Wed, 25 Mar 2026 23:53:17 -0400 Subject: [PATCH 07/26] Update plots, readme --- README.md | 105 +++++++++++++++++++---------- docs/generate.js | 2 +- docs/plots/bartlett.svg | 2 +- docs/plots/bartlettHann.svg | 2 +- docs/plots/blackman.svg | 2 +- docs/plots/blackmanHarris.svg | 2 +- docs/plots/blackmanNuttall.svg | 2 +- docs/plots/bohman.svg | 2 +- docs/plots/cauchy.svg | 2 +- docs/plots/confinedGaussian.svg | 2 +- docs/plots/connes.svg | 2 +- docs/plots/cosine.svg | 2 +- docs/plots/dolphChebyshev.svg | 2 +- docs/plots/dpss.svg | 2 +- docs/plots/exactBlackman.svg | 2 +- docs/plots/exponential.svg | 2 +- docs/plots/flatTop.svg | 2 +- docs/plots/gaussian.svg | 2 +- docs/plots/generalizedNormal.svg | 2 +- docs/plots/hamming.svg | 2 +- docs/plots/hann.svg | 2 +- docs/plots/hannPoisson.svg | 2 +- docs/plots/kaiser.svg | 2 +- docs/plots/kaiserBesselDerived.svg | 2 +- docs/plots/lanczos.svg | 2 +- docs/plots/nuttall.svg | 2 +- docs/plots/parzen.svg | 2 +- docs/plots/planckTaper.svg | 2 +- docs/plots/powerOfSine.svg | 2 +- docs/plots/rectangular.svg | 2 +- docs/plots/rifeVincent.svg | 2 +- docs/plots/taylor.svg | 2 +- docs/plots/triangular.svg | 2 +- docs/plots/tukey.svg | 2 +- docs/plots/ultraspherical.svg | 2 +- docs/plots/welch.svg | 2 +- 36 files changed, 106 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index 26b7e63..fed2c19 100644 --- a/README.md +++ b/README.md @@ -58,189 +58,209 @@ cola(hann, 1024, 512) // → 0 (perfect STFT reconstruction at 50% ove ### Simple — no parameters +
### `rectangular(i, N)` $w(n) = 1$ - + No windowing at all. Best frequency resolution, worst spectral leakage. Use for transient signals already zero at edges, or harmonic analysis with integer cycles. **-13 dB** peak sidelobe · **-6 dB/oct** rolloff +
### `triangular(i, N)` $w(n) = 1 - \left|\frac{2n - N + 1}{N}\right|$ - + Linear taper, nonzero endpoints. Simple smoothing, 2nd-order B-spline. **-27 dB** peak sidelobe · **-12 dB/oct** rolloff +
### `bartlett(i, N)` $w(n) = 1 - \left|\frac{2n - N + 1}{N - 1}\right|$ - + Linear taper, zero endpoints. Bartlett's method PSD estimation. Bartlett 1950. **-27 dB** peak sidelobe · **-12 dB/oct** rolloff +
### `welch(i, N)` $w(n) = 1 - \left(\frac{2n - N + 1}{N - 1}\right)^2$ - + Parabolic taper. Welch's method PSD estimation. Welch 1967. **-21 dB** peak sidelobe · **-12 dB/oct** rolloff +
### `connes(i, N)` $w(n) = \left[1 - \left(\frac{2n - N + 1}{N - 1}\right)^2\right]^2$ - + Welch squared (4th power parabolic). FTIR spectroscopy, interferogram apodization. Connes 1961. **-24 dB/oct** rolloff +
### `hann(i, N)` $w(n) = 0.5 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right)$ - + Raised cosine, zero endpoints. The default general-purpose choice. STFT with 50% overlap (COLA). Also called "Hanning" (misnomer). Blackman & Tukey 1958. **-32 dB** peak sidelobe · **-18 dB/oct** rolloff +
### `hamming(i, N)` $w(n) = 0.54 - 0.46\cos\!\left(\frac{2\pi n}{N-1}\right)$ - + Raised cosine, nonzero endpoints. Optimized for first sidelobe cancellation. FIR filter design, speech processing. Hamming 1977. **-43 dB** peak sidelobe · **-6 dB/oct** rolloff +
### `cosine(i, N)` $w(n) = \sin\!\left(\frac{\pi n}{N-1}\right)$ - + Half-period sine. MDCT audio codecs: MP3, AAC, Vorbis. Princen & Bradley 1987. **-23 dB** peak sidelobe · **-12 dB/oct** rolloff +
### `blackman(i, N)` $w(n) = 0.42 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.08\cos\!\left(\frac{4\pi n}{N-1}\right)$ - + 3-term cosine sum. Better leakage than Hann at the cost of wider main lobe. Blackman & Tukey 1958. **-58 dB** peak sidelobe · **-18 dB/oct** rolloff +
### `exactBlackman(i, N)` $w(n) = 0.42659 - 0.49656\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.076849\cos\!\left(\frac{4\pi n}{N-1}\right)$ - + Blackman with exact zero placement at 3rd and 4th sidelobes. Harris 1978. **-69 dB** peak sidelobe · **-6 dB/oct** rolloff +
### `nuttall(i, N)` $w(n) = 0.355768 - 0.487396\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.144232\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.012604\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ - + 4-term cosine sum, continuous 1st derivative. High-dynamic-range analysis without edge discontinuity. Nuttall 1981. **-93 dB** peak sidelobe · **-18 dB/oct** rolloff +
### `blackmanNuttall(i, N)` $w(n) = 0.3635819 - 0.4891775\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.1365995\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.0106411\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ - + 4-term cosine sum, lowest sidelobes among 4-term windows. Nuttall 1981. **-98 dB** peak sidelobe · **-6 dB/oct** rolloff +
### `blackmanHarris(i, N)` $w(n) = 0.35875 - 0.48829\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.14128\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.01168\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ - + 4-term minimum sidelobe. ADC testing, measurement instrumentation, >80 dB dynamic range. Harris 1978. **-92 dB** peak sidelobe · **-6 dB/oct** rolloff +
### `flatTop(i, N)` $w(n) = 1 - 1.93\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 1.29\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.388\cos\!\left(\frac{6\pi n}{N\!-\!1}\right) + 0.028\cos\!\left(\frac{8\pi n}{N\!-\!1}\right)$ - + 5-term cosine sum with near-zero scalloping. Peak value ~4.64 (by design — optimized for amplitude accuracy, not energy). Amplitude calibration, transducer calibration (~0.01 dB accuracy). ISO 18431. Heinzel 2002. **-93 dB** peak sidelobe · **-6 dB/oct** rolloff +
### `bartlettHann(i, N)` $w(n) = 0.62 - 0.48\left|\frac{n}{N\!-\!1} - 0.5\right| - 0.38\cos\!\left(\frac{2\pi n}{N-1}\right)$ - + Bartlett-Hann hybrid. Balanced near/far sidelobe levels. Ha & Pearce 1989. **-36 dB** peak sidelobe +
### `lanczos(i, N)` $w(n) = \text{sinc}\!\left(\frac{2n}{N-1} - 1\right)$ - + Sinc main lobe. Image resampling, interpolation (FFmpeg, ImageMagick). Duchon 1979. **-26 dB** peak sidelobe +
### `parzen(i, N)` $w(n) = 1 - 6a^2(1-a)$ for $|a| \le 0.5$,   $w(n) = 2(1-a)^3$ for $|a| > 0.5$,   where $a = |{(2n-N+1)}/{(N-1)}|$ - + 4th-order B-spline. Always-positive spectrum. Kernel density estimation. Parzen 1961. **-53 dB** peak sidelobe · **-24 dB/oct** rolloff +
### `bohman(i, N)` $w(n) = (1-|a|)\cos(\pi|a|) + \frac{\sin(\pi|a|)}{\pi} \quad a = \frac{2n-N+1}{N-1}$ - + Autocorrelation of cosine window. Fast sidelobe decay, spectral estimation. **-46 dB** peak sidelobe · **-24 dB/oct** rolloff +
### Parameterized — adjustable tradeoff +
### `kaiser(i, N, beta)` @@ -248,10 +268,11 @@ Autocorrelation of cosine window. Fast sidelobe decay, spectral estimation. $w(n) = \frac{I_0\!\left(\beta\sqrt{1 - \left(\frac{2n-N+1}{N-1}\right)^2}\right)}{I_0(\beta)}$ - + Near-optimal DPSS approximation via Bessel I₀. The standard parameterized window for FIR filter design. Kaiser 1974. +
### `gaussian(i, N, sigma)` @@ -259,10 +280,11 @@ Near-optimal DPSS approximation via Bessel I₀. The standard parameterized wind $w(n) = \exp\!\left[-\frac{1}{2}\left(\frac{2n-N+1}{\sigma(N-1)}\right)^2\right]$ - + Gaussian bell, minimum time-bandwidth product. STFT/Gabor transform, frequency estimation via parabolic interpolation. Gabor 1946. +
### `generalizedNormal(i, N, sigma, p)` @@ -270,10 +292,11 @@ Gaussian bell, minimum time-bandwidth product. STFT/Gabor transform, frequency e $w(n) = \exp\!\left[-\frac{1}{2}\left|\frac{2n-N+1}{\sigma(N-1)}\right|^p\right]$ - + Continuous family between Gaussian and rectangular. Adjustable time-frequency tradeoff. +
### `tukey(i, N, alpha)` @@ -281,19 +304,21 @@ Continuous family between Gaussian and rectangular. Adjustable time-frequency tr $w(n) = \tfrac{1}{2}[1+\cos(\pi(n/(\alpha(N\!-\!1)/2)-1))]$ in the tapered edges, $w(n) = 1$ in the flat center. - + Flat center with cosine-tapered edges. Preserves signal amplitude while tapering. Vibration analysis, LIGO. +
### `planckTaper(i, N, epsilon)` `epsilon`: taper fraction, default **0.1**. - + C∞-smooth bump function (infinitely differentiable). Gravitational wave analysis (LIGO/Virgo). McKechan 2010. +
### `powerOfSine(i, N, alpha)` @@ -301,10 +326,11 @@ C∞-smooth bump function (infinitely differentiable). Gravitational wave analys $w(n) = \sin^\alpha\!\left(\frac{\pi n}{N-1}\right)$ - + sin^α family. Codec design, parameterized spectral analysis. +
### `exponential(i, N, tau)` @@ -312,10 +338,11 @@ sin^α family. Codec design, parameterized spectral analysis. $w(n) = \exp\!\left(\frac{-|2n-N+1|}{\tau(N-1)}\right)$ - + Exponential decay from center. Modal analysis, impact testing (compensates underdamped responses). Harris 1978. +
### `hannPoisson(i, N, alpha)` @@ -323,10 +350,11 @@ Exponential decay from center. Modal analysis, impact testing (compensates under $w(n) = \frac{1}{2}\left(1-\cos\frac{2\pi n}{N\!-\!1}\right)\exp\!\left(\frac{-\alpha|2n\!-\!N\!+\!1|}{N-1}\right)$ - + Hann × exponential product. Unique no-sidelobe property enables frequency estimators using convex optimization. +
### `cauchy(i, N, alpha)` @@ -334,10 +362,11 @@ Hann × exponential product. Unique no-sidelobe property enables frequency estim $w(n) = \frac{1}{1+\left(\frac{\alpha(2n-N+1)}{N-1}\right)^2}$ - + Lorentzian 1/(1+x²) shape. Matches spectral line shapes in spectroscopy. Harris 1978. +
### `rifeVincent(i, N, order)` @@ -345,24 +374,27 @@ Lorentzian 1/(1+x²) shape. Matches spectral line shapes in spectroscopy. Harris $w(n) = \frac{1}{Z}\sum_{k=0}^{K}(-1)^k a_k\cos\frac{2\pi kn}{N-1}$ - + Class I cosine-sum optimized for sidelobe fall-off. Power grid harmonic analysis, interpolated DFT. Rife & Vincent 1970. +
### `confinedGaussian(i, N, sigmaT)` `sigmaT`: temporal width, default **0.1**. - + Approximate confined Gaussian — optimal RMS time-frequency bandwidth. Time-frequency analysis, audio coding (MP3/AAC). Starosielec 2014. +
### Array-computed — cached These compute the full window on first call and cache the result. Recomputed when N or parameters change. +
### `dolphChebyshev(i, N, dB)` @@ -370,10 +402,11 @@ These compute the full window on first call and cache the result. Recomputed whe $W(k) = (-1)^k T_{N-1}\!\left(\beta\cos\frac{\pi k}{N}\right),\quad w = \text{IDFT}(W)$ - + Optimal: narrowest main lobe for a given equiripple sidelobe level. Antenna array design, radar beam patterns. Dolph 1946. +
### `taylor(i, N, nbar, sll)` @@ -381,10 +414,11 @@ Optimal: narrowest main lobe for a given equiripple sidelobe level. Antenna arra $w(n) = 1 + 2\sum_{m=1}^{\bar{n}-1} F_m \cos\frac{2\pi m(n-(N\!-\!1)/2)}{N}$ - + Dolph-Chebyshev variant with monotonically decreasing sidelobes. The radar community standard for SAR image formation. Taylor 1955. +
### `kaiserBesselDerived(i, N, beta)` @@ -392,10 +426,11 @@ Dolph-Chebyshev variant with monotonically decreasing sidelobes. The radar commu $w(n) = \sqrt{\frac{\sum_{j=0}^{n} K(j)}{\sum_{j=0}^{N/2} K(j)}} \quad K(j) = I_0\!\left(\beta\sqrt{1-\left(\frac{2j-N/2}{N/2}\right)^2}\right)$ - + Satisfies the Princen-Bradley condition for perfect MDCT reconstruction. AAC, Vorbis, Opus audio codecs (long blocks). Princen & Bradley 1987. +
### `dpss(i, N, W)` @@ -403,10 +438,11 @@ Satisfies the Princen-Bradley condition for perfect MDCT reconstruction. AAC, Vo $\mathbf{T}\mathbf{v} = \lambda\mathbf{v} \quad T_{jk} = \frac{\sin 2\pi W(j-k)}{\pi(j-k)}$ - + Dominant eigenvector of the sinc Toeplitz matrix — provably optimal energy concentration in a frequency band. Also called Slepian window. Thomson multitaper spectral estimation, neuroscience (EEG/MEG), climate science. Slepian 1978. +
### `ultraspherical(i, N, mu, xmu)` @@ -414,10 +450,11 @@ Dominant eigenvector of the sinc Toeplitz matrix — provably optimal energy con $W(k) = C_n^\mu\!\left(x_\mu\cos\frac{\pi k}{N}\right),\quad w = \text{IDFT}(W)$ - + Gegenbauer polynomial window. Independent control of sidelobe level and taper rate. Advanced antenna design, beamforming. Streit 1984. +
## Quantitative metrics diff --git a/docs/generate.js b/docs/generate.js index 527498b..497118d 100644 --- a/docs/generate.js +++ b/docs/generate.js @@ -48,7 +48,7 @@ for (let name of wins) { let yTicks = tMax <= 1.1 ? [0, 0.5, 1] : Array.from({ length: yTop + 1 }, (_, i) => i) let svg = `\n` - svg += ` \n` + svg += ` \n` svg += panel(L, samples, 0, 1, 0, yTop, [0, 0.5, 1], yTicks, true) svg += panel(R, db, 0, 0.5, -120, 0, [0, 0.1, 0.2, 0.3, 0.4, 0.5], [0, -40, -80, -120], false) diff --git a/docs/plots/bartlett.svg b/docs/plots/bartlett.svg index 9371f08..953d841 100644 --- a/docs/plots/bartlett.svg +++ b/docs/plots/bartlett.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/bartlettHann.svg b/docs/plots/bartlettHann.svg index 0ede530..d8a2ce2 100644 --- a/docs/plots/bartlettHann.svg +++ b/docs/plots/bartlettHann.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/blackman.svg b/docs/plots/blackman.svg index 14a4415..6f684ef 100644 --- a/docs/plots/blackman.svg +++ b/docs/plots/blackman.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/blackmanHarris.svg b/docs/plots/blackmanHarris.svg index 06a2da6..f8bd4e2 100644 --- a/docs/plots/blackmanHarris.svg +++ b/docs/plots/blackmanHarris.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/blackmanNuttall.svg b/docs/plots/blackmanNuttall.svg index a4aaecd..44e4e11 100644 --- a/docs/plots/blackmanNuttall.svg +++ b/docs/plots/blackmanNuttall.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/bohman.svg b/docs/plots/bohman.svg index 662b23e..e40dbb3 100644 --- a/docs/plots/bohman.svg +++ b/docs/plots/bohman.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/cauchy.svg b/docs/plots/cauchy.svg index a247e58..15206e6 100644 --- a/docs/plots/cauchy.svg +++ b/docs/plots/cauchy.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/confinedGaussian.svg b/docs/plots/confinedGaussian.svg index 2ef5238..2868554 100644 --- a/docs/plots/confinedGaussian.svg +++ b/docs/plots/confinedGaussian.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/connes.svg b/docs/plots/connes.svg index 2bea071..644c1a0 100644 --- a/docs/plots/connes.svg +++ b/docs/plots/connes.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/cosine.svg b/docs/plots/cosine.svg index 036afff..e028d59 100644 --- a/docs/plots/cosine.svg +++ b/docs/plots/cosine.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/dolphChebyshev.svg b/docs/plots/dolphChebyshev.svg index 123af36..2c0d4bb 100644 --- a/docs/plots/dolphChebyshev.svg +++ b/docs/plots/dolphChebyshev.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/dpss.svg b/docs/plots/dpss.svg index 73c02b5..0c3efa9 100644 --- a/docs/plots/dpss.svg +++ b/docs/plots/dpss.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/exactBlackman.svg b/docs/plots/exactBlackman.svg index 7d32fe5..95b473d 100644 --- a/docs/plots/exactBlackman.svg +++ b/docs/plots/exactBlackman.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/exponential.svg b/docs/plots/exponential.svg index 0fcf90f..543eb00 100644 --- a/docs/plots/exponential.svg +++ b/docs/plots/exponential.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/flatTop.svg b/docs/plots/flatTop.svg index b15c891..7536aa5 100644 --- a/docs/plots/flatTop.svg +++ b/docs/plots/flatTop.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/gaussian.svg b/docs/plots/gaussian.svg index 4c64c37..6fd6b40 100644 --- a/docs/plots/gaussian.svg +++ b/docs/plots/gaussian.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/generalizedNormal.svg b/docs/plots/generalizedNormal.svg index 4c64c37..6fd6b40 100644 --- a/docs/plots/generalizedNormal.svg +++ b/docs/plots/generalizedNormal.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/hamming.svg b/docs/plots/hamming.svg index 67cd074..1848870 100644 --- a/docs/plots/hamming.svg +++ b/docs/plots/hamming.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/hann.svg b/docs/plots/hann.svg index 493cd24..4ad4c97 100644 --- a/docs/plots/hann.svg +++ b/docs/plots/hann.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/hannPoisson.svg b/docs/plots/hannPoisson.svg index 79eba30..dbb920b 100644 --- a/docs/plots/hannPoisson.svg +++ b/docs/plots/hannPoisson.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/kaiser.svg b/docs/plots/kaiser.svg index ef99b50..e925597 100644 --- a/docs/plots/kaiser.svg +++ b/docs/plots/kaiser.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/kaiserBesselDerived.svg b/docs/plots/kaiserBesselDerived.svg index 1e30ba6..40fe5e2 100644 --- a/docs/plots/kaiserBesselDerived.svg +++ b/docs/plots/kaiserBesselDerived.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/lanczos.svg b/docs/plots/lanczos.svg index 4351188..cabeb3f 100644 --- a/docs/plots/lanczos.svg +++ b/docs/plots/lanczos.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/nuttall.svg b/docs/plots/nuttall.svg index 1cb502c..bfa9da8 100644 --- a/docs/plots/nuttall.svg +++ b/docs/plots/nuttall.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/parzen.svg b/docs/plots/parzen.svg index 41f634e..d04f156 100644 --- a/docs/plots/parzen.svg +++ b/docs/plots/parzen.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/planckTaper.svg b/docs/plots/planckTaper.svg index 48b5e60..d23b732 100644 --- a/docs/plots/planckTaper.svg +++ b/docs/plots/planckTaper.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/powerOfSine.svg b/docs/plots/powerOfSine.svg index 493cd24..4ad4c97 100644 --- a/docs/plots/powerOfSine.svg +++ b/docs/plots/powerOfSine.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/rectangular.svg b/docs/plots/rectangular.svg index 33a4c7c..914ae3e 100644 --- a/docs/plots/rectangular.svg +++ b/docs/plots/rectangular.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/rifeVincent.svg b/docs/plots/rifeVincent.svg index 493cd24..4ad4c97 100644 --- a/docs/plots/rifeVincent.svg +++ b/docs/plots/rifeVincent.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/taylor.svg b/docs/plots/taylor.svg index 543979f..07c4ed3 100644 --- a/docs/plots/taylor.svg +++ b/docs/plots/taylor.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/triangular.svg b/docs/plots/triangular.svg index e6e2563..2e94a51 100644 --- a/docs/plots/triangular.svg +++ b/docs/plots/triangular.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/tukey.svg b/docs/plots/tukey.svg index d059cd8..90080d7 100644 --- a/docs/plots/tukey.svg +++ b/docs/plots/tukey.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/ultraspherical.svg b/docs/plots/ultraspherical.svg index 33a4c7c..914ae3e 100644 --- a/docs/plots/ultraspherical.svg +++ b/docs/plots/ultraspherical.svg @@ -1,5 +1,5 @@ - + 0 diff --git a/docs/plots/welch.svg b/docs/plots/welch.svg index 742dbb8..b1d8257 100644 --- a/docs/plots/welch.svg +++ b/docs/plots/welch.svg @@ -1,5 +1,5 @@ - + 0 From 3a850321bacd9023cc9e40f02bbeb4f5a43547af Mon Sep 17 00:00:00 2001 From: "Dmitry Iv." Date: Wed, 25 Mar 2026 23:57:23 -0400 Subject: [PATCH 08/26] Regenerate svgs --- README.md | 14 ++--- docs/generate.js | 29 +++++---- docs/plots/bartlett.svg | 85 +++++++++++++------------- docs/plots/bartlettHann.svg | 85 +++++++++++++------------- docs/plots/blackman.svg | 85 +++++++++++++------------- docs/plots/blackmanHarris.svg | 85 +++++++++++++------------- docs/plots/blackmanNuttall.svg | 85 +++++++++++++------------- docs/plots/bohman.svg | 85 +++++++++++++------------- docs/plots/cauchy.svg | 85 +++++++++++++------------- docs/plots/confinedGaussian.svg | 85 +++++++++++++------------- docs/plots/connes.svg | 85 +++++++++++++------------- docs/plots/cosine.svg | 85 +++++++++++++------------- docs/plots/dolphChebyshev.svg | 85 +++++++++++++------------- docs/plots/dpss.svg | 85 +++++++++++++------------- docs/plots/exactBlackman.svg | 85 +++++++++++++------------- docs/plots/exponential.svg | 85 +++++++++++++------------- docs/plots/flatTop.svg | 97 +++++++++++++++--------------- docs/plots/gaussian.svg | 85 +++++++++++++------------- docs/plots/generalizedNormal.svg | 85 +++++++++++++------------- docs/plots/hamming.svg | 85 +++++++++++++------------- docs/plots/hann.svg | 85 +++++++++++++------------- docs/plots/hannPoisson.svg | 85 +++++++++++++------------- docs/plots/kaiser.svg | 85 +++++++++++++------------- docs/plots/kaiserBesselDerived.svg | 85 +++++++++++++------------- docs/plots/lanczos.svg | 85 +++++++++++++------------- docs/plots/nuttall.svg | 85 +++++++++++++------------- docs/plots/parzen.svg | 85 +++++++++++++------------- docs/plots/planckTaper.svg | 85 +++++++++++++------------- docs/plots/powerOfSine.svg | 85 +++++++++++++------------- docs/plots/rectangular.svg | 85 +++++++++++++------------- docs/plots/rifeVincent.svg | 85 +++++++++++++------------- docs/plots/taylor.svg | 85 +++++++++++++------------- docs/plots/triangular.svg | 85 +++++++++++++------------- docs/plots/tukey.svg | 85 +++++++++++++------------- docs/plots/ultraspherical.svg | 85 +++++++++++++------------- docs/plots/welch.svg | 85 +++++++++++++------------- 36 files changed, 1452 insertions(+), 1493 deletions(-) diff --git a/README.md b/README.md index fed2c19..b885fa5 100644 --- a/README.md +++ b/README.md @@ -54,11 +54,9 @@ scallopLoss(hann, 1024) // → 1.42 dB (worst-case amplitude error) cola(hann, 1024, 512) // → 0 (perfect STFT reconstruction at 50% overlap) ``` -## Window Reference +## Reference -### Simple — no parameters - -
+## Simple — no parameters ### `rectangular(i, N)` @@ -258,9 +256,7 @@ Autocorrelation of cosine window. Fast sidelobe decay, spectral estimation.
-### Parameterized — adjustable tradeoff - -
+## Parameterized — adjustable tradeoff ### `kaiser(i, N, beta)` @@ -390,12 +386,10 @@ Approximate confined Gaussian — optimal RMS time-frequency bandwidth. Time-fre
-### Array-computed — cached +## Array-computed — cached These compute the full window on first call and cache the result. Recomputed when N or parameters change. -
- ### `dolphChebyshev(i, N, dB)` `dB`: sidelobe attenuation, default **100**. diff --git a/docs/generate.js b/docs/generate.js index 497118d..8ad4958 100644 --- a/docs/generate.js +++ b/docs/generate.js @@ -8,10 +8,10 @@ import { writeFileSync, mkdirSync } from 'node:fs' let N = 256, NFFT = 4096 -// Layout -let W = 560, H = 190 -let L = { x: 52, y: 12, w: 205, h: 140 } // left panel (time) -let R = { x: 322, y: 12, w: 205, h: 140 } // right panel (freq) +// Layout — viewBox only, no fixed width/height, scales to container +let W = 800, H = 230 +let L = { x: 42, y: 8, w: 335, h: 174 } // left panel (time) +let R = { x: 450, y: 8, w: 335, h: 174 } // right panel (freq) // Palette let CLR = '#4a90d9', GRID = '#e5e7eb', AXIS = '#d1d5db', TXT = '#6b7280' @@ -47,15 +47,14 @@ for (let name of wins) { let yTop = tMax <= 1.1 ? 1 : Math.ceil(tMax) let yTicks = tMax <= 1.1 ? [0, 0.5, 1] : Array.from({ length: yTop + 1 }, (_, i) => i) - let svg = `\n` - svg += ` \n` + let svg = `\n` svg += panel(L, samples, 0, 1, 0, yTop, [0, 0.5, 1], yTicks, true) svg += panel(R, db, 0, 0.5, -120, 0, [0, 0.1, 0.2, 0.3, 0.4, 0.5], [0, -40, -80, -120], false) // Axis labels - svg += ` n / N\n` - svg += ` Normalized frequency (\u00d7 F\u209b)\n` + svg += ` n / N\n` + svg += ` Normalized frequency (\u00d7 F\u209b)\n` svg += `\n` writeFileSync(`docs/plots/${name}.svg`, svg) @@ -74,20 +73,20 @@ function panel (p, data, xMin, xMax, yMin, yMax, xTicks, yTicks, fill) { // Grid — horizontal for (let yt of yTicks) { let y = sy(yt).toFixed(1) - s += ` \n` - s += ` ${yt}\n` + s += ` \n` + s += ` ${yt}\n` } // Grid — vertical for (let xt of xTicks) { let x = sx(xt).toFixed(1) - s += ` \n` - s += ` ${xt}\n` + s += ` \n` + s += ` ${xt}\n` } // L-shaped axes - s += ` \n` - s += ` \n` + s += ` \n` + s += ` \n` // Polyline let pts = '' @@ -103,6 +102,6 @@ function panel (p, data, xMin, xMax, yMin, yMax, xTicks, yTicks, fill) { s += ` \n` } - s += ` \n` + s += ` \n` return s } diff --git a/docs/plots/bartlett.svg b/docs/plots/bartlett.svg index 953d841..8800f29 100644 --- a/docs/plots/bartlett.svg +++ b/docs/plots/bartlett.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/bartlettHann.svg b/docs/plots/bartlettHann.svg index d8a2ce2..4109e23 100644 --- a/docs/plots/bartlettHann.svg +++ b/docs/plots/bartlettHann.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/blackman.svg b/docs/plots/blackman.svg index 6f684ef..4852901 100644 --- a/docs/plots/blackman.svg +++ b/docs/plots/blackman.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/blackmanHarris.svg b/docs/plots/blackmanHarris.svg index f8bd4e2..8107966 100644 --- a/docs/plots/blackmanHarris.svg +++ b/docs/plots/blackmanHarris.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/blackmanNuttall.svg b/docs/plots/blackmanNuttall.svg index 44e4e11..4ff49c2 100644 --- a/docs/plots/blackmanNuttall.svg +++ b/docs/plots/blackmanNuttall.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/bohman.svg b/docs/plots/bohman.svg index e40dbb3..793eefa 100644 --- a/docs/plots/bohman.svg +++ b/docs/plots/bohman.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/cauchy.svg b/docs/plots/cauchy.svg index 15206e6..746f436 100644 --- a/docs/plots/cauchy.svg +++ b/docs/plots/cauchy.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/confinedGaussian.svg b/docs/plots/confinedGaussian.svg index 2868554..2089a8a 100644 --- a/docs/plots/confinedGaussian.svg +++ b/docs/plots/confinedGaussian.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/connes.svg b/docs/plots/connes.svg index 644c1a0..0cd8108 100644 --- a/docs/plots/connes.svg +++ b/docs/plots/connes.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/cosine.svg b/docs/plots/cosine.svg index e028d59..9ce1627 100644 --- a/docs/plots/cosine.svg +++ b/docs/plots/cosine.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/dolphChebyshev.svg b/docs/plots/dolphChebyshev.svg index 2c0d4bb..1c394e1 100644 --- a/docs/plots/dolphChebyshev.svg +++ b/docs/plots/dolphChebyshev.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/dpss.svg b/docs/plots/dpss.svg index 0c3efa9..efb2c7d 100644 --- a/docs/plots/dpss.svg +++ b/docs/plots/dpss.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/exactBlackman.svg b/docs/plots/exactBlackman.svg index 95b473d..de5695b 100644 --- a/docs/plots/exactBlackman.svg +++ b/docs/plots/exactBlackman.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/exponential.svg b/docs/plots/exponential.svg index 543eb00..55ce861 100644 --- a/docs/plots/exponential.svg +++ b/docs/plots/exponential.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/flatTop.svg b/docs/plots/flatTop.svg index 7536aa5..12f92c4 100644 --- a/docs/plots/flatTop.svg +++ b/docs/plots/flatTop.svg @@ -1,50 +1,49 @@ - - - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 1 + + 2 + + 3 + + 4 + + 5 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/gaussian.svg b/docs/plots/gaussian.svg index 6fd6b40..b454668 100644 --- a/docs/plots/gaussian.svg +++ b/docs/plots/gaussian.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/generalizedNormal.svg b/docs/plots/generalizedNormal.svg index 6fd6b40..b454668 100644 --- a/docs/plots/generalizedNormal.svg +++ b/docs/plots/generalizedNormal.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/hamming.svg b/docs/plots/hamming.svg index 1848870..44ae133 100644 --- a/docs/plots/hamming.svg +++ b/docs/plots/hamming.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/hann.svg b/docs/plots/hann.svg index 4ad4c97..56b690f 100644 --- a/docs/plots/hann.svg +++ b/docs/plots/hann.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/hannPoisson.svg b/docs/plots/hannPoisson.svg index dbb920b..a1b108d 100644 --- a/docs/plots/hannPoisson.svg +++ b/docs/plots/hannPoisson.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/kaiser.svg b/docs/plots/kaiser.svg index e925597..23b0fd7 100644 --- a/docs/plots/kaiser.svg +++ b/docs/plots/kaiser.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/kaiserBesselDerived.svg b/docs/plots/kaiserBesselDerived.svg index 40fe5e2..e88cb1e 100644 --- a/docs/plots/kaiserBesselDerived.svg +++ b/docs/plots/kaiserBesselDerived.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/lanczos.svg b/docs/plots/lanczos.svg index cabeb3f..0104a7b 100644 --- a/docs/plots/lanczos.svg +++ b/docs/plots/lanczos.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/nuttall.svg b/docs/plots/nuttall.svg index bfa9da8..90ddd5e 100644 --- a/docs/plots/nuttall.svg +++ b/docs/plots/nuttall.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/parzen.svg b/docs/plots/parzen.svg index d04f156..e16a164 100644 --- a/docs/plots/parzen.svg +++ b/docs/plots/parzen.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/planckTaper.svg b/docs/plots/planckTaper.svg index d23b732..2640803 100644 --- a/docs/plots/planckTaper.svg +++ b/docs/plots/planckTaper.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/powerOfSine.svg b/docs/plots/powerOfSine.svg index 4ad4c97..56b690f 100644 --- a/docs/plots/powerOfSine.svg +++ b/docs/plots/powerOfSine.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/rectangular.svg b/docs/plots/rectangular.svg index 914ae3e..22761e8 100644 --- a/docs/plots/rectangular.svg +++ b/docs/plots/rectangular.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/rifeVincent.svg b/docs/plots/rifeVincent.svg index 4ad4c97..56b690f 100644 --- a/docs/plots/rifeVincent.svg +++ b/docs/plots/rifeVincent.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/taylor.svg b/docs/plots/taylor.svg index 07c4ed3..a6a1482 100644 --- a/docs/plots/taylor.svg +++ b/docs/plots/taylor.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/triangular.svg b/docs/plots/triangular.svg index 2e94a51..691d145 100644 --- a/docs/plots/triangular.svg +++ b/docs/plots/triangular.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/tukey.svg b/docs/plots/tukey.svg index 90080d7..6184f35 100644 --- a/docs/plots/tukey.svg +++ b/docs/plots/tukey.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/ultraspherical.svg b/docs/plots/ultraspherical.svg index 914ae3e..22761e8 100644 --- a/docs/plots/ultraspherical.svg +++ b/docs/plots/ultraspherical.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) diff --git a/docs/plots/welch.svg b/docs/plots/welch.svg index b1d8257..175b87c 100644 --- a/docs/plots/welch.svg +++ b/docs/plots/welch.svg @@ -1,44 +1,43 @@ - - - - 0 - - 0.5 - - 1 - - 0 - - 0.5 - - 1 - - - - - - 0 - - -40 - - -80 - - -120 - - 0 - - 0.1 - - 0.2 - - 0.3 - - 0.4 - - 0.5 - - - - n / N - Normalized frequency (× Fₛ) + + + 0 + + 0.5 + + 1 + + 0 + + 0.5 + + 1 + + + + + + 0 + + -40 + + -80 + + -120 + + 0 + + 0.1 + + 0.2 + + 0.3 + + 0.4 + + 0.5 + + + + n / N + Normalized frequency (× Fₛ) From 0cbde86b3bca854847342af57ff23a77371a2bc9 Mon Sep 17 00:00:00 2001 From: "Dmitry Iv." Date: Wed, 25 Mar 2026 23:58:20 -0400 Subject: [PATCH 09/26] Update README.md --- README.md | 74 +++++++++++++++++++++++++++---------------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index b885fa5..c9b31e7 100644 --- a/README.md +++ b/README.md @@ -56,9 +56,9 @@ cola(hann, 1024, 512) // → 0 (perfect STFT reconstruction at 50% ove ## Reference -## Simple — no parameters +### Simple — no parameters -### `rectangular(i, N)` +#### `rectangular(i, N)` $w(n) = 1$ @@ -69,7 +69,7 @@ No windowing at all. Best frequency resolution, worst spectral leakage. Use for
-### `triangular(i, N)` +#### `triangular(i, N)` $w(n) = 1 - \left|\frac{2n - N + 1}{N}\right|$ @@ -80,7 +80,7 @@ Linear taper, nonzero endpoints. Simple smoothing, 2nd-order B-spline.
-### `bartlett(i, N)` +#### `bartlett(i, N)` $w(n) = 1 - \left|\frac{2n - N + 1}{N - 1}\right|$ @@ -91,7 +91,7 @@ Linear taper, zero endpoints. Bartlett's method PSD estimation. Bartlett 1950.
-### `welch(i, N)` +#### `welch(i, N)` $w(n) = 1 - \left(\frac{2n - N + 1}{N - 1}\right)^2$ @@ -102,7 +102,7 @@ Parabolic taper. Welch's method PSD estimation. Welch 1967.
-### `connes(i, N)` +#### `connes(i, N)` $w(n) = \left[1 - \left(\frac{2n - N + 1}{N - 1}\right)^2\right]^2$ @@ -113,7 +113,7 @@ Welch squared (4th power parabolic). FTIR spectroscopy, interferogram apodizatio
-### `hann(i, N)` +#### `hann(i, N)` $w(n) = 0.5 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right)$ @@ -124,7 +124,7 @@ Raised cosine, zero endpoints. The default general-purpose choice. STFT with 50%
-### `hamming(i, N)` +#### `hamming(i, N)` $w(n) = 0.54 - 0.46\cos\!\left(\frac{2\pi n}{N-1}\right)$ @@ -135,7 +135,7 @@ Raised cosine, nonzero endpoints. Optimized for first sidelobe cancellation. FIR
-### `cosine(i, N)` +#### `cosine(i, N)` $w(n) = \sin\!\left(\frac{\pi n}{N-1}\right)$ @@ -146,7 +146,7 @@ Half-period sine. MDCT audio codecs: MP3, AAC, Vorbis. Princen & Bradley 1987.
-### `blackman(i, N)` +#### `blackman(i, N)` $w(n) = 0.42 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.08\cos\!\left(\frac{4\pi n}{N-1}\right)$ @@ -157,7 +157,7 @@ $w(n) = 0.42 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.08\cos\!\left(\frac{
-### `exactBlackman(i, N)` +#### `exactBlackman(i, N)` $w(n) = 0.42659 - 0.49656\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.076849\cos\!\left(\frac{4\pi n}{N-1}\right)$ @@ -168,7 +168,7 @@ Blackman with exact zero placement at 3rd and 4th sidelobes. Harris 1978.
-### `nuttall(i, N)` +#### `nuttall(i, N)` $w(n) = 0.355768 - 0.487396\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.144232\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.012604\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ @@ -179,7 +179,7 @@ $w(n) = 0.355768 - 0.487396\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.144232\
-### `blackmanNuttall(i, N)` +#### `blackmanNuttall(i, N)` $w(n) = 0.3635819 - 0.4891775\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.1365995\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.0106411\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ @@ -190,7 +190,7 @@ $w(n) = 0.3635819 - 0.4891775\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.13659
-### `blackmanHarris(i, N)` +#### `blackmanHarris(i, N)` $w(n) = 0.35875 - 0.48829\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.14128\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.01168\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ @@ -201,7 +201,7 @@ $w(n) = 0.35875 - 0.48829\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.14128\cos
-### `flatTop(i, N)` +#### `flatTop(i, N)` $w(n) = 1 - 1.93\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 1.29\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.388\cos\!\left(\frac{6\pi n}{N\!-\!1}\right) + 0.028\cos\!\left(\frac{8\pi n}{N\!-\!1}\right)$ @@ -212,7 +212,7 @@ $w(n) = 1 - 1.93\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 1.29\cos\!\left(\fra
-### `bartlettHann(i, N)` +#### `bartlettHann(i, N)` $w(n) = 0.62 - 0.48\left|\frac{n}{N\!-\!1} - 0.5\right| - 0.38\cos\!\left(\frac{2\pi n}{N-1}\right)$ @@ -223,7 +223,7 @@ Bartlett-Hann hybrid. Balanced near/far sidelobe levels. Ha & Pearce 1989.
-### `lanczos(i, N)` +#### `lanczos(i, N)` $w(n) = \text{sinc}\!\left(\frac{2n}{N-1} - 1\right)$ @@ -234,7 +234,7 @@ Sinc main lobe. Image resampling, interpolation (FFmpeg, ImageMagick). Duchon 19
-### `parzen(i, N)` +#### `parzen(i, N)` $w(n) = 1 - 6a^2(1-a)$ for $|a| \le 0.5$,   $w(n) = 2(1-a)^3$ for $|a| > 0.5$,   where $a = |{(2n-N+1)}/{(N-1)}|$ @@ -245,7 +245,7 @@ $w(n) = 1 - 6a^2(1-a)$ for $|a| \le 0.5$,   $w(n) = 2(1-a)^3$ for $|a| > 0.
-### `bohman(i, N)` +#### `bohman(i, N)` $w(n) = (1-|a|)\cos(\pi|a|) + \frac{\sin(\pi|a|)}{\pi} \quad a = \frac{2n-N+1}{N-1}$ @@ -256,9 +256,9 @@ Autocorrelation of cosine window. Fast sidelobe decay, spectral estimation.
-## Parameterized — adjustable tradeoff +### Parameterized — adjustable tradeoff -### `kaiser(i, N, beta)` +#### `kaiser(i, N, beta)` `beta`: shape — 0 → rectangular, 5.4 → Hamming, **8.6** (default) → Blackman. @@ -270,7 +270,7 @@ Near-optimal DPSS approximation via Bessel I₀. The standard parameterized wind
-### `gaussian(i, N, sigma)` +#### `gaussian(i, N, sigma)` `sigma`: width, default **0.4**. @@ -282,7 +282,7 @@ Gaussian bell, minimum time-bandwidth product. STFT/Gabor transform, frequency e
-### `generalizedNormal(i, N, sigma, p)` +#### `generalizedNormal(i, N, sigma, p)` `sigma`: width (default **0.4**), `p`: shape — 2 = Gaussian, →∞ = rectangular. @@ -294,7 +294,7 @@ Continuous family between Gaussian and rectangular. Adjustable time-frequency tr
-### `tukey(i, N, alpha)` +#### `tukey(i, N, alpha)` `alpha`: taper fraction — 0 → rectangular, **0.5** (default), 1 → Hann. @@ -306,7 +306,7 @@ Flat center with cosine-tapered edges. Preserves signal amplitude while tapering
-### `planckTaper(i, N, epsilon)` +#### `planckTaper(i, N, epsilon)` `epsilon`: taper fraction, default **0.1**. @@ -316,7 +316,7 @@ C∞-smooth bump function (infinitely differentiable). Gravitational wave analys
-### `powerOfSine(i, N, alpha)` +#### `powerOfSine(i, N, alpha)` `alpha`: exponent — 0 → rectangular, 1 → cosine, **2** (default) → Hann. @@ -328,7 +328,7 @@ sin^α family. Codec design, parameterized spectral analysis.
-### `exponential(i, N, tau)` +#### `exponential(i, N, tau)` `tau`: time constant, default **1**. @@ -340,7 +340,7 @@ Exponential decay from center. Modal analysis, impact testing (compensates under
-### `hannPoisson(i, N, alpha)` +#### `hannPoisson(i, N, alpha)` `alpha`: decay — default **2**. At α ≥ 2, the transform has no sidelobes. @@ -352,7 +352,7 @@ Hann × exponential product. Unique no-sidelobe property enables frequency estim
-### `cauchy(i, N, alpha)` +#### `cauchy(i, N, alpha)` `alpha`: width, default **3**. @@ -364,7 +364,7 @@ Lorentzian 1/(1+x²) shape. Matches spectral line shapes in spectroscopy. Harris
-### `rifeVincent(i, N, order)` +#### `rifeVincent(i, N, order)` `order`: **1** (default) = Hann, 2, 3. Throws for other values. @@ -376,7 +376,7 @@ Class I cosine-sum optimized for sidelobe fall-off. Power grid harmonic analysis
-### `confinedGaussian(i, N, sigmaT)` +#### `confinedGaussian(i, N, sigmaT)` `sigmaT`: temporal width, default **0.1**. @@ -386,11 +386,11 @@ Approximate confined Gaussian — optimal RMS time-frequency bandwidth. Time-fre
-## Array-computed — cached +### Array-computed — cached These compute the full window on first call and cache the result. Recomputed when N or parameters change. -### `dolphChebyshev(i, N, dB)` +#### `dolphChebyshev(i, N, dB)` `dB`: sidelobe attenuation, default **100**. @@ -402,7 +402,7 @@ Optimal: narrowest main lobe for a given equiripple sidelobe level. Antenna arra
-### `taylor(i, N, nbar, sll)` +#### `taylor(i, N, nbar, sll)` `nbar`: number of constant-level sidelobes (default **4**), `sll`: sidelobe level in dB (default **30**). @@ -414,7 +414,7 @@ Dolph-Chebyshev variant with monotonically decreasing sidelobes. The radar commu
-### `kaiserBesselDerived(i, N, beta)` +#### `kaiserBesselDerived(i, N, beta)` `beta`: shape, default **8.6**. N must be even. @@ -426,7 +426,7 @@ Satisfies the Princen-Bradley condition for perfect MDCT reconstruction. AAC, Vo
-### `dpss(i, N, W)` +#### `dpss(i, N, W)` `W`: half-bandwidth [0, 0.5], default **0.1**. @@ -438,7 +438,7 @@ Dominant eigenvector of the sinc Toeplitz matrix — provably optimal energy con
-### `ultraspherical(i, N, mu, xmu)` +#### `ultraspherical(i, N, mu, xmu)` `mu`: 0 → Dolph-Chebyshev, **1** (default) → Saramaki. `xmu`: sidelobe control (default **1**). From 658fb90f4e0ca56de27386b42ee7e6b497d2b19b Mon Sep 17 00:00:00 2001 From: "Dmitry Iv." Date: Thu, 26 Mar 2026 00:08:25 -0400 Subject: [PATCH 10/26] Update README.md --- README.md | 528 +++++++++++++++++++++++++++--------------------------- 1 file changed, 267 insertions(+), 261 deletions(-) diff --git a/README.md b/README.md index c9b31e7..9cae1bb 100644 --- a/README.md +++ b/README.md @@ -1,290 +1,279 @@ # window-function Complete collection of window functions for signal processing and spectral analysis. +**34 windows** · zero dependencies · pure ESM · individual imports. -**34 window functions** · Zero dependencies · Pure ESM · Individual imports +```js +npm install window-function +``` ```js +// Import everything, or just what you need import { hann, kaiser, generate, apply } from 'window-function' import { hann } from 'window-function/hann' -hann(50, 101) // → 1.0 (single sample) -generate(hann, 1024) // → Float64Array(1024) -generate(kaiser, 1024, 8.6) // → parameterized window -apply(signal, kaiser, 8.6) // → signal, windowed in-place -``` +// Every window: fn(i, N, ...params) → number +hann(50, 101) // single sample → 1.0 -## Which window should I pick? - -Every window is a tradeoff between **frequency resolution** (narrow main lobe), **spectral leakage** (low sidelobes), and **amplitude accuracy** (flat main lobe top). No single window optimizes all three. - -| I need to... | Use | Why | -|---|---|---| -| Just get started | `hann` | Good all-round, zero edges, 50% COLA | -| Design FIR filters | `kaiser` or `hamming` | Kaiser is tunable, Hamming is the classic | -| Measure amplitudes accurately | `flatTop` | < 0.01 dB scalloping loss | -| High dynamic range (>80 dB) | `blackmanHarris` | -92 dB equiripple sidelobes | -| Audio codec (MDCT) | `kaiserBesselDerived` or `cosine` | Princen-Bradley perfect reconstruction | -| Preserve center, taper edges | `tukey` | Adjustable flat-top fraction | -| Robust spectral estimation | `dpss` | Optimal for multitaper method | -| Radar / SAR | `taylor` | Monotonic sidelobes, radar standard | -| Antenna array design | `dolphChebyshev` or `ultraspherical` | Optimal equiripple or tunable taper | -| Tune resolution/leakage | `kaiser` or `gaussian` | Single-parameter adjustment | -| Modal / impact analysis | `exponential` | Controlled decay for underdamped systems | -| FTIR spectroscopy | `connes` | Smooth apodization for interferograms | -| Gravitational waves | `planckTaper` | C∞ smooth, no spectral artifacts | - -## API - -Every window: `fn(i, N, ...params) → number` — sample `i` of window length `N`. - -| Function | Parameters | Returns | -|---|---|---| -| `generate(fn, N, ...params)` | window function, length, params | `Float64Array` | -| `apply(signal, fn, ...params)` | signal array, window function, params | `signal` (modified) | -| `enbw(fn, N, ...params)` | window function, length, params | Equivalent noise bandwidth (bins) | -| `scallopLoss(fn, N, ...params)` | window function, length, params | Worst-case amplitude error (dB) | -| `cola(fn, N, hop, ...params)` | window function, length, hop, params | COLA deviation (0 = perfect) | +// Generate full window as Float64Array +let w = generate(hann, 1024) -```js -import { hann, enbw, scallopLoss, cola } from 'window-function' +// Apply window to a signal in-place +let signal = new Float64Array(1024).fill(1) +apply(signal, hann) // signal *= hann -enbw(hann, 1024) // → 1.5 (noise bandwidth in bins) -scallopLoss(hann, 1024) // → 1.42 dB (worst-case amplitude error) -cola(hann, 1024, 512) // → 0 (perfect STFT reconstruction at 50% overlap) +// Parameterized windows pass extra args +generate(kaiser, 1024, 8.6) // Kaiser with β = 8.6 + +// Quantitative comparison +import { enbw, scallopLoss, cola } from 'window-function' +enbw(hann, 1024) // 1.5 — noise bandwidth (bins) +scallopLoss(hann, 1024) // 1.42 — worst-case amplitude error (dB) +cola(hann, 1024, 512) // 0 — perfect STFT reconstruction ``` -## Reference +## Contents + +**Simple:** +[rectangular](#rectangular) · +[triangular](#triangular) · +[bartlett](#bartlett) · +[welch](#welch) · +[connes](#connes) · +[hann](#hann) · +[hamming](#hamming) · +[cosine](#cosine) · +[blackman](#blackman) · +[exactBlackman](#exactblackman) · +[nuttall](#nuttall) · +[blackmanNuttall](#blackmannuttall) · +[blackmanHarris](#blackmanharris) · +[flatTop](#flattop) · +[bartlettHann](#bartletthann) · +[lanczos](#lanczos) · +[parzen](#parzen) · +[bohman](#bohman) + +**Parameterized:** +[kaiser](#kaiser) · +[gaussian](#gaussian) · +[generalizedNormal](#generalizednormal) · +[tukey](#tukey) · +[planckTaper](#plancktaper) · +[powerOfSine](#powerofsine) · +[exponential](#exponential) · +[hannPoisson](#hannpoisson) · +[cauchy](#cauchy) · +[rifeVincent](#rifevincent) · +[confinedGaussian](#confinedgaussian) + +**Array-computed:** +[dolphChebyshev](#dolphchebyshev) · +[taylor](#taylor) · +[kaiserBesselDerived](#kaiserbesselderived) · +[dpss](#dpss) · +[ultraspherical](#ultraspherical) + +**Other:** +[Choosing a window](#choosing-a-window) · +[Metrics](#metrics) · +[Migrating from v2](#migrating-from-v2) + +--- + +## Window Reference ### Simple — no parameters -#### `rectangular(i, N)` +#### `rectangular` -$w(n) = 1$ +`rectangular(i, N)` · $w(n) = 1$ -No windowing at all. Best frequency resolution, worst spectral leakage. Use for transient signals already zero at edges, or harmonic analysis with integer cycles. -**-13 dB** peak sidelobe · **-6 dB/oct** rolloff - -
+No windowing. Best frequency resolution, worst spectral leakage. Use for transient signals already zero at edges, or harmonic analysis with integer cycles. +**-13 dB** sidelobe · **-6 dB/oct** rolloff -#### `triangular(i, N)` +#### `triangular` -$w(n) = 1 - \left|\frac{2n - N + 1}{N}\right|$ +`triangular(i, N)` · $w(n) = 1 - \left|\frac{2n - N + 1}{N}\right|$ Linear taper, nonzero endpoints. Simple smoothing, 2nd-order B-spline. -**-27 dB** peak sidelobe · **-12 dB/oct** rolloff - -
+**-27 dB** sidelobe · **-12 dB/oct** rolloff -#### `bartlett(i, N)` +#### `bartlett` -$w(n) = 1 - \left|\frac{2n - N + 1}{N - 1}\right|$ +`bartlett(i, N)` · $w(n) = 1 - \left|\frac{2n - N + 1}{N - 1}\right|$ -Linear taper, zero endpoints. Bartlett's method PSD estimation. Bartlett 1950. -**-27 dB** peak sidelobe · **-12 dB/oct** rolloff +Linear taper, zero endpoints. Bartlett's method PSD estimation.[^bartlett1950] +**-27 dB** sidelobe · **-12 dB/oct** rolloff -
+#### `welch` -#### `welch(i, N)` - -$w(n) = 1 - \left(\frac{2n - N + 1}{N - 1}\right)^2$ +`welch(i, N)` · $w(n) = 1 - \left(\frac{2n - N + 1}{N - 1}\right)^2$ -Parabolic taper. Welch's method PSD estimation. Welch 1967. -**-21 dB** peak sidelobe · **-12 dB/oct** rolloff - -
+Parabolic taper. Welch's method PSD estimation.[^welch1967] +**-21 dB** sidelobe · **-12 dB/oct** rolloff -#### `connes(i, N)` +#### `connes` -$w(n) = \left[1 - \left(\frac{2n - N + 1}{N - 1}\right)^2\right]^2$ +`connes(i, N)` · $w(n) = \left[1 - \left(\frac{2n - N + 1}{N - 1}\right)^2\right]^2$ -Welch squared (4th power parabolic). FTIR spectroscopy, interferogram apodization. Connes 1961. +Welch squared (4th power parabolic). FTIR spectroscopy, interferogram apodization.[^connes1961] **-24 dB/oct** rolloff -
- -#### `hann(i, N)` +#### `hann` -$w(n) = 0.5 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right)$ +`hann(i, N)` · $w(n) = 0.5 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right)$ -Raised cosine, zero endpoints. The default general-purpose choice. STFT with 50% overlap (COLA). Also called "Hanning" (misnomer). Blackman & Tukey 1958. -**-32 dB** peak sidelobe · **-18 dB/oct** rolloff +Raised cosine, zero endpoints. The default general-purpose choice. STFT with 50% overlap (COLA). Also called "Hanning" (misnomer).[^blackman1958] +**-32 dB** sidelobe · **-18 dB/oct** rolloff -
+#### `hamming` -#### `hamming(i, N)` - -$w(n) = 0.54 - 0.46\cos\!\left(\frac{2\pi n}{N-1}\right)$ +`hamming(i, N)` · $w(n) = 0.54 - 0.46\cos\!\left(\frac{2\pi n}{N-1}\right)$ -Raised cosine, nonzero endpoints. Optimized for first sidelobe cancellation. FIR filter design, speech processing. Hamming 1977. -**-43 dB** peak sidelobe · **-6 dB/oct** rolloff - -
+Raised cosine, nonzero endpoints. Optimized for first sidelobe cancellation. FIR filter design, speech processing.[^hamming1977] +**-43 dB** sidelobe · **-6 dB/oct** rolloff -#### `cosine(i, N)` +#### `cosine` -$w(n) = \sin\!\left(\frac{\pi n}{N-1}\right)$ +`cosine(i, N)` · $w(n) = \sin\!\left(\frac{\pi n}{N-1}\right)$ -Half-period sine. MDCT audio codecs: MP3, AAC, Vorbis. Princen & Bradley 1987. -**-23 dB** peak sidelobe · **-12 dB/oct** rolloff - -
+Half-period sine. MDCT audio codecs: MP3, AAC, Vorbis.[^princen1987] +**-23 dB** sidelobe · **-12 dB/oct** rolloff -#### `blackman(i, N)` +#### `blackman` -$w(n) = 0.42 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.08\cos\!\left(\frac{4\pi n}{N-1}\right)$ +`blackman(i, N)` · $w(n) = 0.42 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.08\cos\!\left(\frac{4\pi n}{N-1}\right)$ -3-term cosine sum. Better leakage than Hann at the cost of wider main lobe. Blackman & Tukey 1958. -**-58 dB** peak sidelobe · **-18 dB/oct** rolloff +3-term cosine sum. Better leakage than Hann at the cost of wider main lobe.[^blackman1958] +**-58 dB** sidelobe · **-18 dB/oct** rolloff -
+#### `exactBlackman` -#### `exactBlackman(i, N)` - -$w(n) = 0.42659 - 0.49656\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.076849\cos\!\left(\frac{4\pi n}{N-1}\right)$ +`exactBlackman(i, N)` · $w(n) = 0.42659 - 0.49656\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.076849\cos\!\left(\frac{4\pi n}{N-1}\right)$ -Blackman with exact zero placement at 3rd and 4th sidelobes. Harris 1978. -**-69 dB** peak sidelobe · **-6 dB/oct** rolloff - -
+Blackman with exact zero placement at 3rd and 4th sidelobes.[^harris1978] +**-69 dB** sidelobe · **-6 dB/oct** rolloff -#### `nuttall(i, N)` +#### `nuttall` -$w(n) = 0.355768 - 0.487396\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.144232\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.012604\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ +`nuttall(i, N)` · $w(n) = 0.355768 - 0.487396\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.144232\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.012604\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ -4-term cosine sum, continuous 1st derivative. High-dynamic-range analysis without edge discontinuity. Nuttall 1981. -**-93 dB** peak sidelobe · **-18 dB/oct** rolloff +4-term cosine sum, continuous 1st derivative. High-dynamic-range analysis without edge discontinuity.[^nuttall1981] +**-93 dB** sidelobe · **-18 dB/oct** rolloff -
+#### `blackmanNuttall` -#### `blackmanNuttall(i, N)` - -$w(n) = 0.3635819 - 0.4891775\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.1365995\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.0106411\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ +`blackmanNuttall(i, N)` · $w(n) = 0.3635819 - 0.4891775\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.1365995\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.0106411\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ -4-term cosine sum, lowest sidelobes among 4-term windows. Nuttall 1981. -**-98 dB** peak sidelobe · **-6 dB/oct** rolloff - -
+4-term cosine sum, lowest sidelobes among 4-term windows.[^nuttall1981] +**-98 dB** sidelobe · **-6 dB/oct** rolloff -#### `blackmanHarris(i, N)` +#### `blackmanHarris` -$w(n) = 0.35875 - 0.48829\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.14128\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.01168\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ +`blackmanHarris(i, N)` · $w(n) = 0.35875 - 0.48829\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.14128\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.01168\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ -4-term minimum sidelobe. ADC testing, measurement instrumentation, >80 dB dynamic range. Harris 1978. -**-92 dB** peak sidelobe · **-6 dB/oct** rolloff +4-term minimum sidelobe. ADC testing, measurement instrumentation, >80 dB dynamic range.[^harris1978] +**-92 dB** sidelobe · **-6 dB/oct** rolloff -
+#### `flatTop` -#### `flatTop(i, N)` - -$w(n) = 1 - 1.93\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 1.29\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.388\cos\!\left(\frac{6\pi n}{N\!-\!1}\right) + 0.028\cos\!\left(\frac{8\pi n}{N\!-\!1}\right)$ +`flatTop(i, N)` · $w(n) = 1 - 1.93\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 1.29\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.388\cos\!\left(\frac{6\pi n}{N\!-\!1}\right) + 0.028\cos\!\left(\frac{8\pi n}{N\!-\!1}\right)$ -5-term cosine sum with near-zero scalloping. Peak value ~4.64 (by design — optimized for amplitude accuracy, not energy). Amplitude calibration, transducer calibration (~0.01 dB accuracy). ISO 18431. Heinzel 2002. -**-93 dB** peak sidelobe · **-6 dB/oct** rolloff - -
+5-term cosine sum, near-zero scalloping. Peak ~4.64 (by design). Amplitude calibration, transducer calibration (~0.01 dB accuracy). ISO 18431.[^heinzel2002] +**-93 dB** sidelobe · **-6 dB/oct** rolloff -#### `bartlettHann(i, N)` +#### `bartlettHann` -$w(n) = 0.62 - 0.48\left|\frac{n}{N\!-\!1} - 0.5\right| - 0.38\cos\!\left(\frac{2\pi n}{N-1}\right)$ +`bartlettHann(i, N)` · $w(n) = 0.62 - 0.48\left|\frac{n}{N\!-\!1} - 0.5\right| - 0.38\cos\!\left(\frac{2\pi n}{N-1}\right)$ -Bartlett-Hann hybrid. Balanced near/far sidelobe levels. Ha & Pearce 1989. -**-36 dB** peak sidelobe - -
+Bartlett-Hann hybrid. Balanced near/far sidelobe levels.[^ha1989] +**-36 dB** sidelobe -#### `lanczos(i, N)` +#### `lanczos` -$w(n) = \text{sinc}\!\left(\frac{2n}{N-1} - 1\right)$ +`lanczos(i, N)` · $w(n) = \text{sinc}\!\left(\frac{2n}{N-1} - 1\right)$ -Sinc main lobe. Image resampling, interpolation (FFmpeg, ImageMagick). Duchon 1979. -**-26 dB** peak sidelobe +Sinc main lobe. Image resampling, interpolation (FFmpeg, ImageMagick).[^duchon1979] +**-26 dB** sidelobe -
+#### `parzen` -#### `parzen(i, N)` - -$w(n) = 1 - 6a^2(1-a)$ for $|a| \le 0.5$,   $w(n) = 2(1-a)^3$ for $|a| > 0.5$,   where $a = |{(2n-N+1)}/{(N-1)}|$ +`parzen(i, N)` · $w(n) = 1 - 6a^2(1-a)$ for $|a| \le 0.5$,   $w(n) = 2(1-a)^3$ for $|a| > 0.5$,   $a = |(2n-N+1)/(N-1)|$ -4th-order B-spline. Always-positive spectrum. Kernel density estimation. Parzen 1961. -**-53 dB** peak sidelobe · **-24 dB/oct** rolloff - -
+4th-order B-spline. Always-positive spectrum. Kernel density estimation.[^parzen1961] +**-53 dB** sidelobe · **-24 dB/oct** rolloff -#### `bohman(i, N)` +#### `bohman` -$w(n) = (1-|a|)\cos(\pi|a|) + \frac{\sin(\pi|a|)}{\pi} \quad a = \frac{2n-N+1}{N-1}$ +`bohman(i, N)` · $w(n) = (1-|a|)\cos(\pi|a|) + \frac{\sin(\pi|a|)}{\pi}$,   $a = \frac{2n-N+1}{N-1}$ Autocorrelation of cosine window. Fast sidelobe decay, spectral estimation. -**-46 dB** peak sidelobe · **-24 dB/oct** rolloff +**-46 dB** sidelobe · **-24 dB/oct** rolloff -
+--- ### Parameterized — adjustable tradeoff -#### `kaiser(i, N, beta)` +#### `kaiser` -`beta`: shape — 0 → rectangular, 5.4 → Hamming, **8.6** (default) → Blackman. +`kaiser(i, N, beta)` · `beta`: shape — 0 = rectangular, 5.4 = Hamming, **8.6** (default) = Blackman. $w(n) = \frac{I_0\!\left(\beta\sqrt{1 - \left(\frac{2n-N+1}{N-1}\right)^2}\right)}{I_0(\beta)}$ -Near-optimal DPSS approximation via Bessel I₀. The standard parameterized window for FIR filter design. Kaiser 1974. - -
+Near-optimal DPSS approximation via Bessel I₀. The standard parameterized window for FIR filter design.[^kaiser1974] -#### `gaussian(i, N, sigma)` +#### `gaussian` -`sigma`: width, default **0.4**. +`gaussian(i, N, sigma)` · `sigma`: width, default **0.4**. $w(n) = \exp\!\left[-\frac{1}{2}\left(\frac{2n-N+1}{\sigma(N-1)}\right)^2\right]$ -Gaussian bell, minimum time-bandwidth product. STFT/Gabor transform, frequency estimation via parabolic interpolation. Gabor 1946. +Gaussian bell, minimum time-bandwidth product. STFT/Gabor transform, frequency estimation via parabolic interpolation.[^gabor1946] -
+#### `generalizedNormal` -#### `generalizedNormal(i, N, sigma, p)` - -`sigma`: width (default **0.4**), `p`: shape — 2 = Gaussian, →∞ = rectangular. +`generalizedNormal(i, N, sigma, p)` · `sigma`: width (default **0.4**), `p`: shape — 2 = Gaussian, large = rectangular. $w(n) = \exp\!\left[-\frac{1}{2}\left|\frac{2n-N+1}{\sigma(N-1)}\right|^p\right]$ @@ -292,33 +281,27 @@ $w(n) = \exp\!\left[-\frac{1}{2}\left|\frac{2n-N+1}{\sigma(N-1)}\right|^p\right] Continuous family between Gaussian and rectangular. Adjustable time-frequency tradeoff. -
- -#### `tukey(i, N, alpha)` +#### `tukey` -`alpha`: taper fraction — 0 → rectangular, **0.5** (default), 1 → Hann. +`tukey(i, N, alpha)` · `alpha`: taper fraction — 0 = rectangular, **0.5** (default), 1 = Hann. -$w(n) = \tfrac{1}{2}[1+\cos(\pi(n/(\alpha(N\!-\!1)/2)-1))]$ in the tapered edges, $w(n) = 1$ in the flat center. +$w(n) = \tfrac{1}{2}[1+\cos(\pi(n/(\alpha(N\!-\!1)/2)-1))]$ in tapered edges,   $w(n) = 1$ in flat center. Flat center with cosine-tapered edges. Preserves signal amplitude while tapering. Vibration analysis, LIGO. -
- -#### `planckTaper(i, N, epsilon)` +#### `planckTaper` -`epsilon`: taper fraction, default **0.1**. +`planckTaper(i, N, epsilon)` · `epsilon`: taper fraction, default **0.1**. -C∞-smooth bump function (infinitely differentiable). Gravitational wave analysis (LIGO/Virgo). McKechan 2010. +C∞-smooth bump function (infinitely differentiable). Gravitational wave analysis (LIGO/Virgo).[^mckechan2010] -
+#### `powerOfSine` -#### `powerOfSine(i, N, alpha)` - -`alpha`: exponent — 0 → rectangular, 1 → cosine, **2** (default) → Hann. +`powerOfSine(i, N, alpha)` · `alpha`: exponent — 0 = rectangular, 1 = cosine, **2** (default) = Hann. $w(n) = \sin^\alpha\!\left(\frac{\pi n}{N-1}\right)$ @@ -326,141 +309,168 @@ $w(n) = \sin^\alpha\!\left(\frac{\pi n}{N-1}\right)$ sin^α family. Codec design, parameterized spectral analysis. -
- -#### `exponential(i, N, tau)` +#### `exponential` -`tau`: time constant, default **1**. +`exponential(i, N, tau)` · `tau`: time constant, default **1**. $w(n) = \exp\!\left(\frac{-|2n-N+1|}{\tau(N-1)}\right)$ -Exponential decay from center. Modal analysis, impact testing (compensates underdamped responses). Harris 1978. +Exponential decay from center. Modal analysis, impact testing.[^harris1978] -
+#### `hannPoisson` -#### `hannPoisson(i, N, alpha)` - -`alpha`: decay — default **2**. At α ≥ 2, the transform has no sidelobes. +`hannPoisson(i, N, alpha)` · `alpha`: decay, default **2**. At α ≥ 2 the transform has no sidelobes. $w(n) = \frac{1}{2}\left(1-\cos\frac{2\pi n}{N\!-\!1}\right)\exp\!\left(\frac{-\alpha|2n\!-\!N\!+\!1|}{N-1}\right)$ -Hann × exponential product. Unique no-sidelobe property enables frequency estimators using convex optimization. - -
+Hann × exponential. Unique no-sidelobe property enables frequency estimators using convex optimization. -#### `cauchy(i, N, alpha)` +#### `cauchy` -`alpha`: width, default **3**. +`cauchy(i, N, alpha)` · `alpha`: width, default **3**. $w(n) = \frac{1}{1+\left(\frac{\alpha(2n-N+1)}{N-1}\right)^2}$ -Lorentzian 1/(1+x²) shape. Matches spectral line shapes in spectroscopy. Harris 1978. +Lorentzian shape. Matches spectral line shapes in spectroscopy.[^harris1978] -
+#### `rifeVincent` -#### `rifeVincent(i, N, order)` - -`order`: **1** (default) = Hann, 2, 3. Throws for other values. +`rifeVincent(i, N, order)` · `order`: **1** (default) = Hann, 2, 3. Throws for other values. $w(n) = \frac{1}{Z}\sum_{k=0}^{K}(-1)^k a_k\cos\frac{2\pi kn}{N-1}$ -Class I cosine-sum optimized for sidelobe fall-off. Power grid harmonic analysis, interpolated DFT. Rife & Vincent 1970. - -
+Class I cosine-sum optimized for sidelobe fall-off. Power grid harmonic analysis, interpolated DFT.[^rife1970] -#### `confinedGaussian(i, N, sigmaT)` +#### `confinedGaussian` -`sigmaT`: temporal width, default **0.1**. +`confinedGaussian(i, N, sigmaT)` · `sigmaT`: temporal width, default **0.1**. -Approximate confined Gaussian — optimal RMS time-frequency bandwidth. Time-frequency analysis, audio coding (MP3/AAC). Starosielec 2014. +Optimal RMS time-frequency bandwidth. Time-frequency analysis, audio coding.[^starosielec2014] -
+--- ### Array-computed — cached -These compute the full window on first call and cache the result. Recomputed when N or parameters change. +Compute the full window on first call, cache the result. Recomputed when parameters change. -#### `dolphChebyshev(i, N, dB)` +#### `dolphChebyshev` -`dB`: sidelobe attenuation, default **100**. +`dolphChebyshev(i, N, dB)` · `dB`: sidelobe attenuation, default **100**. -$W(k) = (-1)^k T_{N-1}\!\left(\beta\cos\frac{\pi k}{N}\right),\quad w = \text{IDFT}(W)$ +$W(k) = (-1)^k T_{N-1}\!\left(\beta\cos\frac{\pi k}{N}\right)$,   $w = \text{IDFT}(W)$ -Optimal: narrowest main lobe for a given equiripple sidelobe level. Antenna array design, radar beam patterns. Dolph 1946. - -
+Optimal: narrowest main lobe for given equiripple sidelobe level. Antenna design, radar.[^dolph1946] -#### `taylor(i, N, nbar, sll)` +#### `taylor` -`nbar`: number of constant-level sidelobes (default **4**), `sll`: sidelobe level in dB (default **30**). +`taylor(i, N, nbar, sll)` · `nbar`: constant-level sidelobes (default **4**), `sll`: level in dB (default **30**). $w(n) = 1 + 2\sum_{m=1}^{\bar{n}-1} F_m \cos\frac{2\pi m(n-(N\!-\!1)/2)}{N}$ -Dolph-Chebyshev variant with monotonically decreasing sidelobes. The radar community standard for SAR image formation. Taylor 1955. +Monotonically decreasing sidelobes. The radar community standard for SAR image formation.[^taylor1955] -
+#### `kaiserBesselDerived` -#### `kaiserBesselDerived(i, N, beta)` +`kaiserBesselDerived(i, N, beta)` · `beta`: shape, default **8.6**. N must be even. -`beta`: shape, default **8.6**. N must be even. - -$w(n) = \sqrt{\frac{\sum_{j=0}^{n} K(j)}{\sum_{j=0}^{N/2} K(j)}} \quad K(j) = I_0\!\left(\beta\sqrt{1-\left(\frac{2j-N/2}{N/2}\right)^2}\right)$ +$w(n) = \sqrt{\frac{\sum_{j=0}^{n} K(j)}{\sum_{j=0}^{N/2} K(j)}}$,   $K(j) = I_0\!\left(\beta\sqrt{1-\left(\frac{2j-N/2}{N/2}\right)^2}\right)$ -Satisfies the Princen-Bradley condition for perfect MDCT reconstruction. AAC, Vorbis, Opus audio codecs (long blocks). Princen & Bradley 1987. - -
+Princen-Bradley condition for perfect MDCT reconstruction. AAC, Vorbis, Opus audio codecs.[^princen1987] -#### `dpss(i, N, W)` +#### `dpss` -`W`: half-bandwidth [0, 0.5], default **0.1**. +`dpss(i, N, W)` · `W`: half-bandwidth [0, 0.5], default **0.1**. -$\mathbf{T}\mathbf{v} = \lambda\mathbf{v} \quad T_{jk} = \frac{\sin 2\pi W(j-k)}{\pi(j-k)}$ +$\mathbf{T}\mathbf{v} = \lambda\mathbf{v}$,   $T_{jk} = \frac{\sin 2\pi W(j-k)}{\pi(j-k)}$ -Dominant eigenvector of the sinc Toeplitz matrix — provably optimal energy concentration in a frequency band. Also called Slepian window. Thomson multitaper spectral estimation, neuroscience (EEG/MEG), climate science. Slepian 1978. - -
+Dominant eigenvector of sinc Toeplitz matrix — provably optimal energy concentration. Also called Slepian window. Multitaper spectral estimation, neuroscience, climate science.[^slepian1978] -#### `ultraspherical(i, N, mu, xmu)` +#### `ultraspherical` -`mu`: 0 → Dolph-Chebyshev, **1** (default) → Saramaki. `xmu`: sidelobe control (default **1**). +`ultraspherical(i, N, mu, xmu)` · `mu`: 0 = Dolph-Chebyshev, **1** (default) = Saramaki. `xmu`: sidelobe control (default **1**). -$W(k) = C_n^\mu\!\left(x_\mu\cos\frac{\pi k}{N}\right),\quad w = \text{IDFT}(W)$ +$W(k) = C_n^\mu\!\left(x_\mu\cos\frac{\pi k}{N}\right)$,   $w = \text{IDFT}(W)$ -Gegenbauer polynomial window. Independent control of sidelobe level and taper rate. Advanced antenna design, beamforming. Streit 1984. +Gegenbauer polynomial window. Independent control of sidelobe level and taper rate. Antenna design, beamforming.[^streit1984] + +--- + +## Choosing a window + +Every window trades **frequency resolution** (narrow main lobe), **spectral leakage** (low sidelobes), and **amplitude accuracy** (flat top). No single window wins all three. -
+
+
Just get started
+
hann — good all-round, zero edges, 50% COLA
-## Quantitative metrics +
Design FIR filters
+
kaiser or hamming — Kaiser is tunable, Hamming is the classic
-Three functions to verify and compare windows numerically: +
Measure amplitudes accurately
+
flatTop — < 0.01 dB scalloping loss
-- **ENBW** (Equivalent Noise Bandwidth) — frequency bins of noise power leaking through. Rectangular = 1.0, Hann = 1.5, Blackman-Harris = 2.0. Lower = less noise. -- **Scallop loss** — worst-case amplitude error between DFT bins. Rectangular = 3.92 dB, Hann = 1.42 dB, flat-top ≈ 0 dB. -- **COLA** (Constant Overlap-Add) — 0 = perfect STFT reconstruction at the given hop size. +
High dynamic range (>80 dB)
+
blackmanHarris — -92 dB equiripple sidelobes
+ +
Audio codec (MDCT)
+
kaiserBesselDerived or cosine — Princen-Bradley perfect reconstruction
+ +
Preserve center, taper edges
+
tukey — adjustable flat-top fraction
+ +
Robust spectral estimation
+
dpss — optimal for multitaper method
+ +
Radar / SAR
+
taylor — monotonic sidelobes, radar standard
+ +
Antenna array design
+
dolphChebyshev or ultraspherical — optimal equiripple or tunable taper
+ +
Tune resolution/leakage continuously
+
kaiser or gaussian — single-parameter adjustment
+ +
Modal / impact analysis
+
exponential — controlled decay for underdamped systems
+ +
FTIR spectroscopy
+
connes — smooth apodization for interferograms
+ +
Gravitational waves
+
planckTaper — C∞ smooth, no spectral artifacts
+
+ +## Metrics + +Three functions for quantitative comparison: + +- **`enbw(fn, N, ...params)`** — equivalent noise bandwidth in frequency bins. Rectangular = 1.0, Hann = 1.5, Blackman-Harris = 2.0. Lower = less noise. +- **`scallopLoss(fn, N, ...params)`** — worst-case amplitude error in dB between DFT bins. Rectangular = 3.92, Hann = 1.42, flat-top ≈ 0. +- **`cola(fn, N, hop, ...params)`** — COLA deviation. 0 = perfect STFT reconstruction at given hop size. ## Migrating from v2 -v3 is a complete rewrite: CJS → ESM, 18 → 34 windows, subpath imports preserved. +CJS → ESM, 18 → 34 windows. Subpath imports preserved. ```diff - const hann = require('window-function/hann') @@ -469,34 +479,30 @@ v3 is a complete rewrite: CJS → ESM, 18 → 34 windows, subpath imports preser + import { apply } from 'window-function/apply' ``` -The per-sample API (`fn(i, N, ...params) → number`) is unchanged. - -## References - -| Year | Reference | Windows | -|---|---|---| -| 1946 | Dolph, *Proc. IRE* 34 | Dolph-Chebyshev | -| 1946 | Gabor, *J. IEE* 93 | Gaussian | -| 1950 | Bartlett, *Biometrika* 37 | Bartlett | -| 1955 | Taylor, *IRE Trans. Antennas Propag.* AP-4 | Taylor | -| 1958 | Blackman & Tukey, *The Measurement of Power Spectra* | Hann, Blackman | -| 1961 | Connes, *Revue d'Optique* 40 | Connes | -| 1961 | Parzen, *Technometrics* 3 | Parzen | -| 1967 | Welch, *IEEE Trans. Audio Electroacoustics* AU-15 | Welch | -| 1970 | Rife & Vincent, *IEEE Trans. Instrumentation* | Rife-Vincent | -| 1974 | Kaiser, *IEEE Int. Symp. Circuits and Systems* | Kaiser | -| 1977 | Hamming, *Digital Filters* | Hamming | -| 1978 | Harris, *Proc. IEEE* 66 — **the comprehensive survey** | Blackman-Harris, survey of all | -| 1978 | Slepian, *Bell System Technical Journal* 57 | DPSS | -| 1979 | Duchon, *J. Applied Meteorology* 18 | Lanczos | -| 1981 | Nuttall, *IEEE Trans. ASSP* 29 | Nuttall, Blackman-Nuttall | -| 1984 | Streit, *IEEE Trans. ASSP* 32 | Ultraspherical | -| 1987 | Princen, Johnson & Bradley, *ICASSP* | KBD, Cosine | -| 1989 | Ha & Pearce, *IEEE* | Bartlett-Hann | -| 2002 | Heinzel, Rudiger & Schilling (ISO 18431) | Flat-top | -| 2010 | McKechan, Robinson & Sathyaprakash, *Class. Quantum Grav.* | Planck-taper | -| 2014 | Starosielec & Hagemeier, *Signal Processing* | Confined Gaussian | +The per-sample API `fn(i, N, ...params) → number` is unchanged. ## License MIT · + +[^bartlett1950]: M.S. Bartlett, "Periodogram Analysis and Continuous Spectra," *Biometrika* 37, 1950. +[^welch1967]: P.D. Welch, "The Use of Fast Fourier Transform for the Estimation of Power Spectra," *IEEE Trans. Audio Electroacoustics* AU-15, 1967. +[^connes1961]: J. Connes, "Recherches sur la spectroscopie par transformation de Fourier," *Revue d'Optique* 40, 1961. +[^blackman1958]: R.B. Blackman & J.W. Tukey, *The Measurement of Power Spectra*, Dover, 1958. +[^hamming1977]: R.W. Hamming, *Digital Filters*, Prentice-Hall, 1977. +[^princen1987]: J.P. Princen, A.W. Johnson & A.B. Bradley, "Subband/Transform Coding Using Filter Bank Designs Based on Time Domain Aliasing Cancellation," *ICASSP*, 1987. +[^harris1978]: F.J. Harris, "On the Use of Windows for Harmonic Analysis with the Discrete Fourier Transform," *Proc. IEEE* 66, 1978. +[^nuttall1981]: A.H. Nuttall, "Some Windows with Very Good Sidelobe Behavior," *IEEE Trans. ASSP* 29, 1981. +[^heinzel2002]: G. Heinzel, A. Rudiger & R. Schilling, "Spectrum and Spectral Density Estimation by the DFT," Max Planck Institute, 2002. +[^ha1989]: Y.H. Ha & J.A. Pearce, "A New Window and Comparison to Standard Windows," *IEEE Trans. ASSP*, 1989. +[^duchon1979]: C.E. Duchon, "Lanczos Filtering in One and Two Dimensions," *J. Applied Meteorology* 18, 1979. +[^parzen1961]: E. Parzen, "Mathematical Considerations in the Estimation of Spectra," *Technometrics* 3, 1961. +[^kaiser1974]: J.F. Kaiser, "Nonrecursive Digital Filter Design Using the Sinh Window Function," *IEEE Int. Symp. Circuits and Systems*, 1974. +[^gabor1946]: D. Gabor, "Theory of Communication," *J. IEE* 93, 1946. +[^mckechan2010]: D.J.A. McKechan, C. Robinson & B.S. Sathyaprakash, "A Tapering Window for Time-Domain Templates and Simulated Signals in the Detection of Gravitational Waves," *Class. Quantum Grav.* 27, 2010. +[^starosielec2014]: S. Starosielec & D. Hagemeier, "Discrete-Time Windows with Minimal RMS Bandwidth for Given RMS Temporal Width," *Signal Processing* 102, 2014. +[^rife1970]: D.C. Rife & G.A. Vincent, "Use of the Discrete Fourier Transform in the Measurement of Frequencies and Levels of Tones," *Bell System Technical Journal* 49, 1970. +[^dolph1946]: C.L. Dolph, "A Current Distribution for Broadside Arrays Which Optimizes the Relationship Between Beam Width and Side-Lobe Level," *Proc. IRE* 34, 1946. +[^taylor1955]: T.T. Taylor, "Design of Line-Source Antennas for Narrow Beamwidth and Low Side Lobes," *IRE Trans. Antennas Propag.* AP-4, 1955. +[^slepian1978]: D. Slepian, "Prolate Spheroidal Wave Functions, Fourier Analysis, and Uncertainty — V," *Bell System Technical Journal* 57, 1978. +[^streit1984]: R.L. Streit, "A Two-Parameter Family of Weights for Nonrecursive Digital Filters and Antennas," *IEEE Trans. ASSP* 32, 1984. From b6078d4fb3b60fc3d17223931facbbd4857ae026 Mon Sep 17 00:00:00 2001 From: "Dmitry Iv." Date: Thu, 26 Mar 2026 00:16:45 -0400 Subject: [PATCH 11/26] Update README.md --- README.md | 273 +++++++++++++++++++++++++++--------------------------- 1 file changed, 137 insertions(+), 136 deletions(-) diff --git a/README.md b/README.md index 9cae1bb..1106a2e 100644 --- a/README.md +++ b/README.md @@ -24,236 +24,230 @@ apply(signal, hann) // signal *= hann // Parameterized windows pass extra args generate(kaiser, 1024, 8.6) // Kaiser with β = 8.6 - -// Quantitative comparison -import { enbw, scallopLoss, cola } from 'window-function' -enbw(hann, 1024) // 1.5 — noise bandwidth (bins) -scallopLoss(hann, 1024) // 1.42 — worst-case amplitude error (dB) -cola(hann, 1024, 512) // 0 — perfect STFT reconstruction ``` ## Contents -**Simple:** -[rectangular](#rectangular) · -[triangular](#triangular) · -[bartlett](#bartlett) · -[welch](#welch) · -[connes](#connes) · -[hann](#hann) · -[hamming](#hamming) · -[cosine](#cosine) · -[blackman](#blackman) · -[exactBlackman](#exactblackman) · -[nuttall](#nuttall) · -[blackmanNuttall](#blackmannuttall) · -[blackmanHarris](#blackmanharris) · -[flatTop](#flattop) · -[bartlettHann](#bartletthann) · -[lanczos](#lanczos) · -[parzen](#parzen) · -[bohman](#bohman) - -**Parameterized:** -[kaiser](#kaiser) · -[gaussian](#gaussian) · -[generalizedNormal](#generalizednormal) · -[tukey](#tukey) · -[planckTaper](#plancktaper) · -[powerOfSine](#powerofsine) · -[exponential](#exponential) · -[hannPoisson](#hannpoisson) · -[cauchy](#cauchy) · -[rifeVincent](#rifevincent) · -[confinedGaussian](#confinedgaussian) - -**Array-computed:** -[dolphChebyshev](#dolphchebyshev) · -[taylor](#taylor) · -[kaiserBesselDerived](#kaiserbesselderived) · -[dpss](#dpss) · -[ultraspherical](#ultraspherical) - -**Other:** +**Simple:**
+[rectangular](#rectangulari-n) · +[triangular](#triangulari-n) · +[bartlett](#bartletti-n) · +[welch](#welchi-n) · +[connes](#connesi-n) · +[hann](#hanni-n) · +[hamming](#hammingi-n) · +[cosine](#cosinei-n) · +[blackman](#blackmani-n) · +[exactBlackman](#exactblackmani-n) · +[nuttall](#nuttalli-n) · +[blackmanNuttall](#blackmannuttalli-n) · +[blackmanHarris](#blackmanharrisi-n) · +[flatTop](#flattopi-n) · +[bartlettHann](#bartletthanni-n) · +[lanczos](#lanczosi-n) · +[parzen](#parzeni-n) · +[bohman](#bohmani-n) + +**Parameterized:**
+[kaiser](#kaiseri-n-beta) · +[gaussian](#gaussiani-n-sigma) · +[generalizedNormal](#generalizednormali-n-sigma-p) · +[tukey](#tukeyi-n-alpha) · +[planckTaper](#plancktaperi-n-epsilon) · +[powerOfSine](#powerofsinei-n-alpha) · +[exponential](#exponentiali-n-tau) · +[hannPoisson](#hannpoissoni-n-alpha) · +[cauchy](#cauchyi-n-alpha) · +[rifeVincent](#rifevincenti-n-order) · +[confinedGaussian](#confinedgaussiani-n-sigmat) + +**Array-computed:**
+[dolphChebyshev](#dolphchebyshevi-n-db) · +[taylor](#taylori-n-nbar-sll) · +[kaiserBesselDerived](#kaiserbesselderivedi-n-beta) · +[dpss](#dpssi-n-w) · +[ultraspherical](#ultrasphericali-n-mu-xmu) + +**Other:**
[Choosing a window](#choosing-a-window) · [Metrics](#metrics) · [Migrating from v2](#migrating-from-v2) ---- + ## Window Reference ### Simple — no parameters -#### `rectangular` +#### `rectangular(i, N)` -`rectangular(i, N)` · $w(n) = 1$ +$w(n) = 1$ No windowing. Best frequency resolution, worst spectral leakage. Use for transient signals already zero at edges, or harmonic analysis with integer cycles. **-13 dB** sidelobe · **-6 dB/oct** rolloff -#### `triangular` +#### `triangular(i, N)` -`triangular(i, N)` · $w(n) = 1 - \left|\frac{2n - N + 1}{N}\right|$ +$w(n) = 1 - \left|\frac{2n - N + 1}{N}\right|$ Linear taper, nonzero endpoints. Simple smoothing, 2nd-order B-spline. **-27 dB** sidelobe · **-12 dB/oct** rolloff -#### `bartlett` +#### `bartlett(i, N)` -`bartlett(i, N)` · $w(n) = 1 - \left|\frac{2n - N + 1}{N - 1}\right|$ +$w(n) = 1 - \left|\frac{2n - N + 1}{N - 1}\right|$ Linear taper, zero endpoints. Bartlett's method PSD estimation.[^bartlett1950] **-27 dB** sidelobe · **-12 dB/oct** rolloff -#### `welch` +#### `welch(i, N)` -`welch(i, N)` · $w(n) = 1 - \left(\frac{2n - N + 1}{N - 1}\right)^2$ +$w(n) = 1 - \left(\frac{2n - N + 1}{N - 1}\right)^2$ Parabolic taper. Welch's method PSD estimation.[^welch1967] **-21 dB** sidelobe · **-12 dB/oct** rolloff -#### `connes` +#### `connes(i, N)` -`connes(i, N)` · $w(n) = \left[1 - \left(\frac{2n - N + 1}{N - 1}\right)^2\right]^2$ +$w(n) = \left[1 - \left(\frac{2n - N + 1}{N - 1}\right)^2\right]^2$ Welch squared (4th power parabolic). FTIR spectroscopy, interferogram apodization.[^connes1961] **-24 dB/oct** rolloff -#### `hann` +#### `hann(i, N)` -`hann(i, N)` · $w(n) = 0.5 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right)$ +$w(n) = 0.5 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right)$ Raised cosine, zero endpoints. The default general-purpose choice. STFT with 50% overlap (COLA). Also called "Hanning" (misnomer).[^blackman1958] **-32 dB** sidelobe · **-18 dB/oct** rolloff -#### `hamming` +#### `hamming(i, N)` -`hamming(i, N)` · $w(n) = 0.54 - 0.46\cos\!\left(\frac{2\pi n}{N-1}\right)$ +$w(n) = 0.54 - 0.46\cos\!\left(\frac{2\pi n}{N-1}\right)$ Raised cosine, nonzero endpoints. Optimized for first sidelobe cancellation. FIR filter design, speech processing.[^hamming1977] **-43 dB** sidelobe · **-6 dB/oct** rolloff -#### `cosine` +#### `cosine(i, N)` -`cosine(i, N)` · $w(n) = \sin\!\left(\frac{\pi n}{N-1}\right)$ +$w(n) = \sin\!\left(\frac{\pi n}{N-1}\right)$ Half-period sine. MDCT audio codecs: MP3, AAC, Vorbis.[^princen1987] **-23 dB** sidelobe · **-12 dB/oct** rolloff -#### `blackman` +#### `blackman(i, N)` -`blackman(i, N)` · $w(n) = 0.42 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.08\cos\!\left(\frac{4\pi n}{N-1}\right)$ +$w(n) = 0.42 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.08\cos\!\left(\frac{4\pi n}{N-1}\right)$ 3-term cosine sum. Better leakage than Hann at the cost of wider main lobe.[^blackman1958] **-58 dB** sidelobe · **-18 dB/oct** rolloff -#### `exactBlackman` +#### `exactBlackman(i, N)` -`exactBlackman(i, N)` · $w(n) = 0.42659 - 0.49656\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.076849\cos\!\left(\frac{4\pi n}{N-1}\right)$ +$w(n) = 0.42659 - 0.49656\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.076849\cos\!\left(\frac{4\pi n}{N-1}\right)$ Blackman with exact zero placement at 3rd and 4th sidelobes.[^harris1978] **-69 dB** sidelobe · **-6 dB/oct** rolloff -#### `nuttall` +#### `nuttall(i, N)` -`nuttall(i, N)` · $w(n) = 0.355768 - 0.487396\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.144232\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.012604\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ +$w(n) = 0.355768 - 0.487396\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.144232\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.012604\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ 4-term cosine sum, continuous 1st derivative. High-dynamic-range analysis without edge discontinuity.[^nuttall1981] **-93 dB** sidelobe · **-18 dB/oct** rolloff -#### `blackmanNuttall` +#### `blackmanNuttall(i, N)` -`blackmanNuttall(i, N)` · $w(n) = 0.3635819 - 0.4891775\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.1365995\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.0106411\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ +$w(n) = 0.3635819 - 0.4891775\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.1365995\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.0106411\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ 4-term cosine sum, lowest sidelobes among 4-term windows.[^nuttall1981] **-98 dB** sidelobe · **-6 dB/oct** rolloff -#### `blackmanHarris` +#### `blackmanHarris(i, N)` -`blackmanHarris(i, N)` · $w(n) = 0.35875 - 0.48829\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.14128\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.01168\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ +$w(n) = 0.35875 - 0.48829\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.14128\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.01168\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ 4-term minimum sidelobe. ADC testing, measurement instrumentation, >80 dB dynamic range.[^harris1978] **-92 dB** sidelobe · **-6 dB/oct** rolloff -#### `flatTop` +#### `flatTop(i, N)` -`flatTop(i, N)` · $w(n) = 1 - 1.93\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 1.29\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.388\cos\!\left(\frac{6\pi n}{N\!-\!1}\right) + 0.028\cos\!\left(\frac{8\pi n}{N\!-\!1}\right)$ +$w(n) = 1 - 1.93\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 1.29\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.388\cos\!\left(\frac{6\pi n}{N\!-\!1}\right) + 0.028\cos\!\left(\frac{8\pi n}{N\!-\!1}\right)$ 5-term cosine sum, near-zero scalloping. Peak ~4.64 (by design). Amplitude calibration, transducer calibration (~0.01 dB accuracy). ISO 18431.[^heinzel2002] **-93 dB** sidelobe · **-6 dB/oct** rolloff -#### `bartlettHann` +#### `bartlettHann(i, N)` -`bartlettHann(i, N)` · $w(n) = 0.62 - 0.48\left|\frac{n}{N\!-\!1} - 0.5\right| - 0.38\cos\!\left(\frac{2\pi n}{N-1}\right)$ +$w(n) = 0.62 - 0.48\left|\frac{n}{N\!-\!1} - 0.5\right| - 0.38\cos\!\left(\frac{2\pi n}{N-1}\right)$ Bartlett-Hann hybrid. Balanced near/far sidelobe levels.[^ha1989] **-36 dB** sidelobe -#### `lanczos` +#### `lanczos(i, N)` -`lanczos(i, N)` · $w(n) = \text{sinc}\!\left(\frac{2n}{N-1} - 1\right)$ +$w(n) = \text{sinc}\!\left(\frac{2n}{N-1} - 1\right)$ Sinc main lobe. Image resampling, interpolation (FFmpeg, ImageMagick).[^duchon1979] **-26 dB** sidelobe -#### `parzen` +#### `parzen(i, N)` -`parzen(i, N)` · $w(n) = 1 - 6a^2(1-a)$ for $|a| \le 0.5$,   $w(n) = 2(1-a)^3$ for $|a| > 0.5$,   $a = |(2n-N+1)/(N-1)|$ +$w(n) = 1 - 6a^2(1-a)$ for $|a| \le 0.5$,   $w(n) = 2(1-a)^3$ for $|a| > 0.5$,   $a = |(2n-N+1)/(N-1)|$ 4th-order B-spline. Always-positive spectrum. Kernel density estimation.[^parzen1961] **-53 dB** sidelobe · **-24 dB/oct** rolloff -#### `bohman` +#### `bohman(i, N)` -`bohman(i, N)` · $w(n) = (1-|a|)\cos(\pi|a|) + \frac{\sin(\pi|a|)}{\pi}$,   $a = \frac{2n-N+1}{N-1}$ +$w(n) = (1-|a|)\cos(\pi|a|) + \frac{\sin(\pi|a|)}{\pi}$,   $a = \frac{2n-N+1}{N-1}$ Autocorrelation of cosine window. Fast sidelobe decay, spectral estimation. **-46 dB** sidelobe · **-24 dB/oct** rolloff ---- + ### Parameterized — adjustable tradeoff -#### `kaiser` +#### `kaiser(i, N, beta)` -`kaiser(i, N, beta)` · `beta`: shape — 0 = rectangular, 5.4 = Hamming, **8.6** (default) = Blackman. +`beta`: shape — 0 = rectangular, 5.4 = Hamming, **8.6** (default) = Blackman. $w(n) = \frac{I_0\!\left(\beta\sqrt{1 - \left(\frac{2n-N+1}{N-1}\right)^2}\right)}{I_0(\beta)}$ @@ -261,9 +255,9 @@ $w(n) = \frac{I_0\!\left(\beta\sqrt{1 - \left(\frac{2n-N+1}{N-1}\right)^2}\right Near-optimal DPSS approximation via Bessel I₀. The standard parameterized window for FIR filter design.[^kaiser1974] -#### `gaussian` +#### `gaussian(i, N, sigma)` -`gaussian(i, N, sigma)` · `sigma`: width, default **0.4**. +`sigma`: width, default **0.4**. $w(n) = \exp\!\left[-\frac{1}{2}\left(\frac{2n-N+1}{\sigma(N-1)}\right)^2\right]$ @@ -271,9 +265,9 @@ $w(n) = \exp\!\left[-\frac{1}{2}\left(\frac{2n-N+1}{\sigma(N-1)}\right)^2\right] Gaussian bell, minimum time-bandwidth product. STFT/Gabor transform, frequency estimation via parabolic interpolation.[^gabor1946] -#### `generalizedNormal` +#### `generalizedNormal(i, N, sigma, p)` -`generalizedNormal(i, N, sigma, p)` · `sigma`: width (default **0.4**), `p`: shape — 2 = Gaussian, large = rectangular. +`sigma`: width (default **0.4**), `p`: shape — 2 = Gaussian, large = rectangular. $w(n) = \exp\!\left[-\frac{1}{2}\left|\frac{2n-N+1}{\sigma(N-1)}\right|^p\right]$ @@ -281,9 +275,9 @@ $w(n) = \exp\!\left[-\frac{1}{2}\left|\frac{2n-N+1}{\sigma(N-1)}\right|^p\right] Continuous family between Gaussian and rectangular. Adjustable time-frequency tradeoff. -#### `tukey` +#### `tukey(i, N, alpha)` -`tukey(i, N, alpha)` · `alpha`: taper fraction — 0 = rectangular, **0.5** (default), 1 = Hann. +`alpha`: taper fraction — 0 = rectangular, **0.5** (default), 1 = Hann. $w(n) = \tfrac{1}{2}[1+\cos(\pi(n/(\alpha(N\!-\!1)/2)-1))]$ in tapered edges,   $w(n) = 1$ in flat center. @@ -291,17 +285,17 @@ $w(n) = \tfrac{1}{2}[1+\cos(\pi(n/(\alpha(N\!-\!1)/2)-1))]$ in tapered edges, &e Flat center with cosine-tapered edges. Preserves signal amplitude while tapering. Vibration analysis, LIGO. -#### `planckTaper` +#### `planckTaper(i, N, epsilon)` -`planckTaper(i, N, epsilon)` · `epsilon`: taper fraction, default **0.1**. +`epsilon`: taper fraction, default **0.1**. C∞-smooth bump function (infinitely differentiable). Gravitational wave analysis (LIGO/Virgo).[^mckechan2010] -#### `powerOfSine` +#### `powerOfSine(i, N, alpha)` -`powerOfSine(i, N, alpha)` · `alpha`: exponent — 0 = rectangular, 1 = cosine, **2** (default) = Hann. +`alpha`: exponent — 0 = rectangular, 1 = cosine, **2** (default) = Hann. $w(n) = \sin^\alpha\!\left(\frac{\pi n}{N-1}\right)$ @@ -309,9 +303,9 @@ $w(n) = \sin^\alpha\!\left(\frac{\pi n}{N-1}\right)$ sin^α family. Codec design, parameterized spectral analysis. -#### `exponential` +#### `exponential(i, N, tau)` -`exponential(i, N, tau)` · `tau`: time constant, default **1**. +`tau`: time constant, default **1**. $w(n) = \exp\!\left(\frac{-|2n-N+1|}{\tau(N-1)}\right)$ @@ -319,9 +313,9 @@ $w(n) = \exp\!\left(\frac{-|2n-N+1|}{\tau(N-1)}\right)$ Exponential decay from center. Modal analysis, impact testing.[^harris1978] -#### `hannPoisson` +#### `hannPoisson(i, N, alpha)` -`hannPoisson(i, N, alpha)` · `alpha`: decay, default **2**. At α ≥ 2 the transform has no sidelobes. +`alpha`: decay, default **2**. At α ≥ 2 the transform has no sidelobes. $w(n) = \frac{1}{2}\left(1-\cos\frac{2\pi n}{N\!-\!1}\right)\exp\!\left(\frac{-\alpha|2n\!-\!N\!+\!1|}{N-1}\right)$ @@ -329,9 +323,9 @@ $w(n) = \frac{1}{2}\left(1-\cos\frac{2\pi n}{N\!-\!1}\right)\exp\!\left(\frac{-\ Hann × exponential. Unique no-sidelobe property enables frequency estimators using convex optimization. -#### `cauchy` +#### `cauchy(i, N, alpha)` -`cauchy(i, N, alpha)` · `alpha`: width, default **3**. +`alpha`: width, default **3**. $w(n) = \frac{1}{1+\left(\frac{\alpha(2n-N+1)}{N-1}\right)^2}$ @@ -339,9 +333,9 @@ $w(n) = \frac{1}{1+\left(\frac{\alpha(2n-N+1)}{N-1}\right)^2}$ Lorentzian shape. Matches spectral line shapes in spectroscopy.[^harris1978] -#### `rifeVincent` +#### `rifeVincent(i, N, order)` -`rifeVincent(i, N, order)` · `order`: **1** (default) = Hann, 2, 3. Throws for other values. +`order`: **1** (default) = Hann, 2, 3. Throws for other values. $w(n) = \frac{1}{Z}\sum_{k=0}^{K}(-1)^k a_k\cos\frac{2\pi kn}{N-1}$ @@ -349,23 +343,23 @@ $w(n) = \frac{1}{Z}\sum_{k=0}^{K}(-1)^k a_k\cos\frac{2\pi kn}{N-1}$ Class I cosine-sum optimized for sidelobe fall-off. Power grid harmonic analysis, interpolated DFT.[^rife1970] -#### `confinedGaussian` +#### `confinedGaussian(i, N, sigmaT)` -`confinedGaussian(i, N, sigmaT)` · `sigmaT`: temporal width, default **0.1**. +`sigmaT`: temporal width, default **0.1**. Optimal RMS time-frequency bandwidth. Time-frequency analysis, audio coding.[^starosielec2014] ---- + ### Array-computed — cached Compute the full window on first call, cache the result. Recomputed when parameters change. -#### `dolphChebyshev` +#### `dolphChebyshev(i, N, dB)` -`dolphChebyshev(i, N, dB)` · `dB`: sidelobe attenuation, default **100**. +`dB`: sidelobe attenuation, default **100**. $W(k) = (-1)^k T_{N-1}\!\left(\beta\cos\frac{\pi k}{N}\right)$,   $w = \text{IDFT}(W)$ @@ -373,9 +367,9 @@ $W(k) = (-1)^k T_{N-1}\!\left(\beta\cos\frac{\pi k}{N}\right)$,   $w = \tex Optimal: narrowest main lobe for given equiripple sidelobe level. Antenna design, radar.[^dolph1946] -#### `taylor` +#### `taylor(i, N, nbar, sll)` -`taylor(i, N, nbar, sll)` · `nbar`: constant-level sidelobes (default **4**), `sll`: level in dB (default **30**). +`nbar`: constant-level sidelobes (default **4**), `sll`: level in dB (default **30**). $w(n) = 1 + 2\sum_{m=1}^{\bar{n}-1} F_m \cos\frac{2\pi m(n-(N\!-\!1)/2)}{N}$ @@ -383,9 +377,9 @@ $w(n) = 1 + 2\sum_{m=1}^{\bar{n}-1} F_m \cos\frac{2\pi m(n-(N\!-\!1)/2)}{N}$ Monotonically decreasing sidelobes. The radar community standard for SAR image formation.[^taylor1955] -#### `kaiserBesselDerived` +#### `kaiserBesselDerived(i, N, beta)` -`kaiserBesselDerived(i, N, beta)` · `beta`: shape, default **8.6**. N must be even. +`beta`: shape, default **8.6**. N must be even. $w(n) = \sqrt{\frac{\sum_{j=0}^{n} K(j)}{\sum_{j=0}^{N/2} K(j)}}$,   $K(j) = I_0\!\left(\beta\sqrt{1-\left(\frac{2j-N/2}{N/2}\right)^2}\right)$ @@ -393,9 +387,9 @@ $w(n) = \sqrt{\frac{\sum_{j=0}^{n} K(j)}{\sum_{j=0}^{N/2} K(j)}}$,   $K(j) Princen-Bradley condition for perfect MDCT reconstruction. AAC, Vorbis, Opus audio codecs.[^princen1987] -#### `dpss` +#### `dpss(i, N, W)` -`dpss(i, N, W)` · `W`: half-bandwidth [0, 0.5], default **0.1**. +`W`: half-bandwidth [0, 0.5], default **0.1**. $\mathbf{T}\mathbf{v} = \lambda\mathbf{v}$,   $T_{jk} = \frac{\sin 2\pi W(j-k)}{\pi(j-k)}$ @@ -403,9 +397,9 @@ $\mathbf{T}\mathbf{v} = \lambda\mathbf{v}$,   $T_{jk} = \frac{\sin 2\pi W(j Dominant eigenvector of sinc Toeplitz matrix — provably optimal energy concentration. Also called Slepian window. Multitaper spectral estimation, neuroscience, climate science.[^slepian1978] -#### `ultraspherical` +#### `ultraspherical(i, N, mu, xmu)` -`ultraspherical(i, N, mu, xmu)` · `mu`: 0 = Dolph-Chebyshev, **1** (default) = Saramaki. `xmu`: sidelobe control (default **1**). +`mu`: 0 = Dolph-Chebyshev, **1** (default) = Saramaki. `xmu`: sidelobe control (default **1**). $W(k) = C_n^\mu\!\left(x_\mu\cos\frac{\pi k}{N}\right)$,   $w = \text{IDFT}(W)$ @@ -413,7 +407,7 @@ $W(k) = C_n^\mu\!\left(x_\mu\cos\frac{\pi k}{N}\right)$,   $w = \text{IDFT} Gegenbauer polynomial window. Independent control of sidelobe level and taper rate. Antenna design, beamforming.[^streit1984] ---- + ## Choosing a window @@ -421,48 +415,56 @@ Every window trades **frequency resolution** (narrow main lobe), **spectral leak
Just get started
-
hann — good all-round, zero edges, 50% COLA
+
hann — good all-round, zero edges, 50% COLA
Design FIR filters
-
kaiser or hamming — Kaiser is tunable, Hamming is the classic
+
kaiser or hamming — Kaiser is tunable, Hamming is the classic
Measure amplitudes accurately
-
flatTop — < 0.01 dB scalloping loss
+
flatTop — < 0.01 dB scalloping loss
High dynamic range (>80 dB)
-
blackmanHarris — -92 dB equiripple sidelobes
+
blackmanHarris — -92 dB equiripple sidelobes
Audio codec (MDCT)
-
kaiserBesselDerived or cosine — Princen-Bradley perfect reconstruction
+
kaiserBesselDerived or cosine — Princen-Bradley perfect reconstruction
Preserve center, taper edges
-
tukey — adjustable flat-top fraction
+
tukey — adjustable flat-top fraction
Robust spectral estimation
-
dpss — optimal for multitaper method
+
dpss — optimal for multitaper method
Radar / SAR
-
taylor — monotonic sidelobes, radar standard
+
taylor — monotonic sidelobes, radar standard
Antenna array design
-
dolphChebyshev or ultraspherical — optimal equiripple or tunable taper
+
dolphChebyshev or ultraspherical — optimal equiripple or tunable taper
Tune resolution/leakage continuously
-
kaiser or gaussian — single-parameter adjustment
+
kaiser or gaussian — single-parameter adjustment
Modal / impact analysis
-
exponential — controlled decay for underdamped systems
+
exponential — controlled decay for underdamped systems
FTIR spectroscopy
-
connes — smooth apodization for interferograms
+
connes — smooth apodization for interferograms
Gravitational waves
-
planckTaper — C∞ smooth, no spectral artifacts
+
planckTaper — C∞ smooth, no spectral artifacts
## Metrics -Three functions for quantitative comparison: +Three functions for quantitative window comparison: + +```js +import { hann, enbw, scallopLoss, cola } from 'window-function' + +enbw(hann, 1024) // 1.5 — noise bandwidth (bins) +scallopLoss(hann, 1024) // 1.42 — worst-case amplitude error (dB) +cola(hann, 1024, 512) // 0 — perfect STFT reconstruction +``` - **`enbw(fn, N, ...params)`** — equivalent noise bandwidth in frequency bins. Rectangular = 1.0, Hann = 1.5, Blackman-Harris = 2.0. Lower = less noise. - **`scallopLoss(fn, N, ...params)`** — worst-case amplitude error in dB between DFT bins. Rectangular = 3.92, Hann = 1.42, flat-top ≈ 0. @@ -481,9 +483,6 @@ CJS → ESM, 18 → 34 windows. Subpath imports preserved. The per-sample API `fn(i, N, ...params) → number` is unchanged. -## License - -MIT · [^bartlett1950]: M.S. Bartlett, "Periodogram Analysis and Continuous Spectra," *Biometrika* 37, 1950. [^welch1967]: P.D. Welch, "The Use of Fast Fourier Transform for the Estimation of Power Spectra," *IEEE Trans. Audio Electroacoustics* AU-15, 1967. @@ -506,3 +505,5 @@ MIT · [^taylor1955]: T.T. Taylor, "Design of Line-Source Antennas for Narrow Beamwidth and Low Side Lobes," *IRE Trans. Antennas Propag.* AP-4, 1955. [^slepian1978]: D. Slepian, "Prolate Spheroidal Wave Functions, Fourier Analysis, and Uncertainty — V," *Bell System Technical Journal* 57, 1978. [^streit1984]: R.L. Streit, "A Two-Parameter Family of Weights for Nonrecursive Digital Filters and Antennas," *IEEE Trans. ASSP* 32, 1984. + +

MIT ·

From dff534072508990bcb4fc328bb9dc32e004ceb98 Mon Sep 17 00:00:00 2001 From: "Dmitry Iv." Date: Thu, 26 Mar 2026 00:17:31 -0400 Subject: [PATCH 12/26] Update README.md --- README.md | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 1106a2e..c8188f1 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,8 @@ apply(signal, hann) // signal *= hann generate(kaiser, 1024, 8.6) // Kaiser with β = 8.6 ``` -## Contents + +## Window Reference **Simple:**
[rectangular](#rectangulari-n) · @@ -68,14 +69,6 @@ generate(kaiser, 1024, 8.6) // Kaiser with β = 8.6 [dpss](#dpssi-n-w) · [ultraspherical](#ultrasphericali-n-mu-xmu) -**Other:**
-[Choosing a window](#choosing-a-window) · -[Metrics](#metrics) · -[Migrating from v2](#migrating-from-v2) - - - -## Window Reference ### Simple — no parameters From 9f2890561580f0bb083f396823d29f0c35ae6899 Mon Sep 17 00:00:00 2001 From: "Dmitry Iv." Date: Thu, 26 Mar 2026 00:18:27 -0400 Subject: [PATCH 13/26] Update README.md --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c8188f1..03071bb 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,8 @@ # window-function -Complete collection of window functions for signal processing and spectral analysis. -**34 windows** · zero dependencies · pure ESM · individual imports. +Collection of window functions for signal processing and spectral analysis. -```js +```sh npm install window-function ``` From ee50aebf04450481f5dd4a4360061fe54501f7ae Mon Sep 17 00:00:00 2001 From: "Dmitry Iv." Date: Thu, 26 Mar 2026 00:20:07 -0400 Subject: [PATCH 14/26] Update README.md --- README.md | 88 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 47 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 03071bb..07f71a4 100644 --- a/README.md +++ b/README.md @@ -26,9 +26,9 @@ generate(kaiser, 1024, 8.6) // Kaiser with β = 8.6 ``` -## Window Reference +## Contents -**Simple:**
+**Simple:** [rectangular](#rectangulari-n) · [triangular](#triangulari-n) · [bartlett](#bartletti-n) · @@ -48,7 +48,7 @@ generate(kaiser, 1024, 8.6) // Kaiser with β = 8.6 [parzen](#parzeni-n) · [bohman](#bohmani-n) -**Parameterized:**
+**Parameterized:** [kaiser](#kaiseri-n-beta) · [gaussian](#gaussiani-n-sigma) · [generalizedNormal](#generalizednormali-n-sigma-p) · @@ -61,17 +61,23 @@ generate(kaiser, 1024, 8.6) // Kaiser with β = 8.6 [rifeVincent](#rifevincenti-n-order) · [confinedGaussian](#confinedgaussiani-n-sigmat) -**Array-computed:**
+**Array-computed:** [dolphChebyshev](#dolphchebyshevi-n-db) · [taylor](#taylori-n-nbar-sll) · [kaiserBesselDerived](#kaiserbesselderivedi-n-beta) · [dpss](#dpssi-n-w) · [ultraspherical](#ultrasphericali-n-mu-xmu) +**Other:** +[Choosing a window](#choosing-a-window) · +[Metrics](#metrics) · +[Migrating from v2](#migrating-from-v2) -### Simple — no parameters -#### `rectangular(i, N)` + +## Simple — no parameters + +### `rectangular(i, N)` $w(n) = 1$ @@ -80,7 +86,7 @@ $w(n) = 1$ No windowing. Best frequency resolution, worst spectral leakage. Use for transient signals already zero at edges, or harmonic analysis with integer cycles. **-13 dB** sidelobe · **-6 dB/oct** rolloff -#### `triangular(i, N)` +### `triangular(i, N)` $w(n) = 1 - \left|\frac{2n - N + 1}{N}\right|$ @@ -89,7 +95,7 @@ $w(n) = 1 - \left|\frac{2n - N + 1}{N}\right|$ Linear taper, nonzero endpoints. Simple smoothing, 2nd-order B-spline. **-27 dB** sidelobe · **-12 dB/oct** rolloff -#### `bartlett(i, N)` +### `bartlett(i, N)` $w(n) = 1 - \left|\frac{2n - N + 1}{N - 1}\right|$ @@ -98,7 +104,7 @@ $w(n) = 1 - \left|\frac{2n - N + 1}{N - 1}\right|$ Linear taper, zero endpoints. Bartlett's method PSD estimation.[^bartlett1950] **-27 dB** sidelobe · **-12 dB/oct** rolloff -#### `welch(i, N)` +### `welch(i, N)` $w(n) = 1 - \left(\frac{2n - N + 1}{N - 1}\right)^2$ @@ -107,7 +113,7 @@ $w(n) = 1 - \left(\frac{2n - N + 1}{N - 1}\right)^2$ Parabolic taper. Welch's method PSD estimation.[^welch1967] **-21 dB** sidelobe · **-12 dB/oct** rolloff -#### `connes(i, N)` +### `connes(i, N)` $w(n) = \left[1 - \left(\frac{2n - N + 1}{N - 1}\right)^2\right]^2$ @@ -116,7 +122,7 @@ $w(n) = \left[1 - \left(\frac{2n - N + 1}{N - 1}\right)^2\right]^2$ Welch squared (4th power parabolic). FTIR spectroscopy, interferogram apodization.[^connes1961] **-24 dB/oct** rolloff -#### `hann(i, N)` +### `hann(i, N)` $w(n) = 0.5 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right)$ @@ -125,7 +131,7 @@ $w(n) = 0.5 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right)$ Raised cosine, zero endpoints. The default general-purpose choice. STFT with 50% overlap (COLA). Also called "Hanning" (misnomer).[^blackman1958] **-32 dB** sidelobe · **-18 dB/oct** rolloff -#### `hamming(i, N)` +### `hamming(i, N)` $w(n) = 0.54 - 0.46\cos\!\left(\frac{2\pi n}{N-1}\right)$ @@ -134,7 +140,7 @@ $w(n) = 0.54 - 0.46\cos\!\left(\frac{2\pi n}{N-1}\right)$ Raised cosine, nonzero endpoints. Optimized for first sidelobe cancellation. FIR filter design, speech processing.[^hamming1977] **-43 dB** sidelobe · **-6 dB/oct** rolloff -#### `cosine(i, N)` +### `cosine(i, N)` $w(n) = \sin\!\left(\frac{\pi n}{N-1}\right)$ @@ -143,7 +149,7 @@ $w(n) = \sin\!\left(\frac{\pi n}{N-1}\right)$ Half-period sine. MDCT audio codecs: MP3, AAC, Vorbis.[^princen1987] **-23 dB** sidelobe · **-12 dB/oct** rolloff -#### `blackman(i, N)` +### `blackman(i, N)` $w(n) = 0.42 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.08\cos\!\left(\frac{4\pi n}{N-1}\right)$ @@ -152,7 +158,7 @@ $w(n) = 0.42 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.08\cos\!\left(\frac{ 3-term cosine sum. Better leakage than Hann at the cost of wider main lobe.[^blackman1958] **-58 dB** sidelobe · **-18 dB/oct** rolloff -#### `exactBlackman(i, N)` +### `exactBlackman(i, N)` $w(n) = 0.42659 - 0.49656\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.076849\cos\!\left(\frac{4\pi n}{N-1}\right)$ @@ -161,7 +167,7 @@ $w(n) = 0.42659 - 0.49656\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.076849\cos\!\ Blackman with exact zero placement at 3rd and 4th sidelobes.[^harris1978] **-69 dB** sidelobe · **-6 dB/oct** rolloff -#### `nuttall(i, N)` +### `nuttall(i, N)` $w(n) = 0.355768 - 0.487396\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.144232\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.012604\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ @@ -170,7 +176,7 @@ $w(n) = 0.355768 - 0.487396\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.144232\ 4-term cosine sum, continuous 1st derivative. High-dynamic-range analysis without edge discontinuity.[^nuttall1981] **-93 dB** sidelobe · **-18 dB/oct** rolloff -#### `blackmanNuttall(i, N)` +### `blackmanNuttall(i, N)` $w(n) = 0.3635819 - 0.4891775\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.1365995\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.0106411\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ @@ -179,7 +185,7 @@ $w(n) = 0.3635819 - 0.4891775\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.13659 4-term cosine sum, lowest sidelobes among 4-term windows.[^nuttall1981] **-98 dB** sidelobe · **-6 dB/oct** rolloff -#### `blackmanHarris(i, N)` +### `blackmanHarris(i, N)` $w(n) = 0.35875 - 0.48829\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.14128\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.01168\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ @@ -188,7 +194,7 @@ $w(n) = 0.35875 - 0.48829\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.14128\cos 4-term minimum sidelobe. ADC testing, measurement instrumentation, >80 dB dynamic range.[^harris1978] **-92 dB** sidelobe · **-6 dB/oct** rolloff -#### `flatTop(i, N)` +### `flatTop(i, N)` $w(n) = 1 - 1.93\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 1.29\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.388\cos\!\left(\frac{6\pi n}{N\!-\!1}\right) + 0.028\cos\!\left(\frac{8\pi n}{N\!-\!1}\right)$ @@ -197,7 +203,7 @@ $w(n) = 1 - 1.93\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 1.29\cos\!\left(\fra 5-term cosine sum, near-zero scalloping. Peak ~4.64 (by design). Amplitude calibration, transducer calibration (~0.01 dB accuracy). ISO 18431.[^heinzel2002] **-93 dB** sidelobe · **-6 dB/oct** rolloff -#### `bartlettHann(i, N)` +### `bartlettHann(i, N)` $w(n) = 0.62 - 0.48\left|\frac{n}{N\!-\!1} - 0.5\right| - 0.38\cos\!\left(\frac{2\pi n}{N-1}\right)$ @@ -206,7 +212,7 @@ $w(n) = 0.62 - 0.48\left|\frac{n}{N\!-\!1} - 0.5\right| - 0.38\cos\!\left(\frac{ Bartlett-Hann hybrid. Balanced near/far sidelobe levels.[^ha1989] **-36 dB** sidelobe -#### `lanczos(i, N)` +### `lanczos(i, N)` $w(n) = \text{sinc}\!\left(\frac{2n}{N-1} - 1\right)$ @@ -215,7 +221,7 @@ $w(n) = \text{sinc}\!\left(\frac{2n}{N-1} - 1\right)$ Sinc main lobe. Image resampling, interpolation (FFmpeg, ImageMagick).[^duchon1979] **-26 dB** sidelobe -#### `parzen(i, N)` +### `parzen(i, N)` $w(n) = 1 - 6a^2(1-a)$ for $|a| \le 0.5$,   $w(n) = 2(1-a)^3$ for $|a| > 0.5$,   $a = |(2n-N+1)/(N-1)|$ @@ -224,7 +230,7 @@ $w(n) = 1 - 6a^2(1-a)$ for $|a| \le 0.5$,   $w(n) = 2(1-a)^3$ for $|a| > 0. 4th-order B-spline. Always-positive spectrum. Kernel density estimation.[^parzen1961] **-53 dB** sidelobe · **-24 dB/oct** rolloff -#### `bohman(i, N)` +### `bohman(i, N)` $w(n) = (1-|a|)\cos(\pi|a|) + \frac{\sin(\pi|a|)}{\pi}$,   $a = \frac{2n-N+1}{N-1}$ @@ -235,9 +241,9 @@ Autocorrelation of cosine window. Fast sidelobe decay, spectral estimation. -### Parameterized — adjustable tradeoff +## Parameterized — adjustable tradeoff -#### `kaiser(i, N, beta)` +### `kaiser(i, N, beta)` `beta`: shape — 0 = rectangular, 5.4 = Hamming, **8.6** (default) = Blackman. @@ -247,7 +253,7 @@ $w(n) = \frac{I_0\!\left(\beta\sqrt{1 - \left(\frac{2n-N+1}{N-1}\right)^2}\right Near-optimal DPSS approximation via Bessel I₀. The standard parameterized window for FIR filter design.[^kaiser1974] -#### `gaussian(i, N, sigma)` +### `gaussian(i, N, sigma)` `sigma`: width, default **0.4**. @@ -257,7 +263,7 @@ $w(n) = \exp\!\left[-\frac{1}{2}\left(\frac{2n-N+1}{\sigma(N-1)}\right)^2\right] Gaussian bell, minimum time-bandwidth product. STFT/Gabor transform, frequency estimation via parabolic interpolation.[^gabor1946] -#### `generalizedNormal(i, N, sigma, p)` +### `generalizedNormal(i, N, sigma, p)` `sigma`: width (default **0.4**), `p`: shape — 2 = Gaussian, large = rectangular. @@ -267,7 +273,7 @@ $w(n) = \exp\!\left[-\frac{1}{2}\left|\frac{2n-N+1}{\sigma(N-1)}\right|^p\right] Continuous family between Gaussian and rectangular. Adjustable time-frequency tradeoff. -#### `tukey(i, N, alpha)` +### `tukey(i, N, alpha)` `alpha`: taper fraction — 0 = rectangular, **0.5** (default), 1 = Hann. @@ -277,7 +283,7 @@ $w(n) = \tfrac{1}{2}[1+\cos(\pi(n/(\alpha(N\!-\!1)/2)-1))]$ in tapered edges, &e Flat center with cosine-tapered edges. Preserves signal amplitude while tapering. Vibration analysis, LIGO. -#### `planckTaper(i, N, epsilon)` +### `planckTaper(i, N, epsilon)` `epsilon`: taper fraction, default **0.1**. @@ -285,7 +291,7 @@ Flat center with cosine-tapered edges. Preserves signal amplitude while tapering C∞-smooth bump function (infinitely differentiable). Gravitational wave analysis (LIGO/Virgo).[^mckechan2010] -#### `powerOfSine(i, N, alpha)` +### `powerOfSine(i, N, alpha)` `alpha`: exponent — 0 = rectangular, 1 = cosine, **2** (default) = Hann. @@ -295,7 +301,7 @@ $w(n) = \sin^\alpha\!\left(\frac{\pi n}{N-1}\right)$ sin^α family. Codec design, parameterized spectral analysis. -#### `exponential(i, N, tau)` +### `exponential(i, N, tau)` `tau`: time constant, default **1**. @@ -305,7 +311,7 @@ $w(n) = \exp\!\left(\frac{-|2n-N+1|}{\tau(N-1)}\right)$ Exponential decay from center. Modal analysis, impact testing.[^harris1978] -#### `hannPoisson(i, N, alpha)` +### `hannPoisson(i, N, alpha)` `alpha`: decay, default **2**. At α ≥ 2 the transform has no sidelobes. @@ -315,7 +321,7 @@ $w(n) = \frac{1}{2}\left(1-\cos\frac{2\pi n}{N\!-\!1}\right)\exp\!\left(\frac{-\ Hann × exponential. Unique no-sidelobe property enables frequency estimators using convex optimization. -#### `cauchy(i, N, alpha)` +### `cauchy(i, N, alpha)` `alpha`: width, default **3**. @@ -325,7 +331,7 @@ $w(n) = \frac{1}{1+\left(\frac{\alpha(2n-N+1)}{N-1}\right)^2}$ Lorentzian shape. Matches spectral line shapes in spectroscopy.[^harris1978] -#### `rifeVincent(i, N, order)` +### `rifeVincent(i, N, order)` `order`: **1** (default) = Hann, 2, 3. Throws for other values. @@ -335,7 +341,7 @@ $w(n) = \frac{1}{Z}\sum_{k=0}^{K}(-1)^k a_k\cos\frac{2\pi kn}{N-1}$ Class I cosine-sum optimized for sidelobe fall-off. Power grid harmonic analysis, interpolated DFT.[^rife1970] -#### `confinedGaussian(i, N, sigmaT)` +### `confinedGaussian(i, N, sigmaT)` `sigmaT`: temporal width, default **0.1**. @@ -345,11 +351,11 @@ Optimal RMS time-frequency bandwidth. Time-frequency analysis, audio coding.[^st -### Array-computed — cached +## Array-computed — cached Compute the full window on first call, cache the result. Recomputed when parameters change. -#### `dolphChebyshev(i, N, dB)` +### `dolphChebyshev(i, N, dB)` `dB`: sidelobe attenuation, default **100**. @@ -359,7 +365,7 @@ $W(k) = (-1)^k T_{N-1}\!\left(\beta\cos\frac{\pi k}{N}\right)$,   $w = \tex Optimal: narrowest main lobe for given equiripple sidelobe level. Antenna design, radar.[^dolph1946] -#### `taylor(i, N, nbar, sll)` +### `taylor(i, N, nbar, sll)` `nbar`: constant-level sidelobes (default **4**), `sll`: level in dB (default **30**). @@ -369,7 +375,7 @@ $w(n) = 1 + 2\sum_{m=1}^{\bar{n}-1} F_m \cos\frac{2\pi m(n-(N\!-\!1)/2)}{N}$ Monotonically decreasing sidelobes. The radar community standard for SAR image formation.[^taylor1955] -#### `kaiserBesselDerived(i, N, beta)` +### `kaiserBesselDerived(i, N, beta)` `beta`: shape, default **8.6**. N must be even. @@ -379,7 +385,7 @@ $w(n) = \sqrt{\frac{\sum_{j=0}^{n} K(j)}{\sum_{j=0}^{N/2} K(j)}}$,   $K(j) Princen-Bradley condition for perfect MDCT reconstruction. AAC, Vorbis, Opus audio codecs.[^princen1987] -#### `dpss(i, N, W)` +### `dpss(i, N, W)` `W`: half-bandwidth [0, 0.5], default **0.1**. @@ -389,7 +395,7 @@ $\mathbf{T}\mathbf{v} = \lambda\mathbf{v}$,   $T_{jk} = \frac{\sin 2\pi W(j Dominant eigenvector of sinc Toeplitz matrix — provably optimal energy concentration. Also called Slepian window. Multitaper spectral estimation, neuroscience, climate science.[^slepian1978] -#### `ultraspherical(i, N, mu, xmu)` +### `ultraspherical(i, N, mu, xmu)` `mu`: 0 = Dolph-Chebyshev, **1** (default) = Saramaki. `xmu`: sidelobe control (default **1**). From 88a103d112567d2ced089a1caa0a18c824f37e9c Mon Sep 17 00:00:00 2001 From: "Dmitry Iv." Date: Thu, 26 Mar 2026 00:29:20 -0400 Subject: [PATCH 15/26] Default funcs --- README.md | 161 +++++++++++++++++++++++++---------------- apply.js | 2 +- bartlett.js | 2 +- bartlettHann.js | 2 +- blackman.js | 2 +- blackmanHarris.js | 2 +- blackmanNuttall.js | 2 +- bohman.js | 2 +- cauchy.js | 2 +- cola.js | 4 +- confinedGaussian.js | 2 +- connes.js | 2 +- cosine.js | 2 +- dolphChebyshev.js | 2 +- dpss.js | 2 +- enbw.js | 2 +- exactBlackman.js | 2 +- exponential.js | 2 +- flatTop.js | 2 +- gaussian.js | 2 +- generalizedNormal.js | 2 +- generate.js | 2 +- hamming.js | 2 +- hann.js | 2 +- hannPoisson.js | 2 +- index.js | 78 ++++++++++---------- kaiser.js | 2 +- kaiserBesselDerived.js | 2 +- lanczos.js | 2 +- nuttall.js | 2 +- parzen.js | 2 +- planckTaper.js | 2 +- powerOfSine.js | 2 +- rectangular.js | 2 +- rifeVincent.js | 2 +- scallopLoss.js | 2 +- taylor.js | 2 +- triangular.js | 2 +- tukey.js | 2 +- ultraspherical.js | 2 +- welch.js | 2 +- 41 files changed, 177 insertions(+), 142 deletions(-) diff --git a/README.md b/README.md index 07f71a4..ad3eebc 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,8 @@ generate(kaiser, 1024, 8.6) // Kaiser with β = 8.6 ## Contents -**Simple:** +#### Simple + [rectangular](#rectangulari-n) · [triangular](#triangulari-n) · [bartlett](#bartletti-n) · @@ -48,7 +49,8 @@ generate(kaiser, 1024, 8.6) // Kaiser with β = 8.6 [parzen](#parzeni-n) · [bohman](#bohmani-n) -**Parameterized:** +#### Parameterized + [kaiser](#kaiseri-n-beta) · [gaussian](#gaussiani-n-sigma) · [generalizedNormal](#generalizednormali-n-sigma-p) · @@ -61,14 +63,16 @@ generate(kaiser, 1024, 8.6) // Kaiser with β = 8.6 [rifeVincent](#rifevincenti-n-order) · [confinedGaussian](#confinedgaussiani-n-sigmat) -**Array-computed:** +#### Array-computed + [dolphChebyshev](#dolphchebyshevi-n-db) · [taylor](#taylori-n-nbar-sll) · [kaiserBesselDerived](#kaiserbesselderivedi-n-beta) · [dpss](#dpssi-n-w) · [ultraspherical](#ultrasphericali-n-mu-xmu) -**Other:** +#### Other + [Choosing a window](#choosing-a-window) · [Metrics](#metrics) · [Migrating from v2](#migrating-from-v2) @@ -101,7 +105,7 @@ $w(n) = 1 - \left|\frac{2n - N + 1}{N - 1}\right|$ -Linear taper, zero endpoints. Bartlett's method PSD estimation.[^bartlett1950] +Linear taper, zero endpoints. Bartlett's method PSD estimation.[3](#ref-3) **-27 dB** sidelobe · **-12 dB/oct** rolloff ### `welch(i, N)` @@ -110,7 +114,7 @@ $w(n) = 1 - \left(\frac{2n - N + 1}{N - 1}\right)^2$ -Parabolic taper. Welch's method PSD estimation.[^welch1967] +Parabolic taper. Welch's method PSD estimation.[8](#ref-8) **-21 dB** sidelobe · **-12 dB/oct** rolloff ### `connes(i, N)` @@ -119,7 +123,7 @@ $w(n) = \left[1 - \left(\frac{2n - N + 1}{N - 1}\right)^2\right]^2$ -Welch squared (4th power parabolic). FTIR spectroscopy, interferogram apodization.[^connes1961] +Welch squared (4th power parabolic). FTIR spectroscopy, interferogram apodization.[6](#ref-6) **-24 dB/oct** rolloff ### `hann(i, N)` @@ -128,7 +132,7 @@ $w(n) = 0.5 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right)$ -Raised cosine, zero endpoints. The default general-purpose choice. STFT with 50% overlap (COLA). Also called "Hanning" (misnomer).[^blackman1958] +Raised cosine, zero endpoints. The default general-purpose choice. STFT with 50% overlap (COLA). Also called "Hanning" (misnomer).[5](#ref-5) **-32 dB** sidelobe · **-18 dB/oct** rolloff ### `hamming(i, N)` @@ -137,7 +141,7 @@ $w(n) = 0.54 - 0.46\cos\!\left(\frac{2\pi n}{N-1}\right)$ -Raised cosine, nonzero endpoints. Optimized for first sidelobe cancellation. FIR filter design, speech processing.[^hamming1977] +Raised cosine, nonzero endpoints. Optimized for first sidelobe cancellation. FIR filter design, speech processing.[11](#ref-11) **-43 dB** sidelobe · **-6 dB/oct** rolloff ### `cosine(i, N)` @@ -146,7 +150,7 @@ $w(n) = \sin\!\left(\frac{\pi n}{N-1}\right)$ -Half-period sine. MDCT audio codecs: MP3, AAC, Vorbis.[^princen1987] +Half-period sine. MDCT audio codecs: MP3, AAC, Vorbis.[17](#ref-17) **-23 dB** sidelobe · **-12 dB/oct** rolloff ### `blackman(i, N)` @@ -155,7 +159,7 @@ $w(n) = 0.42 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.08\cos\!\left(\frac{ -3-term cosine sum. Better leakage than Hann at the cost of wider main lobe.[^blackman1958] +3-term cosine sum. Better leakage than Hann at the cost of wider main lobe.[5](#ref-5) **-58 dB** sidelobe · **-18 dB/oct** rolloff ### `exactBlackman(i, N)` @@ -164,7 +168,7 @@ $w(n) = 0.42659 - 0.49656\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.076849\cos\!\ -Blackman with exact zero placement at 3rd and 4th sidelobes.[^harris1978] +Blackman with exact zero placement at 3rd and 4th sidelobes.[12](#ref-12) **-69 dB** sidelobe · **-6 dB/oct** rolloff ### `nuttall(i, N)` @@ -173,7 +177,7 @@ $w(n) = 0.355768 - 0.487396\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.144232\ -4-term cosine sum, continuous 1st derivative. High-dynamic-range analysis without edge discontinuity.[^nuttall1981] +4-term cosine sum, continuous 1st derivative. High-dynamic-range analysis without edge discontinuity.[15](#ref-15) **-93 dB** sidelobe · **-18 dB/oct** rolloff ### `blackmanNuttall(i, N)` @@ -182,7 +186,7 @@ $w(n) = 0.3635819 - 0.4891775\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.13659 -4-term cosine sum, lowest sidelobes among 4-term windows.[^nuttall1981] +4-term cosine sum, lowest sidelobes among 4-term windows.[15](#ref-15) **-98 dB** sidelobe · **-6 dB/oct** rolloff ### `blackmanHarris(i, N)` @@ -191,7 +195,7 @@ $w(n) = 0.35875 - 0.48829\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.14128\cos -4-term minimum sidelobe. ADC testing, measurement instrumentation, >80 dB dynamic range.[^harris1978] +4-term minimum sidelobe. ADC testing, measurement instrumentation, >80 dB dynamic range.[12](#ref-12) **-92 dB** sidelobe · **-6 dB/oct** rolloff ### `flatTop(i, N)` @@ -200,7 +204,7 @@ $w(n) = 1 - 1.93\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 1.29\cos\!\left(\fra -5-term cosine sum, near-zero scalloping. Peak ~4.64 (by design). Amplitude calibration, transducer calibration (~0.01 dB accuracy). ISO 18431.[^heinzel2002] +5-term cosine sum, near-zero scalloping. Peak ~4.64 (by design). Amplitude calibration, transducer calibration (~0.01 dB accuracy). ISO 18431.[19](#ref-19) **-93 dB** sidelobe · **-6 dB/oct** rolloff ### `bartlettHann(i, N)` @@ -209,7 +213,7 @@ $w(n) = 0.62 - 0.48\left|\frac{n}{N\!-\!1} - 0.5\right| - 0.38\cos\!\left(\frac{ -Bartlett-Hann hybrid. Balanced near/far sidelobe levels.[^ha1989] +Bartlett-Hann hybrid. Balanced near/far sidelobe levels.[18](#ref-18) **-36 dB** sidelobe ### `lanczos(i, N)` @@ -218,7 +222,7 @@ $w(n) = \text{sinc}\!\left(\frac{2n}{N-1} - 1\right)$ -Sinc main lobe. Image resampling, interpolation (FFmpeg, ImageMagick).[^duchon1979] +Sinc main lobe. Image resampling, interpolation (FFmpeg, ImageMagick).[14](#ref-14) **-26 dB** sidelobe ### `parzen(i, N)` @@ -227,7 +231,7 @@ $w(n) = 1 - 6a^2(1-a)$ for $|a| \le 0.5$,   $w(n) = 2(1-a)^3$ for $|a| > 0. -4th-order B-spline. Always-positive spectrum. Kernel density estimation.[^parzen1961] +4th-order B-spline. Always-positive spectrum. Kernel density estimation.[7](#ref-7) **-53 dB** sidelobe · **-24 dB/oct** rolloff ### `bohman(i, N)` @@ -251,7 +255,7 @@ $w(n) = \frac{I_0\!\left(\beta\sqrt{1 - \left(\frac{2n-N+1}{N-1}\right)^2}\right -Near-optimal DPSS approximation via Bessel I₀. The standard parameterized window for FIR filter design.[^kaiser1974] +Near-optimal DPSS approximation via Bessel I₀. The standard parameterized window for FIR filter design.[10](#ref-10) ### `gaussian(i, N, sigma)` @@ -261,7 +265,7 @@ $w(n) = \exp\!\left[-\frac{1}{2}\left(\frac{2n-N+1}{\sigma(N-1)}\right)^2\right] -Gaussian bell, minimum time-bandwidth product. STFT/Gabor transform, frequency estimation via parabolic interpolation.[^gabor1946] +Gaussian bell, minimum time-bandwidth product. STFT/Gabor transform, frequency estimation via parabolic interpolation.[2](#ref-2) ### `generalizedNormal(i, N, sigma, p)` @@ -289,7 +293,7 @@ Flat center with cosine-tapered edges. Preserves signal amplitude while tapering -C∞-smooth bump function (infinitely differentiable). Gravitational wave analysis (LIGO/Virgo).[^mckechan2010] +C∞-smooth bump function (infinitely differentiable). Gravitational wave analysis (LIGO/Virgo).[20](#ref-20) ### `powerOfSine(i, N, alpha)` @@ -309,7 +313,7 @@ $w(n) = \exp\!\left(\frac{-|2n-N+1|}{\tau(N-1)}\right)$ -Exponential decay from center. Modal analysis, impact testing.[^harris1978] +Exponential decay from center. Modal analysis, impact testing.[12](#ref-12) ### `hannPoisson(i, N, alpha)` @@ -329,7 +333,7 @@ $w(n) = \frac{1}{1+\left(\frac{\alpha(2n-N+1)}{N-1}\right)^2}$ -Lorentzian shape. Matches spectral line shapes in spectroscopy.[^harris1978] +Lorentzian shape. Matches spectral line shapes in spectroscopy.[12](#ref-12) ### `rifeVincent(i, N, order)` @@ -339,7 +343,7 @@ $w(n) = \frac{1}{Z}\sum_{k=0}^{K}(-1)^k a_k\cos\frac{2\pi kn}{N-1}$ -Class I cosine-sum optimized for sidelobe fall-off. Power grid harmonic analysis, interpolated DFT.[^rife1970] +Class I cosine-sum optimized for sidelobe fall-off. Power grid harmonic analysis, interpolated DFT.[9](#ref-9) ### `confinedGaussian(i, N, sigmaT)` @@ -347,7 +351,7 @@ Class I cosine-sum optimized for sidelobe fall-off. Power grid harmonic analysis -Optimal RMS time-frequency bandwidth. Time-frequency analysis, audio coding.[^starosielec2014] +Optimal RMS time-frequency bandwidth. Time-frequency analysis, audio coding.[21](#ref-21) @@ -363,7 +367,7 @@ $W(k) = (-1)^k T_{N-1}\!\left(\beta\cos\frac{\pi k}{N}\right)$,   $w = \tex -Optimal: narrowest main lobe for given equiripple sidelobe level. Antenna design, radar.[^dolph1946] +Optimal: narrowest main lobe for given equiripple sidelobe level. Antenna design, radar.[1](#ref-1) ### `taylor(i, N, nbar, sll)` @@ -373,7 +377,7 @@ $w(n) = 1 + 2\sum_{m=1}^{\bar{n}-1} F_m \cos\frac{2\pi m(n-(N\!-\!1)/2)}{N}$ -Monotonically decreasing sidelobes. The radar community standard for SAR image formation.[^taylor1955] +Monotonically decreasing sidelobes. The radar community standard for SAR image formation.[4](#ref-4) ### `kaiserBesselDerived(i, N, beta)` @@ -383,7 +387,7 @@ $w(n) = \sqrt{\frac{\sum_{j=0}^{n} K(j)}{\sum_{j=0}^{N/2} K(j)}}$,   $K(j) -Princen-Bradley condition for perfect MDCT reconstruction. AAC, Vorbis, Opus audio codecs.[^princen1987] +Princen-Bradley condition for perfect MDCT reconstruction. AAC, Vorbis, Opus audio codecs.[17](#ref-17) ### `dpss(i, N, W)` @@ -393,7 +397,7 @@ $\mathbf{T}\mathbf{v} = \lambda\mathbf{v}$,   $T_{jk} = \frac{\sin 2\pi W(j -Dominant eigenvector of sinc Toeplitz matrix — provably optimal energy concentration. Also called Slepian window. Multitaper spectral estimation, neuroscience, climate science.[^slepian1978] +Dominant eigenvector of sinc Toeplitz matrix — provably optimal energy concentration. Also called Slepian window. Multitaper spectral estimation, neuroscience, climate science.[13](#ref-13) ### `ultraspherical(i, N, mu, xmu)` @@ -403,7 +407,7 @@ $W(k) = C_n^\mu\!\left(x_\mu\cos\frac{\pi k}{N}\right)$,   $w = \text{IDFT} -Gegenbauer polynomial window. Independent control of sidelobe level and taper rate. Antenna design, beamforming.[^streit1984] +Gegenbauer polynomial window. Independent control of sidelobe level and taper rate. Antenna design, beamforming.[16](#ref-16) @@ -468,40 +472,71 @@ cola(hann, 1024, 512) // 0 — perfect STFT reconstruction - **`scallopLoss(fn, N, ...params)`** — worst-case amplitude error in dB between DFT bins. Rectangular = 3.92, Hann = 1.42, flat-top ≈ 0. - **`cola(fn, N, hop, ...params)`** — COLA deviation. 0 = perfect STFT reconstruction at given hop size. -## Migrating from v2 -CJS → ESM, 18 → 34 windows. Subpath imports preserved. +[3](#ref-3): M.S. Bartlett, "Periodogram Analysis and Continuous Spectra," *Biometrika* 37, 1950. +[8](#ref-8): P.D. Welch, "The Use of Fast Fourier Transform for the Estimation of Power Spectra," *IEEE Trans. Audio Electroacoustics* AU-15, 1967. +[6](#ref-6): J. Connes, "Recherches sur la spectroscopie par transformation de Fourier," *Revue d'Optique* 40, 1961. +[5](#ref-5): R.B. Blackman & J.W. Tukey, *The Measurement of Power Spectra*, Dover, 1958. +[11](#ref-11): R.W. Hamming, *Digital Filters*, Prentice-Hall, 1977. +[17](#ref-17): J.P. Princen, A.W. Johnson & A.B. Bradley, "Subband/Transform Coding Using Filter Bank Designs Based on Time Domain Aliasing Cancellation," *ICASSP*, 1987. +[12](#ref-12): F.J. Harris, "On the Use of Windows for Harmonic Analysis with the Discrete Fourier Transform," *Proc. IEEE* 66, 1978. +[15](#ref-15): A.H. Nuttall, "Some Windows with Very Good Sidelobe Behavior," *IEEE Trans. ASSP* 29, 1981. +[19](#ref-19): G. Heinzel, A. Rudiger & R. Schilling, "Spectrum and Spectral Density Estimation by the DFT," Max Planck Institute, 2002. +[18](#ref-18): Y.H. Ha & J.A. Pearce, "A New Window and Comparison to Standard Windows," *IEEE Trans. ASSP*, 1989. +[14](#ref-14): C.E. Duchon, "Lanczos Filtering in One and Two Dimensions," *J. Applied Meteorology* 18, 1979. +[7](#ref-7): E. Parzen, "Mathematical Considerations in the Estimation of Spectra," *Technometrics* 3, 1961. +[10](#ref-10): J.F. Kaiser, "Nonrecursive Digital Filter Design Using the Sinh Window Function," *IEEE Int. Symp. Circuits and Systems*, 1974. +[2](#ref-2): D. Gabor, "Theory of Communication," *J. IEE* 93, 1946. +[20](#ref-20): D.J.A. McKechan, C. Robinson & B.S. Sathyaprakash, "A Tapering Window for Time-Domain Templates and Simulated Signals in the Detection of Gravitational Waves," *Class. Quantum Grav.* 27, 2010. +[21](#ref-21): S. Starosielec & D. Hagemeier, "Discrete-Time Windows with Minimal RMS Bandwidth for Given RMS Temporal Width," *Signal Processing* 102, 2014. +[9](#ref-9): D.C. Rife & G.A. Vincent, "Use of the Discrete Fourier Transform in the Measurement of Frequencies and Levels of Tones," *Bell System Technical Journal* 49, 1970. +[1](#ref-1): C.L. Dolph, "A Current Distribution for Broadside Arrays Which Optimizes the Relationship Between Beam Width and Side-Lobe Level," *Proc. IRE* 34, 1946. +[4](#ref-4): T.T. Taylor, "Design of Line-Source Antennas for Narrow Beamwidth and Low Side Lobes," *IRE Trans. Antennas Propag.* AP-4, 1955. +[13](#ref-13): D. Slepian, "Prolate Spheroidal Wave Functions, Fourier Analysis, and Uncertainty — V," *Bell System Technical Journal* 57, 1978. +[16](#ref-16): R.L. Streit, "A Two-Parameter Family of Weights for Nonrecursive Digital Filters and Antennas," *IEEE Trans. ASSP* 32, 1984. -```diff -- const hann = require('window-function/hann') -- const apply = require('window-function/apply') -+ import { hann } from 'window-function/hann' -+ import { apply } from 'window-function/apply' -``` +## References + +[1] C.L. Dolph, "A Current Distribution for Broadside Arrays," *Proc. IRE* 34, 1946. + +[2] D. Gabor, "Theory of Communication," *J. IEE* 93, 1946. + +[3] M.S. Bartlett, "Periodogram Analysis and Continuous Spectra," *Biometrika* 37, 1950. + +[4] T.T. Taylor, "Design of Line-Source Antennas," *IRE Trans. Antennas Propag.* AP-4, 1955. + +[5] R.B. Blackman & J.W. Tukey, *The Measurement of Power Spectra*, Dover, 1958. + +[6] J. Connes, "Recherches sur la spectroscopie par transformation de Fourier," *Revue d'Optique* 40, 1961. + +[7] E. Parzen, "Mathematical Considerations in the Estimation of Spectra," *Technometrics* 3, 1961. + +[8] P.D. Welch, "The Use of FFT for Estimation of Power Spectra," *IEEE Trans. Audio Electroacoustics* AU-15, 1967. + +[9] D.C. Rife & G.A. Vincent, "Use of the DFT in Measurement of Frequencies and Levels of Tones," *Bell Syst. Tech. J.* 49, 1970. + +[10] J.F. Kaiser, "Nonrecursive Digital Filter Design Using the Sinh Window Function," *IEEE Int. Symp. Circuits and Systems*, 1974. + +[11] R.W. Hamming, *Digital Filters*, Prentice-Hall, 1977. + +[12] F.J. Harris, "On the Use of Windows for Harmonic Analysis with the DFT," *Proc. IEEE* 66, 1978. + +[13] D. Slepian, "Prolate Spheroidal Wave Functions — V," *Bell Syst. Tech. J.* 57, 1978. + +[14] C.E. Duchon, "Lanczos Filtering in One and Two Dimensions," *J. Applied Meteorology* 18, 1979. + +[15] A.H. Nuttall, "Some Windows with Very Good Sidelobe Behavior," *IEEE Trans. ASSP* 29, 1981. + +[16] R.L. Streit, "A Two-Parameter Family of Weights for Nonrecursive Digital Filters and Antennas," *IEEE Trans. ASSP* 32, 1984. + +[17] J.P. Princen, A.W. Johnson & A.B. Bradley, "Subband/Transform Coding Using Filter Bank Designs Based on Time Domain Aliasing Cancellation," *ICASSP*, 1987. + +[18] Y.H. Ha & J.A. Pearce, "A New Window and Comparison to Standard Windows," *IEEE Trans. ASSP*, 1989. + +[19] G. Heinzel, A. Rudiger & R. Schilling, "Spectrum and Spectral Density Estimation by the DFT," Max Planck Institute, 2002. + +[20] D.J.A. McKechan et al., "A Tapering Window for Time-Domain Templates," *Class. Quantum Grav.* 27, 2010. -The per-sample API `fn(i, N, ...params) → number` is unchanged. - - -[^bartlett1950]: M.S. Bartlett, "Periodogram Analysis and Continuous Spectra," *Biometrika* 37, 1950. -[^welch1967]: P.D. Welch, "The Use of Fast Fourier Transform for the Estimation of Power Spectra," *IEEE Trans. Audio Electroacoustics* AU-15, 1967. -[^connes1961]: J. Connes, "Recherches sur la spectroscopie par transformation de Fourier," *Revue d'Optique* 40, 1961. -[^blackman1958]: R.B. Blackman & J.W. Tukey, *The Measurement of Power Spectra*, Dover, 1958. -[^hamming1977]: R.W. Hamming, *Digital Filters*, Prentice-Hall, 1977. -[^princen1987]: J.P. Princen, A.W. Johnson & A.B. Bradley, "Subband/Transform Coding Using Filter Bank Designs Based on Time Domain Aliasing Cancellation," *ICASSP*, 1987. -[^harris1978]: F.J. Harris, "On the Use of Windows for Harmonic Analysis with the Discrete Fourier Transform," *Proc. IEEE* 66, 1978. -[^nuttall1981]: A.H. Nuttall, "Some Windows with Very Good Sidelobe Behavior," *IEEE Trans. ASSP* 29, 1981. -[^heinzel2002]: G. Heinzel, A. Rudiger & R. Schilling, "Spectrum and Spectral Density Estimation by the DFT," Max Planck Institute, 2002. -[^ha1989]: Y.H. Ha & J.A. Pearce, "A New Window and Comparison to Standard Windows," *IEEE Trans. ASSP*, 1989. -[^duchon1979]: C.E. Duchon, "Lanczos Filtering in One and Two Dimensions," *J. Applied Meteorology* 18, 1979. -[^parzen1961]: E. Parzen, "Mathematical Considerations in the Estimation of Spectra," *Technometrics* 3, 1961. -[^kaiser1974]: J.F. Kaiser, "Nonrecursive Digital Filter Design Using the Sinh Window Function," *IEEE Int. Symp. Circuits and Systems*, 1974. -[^gabor1946]: D. Gabor, "Theory of Communication," *J. IEE* 93, 1946. -[^mckechan2010]: D.J.A. McKechan, C. Robinson & B.S. Sathyaprakash, "A Tapering Window for Time-Domain Templates and Simulated Signals in the Detection of Gravitational Waves," *Class. Quantum Grav.* 27, 2010. -[^starosielec2014]: S. Starosielec & D. Hagemeier, "Discrete-Time Windows with Minimal RMS Bandwidth for Given RMS Temporal Width," *Signal Processing* 102, 2014. -[^rife1970]: D.C. Rife & G.A. Vincent, "Use of the Discrete Fourier Transform in the Measurement of Frequencies and Levels of Tones," *Bell System Technical Journal* 49, 1970. -[^dolph1946]: C.L. Dolph, "A Current Distribution for Broadside Arrays Which Optimizes the Relationship Between Beam Width and Side-Lobe Level," *Proc. IRE* 34, 1946. -[^taylor1955]: T.T. Taylor, "Design of Line-Source Antennas for Narrow Beamwidth and Low Side Lobes," *IRE Trans. Antennas Propag.* AP-4, 1955. -[^slepian1978]: D. Slepian, "Prolate Spheroidal Wave Functions, Fourier Analysis, and Uncertainty — V," *Bell System Technical Journal* 57, 1978. -[^streit1984]: R.L. Streit, "A Two-Parameter Family of Weights for Nonrecursive Digital Filters and Antennas," *IEEE Trans. ASSP* 32, 1984. +[21] S. Starosielec & D. Hagemeier, "Discrete-Time Windows with Minimal RMS Bandwidth," *Signal Processing* 102, 2014.

MIT ·

diff --git a/apply.js b/apply.js index d9b772a..95749c6 100644 --- a/apply.js +++ b/apply.js @@ -1,4 +1,4 @@ -export function apply (signal, fn, ...params) { +export default function apply (signal, fn, ...params) { for (let i = 0, N = signal.length; i < N; i++) signal[i] *= fn(i, N, ...params) return signal } diff --git a/bartlett.js b/bartlett.js index b9fb42a..dd64505 100644 --- a/bartlett.js +++ b/bartlett.js @@ -1,2 +1,2 @@ let { abs } = Math -export function bartlett (i, N) { return 1 - abs((2 * i - N + 1) / (N - 1)) } +export default function bartlett (i, N) { return 1 - abs((2 * i - N + 1) / (N - 1)) } diff --git a/bartlettHann.js b/bartlettHann.js index 0c58e5c..404dd21 100644 --- a/bartlettHann.js +++ b/bartlettHann.js @@ -1,5 +1,5 @@ import { abs, cos, PI2 } from './_util.js' -export function bartlettHann (i, N) { +export default function bartlettHann (i, N) { let x = i / (N - 1) return 0.62 - 0.48 * abs(x - 0.5) - 0.38 * cos(PI2 * x) } diff --git a/blackman.js b/blackman.js index b5a89f0..7a6f231 100644 --- a/blackman.js +++ b/blackman.js @@ -1,2 +1,2 @@ import { cosineSum } from './_util.js' -export function blackman (i, N) { return cosineSum(i, N, [0.42, 0.5, 0.08]) } +export default function blackman (i, N) { return cosineSum(i, N, [0.42, 0.5, 0.08]) } diff --git a/blackmanHarris.js b/blackmanHarris.js index 10f978a..7f532c1 100644 --- a/blackmanHarris.js +++ b/blackmanHarris.js @@ -1,2 +1,2 @@ import { cosineSum } from './_util.js' -export function blackmanHarris (i, N) { return cosineSum(i, N, [0.35875, 0.48829, 0.14128, 0.01168]) } +export default function blackmanHarris (i, N) { return cosineSum(i, N, [0.35875, 0.48829, 0.14128, 0.01168]) } diff --git a/blackmanNuttall.js b/blackmanNuttall.js index 140912b..e62861c 100644 --- a/blackmanNuttall.js +++ b/blackmanNuttall.js @@ -1,2 +1,2 @@ import { cosineSum } from './_util.js' -export function blackmanNuttall (i, N) { return cosineSum(i, N, [0.3635819, 0.4891775, 0.1365995, 0.0106411]) } +export default function blackmanNuttall (i, N) { return cosineSum(i, N, [0.3635819, 0.4891775, 0.1365995, 0.0106411]) } diff --git a/bohman.js b/bohman.js index c939c59..824df53 100644 --- a/bohman.js +++ b/bohman.js @@ -1,5 +1,5 @@ import { abs, cos, sin, PI } from './_util.js' -export function bohman (i, N) { +export default function bohman (i, N) { let a = abs((2 * i - N + 1) / (N - 1)) if (a >= 1) return 0 return (1 - a) * cos(PI * a) + sin(PI * a) / PI diff --git a/cauchy.js b/cauchy.js index de8adde..857f86f 100644 --- a/cauchy.js +++ b/cauchy.js @@ -1,4 +1,4 @@ -export function cauchy (i, N, alpha) { +export default function cauchy (i, N, alpha) { if (alpha == null) alpha = 3 let x = alpha * (2 * i - N + 1) / (N - 1) return 1 / (1 + x * x) diff --git a/cola.js b/cola.js index e20dd22..06e8ba0 100644 --- a/cola.js +++ b/cola.js @@ -1,6 +1,6 @@ import { abs } from './_util.js' -import { generate } from './generate.js' -export function cola (fn, N, hop, ...params) { +import generate from './generate.js' +export default function cola (fn, N, hop, ...params) { let win = generate(fn, N, ...params) let sums = new Float64Array(hop) for (let t = 0; t < hop; t++) for (let k = t; k < N; k += hop) sums[t] += win[k] diff --git a/confinedGaussian.js b/confinedGaussian.js index fa4c421..6b95798 100644 --- a/confinedGaussian.js +++ b/confinedGaussian.js @@ -1,5 +1,5 @@ import { exp } from './_util.js' -export function confinedGaussian (i, N, sigmaT) { +export default function confinedGaussian (i, N, sigmaT) { if (sigmaT == null) sigmaT = 0.1 let L = N + 1, half = (N - 1) / 2 function G (x) { let t = (x - half) / (2 * L * sigmaT); return exp(-t * t) } diff --git a/connes.js b/connes.js index e4f3e80..f45ef0f 100644 --- a/connes.js +++ b/connes.js @@ -1 +1 @@ -export function connes (i, N) { let x = (2 * i - N + 1) / (N - 1); return (1 - x * x) * (1 - x * x) } +export default function connes (i, N) { let x = (2 * i - N + 1) / (N - 1); return (1 - x * x) * (1 - x * x) } diff --git a/cosine.js b/cosine.js index 56533cf..4433055 100644 --- a/cosine.js +++ b/cosine.js @@ -1,2 +1,2 @@ import { sin, PI } from './_util.js' -export function cosine (i, N) { return sin(PI * i / (N - 1)) } +export default function cosine (i, N) { return sin(PI * i / (N - 1)) } diff --git a/dolphChebyshev.js b/dolphChebyshev.js index a46bf89..14eca10 100644 --- a/dolphChebyshev.js +++ b/dolphChebyshev.js @@ -1,5 +1,5 @@ import { cos, abs, cosh, acosh, acos, pow, PI, PI2, normalize } from './_util.js' -export function dolphChebyshev (i, N, attenuation) { +export default function dolphChebyshev (i, N, attenuation) { if (attenuation == null) attenuation = 100 let c = dolphChebyshev if (c._N !== N || c._a !== attenuation) { diff --git a/dpss.js b/dpss.js index fb05ec9..b045b66 100644 --- a/dpss.js +++ b/dpss.js @@ -1,5 +1,5 @@ import { sin, exp, sqrt, abs, PI, PI2, normalize } from './_util.js' -export function dpss (i, N, W) { +export default function dpss (i, N, W) { if (W == null) W = 0.1 let c = dpss if (c._N !== N || c._W !== W) { diff --git a/enbw.js b/enbw.js index e71755e..cc7858e 100644 --- a/enbw.js +++ b/enbw.js @@ -1,4 +1,4 @@ -export function enbw (fn, N, ...params) { +export default function enbw (fn, N, ...params) { let s = 0, s2 = 0 for (let i = 0; i < N; i++) { let v = fn(i, N, ...params); s += v; s2 += v * v } return N * s2 / (s * s) diff --git a/exactBlackman.js b/exactBlackman.js index 4aa3de2..f9406a1 100644 --- a/exactBlackman.js +++ b/exactBlackman.js @@ -1,2 +1,2 @@ import { cosineSum } from './_util.js' -export function exactBlackman (i, N) { return cosineSum(i, N, [0.42659, 0.49656, 0.076849]) } +export default function exactBlackman (i, N) { return cosineSum(i, N, [0.42659, 0.49656, 0.076849]) } diff --git a/exponential.js b/exponential.js index 5e2ed94..38f9b26 100644 --- a/exponential.js +++ b/exponential.js @@ -1,5 +1,5 @@ import { abs, exp } from './_util.js' -export function exponential (i, N, tau) { +export default function exponential (i, N, tau) { if (tau == null) tau = 1 return exp(-abs(2 * i - N + 1) / (tau * (N - 1))) } diff --git a/flatTop.js b/flatTop.js index 764718a..9d57948 100644 --- a/flatTop.js +++ b/flatTop.js @@ -1,2 +1,2 @@ import { cosineSum } from './_util.js' -export function flatTop (i, N) { return cosineSum(i, N, [1, 1.93, 1.29, 0.388, 0.028]) } +export default function flatTop (i, N) { return cosineSum(i, N, [1, 1.93, 1.29, 0.388, 0.028]) } diff --git a/gaussian.js b/gaussian.js index 45a52b8..7357329 100644 --- a/gaussian.js +++ b/gaussian.js @@ -1,5 +1,5 @@ import { exp } from './_util.js' -export function gaussian (i, N, sigma) { +export default function gaussian (i, N, sigma) { if (sigma == null) sigma = 0.4 let x = (2 * i - N + 1) / (sigma * (N - 1)) return exp(-0.5 * x * x) diff --git a/generalizedNormal.js b/generalizedNormal.js index 9e67335..581ef8d 100644 --- a/generalizedNormal.js +++ b/generalizedNormal.js @@ -1,5 +1,5 @@ import { abs, exp, pow } from './_util.js' -export function generalizedNormal (i, N, sigma, p) { +export default function generalizedNormal (i, N, sigma, p) { if (sigma == null) sigma = 0.4 if (p == null) p = 2 let x = abs((2 * i - N + 1) / (sigma * (N - 1))) diff --git a/generate.js b/generate.js index ddb2c87..0fa890b 100644 --- a/generate.js +++ b/generate.js @@ -1,4 +1,4 @@ -export function generate (fn, N, ...params) { +export default function generate (fn, N, ...params) { let w = new Float64Array(N) for (let i = 0; i < N; i++) w[i] = fn(i, N, ...params) return w diff --git a/hamming.js b/hamming.js index 9e73ad0..9b03482 100644 --- a/hamming.js +++ b/hamming.js @@ -1,2 +1,2 @@ import { cos, PI2 } from './_util.js' -export function hamming (i, N) { return 0.54 - 0.46 * cos(PI2 * i / (N - 1)) } +export default function hamming (i, N) { return 0.54 - 0.46 * cos(PI2 * i / (N - 1)) } diff --git a/hann.js b/hann.js index 6de29fb..dc0d862 100644 --- a/hann.js +++ b/hann.js @@ -1,2 +1,2 @@ import { cos, PI2 } from './_util.js' -export function hann (i, N) { return 0.5 - 0.5 * cos(PI2 * i / (N - 1)) } +export default function hann (i, N) { return 0.5 - 0.5 * cos(PI2 * i / (N - 1)) } diff --git a/hannPoisson.js b/hannPoisson.js index 33303cf..a7a90bc 100644 --- a/hannPoisson.js +++ b/hannPoisson.js @@ -1,5 +1,5 @@ import { cos, exp, abs, PI2 } from './_util.js' -export function hannPoisson (i, N, alpha) { +export default function hannPoisson (i, N, alpha) { if (alpha == null) alpha = 2 return 0.5 * (1 - cos(PI2 * i / (N - 1))) * exp(-alpha * abs(2 * i - N + 1) / (N - 1)) } diff --git a/index.js b/index.js index e3e6919..4255058 100644 --- a/index.js +++ b/index.js @@ -9,48 +9,48 @@ */ // Simple windows -export { rectangular } from './rectangular.js' -export { triangular } from './triangular.js' -export { bartlett } from './bartlett.js' -export { welch } from './welch.js' -export { connes } from './connes.js' -export { hann } from './hann.js' -export { hamming } from './hamming.js' -export { cosine } from './cosine.js' -export { blackman } from './blackman.js' -export { exactBlackman } from './exactBlackman.js' -export { nuttall } from './nuttall.js' -export { blackmanNuttall } from './blackmanNuttall.js' -export { blackmanHarris } from './blackmanHarris.js' -export { flatTop } from './flatTop.js' -export { bartlettHann } from './bartlettHann.js' -export { lanczos } from './lanczos.js' -export { parzen } from './parzen.js' -export { bohman } from './bohman.js' +export { default as rectangular } from './rectangular.js' +export { default as triangular } from './triangular.js' +export { default as bartlett } from './bartlett.js' +export { default as welch } from './welch.js' +export { default as connes } from './connes.js' +export { default as hann } from './hann.js' +export { default as hamming } from './hamming.js' +export { default as cosine } from './cosine.js' +export { default as blackman } from './blackman.js' +export { default as exactBlackman } from './exactBlackman.js' +export { default as nuttall } from './nuttall.js' +export { default as blackmanNuttall } from './blackmanNuttall.js' +export { default as blackmanHarris } from './blackmanHarris.js' +export { default as flatTop } from './flatTop.js' +export { default as bartlettHann } from './bartlettHann.js' +export { default as lanczos } from './lanczos.js' +export { default as parzen } from './parzen.js' +export { default as bohman } from './bohman.js' // Parameterized windows -export { powerOfSine } from './powerOfSine.js' -export { kaiser } from './kaiser.js' -export { gaussian } from './gaussian.js' -export { generalizedNormal } from './generalizedNormal.js' -export { tukey } from './tukey.js' -export { planckTaper } from './planckTaper.js' -export { exponential } from './exponential.js' -export { hannPoisson } from './hannPoisson.js' -export { cauchy } from './cauchy.js' -export { rifeVincent } from './rifeVincent.js' -export { confinedGaussian } from './confinedGaussian.js' +export { default as powerOfSine } from './powerOfSine.js' +export { default as kaiser } from './kaiser.js' +export { default as gaussian } from './gaussian.js' +export { default as generalizedNormal } from './generalizedNormal.js' +export { default as tukey } from './tukey.js' +export { default as planckTaper } from './planckTaper.js' +export { default as exponential } from './exponential.js' +export { default as hannPoisson } from './hannPoisson.js' +export { default as cauchy } from './cauchy.js' +export { default as rifeVincent } from './rifeVincent.js' +export { default as confinedGaussian } from './confinedGaussian.js' // Array-computed windows -export { kaiserBesselDerived } from './kaiserBesselDerived.js' -export { dolphChebyshev } from './dolphChebyshev.js' -export { taylor } from './taylor.js' -export { dpss } from './dpss.js' -export { ultraspherical } from './ultraspherical.js' +export { default as kaiserBesselDerived } from './kaiserBesselDerived.js' +export { default as dolphChebyshev } from './dolphChebyshev.js' +export { default as taylor } from './taylor.js' +export { default as dpss } from './dpss.js' +export { default as ultraspherical } from './ultraspherical.js' // Utilities -export { generate } from './generate.js' -export { apply } from './apply.js' -export { enbw } from './enbw.js' -export { scallopLoss } from './scallopLoss.js' -export { cola } from './cola.js' +export { default as generate } from './generate.js' +export { default as apply } from './apply.js' +export { default as enbw } from './enbw.js' +export { default as scallopLoss } from './scallopLoss.js' +export { default as cola } from './cola.js' diff --git a/kaiser.js b/kaiser.js index 46ba958..5c73b81 100644 --- a/kaiser.js +++ b/kaiser.js @@ -1,5 +1,5 @@ import { sqrt, i0 } from './_util.js' -export function kaiser (i, N, beta) { +export default function kaiser (i, N, beta) { if (beta == null) beta = 8.6 let x = (2 * i - N + 1) / (N - 1) return i0(beta * sqrt(1 - x * x)) / i0(beta) diff --git a/kaiserBesselDerived.js b/kaiserBesselDerived.js index 28f82eb..7f58696 100644 --- a/kaiserBesselDerived.js +++ b/kaiserBesselDerived.js @@ -1,5 +1,5 @@ import { sqrt, abs, i0 } from './_util.js' -export function kaiserBesselDerived (i, N, beta) { +export default function kaiserBesselDerived (i, N, beta) { if (beta == null) beta = 8.6 let c = kaiserBesselDerived if (c._N !== N || c._b !== beta) { diff --git a/lanczos.js b/lanczos.js index f2586ca..279b736 100644 --- a/lanczos.js +++ b/lanczos.js @@ -1,5 +1,5 @@ import { sin, PI } from './_util.js' -export function lanczos (i, N) { +export default function lanczos (i, N) { let x = 2 * i / (N - 1) - 1 return x === 0 ? 1 : sin(PI * x) / (PI * x) } diff --git a/nuttall.js b/nuttall.js index 39d0374..87d3a67 100644 --- a/nuttall.js +++ b/nuttall.js @@ -1,2 +1,2 @@ import { cosineSum } from './_util.js' -export function nuttall (i, N) { return cosineSum(i, N, [0.355768, 0.487396, 0.144232, 0.012604]) } +export default function nuttall (i, N) { return cosineSum(i, N, [0.355768, 0.487396, 0.144232, 0.012604]) } diff --git a/parzen.js b/parzen.js index 690fc98..182ebf4 100644 --- a/parzen.js +++ b/parzen.js @@ -1,5 +1,5 @@ let { abs } = Math -export function parzen (i, N) { +export default function parzen (i, N) { let a = abs((2 * i - N + 1) / (N - 1)) if (a <= 0.5) return 1 - 6 * a * a * (1 - a) let b = 1 - a diff --git a/planckTaper.js b/planckTaper.js index 0778a42..00a61a2 100644 --- a/planckTaper.js +++ b/planckTaper.js @@ -1,5 +1,5 @@ import { exp } from './_util.js' -export function planckTaper (i, N, epsilon) { +export default function planckTaper (i, N, epsilon) { if (epsilon == null) epsilon = 0.1 let eN = epsilon * (N - 1) if (i <= 0 || i >= N - 1) return 0 diff --git a/powerOfSine.js b/powerOfSine.js index 86b7861..270d2c5 100644 --- a/powerOfSine.js +++ b/powerOfSine.js @@ -1,5 +1,5 @@ import { sin, PI, pow } from './_util.js' -export function powerOfSine (i, N, alpha) { +export default function powerOfSine (i, N, alpha) { if (alpha == null) alpha = 2 return pow(sin(PI * i / (N - 1)), alpha) } diff --git a/rectangular.js b/rectangular.js index 98a82c4..4110b28 100644 --- a/rectangular.js +++ b/rectangular.js @@ -1 +1 @@ -export function rectangular () { return 1 } +export default function rectangular () { return 1 } diff --git a/rifeVincent.js b/rifeVincent.js index 1c06b8f..2f1f375 100644 --- a/rifeVincent.js +++ b/rifeVincent.js @@ -1,5 +1,5 @@ import { cos, PI2 } from './_util.js' -export function rifeVincent (i, N, order) { +export default function rifeVincent (i, N, order) { if (order == null) order = 1 let a if (order === 1) a = [1, 1] diff --git a/scallopLoss.js b/scallopLoss.js index 5e04d49..dfd298a 100644 --- a/scallopLoss.js +++ b/scallopLoss.js @@ -1,5 +1,5 @@ import { cos, sin, sqrt, abs, PI } from './_util.js' -export function scallopLoss (fn, N, ...params) { +export default function scallopLoss (fn, N, ...params) { let s = 0, re = 0, im = 0 for (let i = 0; i < N; i++) { let v = fn(i, N, ...params); s += v; re += v * cos(PI * i / N); im -= v * sin(PI * i / N) } return s === 0 ? Infinity : -20 * Math.log10(sqrt(re * re + im * im) / abs(s)) diff --git a/taylor.js b/taylor.js index 8e043a5..455f914 100644 --- a/taylor.js +++ b/taylor.js @@ -1,5 +1,5 @@ import { cos, acosh, pow, PI, PI2, normalize } from './_util.js' -export function taylor (i, N, nbar, sll) { +export default function taylor (i, N, nbar, sll) { if (nbar == null) nbar = 4 if (sll == null) sll = 30 let c = taylor diff --git a/triangular.js b/triangular.js index 2cd2115..f958032 100644 --- a/triangular.js +++ b/triangular.js @@ -1,2 +1,2 @@ let { abs } = Math -export function triangular (i, N) { return 1 - abs((2 * i - N + 1) / N) } +export default function triangular (i, N) { return 1 - abs((2 * i - N + 1) / N) } diff --git a/tukey.js b/tukey.js index 574034d..3c5e487 100644 --- a/tukey.js +++ b/tukey.js @@ -1,5 +1,5 @@ import { cos, PI } from './_util.js' -export function tukey (i, N, alpha) { +export default function tukey (i, N, alpha) { if (alpha == null) alpha = 0.5 let half = 0.5 * alpha * (N - 1) if (half < 1e-12) return 1 diff --git a/ultraspherical.js b/ultraspherical.js index f7c42e8..91fa354 100644 --- a/ultraspherical.js +++ b/ultraspherical.js @@ -1,5 +1,5 @@ import { cos, PI, PI2, gegen, normalize } from './_util.js' -export function ultraspherical (i, N, mu, xmu) { +export default function ultraspherical (i, N, mu, xmu) { if (mu == null) mu = 1 if (xmu == null) xmu = 1 let c = ultraspherical diff --git a/welch.js b/welch.js index cbecec4..9d7207d 100644 --- a/welch.js +++ b/welch.js @@ -1 +1 @@ -export function welch (i, N) { let x = (2 * i - N + 1) / (N - 1); return 1 - x * x } +export default function welch (i, N) { let x = (2 * i - N + 1) / (N - 1); return 1 - x * x } From 7b98bdbfb779c744fa2f4ae2758205dc99541528 Mon Sep 17 00:00:00 2001 From: "Dmitry Iv." Date: Thu, 26 Mar 2026 00:34:48 -0400 Subject: [PATCH 16/26] Revise README structure and section titles Updated the README to replace 'Contents' with 'Reference' and reorganized sections for clarity. --- README.md | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index ad3eebc..70032f9 100644 --- a/README.md +++ b/README.md @@ -26,10 +26,12 @@ generate(kaiser, 1024, 8.6) // Kaiser with β = 8.6 ``` -## Contents - -#### Simple +## Reference +
+
Simple
+
+ [rectangular](#rectangulari-n) · [triangular](#triangulari-n) · [bartlett](#bartletti-n) · @@ -49,8 +51,11 @@ generate(kaiser, 1024, 8.6) // Kaiser with β = 8.6 [parzen](#parzeni-n) · [bohman](#bohmani-n) -#### Parameterized +
+
Parameterized
+
+ [kaiser](#kaiseri-n-beta) · [gaussian](#gaussiani-n-sigma) · [generalizedNormal](#generalizednormali-n-sigma-p) · @@ -63,21 +68,19 @@ generate(kaiser, 1024, 8.6) // Kaiser with β = 8.6 [rifeVincent](#rifevincenti-n-order) · [confinedGaussian](#confinedgaussiani-n-sigmat) -#### Array-computed +
+
Array-computed_
+
+ [dolphChebyshev](#dolphchebyshevi-n-db) · [taylor](#taylori-n-nbar-sll) · [kaiserBesselDerived](#kaiserbesselderivedi-n-beta) · [dpss](#dpssi-n-w) · [ultraspherical](#ultrasphericali-n-mu-xmu) -#### Other - -[Choosing a window](#choosing-a-window) · -[Metrics](#metrics) · -[Migrating from v2](#migrating-from-v2) - - +
+
## Simple — no parameters From 3b9cb8f7b6283df08529a662c8b94463b45b37f9 Mon Sep 17 00:00:00 2001 From: "Dmitry Iv." Date: Thu, 26 Mar 2026 00:35:13 -0400 Subject: [PATCH 17/26] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 70032f9..9cfc1d1 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ generate(kaiser, 1024, 8.6) // Kaiser with β = 8.6
Simple
- + [rectangular](#rectangulari-n) · [triangular](#triangulari-n) · [bartlett](#bartletti-n) · @@ -55,7 +55,7 @@ generate(kaiser, 1024, 8.6) // Kaiser with β = 8.6
Parameterized
- + [kaiser](#kaiseri-n-beta) · [gaussian](#gaussiani-n-sigma) · [generalizedNormal](#generalizednormali-n-sigma-p) · @@ -70,9 +70,9 @@ generate(kaiser, 1024, 8.6) // Kaiser with β = 8.6
-
Array-computed_
+
Array-computed
- + [dolphChebyshev](#dolphchebyshevi-n-db) · [taylor](#taylori-n-nbar-sll) · [kaiserBesselDerived](#kaiserbesselderivedi-n-beta) · From 59455c7a9e688970897534541803d5f0437d2db9 Mon Sep 17 00:00:00 2001 From: "Dmitry Iv." Date: Thu, 26 Mar 2026 00:38:17 -0400 Subject: [PATCH 18/26] Use proper refs --- README.md | 139 +++++++++++++++++++----------------------------------- 1 file changed, 48 insertions(+), 91 deletions(-) diff --git a/README.md b/README.md index 9cfc1d1..a14b2fc 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ $w(n) = 1 - \left|\frac{2n - N + 1}{N - 1}\right|$ -Linear taper, zero endpoints. Bartlett's method PSD estimation.[3](#ref-3) +Linear taper, zero endpoints. Bartlett's method PSD estimation.[3] **-27 dB** sidelobe · **-12 dB/oct** rolloff ### `welch(i, N)` @@ -117,7 +117,7 @@ $w(n) = 1 - \left(\frac{2n - N + 1}{N - 1}\right)^2$ -Parabolic taper. Welch's method PSD estimation.[8](#ref-8) +Parabolic taper. Welch's method PSD estimation.[8] **-21 dB** sidelobe · **-12 dB/oct** rolloff ### `connes(i, N)` @@ -126,7 +126,7 @@ $w(n) = \left[1 - \left(\frac{2n - N + 1}{N - 1}\right)^2\right]^2$ -Welch squared (4th power parabolic). FTIR spectroscopy, interferogram apodization.[6](#ref-6) +Welch squared (4th power parabolic). FTIR spectroscopy, interferogram apodization.[6] **-24 dB/oct** rolloff ### `hann(i, N)` @@ -135,7 +135,7 @@ $w(n) = 0.5 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right)$ -Raised cosine, zero endpoints. The default general-purpose choice. STFT with 50% overlap (COLA). Also called "Hanning" (misnomer).[5](#ref-5) +Raised cosine, zero endpoints. The default general-purpose choice. STFT with 50% overlap (COLA). Also called "Hanning" (misnomer).[5] **-32 dB** sidelobe · **-18 dB/oct** rolloff ### `hamming(i, N)` @@ -144,7 +144,7 @@ $w(n) = 0.54 - 0.46\cos\!\left(\frac{2\pi n}{N-1}\right)$ -Raised cosine, nonzero endpoints. Optimized for first sidelobe cancellation. FIR filter design, speech processing.[11](#ref-11) +Raised cosine, nonzero endpoints. Optimized for first sidelobe cancellation. FIR filter design, speech processing.[11] **-43 dB** sidelobe · **-6 dB/oct** rolloff ### `cosine(i, N)` @@ -153,7 +153,7 @@ $w(n) = \sin\!\left(\frac{\pi n}{N-1}\right)$ -Half-period sine. MDCT audio codecs: MP3, AAC, Vorbis.[17](#ref-17) +Half-period sine. MDCT audio codecs: MP3, AAC, Vorbis.[17] **-23 dB** sidelobe · **-12 dB/oct** rolloff ### `blackman(i, N)` @@ -162,7 +162,7 @@ $w(n) = 0.42 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.08\cos\!\left(\frac{ -3-term cosine sum. Better leakage than Hann at the cost of wider main lobe.[5](#ref-5) +3-term cosine sum. Better leakage than Hann at the cost of wider main lobe.[5] **-58 dB** sidelobe · **-18 dB/oct** rolloff ### `exactBlackman(i, N)` @@ -171,7 +171,7 @@ $w(n) = 0.42659 - 0.49656\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.076849\cos\!\ -Blackman with exact zero placement at 3rd and 4th sidelobes.[12](#ref-12) +Blackman with exact zero placement at 3rd and 4th sidelobes.[12] **-69 dB** sidelobe · **-6 dB/oct** rolloff ### `nuttall(i, N)` @@ -180,7 +180,7 @@ $w(n) = 0.355768 - 0.487396\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.144232\ -4-term cosine sum, continuous 1st derivative. High-dynamic-range analysis without edge discontinuity.[15](#ref-15) +4-term cosine sum, continuous 1st derivative. High-dynamic-range analysis without edge discontinuity.[15] **-93 dB** sidelobe · **-18 dB/oct** rolloff ### `blackmanNuttall(i, N)` @@ -189,7 +189,7 @@ $w(n) = 0.3635819 - 0.4891775\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.13659 -4-term cosine sum, lowest sidelobes among 4-term windows.[15](#ref-15) +4-term cosine sum, lowest sidelobes among 4-term windows.[15] **-98 dB** sidelobe · **-6 dB/oct** rolloff ### `blackmanHarris(i, N)` @@ -198,7 +198,7 @@ $w(n) = 0.35875 - 0.48829\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.14128\cos -4-term minimum sidelobe. ADC testing, measurement instrumentation, >80 dB dynamic range.[12](#ref-12) +4-term minimum sidelobe. ADC testing, measurement instrumentation, >80 dB dynamic range.[12] **-92 dB** sidelobe · **-6 dB/oct** rolloff ### `flatTop(i, N)` @@ -207,7 +207,7 @@ $w(n) = 1 - 1.93\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 1.29\cos\!\left(\fra -5-term cosine sum, near-zero scalloping. Peak ~4.64 (by design). Amplitude calibration, transducer calibration (~0.01 dB accuracy). ISO 18431.[19](#ref-19) +5-term cosine sum, near-zero scalloping. Peak ~4.64 (by design). Amplitude calibration, transducer calibration (~0.01 dB accuracy). ISO 18431.[19] **-93 dB** sidelobe · **-6 dB/oct** rolloff ### `bartlettHann(i, N)` @@ -216,7 +216,7 @@ $w(n) = 0.62 - 0.48\left|\frac{n}{N\!-\!1} - 0.5\right| - 0.38\cos\!\left(\frac{ -Bartlett-Hann hybrid. Balanced near/far sidelobe levels.[18](#ref-18) +Bartlett-Hann hybrid. Balanced near/far sidelobe levels.[18] **-36 dB** sidelobe ### `lanczos(i, N)` @@ -225,7 +225,7 @@ $w(n) = \text{sinc}\!\left(\frac{2n}{N-1} - 1\right)$ -Sinc main lobe. Image resampling, interpolation (FFmpeg, ImageMagick).[14](#ref-14) +Sinc main lobe. Image resampling, interpolation (FFmpeg, ImageMagick).[14] **-26 dB** sidelobe ### `parzen(i, N)` @@ -234,7 +234,7 @@ $w(n) = 1 - 6a^2(1-a)$ for $|a| \le 0.5$,   $w(n) = 2(1-a)^3$ for $|a| > 0. -4th-order B-spline. Always-positive spectrum. Kernel density estimation.[7](#ref-7) +4th-order B-spline. Always-positive spectrum. Kernel density estimation.[7] **-53 dB** sidelobe · **-24 dB/oct** rolloff ### `bohman(i, N)` @@ -258,7 +258,7 @@ $w(n) = \frac{I_0\!\left(\beta\sqrt{1 - \left(\frac{2n-N+1}{N-1}\right)^2}\right -Near-optimal DPSS approximation via Bessel I₀. The standard parameterized window for FIR filter design.[10](#ref-10) +Near-optimal DPSS approximation via Bessel I₀. The standard parameterized window for FIR filter design.[10] ### `gaussian(i, N, sigma)` @@ -268,7 +268,7 @@ $w(n) = \exp\!\left[-\frac{1}{2}\left(\frac{2n-N+1}{\sigma(N-1)}\right)^2\right] -Gaussian bell, minimum time-bandwidth product. STFT/Gabor transform, frequency estimation via parabolic interpolation.[2](#ref-2) +Gaussian bell, minimum time-bandwidth product. STFT/Gabor transform, frequency estimation via parabolic interpolation.[2] ### `generalizedNormal(i, N, sigma, p)` @@ -296,7 +296,7 @@ Flat center with cosine-tapered edges. Preserves signal amplitude while tapering -C∞-smooth bump function (infinitely differentiable). Gravitational wave analysis (LIGO/Virgo).[20](#ref-20) +C∞-smooth bump function (infinitely differentiable). Gravitational wave analysis (LIGO/Virgo).[20] ### `powerOfSine(i, N, alpha)` @@ -316,7 +316,7 @@ $w(n) = \exp\!\left(\frac{-|2n-N+1|}{\tau(N-1)}\right)$ -Exponential decay from center. Modal analysis, impact testing.[12](#ref-12) +Exponential decay from center. Modal analysis, impact testing.[12] ### `hannPoisson(i, N, alpha)` @@ -336,7 +336,7 @@ $w(n) = \frac{1}{1+\left(\frac{\alpha(2n-N+1)}{N-1}\right)^2}$ -Lorentzian shape. Matches spectral line shapes in spectroscopy.[12](#ref-12) +Lorentzian shape. Matches spectral line shapes in spectroscopy.[12] ### `rifeVincent(i, N, order)` @@ -346,7 +346,7 @@ $w(n) = \frac{1}{Z}\sum_{k=0}^{K}(-1)^k a_k\cos\frac{2\pi kn}{N-1}$ -Class I cosine-sum optimized for sidelobe fall-off. Power grid harmonic analysis, interpolated DFT.[9](#ref-9) +Class I cosine-sum optimized for sidelobe fall-off. Power grid harmonic analysis, interpolated DFT.[9] ### `confinedGaussian(i, N, sigmaT)` @@ -354,7 +354,7 @@ Class I cosine-sum optimized for sidelobe fall-off. Power grid harmonic analysis -Optimal RMS time-frequency bandwidth. Time-frequency analysis, audio coding.[21](#ref-21) +Optimal RMS time-frequency bandwidth. Time-frequency analysis, audio coding.[21] @@ -370,7 +370,7 @@ $W(k) = (-1)^k T_{N-1}\!\left(\beta\cos\frac{\pi k}{N}\right)$,   $w = \tex -Optimal: narrowest main lobe for given equiripple sidelobe level. Antenna design, radar.[1](#ref-1) +Optimal: narrowest main lobe for given equiripple sidelobe level. Antenna design, radar.[1] ### `taylor(i, N, nbar, sll)` @@ -380,7 +380,7 @@ $w(n) = 1 + 2\sum_{m=1}^{\bar{n}-1} F_m \cos\frac{2\pi m(n-(N\!-\!1)/2)}{N}$ -Monotonically decreasing sidelobes. The radar community standard for SAR image formation.[4](#ref-4) +Monotonically decreasing sidelobes. The radar community standard for SAR image formation.[4] ### `kaiserBesselDerived(i, N, beta)` @@ -390,7 +390,7 @@ $w(n) = \sqrt{\frac{\sum_{j=0}^{n} K(j)}{\sum_{j=0}^{N/2} K(j)}}$,   $K(j) -Princen-Bradley condition for perfect MDCT reconstruction. AAC, Vorbis, Opus audio codecs.[17](#ref-17) +Princen-Bradley condition for perfect MDCT reconstruction. AAC, Vorbis, Opus audio codecs.[17] ### `dpss(i, N, W)` @@ -400,7 +400,7 @@ $\mathbf{T}\mathbf{v} = \lambda\mathbf{v}$,   $T_{jk} = \frac{\sin 2\pi W(j -Dominant eigenvector of sinc Toeplitz matrix — provably optimal energy concentration. Also called Slepian window. Multitaper spectral estimation, neuroscience, climate science.[13](#ref-13) +Dominant eigenvector of sinc Toeplitz matrix — provably optimal energy concentration. Also called Slepian window. Multitaper spectral estimation, neuroscience, climate science.[13] ### `ultraspherical(i, N, mu, xmu)` @@ -410,7 +410,7 @@ $W(k) = C_n^\mu\!\left(x_\mu\cos\frac{\pi k}{N}\right)$,   $w = \text{IDFT} -Gegenbauer polynomial window. Independent control of sidelobe level and taper rate. Antenna design, beamforming.[16](#ref-16) +Gegenbauer polynomial window. Independent control of sidelobe level and taper rate. Antenna design, beamforming.[16] @@ -475,71 +475,28 @@ cola(hann, 1024, 512) // 0 — perfect STFT reconstruction - **`scallopLoss(fn, N, ...params)`** — worst-case amplitude error in dB between DFT bins. Rectangular = 3.92, Hann = 1.42, flat-top ≈ 0. - **`cola(fn, N, hop, ...params)`** — COLA deviation. 0 = perfect STFT reconstruction at given hop size. - -[3](#ref-3): M.S. Bartlett, "Periodogram Analysis and Continuous Spectra," *Biometrika* 37, 1950. -[8](#ref-8): P.D. Welch, "The Use of Fast Fourier Transform for the Estimation of Power Spectra," *IEEE Trans. Audio Electroacoustics* AU-15, 1967. -[6](#ref-6): J. Connes, "Recherches sur la spectroscopie par transformation de Fourier," *Revue d'Optique* 40, 1961. -[5](#ref-5): R.B. Blackman & J.W. Tukey, *The Measurement of Power Spectra*, Dover, 1958. -[11](#ref-11): R.W. Hamming, *Digital Filters*, Prentice-Hall, 1977. -[17](#ref-17): J.P. Princen, A.W. Johnson & A.B. Bradley, "Subband/Transform Coding Using Filter Bank Designs Based on Time Domain Aliasing Cancellation," *ICASSP*, 1987. -[12](#ref-12): F.J. Harris, "On the Use of Windows for Harmonic Analysis with the Discrete Fourier Transform," *Proc. IEEE* 66, 1978. -[15](#ref-15): A.H. Nuttall, "Some Windows with Very Good Sidelobe Behavior," *IEEE Trans. ASSP* 29, 1981. -[19](#ref-19): G. Heinzel, A. Rudiger & R. Schilling, "Spectrum and Spectral Density Estimation by the DFT," Max Planck Institute, 2002. -[18](#ref-18): Y.H. Ha & J.A. Pearce, "A New Window and Comparison to Standard Windows," *IEEE Trans. ASSP*, 1989. -[14](#ref-14): C.E. Duchon, "Lanczos Filtering in One and Two Dimensions," *J. Applied Meteorology* 18, 1979. -[7](#ref-7): E. Parzen, "Mathematical Considerations in the Estimation of Spectra," *Technometrics* 3, 1961. -[10](#ref-10): J.F. Kaiser, "Nonrecursive Digital Filter Design Using the Sinh Window Function," *IEEE Int. Symp. Circuits and Systems*, 1974. -[2](#ref-2): D. Gabor, "Theory of Communication," *J. IEE* 93, 1946. -[20](#ref-20): D.J.A. McKechan, C. Robinson & B.S. Sathyaprakash, "A Tapering Window for Time-Domain Templates and Simulated Signals in the Detection of Gravitational Waves," *Class. Quantum Grav.* 27, 2010. -[21](#ref-21): S. Starosielec & D. Hagemeier, "Discrete-Time Windows with Minimal RMS Bandwidth for Given RMS Temporal Width," *Signal Processing* 102, 2014. -[9](#ref-9): D.C. Rife & G.A. Vincent, "Use of the Discrete Fourier Transform in the Measurement of Frequencies and Levels of Tones," *Bell System Technical Journal* 49, 1970. -[1](#ref-1): C.L. Dolph, "A Current Distribution for Broadside Arrays Which Optimizes the Relationship Between Beam Width and Side-Lobe Level," *Proc. IRE* 34, 1946. -[4](#ref-4): T.T. Taylor, "Design of Line-Source Antennas for Narrow Beamwidth and Low Side Lobes," *IRE Trans. Antennas Propag.* AP-4, 1955. -[13](#ref-13): D. Slepian, "Prolate Spheroidal Wave Functions, Fourier Analysis, and Uncertainty — V," *Bell System Technical Journal* 57, 1978. -[16](#ref-16): R.L. Streit, "A Two-Parameter Family of Weights for Nonrecursive Digital Filters and Antennas," *IEEE Trans. ASSP* 32, 1984. - ## References -[1] C.L. Dolph, "A Current Distribution for Broadside Arrays," *Proc. IRE* 34, 1946. - -[2] D. Gabor, "Theory of Communication," *J. IEE* 93, 1946. - -[3] M.S. Bartlett, "Periodogram Analysis and Continuous Spectra," *Biometrika* 37, 1950. - -[4] T.T. Taylor, "Design of Line-Source Antennas," *IRE Trans. Antennas Propag.* AP-4, 1955. - -[5] R.B. Blackman & J.W. Tukey, *The Measurement of Power Spectra*, Dover, 1958. - -[6] J. Connes, "Recherches sur la spectroscopie par transformation de Fourier," *Revue d'Optique* 40, 1961. - -[7] E. Parzen, "Mathematical Considerations in the Estimation of Spectra," *Technometrics* 3, 1961. - -[8] P.D. Welch, "The Use of FFT for Estimation of Power Spectra," *IEEE Trans. Audio Electroacoustics* AU-15, 1967. - -[9] D.C. Rife & G.A. Vincent, "Use of the DFT in Measurement of Frequencies and Levels of Tones," *Bell Syst. Tech. J.* 49, 1970. - -[10] J.F. Kaiser, "Nonrecursive Digital Filter Design Using the Sinh Window Function," *IEEE Int. Symp. Circuits and Systems*, 1974. - -[11] R.W. Hamming, *Digital Filters*, Prentice-Hall, 1977. - -[12] F.J. Harris, "On the Use of Windows for Harmonic Analysis with the DFT," *Proc. IEEE* 66, 1978. - -[13] D. Slepian, "Prolate Spheroidal Wave Functions — V," *Bell Syst. Tech. J.* 57, 1978. - -[14] C.E. Duchon, "Lanczos Filtering in One and Two Dimensions," *J. Applied Meteorology* 18, 1979. - -[15] A.H. Nuttall, "Some Windows with Very Good Sidelobe Behavior," *IEEE Trans. ASSP* 29, 1981. - -[16] R.L. Streit, "A Two-Parameter Family of Weights for Nonrecursive Digital Filters and Antennas," *IEEE Trans. ASSP* 32, 1984. - -[17] J.P. Princen, A.W. Johnson & A.B. Bradley, "Subband/Transform Coding Using Filter Bank Designs Based on Time Domain Aliasing Cancellation," *ICASSP*, 1987. - -[18] Y.H. Ha & J.A. Pearce, "A New Window and Comparison to Standard Windows," *IEEE Trans. ASSP*, 1989. - -[19] G. Heinzel, A. Rudiger & R. Schilling, "Spectrum and Spectral Density Estimation by the DFT," Max Planck Institute, 2002. - -[20] D.J.A. McKechan et al., "A Tapering Window for Time-Domain Templates," *Class. Quantum Grav.* 27, 2010. - -[21] S. Starosielec & D. Hagemeier, "Discrete-Time Windows with Minimal RMS Bandwidth," *Signal Processing* 102, 2014. +1. C.L. Dolph, "A Current Distribution for Broadside Arrays," *Proc. IRE* 34, 1946. +2. D. Gabor, "Theory of Communication," *J. IEE* 93, 1946. +3. M.S. Bartlett, "Periodogram Analysis and Continuous Spectra," *Biometrika* 37, 1950. +4. T.T. Taylor, "Design of Line-Source Antennas," *IRE Trans. Antennas Propag.* AP-4, 1955. +5. R.B. Blackman & J.W. Tukey, *The Measurement of Power Spectra*, Dover, 1958. +6. J. Connes, "Recherches sur la spectroscopie par transformation de Fourier," *Revue d'Optique* 40, 1961. +7. E. Parzen, "Mathematical Considerations in the Estimation of Spectra," *Technometrics* 3, 1961. +8. P.D. Welch, "The Use of FFT for Estimation of Power Spectra," *IEEE Trans. Audio Electroacoustics* AU-15, 1967. +9. D.C. Rife & G.A. Vincent, "Use of the DFT in Measurement of Frequencies and Levels of Tones," *Bell Syst. Tech. J.* 49, 1970. +10. J.F. Kaiser, "Nonrecursive Digital Filter Design Using the Sinh Window Function," *IEEE Int. Symp. Circuits and Systems*, 1974. +11. R.W. Hamming, *Digital Filters*, Prentice-Hall, 1977. +12. F.J. Harris, "On the Use of Windows for Harmonic Analysis with the DFT," *Proc. IEEE* 66, 1978. +13. D. Slepian, "Prolate Spheroidal Wave Functions — V," *Bell Syst. Tech. J.* 57, 1978. +14. C.E. Duchon, "Lanczos Filtering in One and Two Dimensions," *J. Applied Meteorology* 18, 1979. +15. A.H. Nuttall, "Some Windows with Very Good Sidelobe Behavior," *IEEE Trans. ASSP* 29, 1981. +16. R.L. Streit, "A Two-Parameter Family of Weights for Nonrecursive Digital Filters and Antennas," *IEEE Trans. ASSP* 32, 1984. +17. J.P. Princen, A.W. Johnson & A.B. Bradley, "Subband/Transform Coding Using Filter Bank Designs Based on Time Domain Aliasing Cancellation," *ICASSP*, 1987. +18. Y.H. Ha & J.A. Pearce, "A New Window and Comparison to Standard Windows," *IEEE Trans. ASSP*, 1989. +19. G. Heinzel, A. Rudiger & R. Schilling, "Spectrum and Spectral Density Estimation by the DFT," Max Planck Institute, 2002. +20. D.J.A. McKechan et al., "A Tapering Window for Time-Domain Templates," *Class. Quantum Grav.* 27, 2010. +21. S. Starosielec & D. Hagemeier, "Discrete-Time Windows with Minimal RMS Bandwidth," *Signal Processing* 102, 2014.

MIT ·

From e54bd546d33781709a7c77a1f4f67246d41f85f9 Mon Sep 17 00:00:00 2001 From: "Dmitry Iv." Date: Thu, 26 Mar 2026 00:39:40 -0400 Subject: [PATCH 19/26] Consolidate util.js --- apply.js | 4 ---- cola.js | 14 -------------- enbw.js | 5 ----- generate.js | 5 ----- index.js | 6 +----- package.json | 6 +----- scallopLoss.js | 6 ------ util.js | 42 ++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 44 insertions(+), 44 deletions(-) delete mode 100644 apply.js delete mode 100644 cola.js delete mode 100644 enbw.js delete mode 100644 generate.js delete mode 100644 scallopLoss.js create mode 100644 util.js diff --git a/apply.js b/apply.js deleted file mode 100644 index 95749c6..0000000 --- a/apply.js +++ /dev/null @@ -1,4 +0,0 @@ -export default function apply (signal, fn, ...params) { - for (let i = 0, N = signal.length; i < N; i++) signal[i] *= fn(i, N, ...params) - return signal -} diff --git a/cola.js b/cola.js deleted file mode 100644 index 06e8ba0..0000000 --- a/cola.js +++ /dev/null @@ -1,14 +0,0 @@ -import { abs } from './_util.js' -import generate from './generate.js' -export default function cola (fn, N, hop, ...params) { - let win = generate(fn, N, ...params) - let sums = new Float64Array(hop) - for (let t = 0; t < hop; t++) for (let k = t; k < N; k += hop) sums[t] += win[k] - let mean = 0 - for (let t = 0; t < hop; t++) mean += sums[t] - mean /= hop - if (mean === 0) return Infinity - let maxDev = 0 - for (let t = 0; t < hop; t++) { let d = abs(sums[t] - mean) / mean; if (d > maxDev) maxDev = d } - return maxDev -} diff --git a/enbw.js b/enbw.js deleted file mode 100644 index cc7858e..0000000 --- a/enbw.js +++ /dev/null @@ -1,5 +0,0 @@ -export default function enbw (fn, N, ...params) { - let s = 0, s2 = 0 - for (let i = 0; i < N; i++) { let v = fn(i, N, ...params); s += v; s2 += v * v } - return N * s2 / (s * s) -} diff --git a/generate.js b/generate.js deleted file mode 100644 index 0fa890b..0000000 --- a/generate.js +++ /dev/null @@ -1,5 +0,0 @@ -export default function generate (fn, N, ...params) { - let w = new Float64Array(N) - for (let i = 0; i < N; i++) w[i] = fn(i, N, ...params) - return w -} diff --git a/index.js b/index.js index 4255058..42c8bc2 100644 --- a/index.js +++ b/index.js @@ -49,8 +49,4 @@ export { default as dpss } from './dpss.js' export { default as ultraspherical } from './ultraspherical.js' // Utilities -export { default as generate } from './generate.js' -export { default as apply } from './apply.js' -export { default as enbw } from './enbw.js' -export { default as scallopLoss } from './scallopLoss.js' -export { default as cola } from './cola.js' +export { generate, apply, enbw, scallopLoss, cola } from './util.js' diff --git a/package.json b/package.json index f5ba5bd..c435498 100644 --- a/package.json +++ b/package.json @@ -39,11 +39,7 @@ "./taylor": "./taylor.js", "./dpss": "./dpss.js", "./ultraspherical": "./ultraspherical.js", - "./generate": "./generate.js", - "./apply": "./apply.js", - "./enbw": "./enbw.js", - "./scallopLoss": "./scallopLoss.js", - "./cola": "./cola.js" + "./util": "./util.js" }, "types": "./index.d.ts", "typesVersions": { "*": { "*": ["./index.d.ts"] } }, diff --git a/scallopLoss.js b/scallopLoss.js deleted file mode 100644 index dfd298a..0000000 --- a/scallopLoss.js +++ /dev/null @@ -1,6 +0,0 @@ -import { cos, sin, sqrt, abs, PI } from './_util.js' -export default function scallopLoss (fn, N, ...params) { - let s = 0, re = 0, im = 0 - for (let i = 0; i < N; i++) { let v = fn(i, N, ...params); s += v; re += v * cos(PI * i / N); im -= v * sin(PI * i / N) } - return s === 0 ? Infinity : -20 * Math.log10(sqrt(re * re + im * im) / abs(s)) -} diff --git a/util.js b/util.js new file mode 100644 index 0000000..1756b7f --- /dev/null +++ b/util.js @@ -0,0 +1,42 @@ +import { cos, sin, sqrt, abs, PI } from './_util.js' + +/** Generate a full window as Float64Array. */ +export function generate (fn, N, ...params) { + let w = new Float64Array(N) + for (let i = 0; i < N; i++) w[i] = fn(i, N, ...params) + return w +} + +/** Apply window to a signal in-place. */ +export function apply (signal, fn, ...params) { + for (let i = 0, N = signal.length; i < N; i++) signal[i] *= fn(i, N, ...params) + return signal +} + +/** Equivalent noise bandwidth in frequency bins. Rectangular = 1.0, Hann ≈ 1.5. */ +export function enbw (fn, N, ...params) { + let s = 0, s2 = 0 + for (let i = 0; i < N; i++) { let v = fn(i, N, ...params); s += v; s2 += v * v } + return N * s2 / (s * s) +} + +/** Worst-case amplitude error in dB when a tone falls between DFT bins. */ +export function scallopLoss (fn, N, ...params) { + let s = 0, re = 0, im = 0 + for (let i = 0; i < N; i++) { let v = fn(i, N, ...params); s += v; re += v * cos(PI * i / N); im -= v * sin(PI * i / N) } + return s === 0 ? Infinity : -20 * Math.log10(sqrt(re * re + im * im) / abs(s)) +} + +/** COLA deviation. Returns max relative deviation from constant overlap-add sum; 0 = perfect. */ +export function cola (fn, N, hop, ...params) { + let win = generate(fn, N, ...params) + let sums = new Float64Array(hop) + for (let t = 0; t < hop; t++) for (let k = t; k < N; k += hop) sums[t] += win[k] + let mean = 0 + for (let t = 0; t < hop; t++) mean += sums[t] + mean /= hop + if (mean === 0) return Infinity + let maxDev = 0 + for (let t = 0; t < hop; t++) { let d = abs(sums[t] - mean) / mean; if (d > maxDev) maxDev = d } + return maxDev +} From 80f5820d0865aa879e7cfaf9e855ad6510e18484 Mon Sep 17 00:00:00 2001 From: "Dmitry Iv." Date: Thu, 26 Mar 2026 00:40:34 -0400 Subject: [PATCH 20/26] Update README.md --- README.md | 98 +++++++++++++++++++++++++++---------------------------- 1 file changed, 48 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index a14b2fc..1d93436 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ $w(n) = 1 - \left|\frac{2n - N + 1}{N - 1}\right|$ -Linear taper, zero endpoints. Bartlett's method PSD estimation.[3] +Linear taper, zero endpoints. Bartlett's method PSD estimation.[^bartlett1950] **-27 dB** sidelobe · **-12 dB/oct** rolloff ### `welch(i, N)` @@ -117,7 +117,7 @@ $w(n) = 1 - \left(\frac{2n - N + 1}{N - 1}\right)^2$ -Parabolic taper. Welch's method PSD estimation.[8] +Parabolic taper. Welch's method PSD estimation.[^welch1967] **-21 dB** sidelobe · **-12 dB/oct** rolloff ### `connes(i, N)` @@ -126,7 +126,7 @@ $w(n) = \left[1 - \left(\frac{2n - N + 1}{N - 1}\right)^2\right]^2$ -Welch squared (4th power parabolic). FTIR spectroscopy, interferogram apodization.[6] +Welch squared (4th power parabolic). FTIR spectroscopy, interferogram apodization.[^connes1961] **-24 dB/oct** rolloff ### `hann(i, N)` @@ -135,7 +135,7 @@ $w(n) = 0.5 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right)$ -Raised cosine, zero endpoints. The default general-purpose choice. STFT with 50% overlap (COLA). Also called "Hanning" (misnomer).[5] +Raised cosine, zero endpoints. The default general-purpose choice. STFT with 50% overlap (COLA). Also called "Hanning" (misnomer).[^blackman1958] **-32 dB** sidelobe · **-18 dB/oct** rolloff ### `hamming(i, N)` @@ -144,7 +144,7 @@ $w(n) = 0.54 - 0.46\cos\!\left(\frac{2\pi n}{N-1}\right)$ -Raised cosine, nonzero endpoints. Optimized for first sidelobe cancellation. FIR filter design, speech processing.[11] +Raised cosine, nonzero endpoints. Optimized for first sidelobe cancellation. FIR filter design, speech processing.[^hamming1977] **-43 dB** sidelobe · **-6 dB/oct** rolloff ### `cosine(i, N)` @@ -153,7 +153,7 @@ $w(n) = \sin\!\left(\frac{\pi n}{N-1}\right)$ -Half-period sine. MDCT audio codecs: MP3, AAC, Vorbis.[17] +Half-period sine. MDCT audio codecs: MP3, AAC, Vorbis.[^princen1987] **-23 dB** sidelobe · **-12 dB/oct** rolloff ### `blackman(i, N)` @@ -162,7 +162,7 @@ $w(n) = 0.42 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.08\cos\!\left(\frac{ -3-term cosine sum. Better leakage than Hann at the cost of wider main lobe.[5] +3-term cosine sum. Better leakage than Hann at the cost of wider main lobe.[^blackman1958] **-58 dB** sidelobe · **-18 dB/oct** rolloff ### `exactBlackman(i, N)` @@ -171,7 +171,7 @@ $w(n) = 0.42659 - 0.49656\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.076849\cos\!\ -Blackman with exact zero placement at 3rd and 4th sidelobes.[12] +Blackman with exact zero placement at 3rd and 4th sidelobes.[^harris1978] **-69 dB** sidelobe · **-6 dB/oct** rolloff ### `nuttall(i, N)` @@ -180,7 +180,7 @@ $w(n) = 0.355768 - 0.487396\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.144232\ -4-term cosine sum, continuous 1st derivative. High-dynamic-range analysis without edge discontinuity.[15] +4-term cosine sum, continuous 1st derivative. High-dynamic-range analysis without edge discontinuity.[^nuttall1981] **-93 dB** sidelobe · **-18 dB/oct** rolloff ### `blackmanNuttall(i, N)` @@ -189,7 +189,7 @@ $w(n) = 0.3635819 - 0.4891775\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.13659 -4-term cosine sum, lowest sidelobes among 4-term windows.[15] +4-term cosine sum, lowest sidelobes among 4-term windows.[^nuttall1981] **-98 dB** sidelobe · **-6 dB/oct** rolloff ### `blackmanHarris(i, N)` @@ -198,7 +198,7 @@ $w(n) = 0.35875 - 0.48829\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.14128\cos -4-term minimum sidelobe. ADC testing, measurement instrumentation, >80 dB dynamic range.[12] +4-term minimum sidelobe. ADC testing, measurement instrumentation, >80 dB dynamic range.[^harris1978] **-92 dB** sidelobe · **-6 dB/oct** rolloff ### `flatTop(i, N)` @@ -207,7 +207,7 @@ $w(n) = 1 - 1.93\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 1.29\cos\!\left(\fra -5-term cosine sum, near-zero scalloping. Peak ~4.64 (by design). Amplitude calibration, transducer calibration (~0.01 dB accuracy). ISO 18431.[19] +5-term cosine sum, near-zero scalloping. Peak ~4.64 (by design). Amplitude calibration, transducer calibration (~0.01 dB accuracy). ISO 18431.[^heinzel2002] **-93 dB** sidelobe · **-6 dB/oct** rolloff ### `bartlettHann(i, N)` @@ -216,7 +216,7 @@ $w(n) = 0.62 - 0.48\left|\frac{n}{N\!-\!1} - 0.5\right| - 0.38\cos\!\left(\frac{ -Bartlett-Hann hybrid. Balanced near/far sidelobe levels.[18] +Bartlett-Hann hybrid. Balanced near/far sidelobe levels.[^ha1989] **-36 dB** sidelobe ### `lanczos(i, N)` @@ -225,7 +225,7 @@ $w(n) = \text{sinc}\!\left(\frac{2n}{N-1} - 1\right)$ -Sinc main lobe. Image resampling, interpolation (FFmpeg, ImageMagick).[14] +Sinc main lobe. Image resampling, interpolation (FFmpeg, ImageMagick).[^duchon1979] **-26 dB** sidelobe ### `parzen(i, N)` @@ -234,7 +234,7 @@ $w(n) = 1 - 6a^2(1-a)$ for $|a| \le 0.5$,   $w(n) = 2(1-a)^3$ for $|a| > 0. -4th-order B-spline. Always-positive spectrum. Kernel density estimation.[7] +4th-order B-spline. Always-positive spectrum. Kernel density estimation.[^parzen1961] **-53 dB** sidelobe · **-24 dB/oct** rolloff ### `bohman(i, N)` @@ -258,7 +258,7 @@ $w(n) = \frac{I_0\!\left(\beta\sqrt{1 - \left(\frac{2n-N+1}{N-1}\right)^2}\right -Near-optimal DPSS approximation via Bessel I₀. The standard parameterized window for FIR filter design.[10] +Near-optimal DPSS approximation via Bessel I₀. The standard parameterized window for FIR filter design.[^kaiser1974] ### `gaussian(i, N, sigma)` @@ -268,7 +268,7 @@ $w(n) = \exp\!\left[-\frac{1}{2}\left(\frac{2n-N+1}{\sigma(N-1)}\right)^2\right] -Gaussian bell, minimum time-bandwidth product. STFT/Gabor transform, frequency estimation via parabolic interpolation.[2] +Gaussian bell, minimum time-bandwidth product. STFT/Gabor transform, frequency estimation via parabolic interpolation.[^gabor1946] ### `generalizedNormal(i, N, sigma, p)` @@ -296,7 +296,7 @@ Flat center with cosine-tapered edges. Preserves signal amplitude while tapering -C∞-smooth bump function (infinitely differentiable). Gravitational wave analysis (LIGO/Virgo).[20] +C∞-smooth bump function (infinitely differentiable). Gravitational wave analysis (LIGO/Virgo).[^mckechan2010] ### `powerOfSine(i, N, alpha)` @@ -316,7 +316,7 @@ $w(n) = \exp\!\left(\frac{-|2n-N+1|}{\tau(N-1)}\right)$ -Exponential decay from center. Modal analysis, impact testing.[12] +Exponential decay from center. Modal analysis, impact testing.[^harris1978] ### `hannPoisson(i, N, alpha)` @@ -336,7 +336,7 @@ $w(n) = \frac{1}{1+\left(\frac{\alpha(2n-N+1)}{N-1}\right)^2}$ -Lorentzian shape. Matches spectral line shapes in spectroscopy.[12] +Lorentzian shape. Matches spectral line shapes in spectroscopy.[^harris1978] ### `rifeVincent(i, N, order)` @@ -346,7 +346,7 @@ $w(n) = \frac{1}{Z}\sum_{k=0}^{K}(-1)^k a_k\cos\frac{2\pi kn}{N-1}$ -Class I cosine-sum optimized for sidelobe fall-off. Power grid harmonic analysis, interpolated DFT.[9] +Class I cosine-sum optimized for sidelobe fall-off. Power grid harmonic analysis, interpolated DFT.[^rife1970] ### `confinedGaussian(i, N, sigmaT)` @@ -354,7 +354,7 @@ Class I cosine-sum optimized for sidelobe fall-off. Power grid harmonic analysis -Optimal RMS time-frequency bandwidth. Time-frequency analysis, audio coding.[21] +Optimal RMS time-frequency bandwidth. Time-frequency analysis, audio coding.[^starosielec2014] @@ -370,7 +370,7 @@ $W(k) = (-1)^k T_{N-1}\!\left(\beta\cos\frac{\pi k}{N}\right)$,   $w = \tex -Optimal: narrowest main lobe for given equiripple sidelobe level. Antenna design, radar.[1] +Optimal: narrowest main lobe for given equiripple sidelobe level. Antenna design, radar.[^dolph1946] ### `taylor(i, N, nbar, sll)` @@ -380,7 +380,7 @@ $w(n) = 1 + 2\sum_{m=1}^{\bar{n}-1} F_m \cos\frac{2\pi m(n-(N\!-\!1)/2)}{N}$ -Monotonically decreasing sidelobes. The radar community standard for SAR image formation.[4] +Monotonically decreasing sidelobes. The radar community standard for SAR image formation.[^taylor1955] ### `kaiserBesselDerived(i, N, beta)` @@ -390,7 +390,7 @@ $w(n) = \sqrt{\frac{\sum_{j=0}^{n} K(j)}{\sum_{j=0}^{N/2} K(j)}}$,   $K(j) -Princen-Bradley condition for perfect MDCT reconstruction. AAC, Vorbis, Opus audio codecs.[17] +Princen-Bradley condition for perfect MDCT reconstruction. AAC, Vorbis, Opus audio codecs.[^princen1987] ### `dpss(i, N, W)` @@ -400,7 +400,7 @@ $\mathbf{T}\mathbf{v} = \lambda\mathbf{v}$,   $T_{jk} = \frac{\sin 2\pi W(j -Dominant eigenvector of sinc Toeplitz matrix — provably optimal energy concentration. Also called Slepian window. Multitaper spectral estimation, neuroscience, climate science.[13] +Dominant eigenvector of sinc Toeplitz matrix — provably optimal energy concentration. Also called Slepian window. Multitaper spectral estimation, neuroscience, climate science.[^slepian1978] ### `ultraspherical(i, N, mu, xmu)` @@ -410,7 +410,7 @@ $W(k) = C_n^\mu\!\left(x_\mu\cos\frac{\pi k}{N}\right)$,   $w = \text{IDFT} -Gegenbauer polynomial window. Independent control of sidelobe level and taper rate. Antenna design, beamforming.[16] +Gegenbauer polynomial window. Independent control of sidelobe level and taper rate. Antenna design, beamforming.[^streit1984] @@ -475,28 +475,26 @@ cola(hann, 1024, 512) // 0 — perfect STFT reconstruction - **`scallopLoss(fn, N, ...params)`** — worst-case amplitude error in dB between DFT bins. Rectangular = 3.92, Hann = 1.42, flat-top ≈ 0. - **`cola(fn, N, hop, ...params)`** — COLA deviation. 0 = perfect STFT reconstruction at given hop size. -## References - -1. C.L. Dolph, "A Current Distribution for Broadside Arrays," *Proc. IRE* 34, 1946. -2. D. Gabor, "Theory of Communication," *J. IEE* 93, 1946. -3. M.S. Bartlett, "Periodogram Analysis and Continuous Spectra," *Biometrika* 37, 1950. -4. T.T. Taylor, "Design of Line-Source Antennas," *IRE Trans. Antennas Propag.* AP-4, 1955. -5. R.B. Blackman & J.W. Tukey, *The Measurement of Power Spectra*, Dover, 1958. -6. J. Connes, "Recherches sur la spectroscopie par transformation de Fourier," *Revue d'Optique* 40, 1961. -7. E. Parzen, "Mathematical Considerations in the Estimation of Spectra," *Technometrics* 3, 1961. -8. P.D. Welch, "The Use of FFT for Estimation of Power Spectra," *IEEE Trans. Audio Electroacoustics* AU-15, 1967. -9. D.C. Rife & G.A. Vincent, "Use of the DFT in Measurement of Frequencies and Levels of Tones," *Bell Syst. Tech. J.* 49, 1970. -10. J.F. Kaiser, "Nonrecursive Digital Filter Design Using the Sinh Window Function," *IEEE Int. Symp. Circuits and Systems*, 1974. -11. R.W. Hamming, *Digital Filters*, Prentice-Hall, 1977. -12. F.J. Harris, "On the Use of Windows for Harmonic Analysis with the DFT," *Proc. IEEE* 66, 1978. -13. D. Slepian, "Prolate Spheroidal Wave Functions — V," *Bell Syst. Tech. J.* 57, 1978. -14. C.E. Duchon, "Lanczos Filtering in One and Two Dimensions," *J. Applied Meteorology* 18, 1979. -15. A.H. Nuttall, "Some Windows with Very Good Sidelobe Behavior," *IEEE Trans. ASSP* 29, 1981. -16. R.L. Streit, "A Two-Parameter Family of Weights for Nonrecursive Digital Filters and Antennas," *IEEE Trans. ASSP* 32, 1984. -17. J.P. Princen, A.W. Johnson & A.B. Bradley, "Subband/Transform Coding Using Filter Bank Designs Based on Time Domain Aliasing Cancellation," *ICASSP*, 1987. -18. Y.H. Ha & J.A. Pearce, "A New Window and Comparison to Standard Windows," *IEEE Trans. ASSP*, 1989. -19. G. Heinzel, A. Rudiger & R. Schilling, "Spectrum and Spectral Density Estimation by the DFT," Max Planck Institute, 2002. -20. D.J.A. McKechan et al., "A Tapering Window for Time-Domain Templates," *Class. Quantum Grav.* 27, 2010. -21. S. Starosielec & D. Hagemeier, "Discrete-Time Windows with Minimal RMS Bandwidth," *Signal Processing* 102, 2014. +[^dolph1946]: C.L. Dolph, "A Current Distribution for Broadside Arrays," *Proc. IRE* 34, 1946. +[^gabor1946]: D. Gabor, "Theory of Communication," *J. IEE* 93, 1946. +[^bartlett1950]: M.S. Bartlett, "Periodogram Analysis and Continuous Spectra," *Biometrika* 37, 1950. +[^taylor1955]: T.T. Taylor, "Design of Line-Source Antennas," *IRE Trans. Antennas Propag.* AP-4, 1955. +[^blackman1958]: R.B. Blackman & J.W. Tukey, *The Measurement of Power Spectra*, Dover, 1958. +[^connes1961]: J. Connes, "Recherches sur la spectroscopie par transformation de Fourier," *Revue d'Optique* 40, 1961. +[^parzen1961]: E. Parzen, "Mathematical Considerations in the Estimation of Spectra," *Technometrics* 3, 1961. +[^welch1967]: P.D. Welch, "The Use of FFT for Estimation of Power Spectra," *IEEE Trans. Audio Electroacoustics* AU-15, 1967. +[^rife1970]: D.C. Rife & G.A. Vincent, "Use of the DFT in Measurement of Frequencies and Levels of Tones," *Bell Syst. Tech. J.* 49, 1970. +[^kaiser1974]: J.F. Kaiser, "Nonrecursive Digital Filter Design Using the Sinh Window Function," *IEEE Int. Symp. Circuits and Systems*, 1974. +[^hamming1977]: R.W. Hamming, *Digital Filters*, Prentice-Hall, 1977. +[^harris1978]: F.J. Harris, "On the Use of Windows for Harmonic Analysis with the DFT," *Proc. IEEE* 66, 1978. +[^slepian1978]: D. Slepian, "Prolate Spheroidal Wave Functions — V," *Bell Syst. Tech. J.* 57, 1978. +[^duchon1979]: C.E. Duchon, "Lanczos Filtering in One and Two Dimensions," *J. Applied Meteorology* 18, 1979. +[^nuttall1981]: A.H. Nuttall, "Some Windows with Very Good Sidelobe Behavior," *IEEE Trans. ASSP* 29, 1981. +[^streit1984]: R.L. Streit, "A Two-Parameter Family of Weights for Nonrecursive Digital Filters and Antennas," *IEEE Trans. ASSP* 32, 1984. +[^princen1987]: J.P. Princen, A.W. Johnson & A.B. Bradley, "Subband/Transform Coding Using Filter Bank Designs Based on TDAC," *ICASSP*, 1987. +[^ha1989]: Y.H. Ha & J.A. Pearce, "A New Window and Comparison to Standard Windows," *IEEE Trans. ASSP*, 1989. +[^heinzel2002]: G. Heinzel, A. Rudiger & R. Schilling, "Spectrum and Spectral Density Estimation by the DFT," Max Planck Institute, 2002. +[^mckechan2010]: D.J.A. McKechan et al., "A Tapering Window for Time-Domain Templates," *Class. Quantum Grav.* 27, 2010. +[^starosielec2014]: S. Starosielec & D. Hagemeier, "Discrete-Time Windows with Minimal RMS Bandwidth," *Signal Processing* 102, 2014.

MIT ·

From f4cb8bbc3a9a6a5759cc98d53001be44c2dced35 Mon Sep 17 00:00:00 2001 From: "Dmitry Iv." Date: Thu, 26 Mar 2026 00:45:51 -0400 Subject: [PATCH 21/26] Add test badge --- .github/workflows/test.yml | 15 +++++++++++++++ README.md | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..a4b693f --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,15 @@ +name: test +on: [push, pull_request] +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + node: [18, 20, 22] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + - run: npm install + - run: npm test diff --git a/README.md b/README.md index 1d93436..9ce7d4d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# window-function +# window-function [![test](https://github.com/audiojs/window-function/actions/workflows/test.yml/badge.svg)](https://github.com/audiojs/window-function/actions/workflows/test.yml) Collection of window functions for signal processing and spectral analysis. @@ -497,4 +497,4 @@ cola(hann, 1024, 512) // 0 — perfect STFT reconstruction [^mckechan2010]: D.J.A. McKechan et al., "A Tapering Window for Time-Domain Templates," *Class. Quantum Grav.* 27, 2010. [^starosielec2014]: S. Starosielec & D. Hagemeier, "Discrete-Time Windows with Minimal RMS Bandwidth," *Signal Processing* 102, 2014. -

MIT ·

+ From 657a28ac24131fb180daff450e8126be141d127b Mon Sep 17 00:00:00 2001 From: "Dmitry Iv." Date: Thu, 26 Mar 2026 00:47:31 -0400 Subject: [PATCH 22/26] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9ce7d4d..dde1537 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# window-function [![test](https://github.com/audiojs/window-function/actions/workflows/test.yml/badge.svg)](https://github.com/audiojs/window-function/actions/workflows/test.yml) +# window-function [![test](https://github.com/audiojs/window-function/actions/workflows/test.yml/badge.svg)](https://github.com/audiojs/window-function/actions/workflows/test.yml) [![npm](https://img.shields.io/npm/v/window-function)](https://www.npmjs.com/package/window-function) Collection of window functions for signal processing and spectral analysis. From b5fe36b4faa11366e34f97d594b59e9e99922255 Mon Sep 17 00:00:00 2001 From: "Dmitry Iv." Date: Thu, 26 Mar 2026 00:57:20 -0400 Subject: [PATCH 23/26] Merge util --- _util.js | 32 -------------------------------- bartlettHann.js | 2 +- blackman.js | 2 +- blackmanHarris.js | 2 +- blackmanNuttall.js | 2 +- bohman.js | 2 +- confinedGaussian.js | 2 +- cosine.js | 2 +- dolphChebyshev.js | 2 +- dpss.js | 2 +- exactBlackman.js | 2 +- exponential.js | 2 +- flatTop.js | 2 +- gaussian.js | 2 +- generalizedNormal.js | 2 +- hamming.js | 2 +- hann.js | 2 +- hannPoisson.js | 2 +- kaiser.js | 2 +- kaiserBesselDerived.js | 2 +- lanczos.js | 2 +- nuttall.js | 2 +- planckTaper.js | 2 +- powerOfSine.js | 2 +- rifeVincent.js | 2 +- taylor.js | 2 +- tukey.js | 2 +- ultraspherical.js | 2 +- util.js | 41 +++++++++++++++++++++++++++++++++++++++-- 29 files changed, 66 insertions(+), 61 deletions(-) delete mode 100644 _util.js diff --git a/_util.js b/_util.js deleted file mode 100644 index 6d539e8..0000000 --- a/_util.js +++ /dev/null @@ -1,32 +0,0 @@ -export let { cos, sin, abs, exp, sqrt, PI, cosh, acosh, acos, pow, log10 } = Math -export let PI2 = 2 * PI - -export function cosineSum (i, N, a) { - let f = PI2 * i / (N - 1), v = a[0] - for (let k = 1; k < a.length; k++) v += (k % 2 ? -1 : 1) * a[k] * cos(k * f) - return v -} - -export function i0 (x) { - let s = 1, t = 1 - for (let k = 1; k <= 25; k++) { t *= (x / (2 * k)) * (x / (2 * k)); s += t; if (t < 1e-15 * s) break } - return s -} - -export function gegen (n, mu, x) { - if (n === 0) return 1 - if (n === 1) return 2 * mu * x - let c0 = 1, c1 = 2 * mu * x - for (let k = 2; k <= n; k++) { - let c2 = (2 * x * (k + mu - 1) * c1 - (k + 2 * mu - 2) * c0) / k - c0 = c1; c1 = c2 - } - return c1 -} - -export function normalize (w) { - let peak = 0 - for (let i = 0; i < w.length; i++) if (abs(w[i]) > peak) peak = abs(w[i]) - if (peak > 0) for (let i = 0; i < w.length; i++) w[i] /= peak - return w -} diff --git a/bartlettHann.js b/bartlettHann.js index 404dd21..546f0a8 100644 --- a/bartlettHann.js +++ b/bartlettHann.js @@ -1,4 +1,4 @@ -import { abs, cos, PI2 } from './_util.js' +import { abs, cos, PI2 } from './util.js' export default function bartlettHann (i, N) { let x = i / (N - 1) return 0.62 - 0.48 * abs(x - 0.5) - 0.38 * cos(PI2 * x) diff --git a/blackman.js b/blackman.js index 7a6f231..55e069d 100644 --- a/blackman.js +++ b/blackman.js @@ -1,2 +1,2 @@ -import { cosineSum } from './_util.js' +import { cosineSum } from './util.js' export default function blackman (i, N) { return cosineSum(i, N, [0.42, 0.5, 0.08]) } diff --git a/blackmanHarris.js b/blackmanHarris.js index 7f532c1..e34078d 100644 --- a/blackmanHarris.js +++ b/blackmanHarris.js @@ -1,2 +1,2 @@ -import { cosineSum } from './_util.js' +import { cosineSum } from './util.js' export default function blackmanHarris (i, N) { return cosineSum(i, N, [0.35875, 0.48829, 0.14128, 0.01168]) } diff --git a/blackmanNuttall.js b/blackmanNuttall.js index e62861c..ce7d4ab 100644 --- a/blackmanNuttall.js +++ b/blackmanNuttall.js @@ -1,2 +1,2 @@ -import { cosineSum } from './_util.js' +import { cosineSum } from './util.js' export default function blackmanNuttall (i, N) { return cosineSum(i, N, [0.3635819, 0.4891775, 0.1365995, 0.0106411]) } diff --git a/bohman.js b/bohman.js index 824df53..2acfe66 100644 --- a/bohman.js +++ b/bohman.js @@ -1,4 +1,4 @@ -import { abs, cos, sin, PI } from './_util.js' +import { abs, cos, sin, PI } from './util.js' export default function bohman (i, N) { let a = abs((2 * i - N + 1) / (N - 1)) if (a >= 1) return 0 diff --git a/confinedGaussian.js b/confinedGaussian.js index 6b95798..3666ec8 100644 --- a/confinedGaussian.js +++ b/confinedGaussian.js @@ -1,4 +1,4 @@ -import { exp } from './_util.js' +import { exp } from './util.js' export default function confinedGaussian (i, N, sigmaT) { if (sigmaT == null) sigmaT = 0.1 let L = N + 1, half = (N - 1) / 2 diff --git a/cosine.js b/cosine.js index 4433055..97dc966 100644 --- a/cosine.js +++ b/cosine.js @@ -1,2 +1,2 @@ -import { sin, PI } from './_util.js' +import { sin, PI } from './util.js' export default function cosine (i, N) { return sin(PI * i / (N - 1)) } diff --git a/dolphChebyshev.js b/dolphChebyshev.js index 14eca10..ceed89f 100644 --- a/dolphChebyshev.js +++ b/dolphChebyshev.js @@ -1,4 +1,4 @@ -import { cos, abs, cosh, acosh, acos, pow, PI, PI2, normalize } from './_util.js' +import { cos, abs, cosh, acosh, acos, pow, PI, PI2, normalize } from './util.js' export default function dolphChebyshev (i, N, attenuation) { if (attenuation == null) attenuation = 100 let c = dolphChebyshev diff --git a/dpss.js b/dpss.js index b045b66..acddd35 100644 --- a/dpss.js +++ b/dpss.js @@ -1,4 +1,4 @@ -import { sin, exp, sqrt, abs, PI, PI2, normalize } from './_util.js' +import { sin, exp, sqrt, abs, PI, PI2, normalize } from './util.js' export default function dpss (i, N, W) { if (W == null) W = 0.1 let c = dpss diff --git a/exactBlackman.js b/exactBlackman.js index f9406a1..ba5428c 100644 --- a/exactBlackman.js +++ b/exactBlackman.js @@ -1,2 +1,2 @@ -import { cosineSum } from './_util.js' +import { cosineSum } from './util.js' export default function exactBlackman (i, N) { return cosineSum(i, N, [0.42659, 0.49656, 0.076849]) } diff --git a/exponential.js b/exponential.js index 38f9b26..d54ae3a 100644 --- a/exponential.js +++ b/exponential.js @@ -1,4 +1,4 @@ -import { abs, exp } from './_util.js' +import { abs, exp } from './util.js' export default function exponential (i, N, tau) { if (tau == null) tau = 1 return exp(-abs(2 * i - N + 1) / (tau * (N - 1))) diff --git a/flatTop.js b/flatTop.js index 9d57948..a0ee74b 100644 --- a/flatTop.js +++ b/flatTop.js @@ -1,2 +1,2 @@ -import { cosineSum } from './_util.js' +import { cosineSum } from './util.js' export default function flatTop (i, N) { return cosineSum(i, N, [1, 1.93, 1.29, 0.388, 0.028]) } diff --git a/gaussian.js b/gaussian.js index 7357329..500a85b 100644 --- a/gaussian.js +++ b/gaussian.js @@ -1,4 +1,4 @@ -import { exp } from './_util.js' +import { exp } from './util.js' export default function gaussian (i, N, sigma) { if (sigma == null) sigma = 0.4 let x = (2 * i - N + 1) / (sigma * (N - 1)) diff --git a/generalizedNormal.js b/generalizedNormal.js index 581ef8d..42d9b6c 100644 --- a/generalizedNormal.js +++ b/generalizedNormal.js @@ -1,4 +1,4 @@ -import { abs, exp, pow } from './_util.js' +import { abs, exp, pow } from './util.js' export default function generalizedNormal (i, N, sigma, p) { if (sigma == null) sigma = 0.4 if (p == null) p = 2 diff --git a/hamming.js b/hamming.js index 9b03482..51b377a 100644 --- a/hamming.js +++ b/hamming.js @@ -1,2 +1,2 @@ -import { cos, PI2 } from './_util.js' +import { cos, PI2 } from './util.js' export default function hamming (i, N) { return 0.54 - 0.46 * cos(PI2 * i / (N - 1)) } diff --git a/hann.js b/hann.js index dc0d862..5fd4826 100644 --- a/hann.js +++ b/hann.js @@ -1,2 +1,2 @@ -import { cos, PI2 } from './_util.js' +import { cos, PI2 } from './util.js' export default function hann (i, N) { return 0.5 - 0.5 * cos(PI2 * i / (N - 1)) } diff --git a/hannPoisson.js b/hannPoisson.js index a7a90bc..adf1669 100644 --- a/hannPoisson.js +++ b/hannPoisson.js @@ -1,4 +1,4 @@ -import { cos, exp, abs, PI2 } from './_util.js' +import { cos, exp, abs, PI2 } from './util.js' export default function hannPoisson (i, N, alpha) { if (alpha == null) alpha = 2 return 0.5 * (1 - cos(PI2 * i / (N - 1))) * exp(-alpha * abs(2 * i - N + 1) / (N - 1)) diff --git a/kaiser.js b/kaiser.js index 5c73b81..54002a9 100644 --- a/kaiser.js +++ b/kaiser.js @@ -1,4 +1,4 @@ -import { sqrt, i0 } from './_util.js' +import { sqrt, i0 } from './util.js' export default function kaiser (i, N, beta) { if (beta == null) beta = 8.6 let x = (2 * i - N + 1) / (N - 1) diff --git a/kaiserBesselDerived.js b/kaiserBesselDerived.js index 7f58696..c44e09c 100644 --- a/kaiserBesselDerived.js +++ b/kaiserBesselDerived.js @@ -1,4 +1,4 @@ -import { sqrt, abs, i0 } from './_util.js' +import { sqrt, abs, i0 } from './util.js' export default function kaiserBesselDerived (i, N, beta) { if (beta == null) beta = 8.6 let c = kaiserBesselDerived diff --git a/lanczos.js b/lanczos.js index 279b736..a9f5aa4 100644 --- a/lanczos.js +++ b/lanczos.js @@ -1,4 +1,4 @@ -import { sin, PI } from './_util.js' +import { sin, PI } from './util.js' export default function lanczos (i, N) { let x = 2 * i / (N - 1) - 1 return x === 0 ? 1 : sin(PI * x) / (PI * x) diff --git a/nuttall.js b/nuttall.js index 87d3a67..42c8300 100644 --- a/nuttall.js +++ b/nuttall.js @@ -1,2 +1,2 @@ -import { cosineSum } from './_util.js' +import { cosineSum } from './util.js' export default function nuttall (i, N) { return cosineSum(i, N, [0.355768, 0.487396, 0.144232, 0.012604]) } diff --git a/planckTaper.js b/planckTaper.js index 00a61a2..841aaf0 100644 --- a/planckTaper.js +++ b/planckTaper.js @@ -1,4 +1,4 @@ -import { exp } from './_util.js' +import { exp } from './util.js' export default function planckTaper (i, N, epsilon) { if (epsilon == null) epsilon = 0.1 let eN = epsilon * (N - 1) diff --git a/powerOfSine.js b/powerOfSine.js index 270d2c5..3f65e57 100644 --- a/powerOfSine.js +++ b/powerOfSine.js @@ -1,4 +1,4 @@ -import { sin, PI, pow } from './_util.js' +import { sin, PI, pow } from './util.js' export default function powerOfSine (i, N, alpha) { if (alpha == null) alpha = 2 return pow(sin(PI * i / (N - 1)), alpha) diff --git a/rifeVincent.js b/rifeVincent.js index 2f1f375..a53cb13 100644 --- a/rifeVincent.js +++ b/rifeVincent.js @@ -1,4 +1,4 @@ -import { cos, PI2 } from './_util.js' +import { cos, PI2 } from './util.js' export default function rifeVincent (i, N, order) { if (order == null) order = 1 let a diff --git a/taylor.js b/taylor.js index 455f914..ed50f11 100644 --- a/taylor.js +++ b/taylor.js @@ -1,4 +1,4 @@ -import { cos, acosh, pow, PI, PI2, normalize } from './_util.js' +import { cos, acosh, pow, PI, PI2, normalize } from './util.js' export default function taylor (i, N, nbar, sll) { if (nbar == null) nbar = 4 if (sll == null) sll = 30 diff --git a/tukey.js b/tukey.js index 3c5e487..578e470 100644 --- a/tukey.js +++ b/tukey.js @@ -1,4 +1,4 @@ -import { cos, PI } from './_util.js' +import { cos, PI } from './util.js' export default function tukey (i, N, alpha) { if (alpha == null) alpha = 0.5 let half = 0.5 * alpha * (N - 1) diff --git a/ultraspherical.js b/ultraspherical.js index 91fa354..654444f 100644 --- a/ultraspherical.js +++ b/ultraspherical.js @@ -1,4 +1,4 @@ -import { cos, PI, PI2, gegen, normalize } from './_util.js' +import { cos, PI, PI2, gegen, normalize } from './util.js' export default function ultraspherical (i, N, mu, xmu) { if (mu == null) mu = 1 if (xmu == null) xmu = 1 diff --git a/util.js b/util.js index 1756b7f..343474d 100644 --- a/util.js +++ b/util.js @@ -1,4 +1,41 @@ -import { cos, sin, sqrt, abs, PI } from './_util.js' +// ── Math shorthand ── + +export let { cos, sin, abs, exp, sqrt, PI, cosh, acosh, acos, pow, log10 } = Math +export let PI2 = 2 * PI + +// ── Internal helpers (used by window functions) ── + +export function cosineSum (i, N, a) { + let f = PI2 * i / (N - 1), v = a[0] + for (let k = 1; k < a.length; k++) v += (k % 2 ? -1 : 1) * a[k] * cos(k * f) + return v +} + +export function i0 (x) { + let s = 1, t = 1 + for (let k = 1; k <= 25; k++) { t *= (x / (2 * k)) * (x / (2 * k)); s += t; if (t < 1e-15 * s) break } + return s +} + +export function gegen (n, mu, x) { + if (n === 0) return 1 + if (n === 1) return 2 * mu * x + let c0 = 1, c1 = 2 * mu * x + for (let k = 2; k <= n; k++) { + let c2 = (2 * x * (k + mu - 1) * c1 - (k + 2 * mu - 2) * c0) / k + c0 = c1; c1 = c2 + } + return c1 +} + +export function normalize (w) { + let peak = 0 + for (let i = 0; i < w.length; i++) if (abs(w[i]) > peak) peak = abs(w[i]) + if (peak > 0) for (let i = 0; i < w.length; i++) w[i] /= peak + return w +} + +// ── Public utilities ── /** Generate a full window as Float64Array. */ export function generate (fn, N, ...params) { @@ -24,7 +61,7 @@ export function enbw (fn, N, ...params) { export function scallopLoss (fn, N, ...params) { let s = 0, re = 0, im = 0 for (let i = 0; i < N; i++) { let v = fn(i, N, ...params); s += v; re += v * cos(PI * i / N); im -= v * sin(PI * i / N) } - return s === 0 ? Infinity : -20 * Math.log10(sqrt(re * re + im * im) / abs(s)) + return s === 0 ? Infinity : -20 * log10(sqrt(re * re + im * im) / abs(s)) } /** COLA deviation. Returns max relative deviation from constant overlap-add sum; 0 = perfect. */ From 52c6cd029706d69cde5ba9862a4f664cd25563a9 Mon Sep 17 00:00:00 2001 From: "Dmitry Iv." Date: Thu, 26 Mar 2026 00:58:36 -0400 Subject: [PATCH 24/26] Update util.js --- util.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/util.js b/util.js index 343474d..15516f6 100644 --- a/util.js +++ b/util.js @@ -5,18 +5,21 @@ export let PI2 = 2 * PI // ── Internal helpers (used by window functions) ── +/** Cosine-sum: w(i) = Σ (-1)^k aₖ cos(2πki/(N-1)). Used by blackman, nuttall, etc. */ export function cosineSum (i, N, a) { let f = PI2 * i / (N - 1), v = a[0] for (let k = 1; k < a.length; k++) v += (k % 2 ? -1 : 1) * a[k] * cos(k * f) return v } +/** Modified Bessel function of the first kind, order 0. Used by kaiser, kaiserBesselDerived. */ export function i0 (x) { let s = 1, t = 1 for (let k = 1; k <= 25; k++) { t *= (x / (2 * k)) * (x / (2 * k)); s += t; if (t < 1e-15 * s) break } return s } +/** Gegenbauer (ultraspherical) polynomial C_n^mu(x) via recurrence. Used by ultraspherical. */ export function gegen (n, mu, x) { if (n === 0) return 1 if (n === 1) return 2 * mu * x @@ -28,6 +31,7 @@ export function gegen (n, mu, x) { return c1 } +/** Normalize array to peak absolute value of 1. Used by array-computed windows. */ export function normalize (w) { let peak = 0 for (let i = 0; i < w.length; i++) if (abs(w[i]) > peak) peak = abs(w[i]) From 8941cea26bd2f323763a6898cf1ea81baabdeaac Mon Sep 17 00:00:00 2001 From: "Dmitry Iv." Date: Thu, 26 Mar 2026 01:11:59 -0400 Subject: [PATCH 25/26] ESM: add named exports, changelog, and docs updates Add CHANGELOG.md and update README docs and image links; tidy .gitignore. Convert modules to ESM-style exports by adding named exports (export { foo }) and replace direct Math destructuring with imports from util.js where appropriate. Small behavioral/safety fixes: validate even N in kaiserBesselDerived, minor ultraspherical iteration fix/optimization, and various window modules adjusted for consistent exports. Update package.json repository/homepage to audiojs. Overall prepares/cleans the package for the ESM v3 release and improves consistency across modules. --- .gitignore | 65 ++------------------------------- CHANGELOG.md | 32 ++++++++++++++++ README.md | 83 ++++++++++++++++++++++++------------------ bartlett.js | 3 +- bartlettHann.js | 1 + blackman.js | 1 + blackmanHarris.js | 1 + blackmanNuttall.js | 1 + bohman.js | 1 + cauchy.js | 1 + confinedGaussian.js | 1 + connes.js | 1 + cosine.js | 1 + dolphChebyshev.js | 1 + dpss.js | 1 + exactBlackman.js | 1 + exponential.js | 1 + flatTop.js | 1 + gaussian.js | 1 + generalizedNormal.js | 1 + hamming.js | 1 + hann.js | 1 + hannPoisson.js | 1 + kaiser.js | 1 + kaiserBesselDerived.js | 2 + lanczos.js | 1 + nuttall.js | 1 + package.json | 4 +- parzen.js | 3 +- planckTaper.js | 1 + powerOfSine.js | 1 + rectangular.js | 1 + rifeVincent.js | 1 + taylor.js | 1 + triangular.js | 3 +- tukey.js | 1 + ultraspherical.js | 5 ++- welch.js | 1 + 38 files changed, 125 insertions(+), 104 deletions(-) create mode 100644 CHANGELOG.md diff --git a/.gitignore b/.gitignore index 49e1f75..1c5c1ce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,63 +1,4 @@ - -# Directories # -############### -reports/ -build/ - -# Compiled source # -################### -*.com -*.class -*.dll -*.exe -*.o -*.so - -# Packages # -############ -# it's better to unpack these files and commit the raw source -# git has its own built in compression methods -*.7z -*.dmg -*.gz -*.iso -*.jar -*.rar -*.tar -*.zip - -# Logs and databases # -###################### -*.log -*.sql -*.sqlite - -# OS generated files # -###################### +node_modules/ .DS_Store -.DS_Store? -._* -.Spotlight-V100 -.Trashes -Icon? -ehthumbs.db -Thumbs.db -Desktop.ini - -# Temporary files # -################### -*~ - -# Node.js # -########### -/node_modules/ - -# Matlab # -########## - -# Windows default autosave extension -*.asv - -# Compiled MEX binaries (all platforms) -*.mex* -/.claude +*.log +.claude/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..ee98510 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,32 @@ +# Changelog + +## 3.0.0 + +Breaking: ESM-only. `require()` no longer works. + +- **34 window functions** (was 18): added connes, powerOfSine, generalizedNormal, planckTaper, exponential, hannPoisson, cauchy, rifeVincent, confinedGaussian, kaiserBesselDerived, dolphChebyshev, taylor, dpss, ultraspherical, parzen, bohman +- **Quantitative metrics**: `enbw`, `scallopLoss`, `cola` +- **Individual files**: `import hann from 'window-function/hann'` +- **Default + named exports**: both `import hann from` and `import { hann } from` work +- **TypeScript declarations** included +- **Scientific SVG plots** for all windows (time domain + frequency response) +- Consolidated to ESM (`"type": "module"`) +- Single `util.js` for shared helpers and utilities +- Zero dependencies (fourier-transform is devDependency for plot generation only) + +### Migration + +```diff +- const hann = require('window-function/hann') ++ import hann from 'window-function/hann' +``` + +The per-sample API `fn(i, N, ...params) → number` is unchanged. + +## 2.1.0 + +- Added `apply` and `generate` helpers + +## 2.0.2 + +- 18 window functions: rectangular, triangular, bartlett, welch, hann, hamming, cosine, blackman, exactBlackman, nuttall, blackmanNuttall, blackmanHarris, flatTop, bartlettHann, lanczos, gaussian, tukey diff --git a/README.md b/README.md index dde1537..b9e81e9 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ npm install window-function ```js // Import everything, or just what you need import { hann, kaiser, generate, apply } from 'window-function' -import { hann } from 'window-function/hann' +import hann from 'window-function/hann' // Every window: fn(i, N, ...params) → number hann(50, 101) // single sample → 1.0 @@ -88,7 +88,7 @@ generate(kaiser, 1024, 8.6) // Kaiser with β = 8.6 $w(n) = 1$ - + No windowing. Best frequency resolution, worst spectral leakage. Use for transient signals already zero at edges, or harmonic analysis with integer cycles. **-13 dB** sidelobe · **-6 dB/oct** rolloff @@ -97,7 +97,7 @@ No windowing. Best frequency resolution, worst spectral leakage. Use for transie $w(n) = 1 - \left|\frac{2n - N + 1}{N}\right|$ - + Linear taper, nonzero endpoints. Simple smoothing, 2nd-order B-spline. **-27 dB** sidelobe · **-12 dB/oct** rolloff @@ -106,7 +106,7 @@ Linear taper, nonzero endpoints. Simple smoothing, 2nd-order B-spline. $w(n) = 1 - \left|\frac{2n - N + 1}{N - 1}\right|$ - + Linear taper, zero endpoints. Bartlett's method PSD estimation.[^bartlett1950] **-27 dB** sidelobe · **-12 dB/oct** rolloff @@ -115,7 +115,7 @@ Linear taper, zero endpoints. Bartlett's method PSD estimation.[^bartlett1950] $w(n) = 1 - \left(\frac{2n - N + 1}{N - 1}\right)^2$ - + Parabolic taper. Welch's method PSD estimation.[^welch1967] **-21 dB** sidelobe · **-12 dB/oct** rolloff @@ -124,7 +124,7 @@ Parabolic taper. Welch's method PSD estimation.[^welch1967] $w(n) = \left[1 - \left(\frac{2n - N + 1}{N - 1}\right)^2\right]^2$ - + Welch squared (4th power parabolic). FTIR spectroscopy, interferogram apodization.[^connes1961] **-24 dB/oct** rolloff @@ -133,7 +133,7 @@ Welch squared (4th power parabolic). FTIR spectroscopy, interferogram apodizatio $w(n) = 0.5 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right)$ - + Raised cosine, zero endpoints. The default general-purpose choice. STFT with 50% overlap (COLA). Also called "Hanning" (misnomer).[^blackman1958] **-32 dB** sidelobe · **-18 dB/oct** rolloff @@ -142,7 +142,7 @@ Raised cosine, zero endpoints. The default general-purpose choice. STFT with 50% $w(n) = 0.54 - 0.46\cos\!\left(\frac{2\pi n}{N-1}\right)$ - + Raised cosine, nonzero endpoints. Optimized for first sidelobe cancellation. FIR filter design, speech processing.[^hamming1977] **-43 dB** sidelobe · **-6 dB/oct** rolloff @@ -151,7 +151,7 @@ Raised cosine, nonzero endpoints. Optimized for first sidelobe cancellation. FIR $w(n) = \sin\!\left(\frac{\pi n}{N-1}\right)$ - + Half-period sine. MDCT audio codecs: MP3, AAC, Vorbis.[^princen1987] **-23 dB** sidelobe · **-12 dB/oct** rolloff @@ -160,7 +160,7 @@ Half-period sine. MDCT audio codecs: MP3, AAC, Vorbis.[^princen1987] $w(n) = 0.42 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.08\cos\!\left(\frac{4\pi n}{N-1}\right)$ - + 3-term cosine sum. Better leakage than Hann at the cost of wider main lobe.[^blackman1958] **-58 dB** sidelobe · **-18 dB/oct** rolloff @@ -169,7 +169,7 @@ $w(n) = 0.42 - 0.5\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.08\cos\!\left(\frac{ $w(n) = 0.42659 - 0.49656\cos\!\left(\frac{2\pi n}{N-1}\right) + 0.076849\cos\!\left(\frac{4\pi n}{N-1}\right)$ - + Blackman with exact zero placement at 3rd and 4th sidelobes.[^harris1978] **-69 dB** sidelobe · **-6 dB/oct** rolloff @@ -178,7 +178,7 @@ Blackman with exact zero placement at 3rd and 4th sidelobes.[^harris1978] $w(n) = 0.355768 - 0.487396\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.144232\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.012604\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ - + 4-term cosine sum, continuous 1st derivative. High-dynamic-range analysis without edge discontinuity.[^nuttall1981] **-93 dB** sidelobe · **-18 dB/oct** rolloff @@ -187,7 +187,7 @@ $w(n) = 0.355768 - 0.487396\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.144232\ $w(n) = 0.3635819 - 0.4891775\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.1365995\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.0106411\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ - + 4-term cosine sum, lowest sidelobes among 4-term windows.[^nuttall1981] **-98 dB** sidelobe · **-6 dB/oct** rolloff @@ -196,7 +196,7 @@ $w(n) = 0.3635819 - 0.4891775\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.13659 $w(n) = 0.35875 - 0.48829\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.14128\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.01168\cos\!\left(\frac{6\pi n}{N\!-\!1}\right)$ - + 4-term minimum sidelobe. ADC testing, measurement instrumentation, >80 dB dynamic range.[^harris1978] **-92 dB** sidelobe · **-6 dB/oct** rolloff @@ -205,7 +205,7 @@ $w(n) = 0.35875 - 0.48829\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 0.14128\cos $w(n) = 1 - 1.93\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 1.29\cos\!\left(\frac{4\pi n}{N\!-\!1}\right) - 0.388\cos\!\left(\frac{6\pi n}{N\!-\!1}\right) + 0.028\cos\!\left(\frac{8\pi n}{N\!-\!1}\right)$ - + 5-term cosine sum, near-zero scalloping. Peak ~4.64 (by design). Amplitude calibration, transducer calibration (~0.01 dB accuracy). ISO 18431.[^heinzel2002] **-93 dB** sidelobe · **-6 dB/oct** rolloff @@ -214,7 +214,7 @@ $w(n) = 1 - 1.93\cos\!\left(\frac{2\pi n}{N\!-\!1}\right) + 1.29\cos\!\left(\fra $w(n) = 0.62 - 0.48\left|\frac{n}{N\!-\!1} - 0.5\right| - 0.38\cos\!\left(\frac{2\pi n}{N-1}\right)$ - + Bartlett-Hann hybrid. Balanced near/far sidelobe levels.[^ha1989] **-36 dB** sidelobe @@ -223,7 +223,7 @@ Bartlett-Hann hybrid. Balanced near/far sidelobe levels.[^ha1989] $w(n) = \text{sinc}\!\left(\frac{2n}{N-1} - 1\right)$ - + Sinc main lobe. Image resampling, interpolation (FFmpeg, ImageMagick).[^duchon1979] **-26 dB** sidelobe @@ -232,7 +232,7 @@ Sinc main lobe. Image resampling, interpolation (FFmpeg, ImageMagick).[^duchon19 $w(n) = 1 - 6a^2(1-a)$ for $|a| \le 0.5$,   $w(n) = 2(1-a)^3$ for $|a| > 0.5$,   $a = |(2n-N+1)/(N-1)|$ - + 4th-order B-spline. Always-positive spectrum. Kernel density estimation.[^parzen1961] **-53 dB** sidelobe · **-24 dB/oct** rolloff @@ -241,7 +241,7 @@ $w(n) = 1 - 6a^2(1-a)$ for $|a| \le 0.5$,   $w(n) = 2(1-a)^3$ for $|a| > 0. $w(n) = (1-|a|)\cos(\pi|a|) + \frac{\sin(\pi|a|)}{\pi}$,   $a = \frac{2n-N+1}{N-1}$ - + Autocorrelation of cosine window. Fast sidelobe decay, spectral estimation. **-46 dB** sidelobe · **-24 dB/oct** rolloff @@ -256,7 +256,7 @@ Autocorrelation of cosine window. Fast sidelobe decay, spectral estimation. $w(n) = \frac{I_0\!\left(\beta\sqrt{1 - \left(\frac{2n-N+1}{N-1}\right)^2}\right)}{I_0(\beta)}$ - + Near-optimal DPSS approximation via Bessel I₀. The standard parameterized window for FIR filter design.[^kaiser1974] @@ -266,7 +266,7 @@ Near-optimal DPSS approximation via Bessel I₀. The standard parameterized wind $w(n) = \exp\!\left[-\frac{1}{2}\left(\frac{2n-N+1}{\sigma(N-1)}\right)^2\right]$ - + Gaussian bell, minimum time-bandwidth product. STFT/Gabor transform, frequency estimation via parabolic interpolation.[^gabor1946] @@ -276,7 +276,7 @@ Gaussian bell, minimum time-bandwidth product. STFT/Gabor transform, frequency e $w(n) = \exp\!\left[-\frac{1}{2}\left|\frac{2n-N+1}{\sigma(N-1)}\right|^p\right]$ - + Continuous family between Gaussian and rectangular. Adjustable time-frequency tradeoff. @@ -286,7 +286,7 @@ Continuous family between Gaussian and rectangular. Adjustable time-frequency tr $w(n) = \tfrac{1}{2}[1+\cos(\pi(n/(\alpha(N\!-\!1)/2)-1))]$ in tapered edges,   $w(n) = 1$ in flat center. - + Flat center with cosine-tapered edges. Preserves signal amplitude while tapering. Vibration analysis, LIGO. @@ -294,7 +294,7 @@ Flat center with cosine-tapered edges. Preserves signal amplitude while tapering `epsilon`: taper fraction, default **0.1**. - + C∞-smooth bump function (infinitely differentiable). Gravitational wave analysis (LIGO/Virgo).[^mckechan2010] @@ -304,7 +304,7 @@ C∞-smooth bump function (infinitely differentiable). Gravitational wave analys $w(n) = \sin^\alpha\!\left(\frac{\pi n}{N-1}\right)$ - + sin^α family. Codec design, parameterized spectral analysis. @@ -314,7 +314,7 @@ sin^α family. Codec design, parameterized spectral analysis. $w(n) = \exp\!\left(\frac{-|2n-N+1|}{\tau(N-1)}\right)$ - + Exponential decay from center. Modal analysis, impact testing.[^harris1978] @@ -324,7 +324,7 @@ Exponential decay from center. Modal analysis, impact testing.[^harris1978] $w(n) = \frac{1}{2}\left(1-\cos\frac{2\pi n}{N\!-\!1}\right)\exp\!\left(\frac{-\alpha|2n\!-\!N\!+\!1|}{N-1}\right)$ - + Hann × exponential. Unique no-sidelobe property enables frequency estimators using convex optimization. @@ -334,7 +334,7 @@ Hann × exponential. Unique no-sidelobe property enables frequency estimators us $w(n) = \frac{1}{1+\left(\frac{\alpha(2n-N+1)}{N-1}\right)^2}$ - + Lorentzian shape. Matches spectral line shapes in spectroscopy.[^harris1978] @@ -344,7 +344,7 @@ Lorentzian shape. Matches spectral line shapes in spectroscopy.[^harris1978] $w(n) = \frac{1}{Z}\sum_{k=0}^{K}(-1)^k a_k\cos\frac{2\pi kn}{N-1}$ - + Class I cosine-sum optimized for sidelobe fall-off. Power grid harmonic analysis, interpolated DFT.[^rife1970] @@ -352,7 +352,7 @@ Class I cosine-sum optimized for sidelobe fall-off. Power grid harmonic analysis `sigmaT`: temporal width, default **0.1**. - + Optimal RMS time-frequency bandwidth. Time-frequency analysis, audio coding.[^starosielec2014] @@ -368,7 +368,7 @@ Compute the full window on first call, cache the result. Recomputed when paramet $W(k) = (-1)^k T_{N-1}\!\left(\beta\cos\frac{\pi k}{N}\right)$,   $w = \text{IDFT}(W)$ - + Optimal: narrowest main lobe for given equiripple sidelobe level. Antenna design, radar.[^dolph1946] @@ -378,7 +378,7 @@ Optimal: narrowest main lobe for given equiripple sidelobe level. Antenna design $w(n) = 1 + 2\sum_{m=1}^{\bar{n}-1} F_m \cos\frac{2\pi m(n-(N\!-\!1)/2)}{N}$ - + Monotonically decreasing sidelobes. The radar community standard for SAR image formation.[^taylor1955] @@ -388,7 +388,7 @@ Monotonically decreasing sidelobes. The radar community standard for SAR image f $w(n) = \sqrt{\frac{\sum_{j=0}^{n} K(j)}{\sum_{j=0}^{N/2} K(j)}}$,   $K(j) = I_0\!\left(\beta\sqrt{1-\left(\frac{2j-N/2}{N/2}\right)^2}\right)$ - + Princen-Bradley condition for perfect MDCT reconstruction. AAC, Vorbis, Opus audio codecs.[^princen1987] @@ -398,7 +398,7 @@ Princen-Bradley condition for perfect MDCT reconstruction. AAC, Vorbis, Opus aud $\mathbf{T}\mathbf{v} = \lambda\mathbf{v}$,   $T_{jk} = \frac{\sin 2\pi W(j-k)}{\pi(j-k)}$ - + Dominant eigenvector of sinc Toeplitz matrix — provably optimal energy concentration. Also called Slepian window. Multitaper spectral estimation, neuroscience, climate science.[^slepian1978] @@ -408,7 +408,7 @@ Dominant eigenvector of sinc Toeplitz matrix — provably optimal energy concent $W(k) = C_n^\mu\!\left(x_\mu\cos\frac{\pi k}{N}\right)$,   $w = \text{IDFT}(W)$ - + Gegenbauer polynomial window. Independent control of sidelobe level and taper rate. Antenna design, beamforming.[^streit1984] @@ -475,6 +475,19 @@ cola(hann, 1024, 512) // 0 — perfect STFT reconstruction - **`scallopLoss(fn, N, ...params)`** — worst-case amplitude error in dB between DFT bins. Rectangular = 3.92, Hann = 1.42, flat-top ≈ 0. - **`cola(fn, N, hop, ...params)`** — COLA deviation. 0 = perfect STFT reconstruction at given hop size. +## Migrating from v2 + +v3 is ESM-only — `require()` no longer works. 18 → 34 windows, subpath imports preserved. + +```diff +- const hann = require('window-function/hann') +- const apply = require('window-function/apply') ++ import hann from 'window-function/hann' ++ import { generate, apply } from 'window-function/util' +``` + +The per-sample API `fn(i, N, ...params) → number` is unchanged. + [^dolph1946]: C.L. Dolph, "A Current Distribution for Broadside Arrays," *Proc. IRE* 34, 1946. [^gabor1946]: D. Gabor, "Theory of Communication," *J. IEE* 93, 1946. [^bartlett1950]: M.S. Bartlett, "Periodogram Analysis and Continuous Spectra," *Biometrika* 37, 1950. diff --git a/bartlett.js b/bartlett.js index dd64505..63650ec 100644 --- a/bartlett.js +++ b/bartlett.js @@ -1,2 +1,3 @@ -let { abs } = Math +import { abs } from './util.js' export default function bartlett (i, N) { return 1 - abs((2 * i - N + 1) / (N - 1)) } +export { bartlett } diff --git a/bartlettHann.js b/bartlettHann.js index 546f0a8..30fe213 100644 --- a/bartlettHann.js +++ b/bartlettHann.js @@ -3,3 +3,4 @@ export default function bartlettHann (i, N) { let x = i / (N - 1) return 0.62 - 0.48 * abs(x - 0.5) - 0.38 * cos(PI2 * x) } +export { bartlettHann } diff --git a/blackman.js b/blackman.js index 55e069d..69aac6a 100644 --- a/blackman.js +++ b/blackman.js @@ -1,2 +1,3 @@ import { cosineSum } from './util.js' export default function blackman (i, N) { return cosineSum(i, N, [0.42, 0.5, 0.08]) } +export { blackman } diff --git a/blackmanHarris.js b/blackmanHarris.js index e34078d..78dd56f 100644 --- a/blackmanHarris.js +++ b/blackmanHarris.js @@ -1,2 +1,3 @@ import { cosineSum } from './util.js' export default function blackmanHarris (i, N) { return cosineSum(i, N, [0.35875, 0.48829, 0.14128, 0.01168]) } +export { blackmanHarris } diff --git a/blackmanNuttall.js b/blackmanNuttall.js index ce7d4ab..8bb13d6 100644 --- a/blackmanNuttall.js +++ b/blackmanNuttall.js @@ -1,2 +1,3 @@ import { cosineSum } from './util.js' export default function blackmanNuttall (i, N) { return cosineSum(i, N, [0.3635819, 0.4891775, 0.1365995, 0.0106411]) } +export { blackmanNuttall } diff --git a/bohman.js b/bohman.js index 2acfe66..5ee4525 100644 --- a/bohman.js +++ b/bohman.js @@ -4,3 +4,4 @@ export default function bohman (i, N) { if (a >= 1) return 0 return (1 - a) * cos(PI * a) + sin(PI * a) / PI } +export { bohman } diff --git a/cauchy.js b/cauchy.js index 857f86f..a8059f1 100644 --- a/cauchy.js +++ b/cauchy.js @@ -3,3 +3,4 @@ export default function cauchy (i, N, alpha) { let x = alpha * (2 * i - N + 1) / (N - 1) return 1 / (1 + x * x) } +export { cauchy } diff --git a/confinedGaussian.js b/confinedGaussian.js index 3666ec8..f3922ab 100644 --- a/confinedGaussian.js +++ b/confinedGaussian.js @@ -6,3 +6,4 @@ export default function confinedGaussian (i, N, sigmaT) { let gn = G(i), gh = G(-0.5) return gn - gh * (G(i + L) + G(i - L)) / (G(-0.5 + L) + G(-0.5 - L)) } +export { confinedGaussian } diff --git a/connes.js b/connes.js index f45ef0f..41ec025 100644 --- a/connes.js +++ b/connes.js @@ -1 +1,2 @@ export default function connes (i, N) { let x = (2 * i - N + 1) / (N - 1); return (1 - x * x) * (1 - x * x) } +export { connes } diff --git a/cosine.js b/cosine.js index 97dc966..9abfffe 100644 --- a/cosine.js +++ b/cosine.js @@ -1,2 +1,3 @@ import { sin, PI } from './util.js' export default function cosine (i, N) { return sin(PI * i / (N - 1)) } +export { cosine } diff --git a/dolphChebyshev.js b/dolphChebyshev.js index ceed89f..608ddbc 100644 --- a/dolphChebyshev.js +++ b/dolphChebyshev.js @@ -20,3 +20,4 @@ export default function dolphChebyshev (i, N, attenuation) { } return c._w[i] } +export { dolphChebyshev } diff --git a/dpss.js b/dpss.js index acddd35..6a937af 100644 --- a/dpss.js +++ b/dpss.js @@ -26,3 +26,4 @@ export default function dpss (i, N, W) { } return c._w[i] } +export { dpss } diff --git a/exactBlackman.js b/exactBlackman.js index ba5428c..1174677 100644 --- a/exactBlackman.js +++ b/exactBlackman.js @@ -1,2 +1,3 @@ import { cosineSum } from './util.js' export default function exactBlackman (i, N) { return cosineSum(i, N, [0.42659, 0.49656, 0.076849]) } +export { exactBlackman } diff --git a/exponential.js b/exponential.js index d54ae3a..3f36e2a 100644 --- a/exponential.js +++ b/exponential.js @@ -3,3 +3,4 @@ export default function exponential (i, N, tau) { if (tau == null) tau = 1 return exp(-abs(2 * i - N + 1) / (tau * (N - 1))) } +export { exponential } diff --git a/flatTop.js b/flatTop.js index a0ee74b..d5e548f 100644 --- a/flatTop.js +++ b/flatTop.js @@ -1,2 +1,3 @@ import { cosineSum } from './util.js' export default function flatTop (i, N) { return cosineSum(i, N, [1, 1.93, 1.29, 0.388, 0.028]) } +export { flatTop } diff --git a/gaussian.js b/gaussian.js index 500a85b..ff578f0 100644 --- a/gaussian.js +++ b/gaussian.js @@ -4,3 +4,4 @@ export default function gaussian (i, N, sigma) { let x = (2 * i - N + 1) / (sigma * (N - 1)) return exp(-0.5 * x * x) } +export { gaussian } diff --git a/generalizedNormal.js b/generalizedNormal.js index 42d9b6c..69b4670 100644 --- a/generalizedNormal.js +++ b/generalizedNormal.js @@ -5,3 +5,4 @@ export default function generalizedNormal (i, N, sigma, p) { let x = abs((2 * i - N + 1) / (sigma * (N - 1))) return exp(-0.5 * pow(x, p)) } +export { generalizedNormal } diff --git a/hamming.js b/hamming.js index 51b377a..b9c90bc 100644 --- a/hamming.js +++ b/hamming.js @@ -1,2 +1,3 @@ import { cos, PI2 } from './util.js' export default function hamming (i, N) { return 0.54 - 0.46 * cos(PI2 * i / (N - 1)) } +export { hamming } diff --git a/hann.js b/hann.js index 5fd4826..f68441f 100644 --- a/hann.js +++ b/hann.js @@ -1,2 +1,3 @@ import { cos, PI2 } from './util.js' export default function hann (i, N) { return 0.5 - 0.5 * cos(PI2 * i / (N - 1)) } +export { hann } diff --git a/hannPoisson.js b/hannPoisson.js index adf1669..2bbe25d 100644 --- a/hannPoisson.js +++ b/hannPoisson.js @@ -3,3 +3,4 @@ export default function hannPoisson (i, N, alpha) { if (alpha == null) alpha = 2 return 0.5 * (1 - cos(PI2 * i / (N - 1))) * exp(-alpha * abs(2 * i - N + 1) / (N - 1)) } +export { hannPoisson } diff --git a/kaiser.js b/kaiser.js index 54002a9..17f74db 100644 --- a/kaiser.js +++ b/kaiser.js @@ -4,3 +4,4 @@ export default function kaiser (i, N, beta) { let x = (2 * i - N + 1) / (N - 1) return i0(beta * sqrt(1 - x * x)) / i0(beta) } +export { kaiser } diff --git a/kaiserBesselDerived.js b/kaiserBesselDerived.js index c44e09c..85bfd06 100644 --- a/kaiserBesselDerived.js +++ b/kaiserBesselDerived.js @@ -1,6 +1,7 @@ import { sqrt, abs, i0 } from './util.js' export default function kaiserBesselDerived (i, N, beta) { if (beta == null) beta = 8.6 + if (N % 2 !== 0) throw new RangeError('kaiserBesselDerived: N must be even') let c = kaiserBesselDerived if (c._N !== N || c._b !== beta) { let h = N / 2, k = new Float64Array(h + 1), s = 0 @@ -13,3 +14,4 @@ export default function kaiserBesselDerived (i, N, beta) { } return c._w[i] } +export { kaiserBesselDerived } diff --git a/lanczos.js b/lanczos.js index a9f5aa4..8837a09 100644 --- a/lanczos.js +++ b/lanczos.js @@ -3,3 +3,4 @@ export default function lanczos (i, N) { let x = 2 * i / (N - 1) - 1 return x === 0 ? 1 : sin(PI * x) / (PI * x) } +export { lanczos } diff --git a/nuttall.js b/nuttall.js index 42c8300..5f1c809 100644 --- a/nuttall.js +++ b/nuttall.js @@ -1,2 +1,3 @@ import { cosineSum } from './util.js' export default function nuttall (i, N) { return cosineSum(i, N, [0.355768, 0.487396, 0.144232, 0.012604]) } +export { nuttall } diff --git a/package.json b/package.json index c435498..e21599c 100644 --- a/package.json +++ b/package.json @@ -51,9 +51,9 @@ }, "repository": { "type": "git", - "url": "git://github.com/scijs/window-function.git" + "url": "git://github.com/audiojs/window-function.git" }, - "homepage": "https://github.com/scijs/window-function", + "homepage": "https://github.com/audiojs/window-function", "keywords": [ "window-function", "window", diff --git a/parzen.js b/parzen.js index 182ebf4..7cd3d53 100644 --- a/parzen.js +++ b/parzen.js @@ -1,7 +1,8 @@ -let { abs } = Math +import { abs } from './util.js' export default function parzen (i, N) { let a = abs((2 * i - N + 1) / (N - 1)) if (a <= 0.5) return 1 - 6 * a * a * (1 - a) let b = 1 - a return 2 * b * b * b } +export { parzen } diff --git a/planckTaper.js b/planckTaper.js index 841aaf0..b371506 100644 --- a/planckTaper.js +++ b/planckTaper.js @@ -7,3 +7,4 @@ export default function planckTaper (i, N, epsilon) { if (i > (N - 1) - eN) return 1 / (1 + exp(eN / (N - 1 - i) - eN / (eN - N + 1 + i))) return 1 } +export { planckTaper } diff --git a/powerOfSine.js b/powerOfSine.js index 3f65e57..8abe739 100644 --- a/powerOfSine.js +++ b/powerOfSine.js @@ -3,3 +3,4 @@ export default function powerOfSine (i, N, alpha) { if (alpha == null) alpha = 2 return pow(sin(PI * i / (N - 1)), alpha) } +export { powerOfSine } diff --git a/rectangular.js b/rectangular.js index 4110b28..8cb1007 100644 --- a/rectangular.js +++ b/rectangular.js @@ -1 +1,2 @@ export default function rectangular () { return 1 } +export { rectangular } diff --git a/rifeVincent.js b/rifeVincent.js index a53cb13..062bf7a 100644 --- a/rifeVincent.js +++ b/rifeVincent.js @@ -11,3 +11,4 @@ export default function rifeVincent (i, N, order) { let peak = a.reduce((s, c) => s + c, 0) return v / peak } +export { rifeVincent } diff --git a/taylor.js b/taylor.js index ed50f11..e0d9738 100644 --- a/taylor.js +++ b/taylor.js @@ -25,3 +25,4 @@ export default function taylor (i, N, nbar, sll) { } return c._w[i] } +export { taylor } diff --git a/triangular.js b/triangular.js index f958032..c6e9354 100644 --- a/triangular.js +++ b/triangular.js @@ -1,2 +1,3 @@ -let { abs } = Math +import { abs } from './util.js' export default function triangular (i, N) { return 1 - abs((2 * i - N + 1) / N) } +export { triangular } diff --git a/tukey.js b/tukey.js index 578e470..d1baf88 100644 --- a/tukey.js +++ b/tukey.js @@ -7,3 +7,4 @@ export default function tukey (i, N, alpha) { if (i >= (N - 1) - half) return 0.5 * (1 + cos(PI * ((N - 1 - i) / half - 1))) return 1 } +export { tukey } diff --git a/ultraspherical.js b/ultraspherical.js index 654444f..91fa2e5 100644 --- a/ultraspherical.js +++ b/ultraspherical.js @@ -4,9 +4,9 @@ export default function ultraspherical (i, N, mu, xmu) { if (xmu == null) xmu = 1 let c = ultraspherical if (c._N !== N || c._mu !== mu || c._xmu !== xmu) { - let ord = N - 1, w = new Float64Array(N) + let ord = N - 1, w = new Float64Array(N), s0 = gegen(ord, mu, xmu) for (let n = 0; n < N; n++) { - let s = gegen(ord, mu, xmu) + let s = s0 for (let k = 1; k < N; k++) { let x = xmu * cos(PI * k / N) s += 2 * (k % 2 ? -1 : 1) * gegen(ord, mu, x) * cos(PI2 * k * n / N) @@ -17,3 +17,4 @@ export default function ultraspherical (i, N, mu, xmu) { } return c._w[i] } +export { ultraspherical } diff --git a/welch.js b/welch.js index 9d7207d..2c57765 100644 --- a/welch.js +++ b/welch.js @@ -1 +1,2 @@ export default function welch (i, N) { let x = (2 * i - N + 1) / (N - 1); return 1 - x * x } +export { welch } From 99d5da3cbce3c7e928eec0397823e743635b6310 Mon Sep 17 00:00:00 2001 From: "Dmitry Iv." Date: Thu, 26 Mar 2026 01:13:51 -0400 Subject: [PATCH 26/26] Update index.d.ts --- index.d.ts | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/index.d.ts b/index.d.ts index f192a68..c95b6f8 100644 --- a/index.d.ts +++ b/index.d.ts @@ -54,3 +54,39 @@ export function enbw(fn: WindowFunction, N: number, ...params: number[]): number export function scallopLoss(fn: WindowFunction, N: number, ...params: number[]): number; /** COLA deviation at given hop size. Returns 0 for perfect constant overlap-add. */ export function cola(fn: WindowFunction, N: number, hop: number, ...params: number[]): number; + +// Subpath default imports: import hann from 'window-function/hann' +declare module 'window-function/rectangular' { export default function rectangular(i: number, N: number): number; } +declare module 'window-function/triangular' { export default function triangular(i: number, N: number): number; } +declare module 'window-function/bartlett' { export default function bartlett(i: number, N: number): number; } +declare module 'window-function/welch' { export default function welch(i: number, N: number): number; } +declare module 'window-function/connes' { export default function connes(i: number, N: number): number; } +declare module 'window-function/hann' { export default function hann(i: number, N: number): number; } +declare module 'window-function/hamming' { export default function hamming(i: number, N: number): number; } +declare module 'window-function/cosine' { export default function cosine(i: number, N: number): number; } +declare module 'window-function/blackman' { export default function blackman(i: number, N: number): number; } +declare module 'window-function/exactBlackman' { export default function exactBlackman(i: number, N: number): number; } +declare module 'window-function/nuttall' { export default function nuttall(i: number, N: number): number; } +declare module 'window-function/blackmanNuttall' { export default function blackmanNuttall(i: number, N: number): number; } +declare module 'window-function/blackmanHarris' { export default function blackmanHarris(i: number, N: number): number; } +declare module 'window-function/flatTop' { export default function flatTop(i: number, N: number): number; } +declare module 'window-function/bartlettHann' { export default function bartlettHann(i: number, N: number): number; } +declare module 'window-function/lanczos' { export default function lanczos(i: number, N: number): number; } +declare module 'window-function/parzen' { export default function parzen(i: number, N: number): number; } +declare module 'window-function/bohman' { export default function bohman(i: number, N: number): number; } +declare module 'window-function/powerOfSine' { export default function powerOfSine(i: number, N: number, alpha?: number): number; } +declare module 'window-function/kaiser' { export default function kaiser(i: number, N: number, beta?: number): number; } +declare module 'window-function/gaussian' { export default function gaussian(i: number, N: number, sigma?: number): number; } +declare module 'window-function/generalizedNormal' { export default function generalizedNormal(i: number, N: number, sigma?: number, p?: number): number; } +declare module 'window-function/tukey' { export default function tukey(i: number, N: number, alpha?: number): number; } +declare module 'window-function/planckTaper' { export default function planckTaper(i: number, N: number, epsilon?: number): number; } +declare module 'window-function/exponential' { export default function exponential(i: number, N: number, tau?: number): number; } +declare module 'window-function/hannPoisson' { export default function hannPoisson(i: number, N: number, alpha?: number): number; } +declare module 'window-function/cauchy' { export default function cauchy(i: number, N: number, alpha?: number): number; } +declare module 'window-function/rifeVincent' { export default function rifeVincent(i: number, N: number, order?: number): number; } +declare module 'window-function/confinedGaussian' { export default function confinedGaussian(i: number, N: number, sigmaT?: number): number; } +declare module 'window-function/kaiserBesselDerived' { export default function kaiserBesselDerived(i: number, N: number, beta?: number): number; } +declare module 'window-function/dolphChebyshev' { export default function dolphChebyshev(i: number, N: number, attenuation?: number): number; } +declare module 'window-function/taylor' { export default function taylor(i: number, N: number, nbar?: number, sll?: number): number; } +declare module 'window-function/dpss' { export default function dpss(i: number, N: number, W?: number): number; } +declare module 'window-function/ultraspherical' { export default function ultraspherical(i: number, N: number, mu?: number, xmu?: number): number; }