Skip to content

Commit

Permalink
Make SecondOrderSections(::ZeroPoleGain)more robust (#435)
Browse files Browse the repository at this point in the history
* Make `SecondOrderSections(::ZeroPoleGain)`more robust

The helper function `groupzp` assumes that both zeros and poles are
ordered such that complex entries appear in conjugate pairs
consecutively and further, that these pairs are orderer consistently:
Either always the positive imaginary part first, or always the negative
imaginary part first. The output of `split_real_complex` fulfills this.
However, for the poles, there is an intermediate sorting step which
apparently can interfere with this ordering under certain circumstances.
This can lead to a `BoundsError` (#432), but also to zeros which are not
conjugate pairs being grouped together, thereby silently producing wrong
results. While #433 was a band-aid preventing the `BoundsError`, this
should fix the underlying issue by integrating the sorting into
`split_real_complex` in such a way that the required ordering is
ensured.

* Bump version to 0.7.2
  • Loading branch information
martinholters authored Jun 21, 2021
1 parent 3d294e6 commit 6a904b3
Show file tree
Hide file tree
Showing 3 changed files with 13 additions and 14 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "DSP"
uuid = "717857b8-e6f2-59f4-9121-6e50c889abd2"
version = "0.7.1"
version = "0.7.2"

[deps]
Compat = "34da2185-b29b-5c13-b0c7-acf172513d20"
Expand Down
17 changes: 7 additions & 10 deletions src/Filters/coefficients.jl
Original file line number Diff line number Diff line change
Expand Up @@ -322,9 +322,6 @@ function groupzp(z, p)
groupedz[i] = splice!(z, closest_zero_idx)
if !isreal(groupedz[i])
i += 1
if closest_zero_idx > length(z)
closest_zero_idx = length(z)
end
groupedz[i] = splice!(z, closest_zero_idx)
end
i += 1
Expand All @@ -337,7 +334,7 @@ end
# Sort zeros or poles lexicographically (so that poles are adjacent to
# their conjugates). Handle repeated values. Split real and complex
# values into separate vectors. Ensure that each value has a conjugate.
function split_real_complex(x::Vector{T}) where T
function split_real_complex(x::Vector{T}; sortby=nothing) where T
# Get counts and store in a Dict
d = Dict{T,Int}()
for v in x
Expand All @@ -349,7 +346,11 @@ function split_real_complex(x::Vector{T}) where T

c = T[]
r = typeof(real(zero(T)))[]
for k in keys(d)
ks = collect(keys(d))
if sortby !== nothing
sort!(ks, by=sortby)
end
for k in ks
if imag(k) != 0
if !haskey(d, conj(k)) || d[k] != d[conj(k)]
# No match for conjugate
Expand Down Expand Up @@ -381,13 +382,9 @@ function SecondOrderSections{D,T,G}(f::ZeroPoleGain{D,Z,P}) where {D,T,G,Z,P}
# Split real and complex poles
(complexz, realz, matched) = split_real_complex(z)
matched || throw(ArgumentError("complex zeros could not be matched to their conjugates"))
(complexp, realp, matched) = split_real_complex(p)
(complexp, realp, matched) = split_real_complex(p; sortby=x->abs(abs(x) - 1))
matched || throw(ArgumentError("complex poles could not be matched to their conjugates"))

# Sort poles according to distance to unit circle (nearest first)
sort!(complexp, by=x->abs(abs(x) - 1))
sort!(realp, by=x->abs(abs(x) - 1))

# Group complex poles with closest complex zeros
z1, p1 = groupzp(complexz, complexp)
# Group real poles with remaining complex zeros
Expand Down
8 changes: 5 additions & 3 deletions test/filter_conversion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,9 @@ end
Fsamp = 180
responsetype = Bandpass(bp1, bp2; fs = Fsamp)
designmethod = Elliptic(11, 0.25, 40)
bpass = digitalfilter(responsetype, designmethod)
H = SecondOrderSections(bpass) # this shouldn't throw
@test H isa SecondOrderSections
H = digitalfilter(responsetype, designmethod)
H′ = ZeroPoleGain(SecondOrderSections(H))
@test sort(H.p, by=z->(real(z), imag(z))) sort(H′.p, by=z->(real(z), imag(z)))
@test sort(H.z, by=z->(real(z), imag(z))) sort(H′.z, by=z->(real(z), imag(z)))
@test H.k H′.k
end

2 comments on commit 6a904b3

@martinholters
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/39316

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.7.2 -m "<description of version>" 6a904b3b3efed5557f4c4b6391c931a23c807514
git push origin v0.7.2

Please sign in to comment.