language-Modula2-0.1: examples/Modula-2_Libraries/PMOS/control/sources/transfer.mod
IMPLEMENTATION MODULE TransferFunctions;
(********************************************************)
(* *)
(* Second order transfer functions *)
(* *)
(* Programmer: P. Moylan *)
(* Last edited: 21 February 1995 *)
(* Status: Working on screen editing *)
(* *)
(********************************************************)
FROM Trace IMPORT
(* proc *) NYI, Pause, InTrace, OutTrace;
FROM Storage1 IMPORT
(* proc *) ALLOCATE, DEALLOCATE;
FROM Windows IMPORT
(* type *) Window, Colour, FrameType, DividerType,
(* proc *) OpenWindow, CloseWindow, WriteChar, WriteString,
SetCursor, SaveCursor, CursorUp, CursorDown;
FROM RealIO IMPORT
(* proc *) WriteReal;
FROM Menus IMPORT
(* type *) Menu, ItemText,
(* proc *) CreateMenu, PositionMenu, DestroyMenu, SelectFromMenu;
FROM ScreenEditor IMPORT
(* type *) Structure,
(* proc *) MenuField, RealField, Combine, ScreenEdit, DeleteStructure;
FROM TaskControl IMPORT
(* type *) Lock,
(* proc *) CreateLock, DestroyLock, Obtain, Release;
(************************************************************************)
TYPE
power = [0..2];
(* We represent a second order polynomial by its coefficients, in *)
(* ascending power order. *)
Poly = ARRAY power OF REAL;
(* A rational function is the ratio of two polynomials. *)
Rational = RECORD
numerator, denominator: Poly
END (*RECORD*);
(* A transfer function has a state, and we represent it by two *)
(* rational functions: a Laplace function form (SForm) and a Delta *)
(* form (DForm). This is redundant, since SForm and DForm are *)
(* related by a bilinear transformation, but it tends to speed up *)
(* the computations to have both forms available at all times. *)
(* Remark: State[2] is a dummy which is never used. *)
TransferFunction = POINTER TO
RECORD
access: Lock;
delta: REAL;
State: Poly;
SForm, DForm: Rational;
END (*RECORD*);
(************************************************************************)
(* OPERATIONS ON POLYNOMIALS *)
(************************************************************************)
PROCEDURE Fill (VAR (*OUT*) p: Poly; value: REAL);
(* Fills all coefficients to the same given value. *)
VAR j: power;
BEGIN
FOR j := MIN(power) TO MAX(power) DO
p[j] := value;
END (*FOR*);
END Fill;
(************************************************************************)
PROCEDURE StoDelta (P: Poly; interval: REAL): Poly;
(* Converts an s-form polynomial to delta form. *)
VAR result: Poly;
BEGIN
InTrace ("StoDelta");
result[0] := P[0];
result[1] := P[1] + 2.0*interval*P[0];
result[2] := P[2] + interval * (P[1] + interval*P[0]);
OutTrace ("StoDelta");
RETURN result;
END StoDelta;
(************************************************************************)
PROCEDURE DisplayPoly (w: Window; P: Poly);
(* Writes a polynomial to the screen, starting at the current *)
(* screen location. *)
BEGIN
InTrace ("DisplayPoly");
WriteReal (w, P[0], 6); WriteString (w, " + ");
WriteReal (w, P[1], 6); WriteString (w, " s + ");
WriteReal (w, P[2], 6); WriteString (w, " s");
CursorUp (w); WriteChar (w, "2");
OutTrace ("DisplayPoly");
END DisplayPoly;
(************************************************************************)
(* OPERATIONS ON RATIONAL FUNCTIONS *)
(************************************************************************)
PROCEDURE DisplayRational (w: Window; R: Rational);
(* Writes a transfer function to the screen, starting at the *)
(* current screen location. *)
VAR row, col: CARDINAL;
BEGIN
InTrace ("DisplayRational");
SaveCursor (w, row, col);
DisplayPoly (w, R.numerator);
SetCursor (w, row+1, col);
WriteString (w, "_______________________________");
SetCursor (w, row+3, col);
DisplayPoly (w, R.denominator);
OutTrace ("DisplayRational");
END DisplayRational;
(************************************************************************)
PROCEDURE DiscreteVersion (SForm: Rational; delta: REAL): Rational;
(* Computes the delta-transform equivalent of an s-domain function. *)
VAR result: Rational;
BEGIN
InTrace ("DiscreteVersion");
result.numerator := StoDelta (SForm.numerator, delta);
result.denominator := StoDelta (SForm.denominator, delta);
OutTrace ("DiscreteVersion");
RETURN result;
END DiscreteVersion;
(************************************************************************)
PROCEDURE SetToUnity (VAR (*INOUT*) TF: Rational);
(* Sets the value of TF to unity. *)
BEGIN
WITH TF DO
Fill (numerator, 1.0);
denominator := numerator;
END (*WITH*);
END SetToUnity;
(************************************************************************)
(* OPERATIONS ON TRANSFER FUNCTIONS *)
(************************************************************************)
PROCEDURE CreateTransferFunction (VAR (*OUT*) G: TransferFunction;
SamplingInterval: REAL);
(* Creates a new transfer function, with value initially set to unity. *)
BEGIN
NEW (G);
WITH G^ DO
CreateLock (access);
delta := SamplingInterval;
Fill (State, 0.0);
SetToUnity (SForm);
SetToUnity (DForm);
END (*WITH*);
END CreateTransferFunction;
(************************************************************************)
PROCEDURE DestroyTransferFunction (VAR (*INOUT*) G: TransferFunction);
(* Discards a transfer function. *)
BEGIN
Obtain (G^.access); DestroyLock (G^.access);
DISPOSE (G);
END DestroyTransferFunction;
(************************************************************************)
PROCEDURE UpdateSamplingInterval (VAR (*INOUT*) G: TransferFunction;
SamplingInterval: REAL);
(* Recomputes the internal details of the transfer function to *)
(* allow for a change in sampling interval. *)
BEGIN
WITH G^ DO
Obtain (access);
delta := SamplingInterval;
DForm := DiscreteVersion (SForm, delta);
Release (access);
END (*WITH*);
END UpdateSamplingInterval;
(************************************************************************)
PROCEDURE Filter (VAR (*INOUT*) G: TransferFunction; input: REAL): REAL;
(* Computes the output from G with the given input. Remark: G is *)
(* an inout parameter because its internal state is affected. *)
VAR temp, result: REAL;
BEGIN
WITH G^ DO
Obtain (access);
WITH DForm DO
temp := State[0] + delta * (input - denominator[1]*State[0]
- denominator[0]*State[1]);
State[1] := State[1] + delta*State[0];
State[0] := temp;
result :=
(numerator[1] - denominator[1]*numerator[2]) * State[0]
+ (numerator[0] - denominator[0]*numerator[2]) * State[1]
+ numerator[2]*input;
END (*WITH*);
Release (access);
END (*WITH*);
RETURN result;
END Filter;
(************************************************************************)
PROCEDURE EditTransferFunction (VAR (*INOUT*) G: TransferFunction;
caption: ARRAY OF CHAR);
(* Allows the keyboard user to alter a transfer function. *)
TYPE optionrange = [0..4];
VAR TF, oldTF: Rational; w: Window;
j: power;
S: Structure; M: Menu;
Messages: ARRAY optionrange OF ItemText;
option: CARDINAL; abort: BOOLEAN;
BEGIN
InTrace ("EditTransferFunction");
OpenWindow (w, white, magenta, 14, 24, 10, 69, simpleframe, nodivider);
WriteString (w, caption);
Messages[0] := "Transfer function";
Messages[1] := "Accept";
Messages[2] := "Set to unity";
Messages[3] := "Cancel changes";
Messages[4] := "Edit";
CreateMenu (M, 1, Messages, MAX(optionrange));
PositionMenu (M, 2, 5+MAX(optionrange), 50, 75);
(* Note that all the editing is done on a copy, with the actual *)
(* transfer function updated only after the user is satisfied *)
(* with the changes. *)
WITH G^ DO
Obtain (access); TF := SForm; Release (access);
END (*WITH*);
(* Create the editing structure used by ScreenEditor. *)
WITH TF DO
S := RealField (numerator[0], 4, 2, 6);
Combine (S, RealField (denominator[0], 7, 2, 6));
FOR j := 1 TO 2 DO
Combine (S, RealField (numerator[j], 4, 2+11*j, 6));
Combine (S, RealField (denominator[j], 7, 2+11*j, 6));
END (*FOR*);
END (*WITH*);
REPEAT
SetCursor (w, 4, 2);
DisplayRational (w, TF);
option := SelectFromMenu (M);
IF option = 2 THEN (* Set to unity *)
SetToUnity (TF);
ELSIF option = 3 THEN (* Cancel changes *)
WITH G^ DO
Obtain (access); TF := SForm; Release (access);
END (*WITH*);
ELSIF option = 4 THEN (* Edit *)
oldTF := TF;
ScreenEdit (w, S, abort);
IF abort THEN
TF := oldTF;
END (*IF*);
END (*IF*);
UNTIL option < 2;
IF option = 1 THEN (* Accept *)
WITH G^ DO
Obtain (access);
SForm := TF;
DForm := DiscreteVersion (TF, delta);
Release (access);
END (*WITH*);
END (*IF*);
DestroyMenu (M); DeleteStructure (S);
CloseWindow (w);
OutTrace ("EditTransferFunction");
END EditTransferFunction;
(************************************************************************)
END TransferFunctions.