Skip to content

Commit 951e98e

Browse files
add Dirichlet kernel diric (#412)
* add diric Co-authored-by: Martin Holters <martin.holters@hsu-hh.de>
1 parent 82ac47a commit 951e98e

File tree

6 files changed

+94
-1
lines changed

6 files changed

+94
-1
lines changed

Project.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "DSP"
22
uuid = "717857b8-e6f2-59f4-9121-6e50c889abd2"
3-
version = "0.7.2"
3+
version = "0.7.3"
44

55
[deps]
66
Compat = "34da2185-b29b-5c13-b0c7-acf172513d20"

docs/src/util.md

+1
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,5 @@ shiftsignal
2323
shiftsignal!
2424
alignsignals
2525
alignsignals!
26+
diric
2627
```

src/DSP.jl

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ include("periodograms.jl")
2020
include("Filters/Filters.jl")
2121
include("lpc.jl")
2222
include("estimation.jl")
23+
include("diric.jl")
2324

2425
using Reexport
2526
@reexport using .Util, .Windows, .Periodograms, .Filters, .LPC, .Unwrap, .Estimation

src/diric.jl

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
export diric
2+
3+
"""
4+
kernel = diric(Ω::Real, n::Integer)
5+
6+
Dirichlet kernel, also known as periodic sinc function,
7+
where `n` should be a positive integer.
8+
Returns `sin(Ω * n/2) / (n * sin(Ω / 2))` typically,
9+
but `±1` when `Ω` is a multiple of 2π.
10+
11+
In the usual case where 'n' is odd, the output is equivalent to
12+
``1/n \\sum_{k=-(n-1)/2}^{(n-1)/2} e^{i k Ω}``,
13+
which is the discrete-time Fourier transform (DTFT)
14+
of a `n`-point moving average filter.
15+
16+
When `n` is odd or even, the function is 2π or 4π periodic, respectively.
17+
The formula for general `n` is
18+
`diric(Ω,n) = ``e^{-i (n-1) Ω/2}/n \\sum_{k=0}^{n-1} e^{i k Ω}``,
19+
which is a real and symmetric function of `Ω`.
20+
21+
As of 2021-03-19, the Wikipedia definition has different factors.
22+
The definition here is consistent with scipy and other software frameworks.
23+
24+
# Examples
25+
26+
```jldoctest
27+
julia> round.(diric.((-2:0.5:2)*π, 5), digits=9)'
28+
1×9 adjoint(::Vector{Float64}) with eltype Float64:
29+
1.0 -0.2 0.2 -0.2 1.0 -0.2 0.2 -0.2 1.0
30+
31+
julia> diric(0, 4)
32+
1.0
33+
```
34+
"""
35+
function diric::T, n::Integer) where T <: AbstractFloat
36+
n > 0 || throw(ArgumentError("n=$n not positive"))
37+
sign = one(T)
38+
if isodd(n)
39+
Ω = rem2pi(Ω, RoundNearest) # [-π,π)
40+
else # wrap to interval [-π,π) to improve precision near ±2π
41+
Ω = 2 * rem2pi/2, RoundNearest) # [-2π,2π)
42+
if Ω > π # [π,2π)
43+
sign = -one(T)
44+
Ω -= T(2π) # [-π,0)
45+
elseif Ω < -π # [-2π,-π)
46+
sign = -one(T)
47+
Ω += T(2π) # [0,π)
48+
end
49+
end
50+
51+
denom = sin/ 2)
52+
atol = eps(T)
53+
if abs(denom) atol # denom ≈ 0 ?
54+
return sign
55+
end
56+
57+
return sign * sin* n/2) / (n * denom) # typical case
58+
end
59+
60+
# handle non AbstractFloat types (e.g., Int, Rational, π)
61+
diric::Real, n::Integer) = diric(float(Ω), n)

test/diric.jl

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# diric.jl Dirichlet kernel tests
2+
3+
@testset "diric" begin
4+
@test_throws ArgumentError diric(0, -2)
5+
@test @inferred(diric(0, 4)) 1
6+
@test @inferred(diric(0 // 1, 5)) == 1
7+
@test @inferred(diric(4π, 4)) 1
8+
@test @inferred(diric(2π, 4)) -1
9+
@test @inferred(diric(0, 5)) 1
10+
@test @inferred(diric(2π, 5)) 1
11+
@test @inferred(diric(π, 5)) 1 // 5
12+
@test @inferred(diric/2, 5)) diric(-π/2, 5) -0.2
13+
@test abs(@inferred(diric/2, 4))) < eps()
14+
@test abs(@inferred(diric(3π/2, 4))) < eps()
15+
16+
# check type inference
17+
@inferred diric(2, 4)
18+
@inferred diric(Float16(1), 4)
19+
@inferred diric(0.1f0, 4)
20+
@inferred diric(0., 4)
21+
@inferred diric(BigFloat(1), 4)
22+
23+
# accuracy test near 2π for even n
24+
dirics(Ω,n) = real(exp(-1im * (n-1) * Ω/2)/n * sum(exp.(1im*(0:(n-1))*Ω)))
25+
Ω = 2π .+ 4π * (-101:2:101)/100 * 1e-9
26+
n = 400
27+
@test dirics.(Ω,n) diric.(Ω,n)
28+
end

test/runtests.jl

+2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
using DSP, FFTW, Test
2+
23
using DSP: allocate_output
34
using Random: seed!
45

56
testfiles = ["dsp.jl", "util.jl", "windows.jl", "filter_conversion.jl",
7+
"diric.jl",
68
"filter_design.jl", "filter_response.jl", "filt.jl", "filt_stream.jl",
79
"periodograms.jl", "multitaper.jl", "resample.jl", "lpc.jl", "estimation.jl", "unwrap.jl",
810
"remez_fir.jl" ]

0 commit comments

Comments
 (0)