Skip to content

Commit

Permalink
Merge pull request JuliaDSP#534 from wheeheee/df2tf
Browse files Browse the repository at this point in the history
add type as another possible argument to `DF2TFilter`
  • Loading branch information
wheeheee authored Feb 15, 2024
2 parents f0c67c6 + f121cab commit 9791404
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 14 deletions.
52 changes: 38 additions & 14 deletions src/Filters/filt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,18 @@ filt!(out, f::FilterCoefficients{:z}, x) = filt!(out, convert(SecondOrderSection

"""
DF2TFilter(coef[, si])
DF2TFilter(coef[, sitype::Type])
Construct a stateful direct form II transposed filter with
coefficients `coef`. `si` is an optional array representing the
initial filter state (defaults to zeros). If `f` is a
`PolynomialRatio`, `Biquad`, or `SecondOrderSections`,
coefficients `coef`.
One can optionally specify as the second argument either
- `si`, an array representing the initial filter state, or
- `sitype`, the eltype of a zeroed `si`
The initial filter state defaults to zeros if called with one argument.
If `f` is a `PolynomialRatio`, `Biquad`, or `SecondOrderSections`,
filtering is implemented directly. If `f` is a `ZeroPoleGain`
object, it is first converted to a `SecondOrderSections` object.
"""
Expand All @@ -148,10 +155,15 @@ struct DF2TFilter{T<:FilterCoefficients{:z},S<:Array}
end
end

DF2TFilter(coef::Union{PolynomialRatio{:z,T},Biquad{:z,T}}, state::Vector{S}) where {T,S} =
DF2TFilter{typeof(coef),Vector{S}}(coef, state)

DF2TFilter(coef::SecondOrderSections{:z,T,G}, state::Matrix{S}) where {T,G,S} =
DF2TFilter{SecondOrderSections{:z,T,G},Matrix{S}}(coef, state)

## PolynomialRatio
DF2TFilter(coef::PolynomialRatio{:z,T},
state::Vector{S}=zeros(T, max(length(coefa(coef)), length(coefb(coef)))-1)) where {T,S} =
DF2TFilter{PolynomialRatio{:z,T}, Vector{S}}(coef, state)
DF2TFilter(coef::PolynomialRatio{:z,T}, ::Type{V}=T) where {T,V} =
DF2TFilter(coef, zeros(promote_type(T, V), max(length(coefa(coef)), length(coefb(coef))) - 1))

function filt!(out::AbstractVector, f::DF2TFilter{<:PolynomialRatio,<:Vector}, x::AbstractVector)
length(x) != length(out) && throw(ArgumentError("out size must match x"))
Expand Down Expand Up @@ -183,9 +195,8 @@ function filt!(out::AbstractVector, f::DF2TFilter{<:PolynomialRatio,<:Vector}, x
end

## SecondOrderSections
DF2TFilter(coef::SecondOrderSections{:z,T,G},
state::Matrix{S}=zeros(promote_type(T, G), 2, length(coef.biquads))) where {T,G,S} =
DF2TFilter{SecondOrderSections{:z,T,G}, Matrix{S}}(coef, state)
DF2TFilter(coef::SecondOrderSections{:z,T,G}, ::Type{V}=T) where {T,G,V} =
DF2TFilter(coef, zeros(promote_type(T, G, V), 2, length(coef.biquads)))

function filt!(out::AbstractVector, f::DF2TFilter{<:SecondOrderSections,<:Matrix}, x::AbstractVector)
length(x) != length(out) && throw(ArgumentError("out size must match x"))
Expand All @@ -194,21 +205,34 @@ function filt!(out::AbstractVector, f::DF2TFilter{<:SecondOrderSections,<:Matrix
end

## Biquad
DF2TFilter(coef::Biquad{:z,T}, state::Vector{S}=zeros(T, 2)) where {T,S} =
DF2TFilter{Biquad{:z,T}, Vector{S}}(coef, state)
DF2TFilter(coef::Biquad{:z,T}, ::Type{V}=T) where {T,V} =
DF2TFilter(coef, zeros(promote_type(T, V), 2))

function filt!(out::AbstractVector, f::DF2TFilter{<:Biquad,<:Vector}, x::AbstractVector)
length(x) != length(out) && throw(ArgumentError("out size must match x"))
si = f.state
(si[1], si[2]) = _filt!(out, si[1], si[2], f.coef, x, 1)
out
end

# Variant that allocates the output
filt(f::DF2TFilter{<:FilterCoefficients{:z},<:Array{T}}, x::AbstractVector) where {T} =
filt!(Vector{T}(undef, length(x)), f, x)
"""
filt(f::DF2TFilter{<:FilterCoefficients{:z},<:Array{T}}, x::AbstractVector{V}) where {T,V}
Apply the [stateful filter](@ref stateful-filter-objects) `f` on `x`.
!!! warning
The output array has eltype `promote_type(T, V)`, where
`T` is the eltype of the filter state.\n
For more control over the output type, provide a preallocated
output array `out` to `filt!(out, f, x)`.
"""
filt(f::DF2TFilter{<:FilterCoefficients{:z},<:Array{T}}, x::AbstractVector{V}) where {T,V} =
filt!(Vector{promote_type(T, V)}(undef, length(x)), f, x)

# Fall back to SecondOrderSections
DF2TFilter(coef::FilterCoefficients{:z}) = DF2TFilter(convert(SecondOrderSections, coef))
DF2TFilter(coef::FilterCoefficients{:z}, arg::Union{Matrix,Type}) =
DF2TFilter(convert(SecondOrderSections, coef), arg)

#
# filtfilt
Expand Down
6 changes: 6 additions & 0 deletions test/filt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ using DSP, Test, Random, FilterTestHelpers
@test filt([0, 0, 1, 0.8], [1], [1; zeros(9)]) == [0; 0; 1; 0.8; zeros(6)]
@test filt(DF2TFilter(PolynomialRatio([1], [1, -0.5])), [1; zeros(9)]) 0.5.^(0:9)
@test filt([1], [1, -0.5], [1; zeros(9)]) 0.5.^(0:9)

# DF2TFilter{:z} state type selection for ZeroPoleGain (issue #371)
s = rand(30) + im * rand(30)
df = digitalfilter(Lowpass(0.25), Butterworth(4))
f = @test_nowarn DF2TFilter(df, ComplexF64)
@test_nowarn filt(f, s)
end

#
Expand Down

0 comments on commit 9791404

Please sign in to comment.