Skip to content

Commit 376b6be

Browse files
committed
add transfer function block
1 parent a5de747 commit 376b6be

File tree

6 files changed

+130
-11
lines changed

6 files changed

+130
-11
lines changed

Project.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,14 @@ OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
1212
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
1313
StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3"
1414

15+
[weakdeps]
16+
ControlSystemsBase = "aaaaaaaa-a6ca-5380-bf3e-84a91bcd477e"
17+
18+
[extensions]
19+
ModelingToolkitSampledDataControlSystemsBaseExt = ["ControlSystemsBase"]
20+
1521
[compat]
22+
ControlSystemsBase = "1"
1623
DiffEqCallbacks = "~3.8"
1724
JuliaSimCompiler = "0.1.19"
1825
ModelingToolkit = "9"

docs/src/blocks.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- [`Difference`](@ref)
1010
- [`DiscreteDerivative`](@ref)
1111
- [`DiscreteIntegrator`](@ref)
12+
- [`DiscreteTransferFunction`](@ref)
1213
- [`Sampler`](@ref)
1314
- [`ZeroOrderHold`](@ref)
1415

@@ -24,6 +25,7 @@
2425

2526
## Discrete-time filters
2627
- [`ExponentialFilter`](@ref)
28+
- [`DiscreteTransferFunction`](@ref)
2729

2830

2931
## Docstrings
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
module ModelingToolkitSampledDataControlSystemsBaseExt
2+
using ModelingToolkitSampledData
3+
using ControlSystemsBase: TransferFunction, issiso, numvec, denvec, Discrete
4+
5+
6+
"""
7+
ModelingToolkitSampledData.DiscreteTransferFunction(G::TransferFunction{<:Discrete}; kwargs...)
8+
9+
Create a DiscreteTransferFunction from a `ControlSystems.TransferFunction`.
10+
11+
The sample time of `G` is used to create a periodic `Clock` object with which the transfer funciton is associated. If this is not desired, pass a custom `ShiftIndex` via the `z` keyword argument. To let the transfer function inherit the sample time of of the surrounding context (clock inference), pass and empty `z = ShiftIndex()`.
12+
"""
13+
function ModelingToolkitSampledData.DiscreteTransferFunction(G::TransferFunction{<:Discrete}; z = nothing, kwargs...)
14+
issiso(G) || throw(ArgumentError("Only SISO systems are supported"))
15+
b,a = numvec(G)[], denvec(G)[]
16+
if z === nothing
17+
z = ShiftIndex(Clock(G.Ts))
18+
end
19+
return DiscreteTransferFunction(b, a; z, kwargs...)
20+
end
21+
22+
end

src/ModelingToolkitSampledData.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export DiscreteIntegrator, DiscreteDerivative, Delay, Difference, ZeroOrderHold,
99
DiscretePIDParallel, DiscretePIDStandard, DiscreteStateSpace,
1010
DiscreteTransferFunction, NormalNoise, UniformNoise, Quantization,
1111
ExponentialFilter
12+
export DiscreteTransferFunction
1213
export DiscreteOnOffController
1314
include("discrete_blocks.jl")
1415

src/discrete_blocks.jl

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -718,25 +718,57 @@ With the coeffiencents specified in decreasing orders of ``z``, i.e., ``b = [b_{
718718
- `output`: Output signal
719719
720720
# Extended help:
721-
This component supports SISO systems only. To simulate MIMO transfer functions, use [ControlSystemsBase.jl](https://juliacontrol.github.io/ControlSystems.jl/stable/man/creating_systems/) to convert the transfer function to a statespace system, optionally compute a minimal realization using [`minreal`](https://juliacontrol.github.io/ControlSystems.jl/stable/lib/constructors/#ControlSystemsBase.minreal), and then use a [`DiscreteStateSpace`](@ref) component instead.
721+
This component supports SISO systems only. To simulate MIMO transfer functions, use [ControlSystemsBase.jl](https://juliacontrol.github.io/ControlSystems.jl/stable/man/creating_systems/) to convert the transfer function to a statespace system.
722722
723-
See also [ControlSystemsMTK.jl](https://juliacontrol.github.io/ControlSystemsMTK.jl/stable/) for an interface between [ControlSystems.jl](https://juliacontrol.github.io/ControlSystems.jl/stable/) and ModelingToolkit.jl for advanced manipulation of transfer functions and linear statespace systems. For linearization, see [`linearize`](@ref) and [Linear Analysis](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/linear_analysis/).
723+
See also [ControlSystemsMTK.jl](https://juliacontrol.github.io/ControlSystemsMTK.jl/stable/) for an interface between [ControlSystems.jl](https://juliacontrol.github.io/ControlSystems.jl/stable/) and ModelingToolkit.jl for advanced manipulation of transfer functions and linear statespace systems.
724724
"""
725+
@component function DiscreteTransferFunction(; a = [1], b = [1],
726+
verbose = true,
727+
z = ShiftIndex(),
728+
name,
729+
)
730+
na = length(a)
731+
nb = length(b)
732+
verbose && nb > na && @warn "DiscreteTransferFunction: Numerator degree is larger than denominator degree, this is not a proper transfer function. Simulation of a model including this transfer funciton will require at least $(nb-na) samples additional delay in series. Silence this warning with verbose=false"
733+
@parameters begin
734+
(b[1:nb] = b), [description = "Numerator coefficients of transfer function (e.g., z - 1 is specified as [1,-1])"]
735+
(a[1:na] = a), [description = "Denominator coefficients of transfer function (e.g., z^2 - 0.78z + 0.37 is specified as [1, -0.78, 0.37])"]
736+
end
737+
a = collect(a)
738+
b = collect(b)
739+
systems = @named begin
740+
input = RealInput()
741+
output = RealOutput()
742+
end
743+
@variables begin
744+
(u(t) = 0.0), [description = "Input flowing through connector `input`"]
745+
(y(t) = 0.0), [description = "Output flowing through connector `output`"]
746+
end
747+
equations = [
748+
sum(y(z-k+1) * a[k] for k in 1:na) ~ sum(u(z-k+1) * b[k] for k in 1:nb)
749+
input.u ~ u
750+
output.u ~ y
751+
]
752+
return compose(ODESystem(equations, t, [y,u], [b; a]; name), systems)
753+
end
754+
755+
DiscreteTransferFunction(b, a; kwargs...) = DiscreteTransferFunction(; b, a, kwargs...)
756+
725757
# @mtkmodel DiscreteTransferFunction begin
726758
# @parameters begin
727-
# b = [1], [description = "Numerator coefficients of transfer function (e.g., z - 1 is specified as [1,-1])"]
728-
# a = [1], [description = "Denominator coefficients of transfer function (e.g., z^2 - 0.78z + 0.37 is specified as [1, -0.78, 0.37])"]
759+
# b[1:1] = [1], [description = "Numerator coefficients of transfer function (e.g., z - 1 is specified as [1,-1])"]
760+
# a[1:1] = [1], [description = "Denominator coefficients of transfer function (e.g., z^2 - 0.78z + 0.37 is specified as [1, -0.78, 0.37])"]
729761
# end
730762
# @structural_parameters begin
731763
# verbose = true
732-
# Ts = SampleTime()
733-
# z = ShiftIndex()
764+
# Ts = SampleTime()
765+
# z = ShiftIndex()
734766
# end
735767
# begin
736-
# na = length(a)
737-
# nb = length(b)
768+
# @show na = length(a)
769+
# @show nb = length(b)
770+
# @show a
738771
# verbose && nb > na && @warn "DiscreteTransferFunction: Numerator degree is larger than denominator degree, this is not a proper transfer function. Simulation of a model including this transfer funciton will require at least $(nb-na) samples additional delay in series. Silence this warning with verbose=false"
739-
# Ts = SampleTime()
740772
# end
741773
# @components begin
742774
# input = RealInput()
@@ -753,7 +785,7 @@ See also [ControlSystemsMTK.jl](https://juliacontrol.github.io/ControlSystemsMTK
753785
# end
754786
# end
755787

756-
# DiscreteTransferFunction(b, a; kwargs...) = DiscreteTransferFunction(; b, a, kwargs...)
788+
#
757789

758790
##
759791

test/test_discrete_blocks.jl

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,4 +514,59 @@ end
514514
@test sol(0.999, idxs=m.filter.y) == 0
515515
@test sol(1.1, idxs=m.filter.y) > 0
516516

517-
end
517+
end
518+
519+
@testset "DiscreteTransferFunction" begin
520+
@info "Testing DiscreteTransferFunction"
521+
z = ShiftIndex(Clock(0.1))
522+
@mtkmodel DiscreteTransferFunctionModel begin
523+
@components begin
524+
input = Step(start_time=1, smooth=false)
525+
filter = DiscreteTransferFunction(; a = [1, -0.9048374180359594], b = [0.09516258196404037], z)
526+
end
527+
@variables begin
528+
x(t) = 0 # Dummy variable to workaround JSCompiler bug
529+
end
530+
@equations begin
531+
connect(input.output, filter.input)
532+
D(x) ~ 0
533+
end
534+
end
535+
@named m = DiscreteTransferFunctionModel()
536+
m = complete(m)
537+
ssys = structural_simplify(IRSystem(m))
538+
prob = ODEProblem(ssys, [m.filter.y(z-1) => 0], (0.0, 10.0))
539+
sol = solve(prob, Tsit5(), dtmax=0.1)
540+
@test sol(10, idxs=m.filter.y) 1 atol=0.001
541+
@test sol(0.999, idxs=m.filter.y) == 0
542+
@test sol(1.1, idxs=m.filter.y) > 0
543+
@test sol(1:10, idxs=m.filter.y).u [0.0, 0.6321205588285568, 0.8646647167633857, 0.950212931632134, 0.9816843611112637, 0.9932620530009122, 0.9975212478233313, 0.9990881180344431, 0.999664537372095, 0.9998765901959109]
544+
545+
import ControlSystemsBase as CS
546+
Gc = CS.tf(1, [1, 1])
547+
G = CS.c2d(Gc, 0.1)
548+
549+
@mtkmodel DiscreteTransferFunctionModel begin
550+
@components begin
551+
input = Step(start_time=1, smooth=false)
552+
filter = DiscreteTransferFunction(G)
553+
end
554+
@variables begin
555+
x(t) = 0 # Dummy variable to workaround JSCompiler bug
556+
end
557+
@equations begin
558+
connect(input.output, filter.input)
559+
D(x) ~ 0
560+
end
561+
end
562+
@named m = DiscreteTransferFunctionModel()
563+
m = complete(m)
564+
ssys = structural_simplify(IRSystem(m))
565+
prob = ODEProblem(ssys, [m.filter.y(z-1) => 0], (0.0, 10.0))
566+
sol = solve(prob, Tsit5(), dtmax=0.1)
567+
@test sol(10, idxs=m.filter.y) 1 atol=0.001
568+
@test sol(0.999, idxs=m.filter.y) == 0
569+
@test sol(1.1, idxs=m.filter.y) > 0
570+
@test sol(1:10, idxs=m.filter.y).u [0.0, 0.6321205588285568, 0.8646647167633857, 0.950212931632134, 0.9816843611112637, 0.9932620530009122, 0.9975212478233313, 0.9990881180344431, 0.999664537372095, 0.9998765901959109]
571+
572+
end

0 commit comments

Comments
 (0)