Skip to content

Commit dcef684

Browse files
authored
Fix _polyprep, PolynomialRatio, for Number args (#571)
1 parent 4ff6208 commit dcef684

File tree

6 files changed

+60
-42
lines changed

6 files changed

+60
-42
lines changed

docs/src/internals.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ DSP.Windows.padplot
1111
DSP._zeropad
1212
DSP._zeropad!
1313
DSP._zeropad_keep_offset
14+
DSP.Filters._polyprep
1415
DSP.Filters.freq_eval
1516
DSP.Filters.build_grid
1617
DSP.Filters.lagrange_interp

src/Filters/coefficients.jl

Lines changed: 45 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,13 @@ ZeroPoleGain{D}(z::Vector{Z}, p::Vector{P}, k::K) where {D,Z<:Number,P<:Number,K
3434
Base.promote_rule(::Type{ZeroPoleGain{D,Z1,P1,K1}}, ::Type{ZeroPoleGain{D,Z2,P2,K2}}) where {D,Z1,P1,K1,Z2,P2,K2} =
3535
ZeroPoleGain{D,promote_type(Z1,Z2),promote_type(P1,P2),promote_type(K1,K2)}
3636

37-
*(f::ZeroPoleGain{D}, g::Number) where {D} = ZeroPoleGain{D}(f.z, f.p, f.k*g)
38-
*(g::Number, f::ZeroPoleGain{D}) where {D} = ZeroPoleGain{D}(f.z, f.p, f.k*g)
37+
*(f::ZeroPoleGain{D}, g::Number) where {D} = ZeroPoleGain{D}(f.z, f.p, f.k * g)
38+
*(g::Number, f::ZeroPoleGain{D}) where {D} = ZeroPoleGain{D}(f.z, f.p, f.k * g)
3939
*(f1::ZeroPoleGain{D}, f2::ZeroPoleGain{D}) where {D} =
40-
ZeroPoleGain{D}([f1.z; f2.z], [f1.p; f2.p], f1.k*f2.k)
40+
ZeroPoleGain{D}([f1.z; f2.z], [f1.p; f2.p], f1.k * f2.k)
4141
*(f1::ZeroPoleGain{D}, fs::ZeroPoleGain{D}...) where {D} =
4242
ZeroPoleGain{D}(vcat(f1.z, map(f -> f.z, fs)...), vcat(f1.p, map(f -> f.p, fs)...),
43-
f1.k*prod(f.k for f in fs))
43+
f1.k * prod(f.k for f in fs))
4444

4545
Base.inv(f::ZeroPoleGain{D}) where {D} = ZeroPoleGain{D}(f.p, f.z, inv(f.k))
4646

@@ -56,9 +56,9 @@ end
5656
# Transfer function form
5757
#
5858

59-
function shiftpoly(p::LaurentPolynomial{T}, i::Integer) where {T<:Number}
59+
function shiftpoly(p::LaurentPolynomial{T,D}, i::Integer) where {T<:Number,D}
6060
if !iszero(i)
61-
return p * LaurentPolynomial([one(T)], i, indeterminate(p))
61+
return p * LaurentPolynomial{T,D}([one(T)], i)
6262
end
6363
return p
6464
end
@@ -122,20 +122,35 @@ PolynomialRatio{:s, Int64}(LaurentPolynomial(3 + 2*s + s²), LaurentPolynomial(4
122122
123123
"""
124124
PolynomialRatio(b, a) = PolynomialRatio{:z}(b, a)
125-
function PolynomialRatio{:z}(b::LaurentPolynomial{T1}, a::LaurentPolynomial{T2}) where {T1,T2}
126-
return PolynomialRatio{:z,typeof(one(T1)/one(T2))}(b, a)
127-
end
128-
function PolynomialRatio{:s}(b::LaurentPolynomial{T1}, a::LaurentPolynomial{T2}) where {T1,T2}
129-
return PolynomialRatio{:s,promote_type(T1, T2)}(b, a)
130-
end
125+
const PolynomialRatioArgTs{T} = Union{T,Vector{T},LaurentPolynomial{T}} where {T<:Number}
126+
PolynomialRatio{:z}(b::PolynomialRatioArgTs{T1}, a::PolynomialRatioArgTs{T2}) where {T1<:Number,T2<:Number} =
127+
PolynomialRatio{:z,typeof(one(T1) / one(T2))}(b, a)
128+
PolynomialRatio{:s}(b::PolynomialRatioArgTs{T1}, a::PolynomialRatioArgTs{T2}) where {T1<:Number,T2<:Number} =
129+
PolynomialRatio{:s,promote_type(T1, T2)}(b, a)
130+
131+
"""
132+
_polyprep(D::Symbol, x::Union{T,Vector{T}}, ::Type) where {T<:Number}
133+
134+
Converts `x` to polynomial form. If x is a `Number`, it has to be converted into
135+
a `Vector`, otherwise `LaurentPolynomial` dispatch goes into stack overflow
136+
trying to collect a 0-d array into a `Vector`.
131137
132-
# The DSP convention for Laplace domain is highest power first. The Polynomials.jl
133-
# convention is lowest power first.
134-
_polyprep(D::Symbol, x, T...) = LaurentPolynomial{T...}(reverse(x), D === :z ? -length(x)+1 : 0, D)
135-
PolynomialRatio{D,T}(b::Union{Number,Vector{<:Number}}, a::Union{Number,Vector{<:Number}}) where {D,T} =
136-
PolynomialRatio{D,T}(_polyprep(D, b, T), _polyprep(D, a, T))
137-
PolynomialRatio{D}(b::Union{Number,Vector{<:Number}}, a::Union{Number,Vector{<:Number}}) where {D} =
138-
PolynomialRatio{D}(_polyprep(D, b), _polyprep(D, a))
138+
!!! warning
139+
140+
The DSP convention for Laplace domain is highest power first.\n
141+
The Polynomials.jl convention is lowest power first.
142+
"""
143+
@inline _polyprep(D::Symbol, x::Union{T,Vector{T}}, ::Type{V}) where {T<:Number,V} =
144+
LaurentPolynomial{V,D}(x isa Vector ? reverse(x) : [x], D === :z ? -length(x) + 1 : 0)
145+
146+
function PolynomialRatio{:z,T}(b::Union{Number,Vector{<:Number}}, a::Union{Number,Vector{<:Number}}) where {T<:Number}
147+
if isempty(a) || iszero(a[1])
148+
throw(ArgumentError("filter must have non-zero leading denominator coefficient"))
149+
end
150+
return PolynomialRatio{:z,T}(_polyprep(:z, b / a[1], T), _polyprep(:z, a / a[1], T))
151+
end
152+
PolynomialRatio{:s,T}(b::Union{Number,Vector{<:Number}}, a::Union{Number,Vector{<:Number}}) where {T<:Number} =
153+
PolynomialRatio{:s,T}(_polyprep(:s, b, T), _polyprep(:s, a, T))
139154

140155
PolynomialRatio{D,T}(f::PolynomialRatio{D}) where {D,T} = PolynomialRatio{D,T}(f.b, f.a)
141156
PolynomialRatio{D}(f::PolynomialRatio{D,T}) where {D,T} = PolynomialRatio{D,T}(f)
@@ -159,16 +174,14 @@ function ZeroPoleGain{D}(f::PolynomialRatio{D,T}) where {D,T}
159174
return ZeroPoleGain{D}(z, p, f.b[end]/f.a[end])
160175
end
161176

162-
*(f::PolynomialRatio{D}, g::Number) where {D} = PolynomialRatio{D}(g*f.b, f.a)
163-
*(g::Number, f::PolynomialRatio{D}) where {D} = PolynomialRatio{D}(g*f.b, f.a)
177+
*(f::PolynomialRatio{D}, g::Number) where {D} = PolynomialRatio{D}(g * f.b, f.a)
178+
*(g::Number, f::PolynomialRatio{D}) where {D} = PolynomialRatio{D}(g * f.b, f.a)
164179
*(f1::PolynomialRatio{D}, f2::PolynomialRatio{D}) where {D} =
165-
PolynomialRatio{D}(f1.b*f2.b, f1.a*f2.a)
180+
PolynomialRatio{D}(f1.b * f2.b, f1.a * f2.a)
166181
*(f1::PolynomialRatio{D}, fs::PolynomialRatio{D}...) where {D} =
167-
PolynomialRatio{D}(f1.b*prod(f.b for f in fs), f1.a*prod(f.a for f in fs))
182+
PolynomialRatio{D}(f1.b * prod(f.b for f in fs), f1.a * prod(f.a for f in fs))
168183

169-
Base.inv(f::PolynomialRatio{D}) where {D} = begin
170-
PolynomialRatio{D}(f.a, f.b)
171-
end
184+
Base.inv(f::PolynomialRatio{D}) where {D} = PolynomialRatio{D}(f.a, f.b)
172185

173186
function Base.:^(f::PolynomialRatio{D,T}, e::Integer) where {D,T}
174187
if e < 0
@@ -312,7 +325,7 @@ function Biquad{D,T}(f::SecondOrderSections{D}) where {D,T}
312325
if length(f.biquads) != 1
313326
throw(ArgumentError("only a single second order section may be converted to a biquad"))
314327
end
315-
Biquad{D,T}(f.biquads[1]*f.g)
328+
Biquad{D,T}(f.biquads[1] * f.g)
316329
end
317330
Biquad{D}(f::SecondOrderSections{D,T,G}) where {D,T,G} = Biquad{D,promote_type(T,G)}(f)
318331

@@ -418,7 +431,7 @@ function SecondOrderSections{D,T,G}(f::ZeroPoleGain{D,Z,P}) where {D,T,G,Z,P}
418431
npairs = length(groupedp) >> 1
419432
odd = isodd(n)
420433
for i = 1:npairs
421-
pairidx = 2*(npairs-i)
434+
pairidx = 2 * (npairs - i)
422435
biquads[odd+i] = convert(Biquad, ZeroPoleGain{D}(groupedz[pairidx+1:min(pairidx+2, length(groupedz))],
423436
groupedp[pairidx+1:pairidx+2], one(T)))
424437
end
@@ -439,12 +452,12 @@ SecondOrderSections{D,T,G}(f::Biquad{D}) where {D,T,G} = SecondOrderSections{D,T
439452
SecondOrderSections{D}(f::Biquad{D,T}) where {D,T} = SecondOrderSections{D,T,Int}(f)
440453
SecondOrderSections{D}(f::FilterCoefficients{D}) where {D} = SecondOrderSections{D}(ZeroPoleGain(f))
441454

442-
*(f::SecondOrderSections{D}, g::Number) where {D} = SecondOrderSections{D}(f.biquads, f.g*g)
443-
*(g::Number, f::SecondOrderSections{D}) where {D} = SecondOrderSections{D}(f.biquads, f.g*g)
455+
*(f::SecondOrderSections{D}, g::Number) where {D} = SecondOrderSections{D}(f.biquads, f.g * g)
456+
*(g::Number, f::SecondOrderSections{D}) where {D} = SecondOrderSections{D}(f.biquads, f.g * g)
444457
*(f1::SecondOrderSections{D}, f2::SecondOrderSections{D}) where {D} =
445-
SecondOrderSections{D}([f1.biquads; f2.biquads], f1.g*f2.g)
458+
SecondOrderSections{D}([f1.biquads; f2.biquads], f1.g * f2.g)
446459
*(f1::SecondOrderSections{D}, fs::SecondOrderSections{D}...) where {D} =
447-
SecondOrderSections{D}(vcat(f1.biquads, map(f -> f.biquads, fs)...), f1.g*prod(f.g for f in fs))
460+
SecondOrderSections{D}(vcat(f1.biquads, map(f -> f.biquads, fs)...), f1.g * prod(f.g for f in fs))
448461

449462
*(f1::Biquad{D}, f2::Biquad{D}) where {D} = SecondOrderSections{D}([f1, f2], 1)
450463
*(f1::Biquad{D}, fs::Biquad{D}...) where {D} = SecondOrderSections{D}([f1, fs...], 1)

src/Filters/remez_fir.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,8 +333,8 @@ Here we compute the frequency responses and plot them in dB.
333333
334334
```julia-repl
335335
julia> using PyPlot
336-
julia> b = DSP.Filters.PolynomialRatio(bpass, [1.0])
337-
julia> b2 = DSP.Filters.PolynomialRatio(bpass2, [1.0])
336+
julia> b = PolynomialRatio(bpass, [1.0])
337+
julia> b2 = PolynomialRatio(bpass2, [1.0])
338338
julia> f = range(0, stop=0.5, length=1000)
339339
julia> plot(f, 20*log10.(abs.(freqresp(b,f,1.0))))
340340
julia> plot(f, 20*log10.(abs.(freqresp(b2,f,1.0))))

test/filt.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ end
138138
b = [0.4, 1]
139139
z = [0.4750]
140140
x = readdlm(joinpath(dirname(@__FILE__), "data", "spectrogram_x.txt"),'\t')
141-
DSP.Filters.filt!(vec(x), b, a, vec(x), z)
141+
filt!(vec(x), b, a, vec(x), z)
142142

143143
@test matlab_filt x
144144
end
@@ -272,7 +272,7 @@ end
272272
@testset "fir_filtfilt" begin
273273
b = randn(10)
274274
for x in (randn(100), randn(100, 2))
275-
@test DSP.Filters.filtfilt(b, x) DSP.Filters.iir_filtfilt(b, [1.0], x)
276-
@test DSP.Filters.filtfilt(b, [2.0], x) DSP.Filters.iir_filtfilt(b, [2.0], x)
275+
@test filtfilt(b, x) DSP.Filters.iir_filtfilt(b, [1.0], x)
276+
@test filtfilt(b, [2.0], x) DSP.Filters.iir_filtfilt(b, [2.0], x)
277277
end
278278
end

test/filter_conversion.jl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,11 +249,15 @@ end
249249
@test_throws InexactError PolynomialRatio{:z,Int}([1, 2], [3, 4])
250250
# throws because normalization is impossible for a0 = 0
251251
@test_throws ArgumentError PolynomialRatio{:z,Float64}([1.0, 2.0], [0.0, 4.0])
252+
# throws because a0 = 0, in inner constructor
253+
@test_throws ArgumentError inv(PolynomialRatio{:z,Float64}(0, 1))
252254
# does not normalize, uses type from input
253255
@test @inferred(PolynomialRatio{:s}([1, 2], [3, 4])) isa PolynomialRatio{:s,Int}
254256
# throws because denominator must not be zero
255257
@test_throws ArgumentError PolynomialRatio{:s}([1.0, 2.0], [0.0])
256258
@test_throws ArgumentError PolynomialRatio{:s}([1.0, 2.0], Float64[])
259+
# test PolynomialRatio{:s} constructor for LaurentPolynomial input
260+
@test inv(PolynomialRatio{:s}(1.0, 2.3)).a == PolynomialRatio{:s}(1.0, 2.3).b
257261
end
258262

259263
@testset "misc" begin
@@ -286,10 +290,10 @@ end
286290
f = convert(PolynomialRatio, Biquad(2.0, 0.0, 0.0, 0.0, 0.0))
287291
@test coefb(f) == [2.0]
288292
@test coefa(f) == [1.0]
289-
@test convert(Biquad, PolynomialRatio([4.0], [2.0])) == Biquad(2.0, 0.0, 0.0, 0.0, 0.0)
293+
@test convert(Biquad, PolynomialRatio(4.0, 2.0)) == Biquad(2.0, 0.0, 0.0, 0.0, 0.0)
290294
@test Biquad(2.0, 0.0, 0.0, 0.0, 0.0)*2 == Biquad(4.0, 0.0, 0.0, 0.0, 0.0)
291295
@test convert(Biquad{:z,Float64}, f1) == convert(Biquad, f1)
292-
f = PolynomialRatio(Float64[1.0], Float64[1.0])
296+
f = PolynomialRatio(1.0, 1.0) # doubles as test for Number arguments (PR #571)
293297

294298
@test_throws ArgumentError convert(SecondOrderSections, ZeroPoleGain([0.5 + 0.5im, 0.5 + 0.5im], [0.5 + 0.5im, 0.5 - 0.5im], 1))
295299
@test_throws ArgumentError convert(SecondOrderSections, ZeroPoleGain([0.5 + 0.5im, 0.5 - 0.5im], [0.5 + 0.5im, 0.5 + 0.5im], 1))

test/remez_fir.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,19 +163,19 @@ end
163163
#
164164
@testset "inverse_sinc_response_function" begin
165165
L = 64
166-
166+
167167
Fs = 4800*L
168168
f = range(0, stop=0.5, length=10000)
169169

170170
P =*f*Fs/4800) ./ sin.(π*f*Fs/4800)
171171
Pdb = 20*log10.(abs.(P))
172172
Pdb[1] = 0.0
173-
173+
174174
g_vec = remez(201, [
175175
( 0.0, 2880.0) => (f -> (f==0) ? 1.0 : abs.((π*f/4800) ./ sin.(π*f/4800)), 1.0),
176176
(10000.0, Fs/2) => (0.0, 100.0)
177177
]; Hz=Fs)
178-
g = DSP.Filters.PolynomialRatio(g_vec, [1.0])
178+
g = PolynomialRatio(g_vec, [1.0])
179179
Gdb = 20*log10.(abs.(freqresp(g, 2π*f)))
180180

181181
passband_indices = (f*Fs) .< 2880.0

0 commit comments

Comments
 (0)