diff --git a/README.md b/README.md index a4acfd050ba2461ad8c3607cc437c161b783eac6..60b2f22570029dffd84cd53586b98105c641f76f 100644 --- a/README.md +++ b/README.md @@ -78,4 +78,59 @@ end ``` `G1` and `G4` must here be represented by [`StateSpace`](http://juliacontrol.github.io/ControlSystems.jl/latest/lib/constructors/#ControlSystems.ss) types from [`ControlSystems.jl`](https://github.com/JuliaControl/ControlSystems.jl). `TransferFunction` types can easily be converted to a `StateSpace` by `Gss = ss(Gtf)`. -Continuous time systems can be discretized using `Gd = c2d(Gc, h)`. (The sample time of a process is available through `h = sampletime(P)`.) +Continuous time systems can be discretized using `Gd = c2d(Gc, h)[1]`. (The sample time of a process is available through `h = sampletime(P)`.) + + +# How to implement a Simulated Process +## Linear process +This is very easy, just get a discrete time `StateSpace` model of your process +(if you have a transfer function, `Gss = ss(Gtf)` will do the trick, if you have continuous time, `Gd = c2d(Gc,h)[1]` is your friend). + +You now have to implement the methods `control` and `measure` for your simulated type. +The implementation for `BeamSimulator` is shown below +```julia +function control(p::BeamSimulator, u) + sysfilter!(p.state, p.sys, u) +end +measure(P) = vecdot(p.sys.C,p.state) +``` +The `control` method accepts a control signal (`u`) and propagates the system state +(`p.state`) forward using the statespace model (`p.sys`) of the beam. The function +[`sysfilter!`](@ref) is familiar from the "Control" section above. What it does +is essentially +```julia +function sysfilter!(state, sys, input) + state .= sys.A*state + sys.B*input + output = sys.C*state + sys.D*input +end +``` +hence, it just performs one iteration of +```math +x' = Ax + Bu +y = Cx + Du +``` + +The `measure` method performs the computation `y = Cx`, the reason for the call +to `vecdot` is that `vecdot` produces a scalar output, whereas `C*x` produces a +1-element `Matrix`. A scalar output is preferred in this case since the `Beam` +is SISO. + +It should now be obvious which fields are required in the `BeamSimulator` type. +It must know which sample time it has been discretized with, as well as its +discrete-time system model. It must also remember the current state of the system. +This is not needed in a physical process since it kind of remembers its own state. +The full type specification for `BeamSimulator` is given below +```julia +struct BeamSimulator <: SimulatedProcess + h::Float64 + state::Vector{Float64} # states defined by the file define_beam_system + sys::StateSpace + BeamSimulator() = new(0.01, init_sysfilter(beam_system), c2d(beam_system, 0.01)[1]) + BeamSimulator(h::Real) = new(Float64(h), init_sysfilter(beam_system), c2d(beam_system, h)[1]) +end +``` +It contains three fields and two inner constructors. The constructors initializes +the system state by calling `init_sysfilter`. The variable `beam_system` is already +defined outside the type specification. +One of the constructors provides a default value for the sample time, in case +the user is unsure about a reasonable value.