/* * Linear state space model in the CT domain. This is the Ptalon version * of a model originally written in Java code. * * The State-Space model implements a system whose behavior is defined by: * * dx/dt = Ax + Bu * y = Cx + Du * x(0) = x0 * * where x is the state vector, u is the input vector, and y is the output * vector. The matrix coefficients must have the following characteristics: * * A must be an n-by-n matrix, where n is the number of states. * B must be an n-by-m matrix, where m is the number of inputs. * C must be an r-by-n matrix, where r is the number of outputs. * D must be an r-by-m matrix. * * The actor accepts m inputs and generates r outputs * through a multi-input port and a multi-output port. The widths of the * ports must match the number of rows and columns in corresponding * matrices, otherwise, an exception will be thrown. * * * @author Adam Cataldo (borrowed example from Jie Liu) */ LinearStateSpace is { /* * These lines create the ports for the actor, which are * all multiports. */ inport[] input; outport[] output; outport[] stateOutput; /* * These lines create the parameters for the actor. */ parameter A; parameter B; parameter C; parameter D; parameter initialStates; /* * These lines define some actor "constants" to be * used later. */ actor integrator = ptolemy.domains.continuous.lib.Integrator; actor adder = ptolemy.actor.lib.AddSubtract; actor scale = ptolemy.actor.lib.Scale; /* * This is a for loop in Ptalon. All evaluation * of normal data is done by the Ptolemy expression * language, which we use in a for loop. Anything in * the denotation brackets [[ ... ]] will be parsed * in the expression language, not Ptalon, but the * results of evaluation will be used by Ptalon to * populate the actor. * * A for loop structure has form * for variable initially [[ initialValueExpression ]] * [[ loopConditionExpression ]] { * ... * loopBody * ... * } next [[ valueUpdateExpression ]] * * The variable can take on any data type the expression * language supports, although it will often be an integer. */ for a initially [[ 0 ]] [[ a < A.getRowCount ]] { /* * These lines create relations whose names * depend on the value of the variable a. They * will take on names like state0, state1, etc. * and stateAdderOut0, stateAdderOut1, etc. */ relation state[[a]]; relation stateAdderOut[[a]]; /* * This creates an instance of the integrator. * It assigns the value of initialStates(0, a) * to the intialState parameter of the integrator. * It connects the input port of the integrator to * the relation stateAdderOut[[a]], where a is the for * loop variable. */ integrator(initialState := [[initialStates(0, a)]], derivative := stateAdderOut[[a]], state := state[[a]] ); /* * This creates a port reference. The port * reference will refer to the plus input of * the adder. Any other components that * later link a port to this port reference * will connect to the plus input of the * adder. The rule for port references is * that they refer to the first port that * is linked to them through an equation * port := reference */ port reference stateAdderIn[[a]]; adder(plus := stateAdderIn[[a]], output := stateAdderOut[[a]] ); /* * The "this" reference refers to the main PtalonActor * we create. We use it to connect internal ports * and relations. In this case, we connect the stateOutput * multiport to the relations state0, state1, etc. */ this(stateOutput := state[[a]] ); } next [[ a + 1 ]] /* * These nested for loops are used to create the scale actors * in the feedback loops corresponding to the input matrices. */ for a initially [[ 0 ]] [[ a < A.getRowCount ]] { for b initially [[ 0 ]] [[ b < A.getRowCount ]] { scale(factor := [[ A(a, b) ]], input := state[[b]], output := stateAdderIn[[a]] ); } next [[ b + 1 ]] } next [[ a + 1 ]] /* * These nested for loops are used to create the scale actors * on the input side of the system. */ for b initially [[ 0 ]] [[ b < B.getColumnCount ]] { relation scaleIn[[b]]; this(input := scaleIn[[b]] ); for a initially [[ 0 ]] [[ a < A.getRowCount ]] { scale(factor := [[ B(a, b) ]], input := scaleIn[[b]], output := stateAdderIn[[a]] ); } next [[ a + 1 ]] } next [[ b + 1 ]] /* * These nested for loops are used to create the adder actors * and scale actors on the output side of the system. */ for c initially [[ 0 ]] [[ c < C.getRowCount ]] { port reference outputAdderIn[[c]]; adder(plus := outputAdderIn[[c]], output := output); for a initially [[ 0 ]] [[ a < A.getRowCount ]] { scale(factor := [[ C(c, a) ]], input := state[[a]], output := outputAdderIn[[c]] ); } next [[ a + 1 ]] } next [[ c + 1 ]] /* * These nested for loops are used to create the * and scale actors for the direct feedthrough subsystem. */ for c initially [[ 0 ]] [[ c < C.getRowCount ]] { for b initially [[ 0 ]] [[ b < B.getColumnCount ]] { scale(factor := [[ D(c, b) ]], input := scaleIn[[b]], output := outputAdderIn[[c]] ); } next [[ b + 1 ]] } next [[ c + 1 ]] }