Skip to content

Commit 32fa8e3

Browse files
committed
Adds Kaiser window
1 parent c167891 commit 32fa8e3

File tree

1 file changed

+77
-0
lines changed

1 file changed

+77
-0
lines changed

lib/nx_signal/windows.ex

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,83 @@ defmodule NxSignal.Windows do
289289
end
290290
end
291291

292+
@doc """
293+
Creates a Kaiser window of size `window_length`.
294+
295+
The Kaiser window is a taper formed by using a Bessel function.
296+
297+
## Options
298+
299+
* `:n` - The window length. Mandatory option.
300+
* `:is_periodic` - If `true`, produces a periodic window,
301+
otherwise produces a symmetric window. Defaults to `true`
302+
* `:type` - the output type for the window. Defaults to `{:f, 32}`
303+
* `:beta` - Shape parameter for the window. As beta increases, the window becomes more focused in frequency domain. Defaults to 12.0.
304+
* `:axis_name` - the axis name. Defaults to `nil`
305+
306+
## Examples
307+
iex> NxSignal.Windows.kaiser(n: 4, beta: 12.0, is_periodic: true)
308+
#Nx.Tensor<
309+
f32[4]
310+
[5.2776191296288744e-5, 0.21566666662693024, 1.0, 0.21566666662693024]
311+
>
312+
313+
iex> NxSignal.Windows.kaiser(n: 5, beta: 12.0, is_periodic: true)
314+
#Nx.Tensor<
315+
f32[5]
316+
[5.2776191296288744e-5, 0.10171464085578918, 0.7929369807243347, 0.7929369807243347, 0.10171464085578918]
317+
>
318+
319+
iex> NxSignal.Windows.kaiser(n: 4, beta: 12.0, is_periodic: false)
320+
#Nx.Tensor<
321+
f32[4]
322+
[5.2776191296288744e-5, 0.5188394784927368, 0.5188390612602234, 5.2776191296288744e-5]
323+
>
324+
"""
325+
@doc type: :windowing
326+
defn kaiser(opts \\ []) do
327+
opts =
328+
keyword!(opts, [:n, :axis_name, eps: 1.0e-7, beta: 12.0, is_periodic: true, type: {:f, 32}])
329+
330+
{l, opts} = pop_window_size(opts)
331+
name = opts[:axis_name]
332+
type = opts[:type]
333+
beta = opts[:beta]
334+
eps = opts[:eps]
335+
is_periodic = opts[:is_periodic]
336+
337+
window_length = if is_periodic, do: l + 1, else: l
338+
339+
ratio = Nx.linspace(-1, 1, n: window_length, endpoint: true, type: type) |> Nx.rename([name])
340+
sqrt_arg = Nx.max(1 - ratio ** 2, eps)
341+
r = beta * Nx.sqrt(sqrt_arg)
342+
343+
window = kaiser_bessel_i0(r) / kaiser_bessel_i0(beta)
344+
345+
if is_periodic do
346+
Nx.slice(window, [0], [l])
347+
else
348+
window
349+
end
350+
end
351+
352+
defnp kaiser_bessel_i0(x) do
353+
abs_x = Nx.abs(x)
354+
355+
small_x_result =
356+
1 +
357+
abs_x ** 2 / 4 +
358+
abs_x ** 4 / 64 +
359+
abs_x ** 6 / 2304 +
360+
abs_x ** 8 / 147_456
361+
362+
large_x_result =
363+
Nx.exp(abs_x) / Nx.sqrt(2 * Nx.Constants.pi() * abs_x) *
364+
(1 + 1 / (8 * abs_x) + 9 / (128 * Nx.pow(abs_x, 2)))
365+
366+
Nx.select(abs_x < 3.75, small_x_result, large_x_result)
367+
end
368+
292369
deftransformp pop_window_size(opts) do
293370
{n, opts} = Keyword.pop(opts, :n)
294371

0 commit comments

Comments
 (0)