home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-08-18 | 48.1 KB | 1,461 lines |
- (* :Title: General Platform for Transforms *)
-
- (* :Authors: Brian Evans, James McClellan *)
-
- (*
- :Summary: Platform for symbolic transforms
- Plots magnitude/phase responses
- Generates pole-zero diagrams and root loci
- Tools for stability analysis based on transform objects.
- *)
-
- (* :Context: SignalProcessing`Support`TransSupport` *)
-
- (* :PackageVersion: 2.7 *)
-
- (*
- :Copyright: Copyright 1989-1991 by Brian L. Evans
- Georgia Tech Research Corporation
-
- Permission to use, copy, modify, and distribute this software
- and its documentation for any purpose and without fee is
- hereby granted, provided that the above copyright notice
- appear in all copies and that both that copyright notice and
- this permission notice appear in supporting documentation,
- and that the name of the Georgia Tech Research Corporation,
- Georgia Tech, or Georgia Institute of Technology not be used
- in advertising or publicity pertaining to distribution of the
- software without specific, written prior permission. Georgia
- Tech makes no representations about the suitability of this
- software for any purpose. It is provided "as is" without
- express or implied warranty.
- *)
-
- (* :History: *)
-
- (* :Keywords: pole-zero diagrams, root locus, frequency response, stability *)
-
- (*
- :Source: {Multidimensional Digital Signal Processing}, 1984,
- by D. Dudgeon and R. Mersereau
- {Digital Signal Processing}, 1975, Oppenheim and Schafer
- *)
-
- (*
- :Warning: The transform objects LTransData and ZTransData must
- be defined before this file is loaded (see "ROC.m").
- *)
-
- (* :Mathematica Version: 1.2 or 2.0 *)
-
- (* :Limitation: *)
-
- (*
- :Discussion: This file has five sections:
-
- A. Routines to aid in the finding of symbolic transforms.
- B. Magnitude and phase plots (for 1-D and 2-D signals)
- C. Root-locus plots
- D. Pole-zero diagrams and root maps (for 1-D and
- 2-D transforms)
- E. Stability analysis which rely on transform objects.
-
- Section B and D display the results of transform objects.
- Section E extracts information from transform objects.
- *)
-
- (*
- :Functions: AddT
- ConjT
- ConvolveT
- DerivativeT
- IntegrateT
- LineImpulsemDT
- MagPhasePlot
- MultiDInvTransform
- MultiDLookup
- MultiDTransform
- MultT
- PoleZeroPlot
- ROCPlot
- RootLocus
- ScaleT
- ShadedAnnulus
- Stable
- SubstituteForT
- SubT
- TransformDialogue
- TransformFixUp
- *)
-
-
-
- (* Enhance usage messages *)
-
- If [ TrueQ[ $VersionNumber >= 2.0 ],
- $NewMessage[Apart, "usage"];
- $NewMessage[Definition, "usage"];
- $NewMessage[Simplify, "usage"] ];
-
- Apart::usage = Apart::usage ~StringJoin~
- " It is also an option for the inverse z and Laplace transforms \
- directing how to handle taking partial fractions of denominators \
- of 5th degree or less. \
- A value of Rational directs exact roots to be taken whereas All \
- indicates numeric roots."
-
- Definition::usage = Definition::usage ~StringJoin~
- " It is also an option for all of the symbolic linear transforms \
- which indicates whether or not to apply the definition of the \
- transform in terms of built-in Mathematica routines if all other
- attempts at the transform have failed."
-
- Simplify::usage = Simplify::usage ~StringJoin~
- " It is also an option for z, Laplace, and continuous Fourier transforms, \
- which can take the value of True or False."
-
-
- (* B E G I N P A C K A G E *)
-
- BeginPackage[ "SignalProcessing`Support`TransSupport`",
- "SignalProcessing`Support`ROC`",
- "SignalProcessing`Support`SigProc`",
- "SignalProcessing`Support`SupCode`" ]
-
-
- If [ TrueQ[ $VersionNumber >= 2.0 ],
- Off[ General::spell ];
- Off[ General::spell1 ] ]
-
-
- (* U S A G E I N F O R M A T I O N *)
-
- AddT::usage =
- "AddT[transq, t1, t2] adds the two transforms t1 and t2 together \
- and determines the new region of convergence. \
- The new transform is returned as a list. \
- AddT[transq, t1, t2, lowerlimit] uses lowerlimit as the lower limit \
- on region of convergence values when combining ROC's (default is 0)."
-
- ConjT::usage =
- "ConjT[transq, X(s), s] implements T{ Conj{x(t)} } --> X*(s*), \
- where X(s) is the transform of x(t)."
-
- ConvolveT::usage =
- "ConvolveT[transq, convop, t1, t2] implements one dimension of the \
- transform of a convolution, Convolution[All, All, t][x1, x2], where \
- convop is the convolution operator, t1 is the transform of x1, \
- and t2 is the transform of x2."
-
- DerivativeT::usage =
- "DerivativeT[transq, expr, s, m] applies the derivative operator \
- to the transform expr which is a function of s. \
- The derivative is taken m times if and only if expr satisfies transq."
-
- DomainScale::usage =
- "DomainScale is an option for MagPhasePlot indicating the \
- scaling of the independent variable axis, Linear or Log, \
- for the magnitude plot."
-
- IntegrateT::usage =
- "IntegrateT[transq, t, variable, lower-limit, upper-limit] integrates \
- the transform t (with respect to variable) from lower-limit to \
- upper-limit. \
- The resulting transform is returned as a list."
-
- Linear::usage =
- "Linear is a possible value for axis scaling. See MagPhasePlot."
-
- LineImpulsemDT::usage =
- "LineImpulsemDT[transq, expr, s, slist, nleft, fun] applies rules for \
- evaluating multidimensional transforms which involve line impulses, \
- like the z-transform of f[n1,n2] Impulse[n1 - n2] or the Laplace \
- transform of f[t1,t2] Delta[t1 - t2]. \
- In either case, the impulse can be removed by setting n1=n2 or t1=t2, \
- which yields a one-dimensional function. \
- The resulting 1-D transform is then altered by the rule z = z1 z2 \
- or s = s1 + s2, respectively. \
- Here, the argument fun is Times for the z-transform and Plus for \
- the Laplace transform. \
- Note that these rules are only applied if expr is a valid transform, \
- which occurs if transq[expr] evaluates to True."
-
- MagRangeScale::usage =
- "MagRangeScale is an option for MagPhasePlot indicating the \
- scaling of the dependent variable axis, Linear or Log, \
- for the magnitude plot. \
- A setting of Null disables the generation of the magnitude plot."
-
- MagPhasePlot::usage =
- "MagPhasePlot[freqresp, {w, wmin, wmax }] plots the magnitude \
- and phase response over the specified range of frequencies. \
- It returns a list of two elements: the magnitude plot and the \
- phase plot. \
- The two-dimensional version has the form \
- MagPhasePlot[freqresp, {w1, wmin1, wmax1}, {w2, wmin2, wmax2}]. \
- The default options are initially biased for continuous-time \
- frequency responses of digital signals. \
- MagnitudePhasePlot is an alias for MagPhasePlot."
-
- MultiDInvTransform::usage =
- "MultiDInvTransform[expression, transvar, timevar, options, transq, \
- transform, makeobject, tdefault] finds the multidimensional inverse \
- transform of expression by one call to transform per dimension. \
- Here, transq, transform, and makeobject are function heads."
-
- MultiDLookup::usage =
- "MultiDLookup[options, timevars, transformvars] expands any \
- multidimensional transform pairs into a set of one-dimensional \
- transform pairs."
-
- MultiDTransform::usage =
- "MultiDTransform[makefun, transform, transtest, expr, expr-vars, \
- def-trans-var, trans-vars] finds the multidimensional transform \
- by calling transform once per dimension. \
- If a call to transform produces an incomplete (invalid) transform, \
- then the multidimensional transform does not exist and this routine is \
- exited. \
- The dimension of the transform is determined from \
- the number of expr-vars (which equals the number of trans-vars). \
- Here, expr is transformed."
-
- MultT::usage =
- "MultT[transq, t1, t2] multiplies the transforms t1 and t2 together. \
- MultT[transq, t1, t2, lowerlimit] uses lowerlimit as the lower \
- limit on the region of convergence (ROC) when combining the ROC's of \
- t1 and t2."
-
- PhaseRangeScale::usage =
- "PhaseRangeScale is an option for MagPhasePlot indicating the \
- scaling of the dependent variable axis, Linear or Log,
- for the phase plot. \
- A setting of Null disables the generation of the phase plot."
-
- PoleZeroPlot::usage =
- "PoleZeroPlot[transform-object] plots the poles and zeros of the \
- transform function as well as the region of convergence. \
- For the z-transform, this function will also plot the unit circle and \
- shade the region of convergence (whenever the ROC does not cover the \
- entire z plane). \
- To plot a transfer function f, use \
- PoleZeroPlot[f, transform-variable, rminus, rplus, zdomainflag]. \
- In the two-dimensional case, the transform-variable, rminus, and \
- rplus are two-element lists. \
- The two-dimensional pole-zero plot has three cases: \
- separable transform, symmetric transform, and root map. \
- The function returns the poles of the transform."
-
- Radian::usage =
- "Radian is a possible value for axis scaling. See MagPhasePlot."
-
- ROCPlot::usage =
- "ROCPlot[{rm, rp}] will plot the region of convergence as a \
- shaded annular region. \
- Possible options for ROCPlot are the same as those for the \
- Show (or Plot) command."
-
- RootLocus::usage =
- "RootLocus[f(z), z, {freeparam, start, end, step}] plots \
- the root locus of f(z) with respect to freeparam over the \
- range of start to end at increments of step. \
- In order to connect the points, the function f(z) will be factored \
- symbolically with respect to z. \
- All closed-form factors of reasonable size will be graphed as \
- parametric plots. \
- RootLocus will generate three plots: the pole zero plot for \
- freeparam = start, the root locus, and the pole-zero plot for \
- freeparam = end. \
- The function returns a list of all three plots. \
- RootLocus supports the same options as does Show. \
- RootLocusPlot is an alias of RootLocus."
-
- ScaleT::usage =
- "ScaleT[transq, t, c] multiplies the transform t by c while leaving \
- the region of convergence unaltered. \
- The resulting transform is returned as a list."
-
- ShadedAnnulus::usage =
- "ShadedAnnulus[rm, rp] and ShadedAnnulus[rm, rp, angulartilt] \
- will create a 2-D graphics object that is an annulus \
- (rm < radius < rp) filled with uniformly spaced horizontal \
- lines rotated by angulartilt degrees."
-
- Stable::usage =
- "Stable[transexpr] returns True if the transform transexpr \
- represents a stable time-domain signal."
-
- SubT::usage =
- "SubT[transq, t1, t2] subtracts the transforms t2 from t1 and \
- determines the new region of convergence. \
- The resulting transform is returned as a list. \
- SubT[transq, t1, t2, lowerlimit] uses lowerlimit as the lower limit \
- on region of convergence values when combining ROC's (default is 0)."
-
- SubstituteForT::usage =
- "SubstituteForT[transq, t, s, news] substitutes news at every \
- occurrence of s in the transform t. \
- The resulting transform is returned as a list."
-
- TransformDialogue::usage =
- "TransformDialogue[ strategy, options, arg1, arg2, ... ] is the \
- routine that displays textual dialogue about the strategy being used \
- by a particular transform."
-
- TransformFixUp::usage =
- "TransformFixUp[time_var, transform_var, options, \
- transform_head, triplet_flag, transform_name, lower, upper] \
- attempts to resolve incomplete transforms according to the \
- transform pairs provided by the user via the TransformLookup option. \
- If the transform is a triplet of information (funct, Rminus, Rplus), \
- e.g. the forward z- and Laplace transforms, then triplet_flag is True \
- and the lower and upper arguments are used for the second and \
- third fields of the triplet."
-
- TransformLookup::usage =
- "TransformLookup is an option for the transform rule bases. \
- It allows the user to specify a list of transform pairs to augment \
- those that already exist in the transform rule bases. \
- For example, ZTransform[ Shift[L, n][x[n]], n, z, \
- TransformLookup -> { x[n] :> X[z] } ]. \
- To specify a region of convergence, use \
- TransformLookup -> { x[n] :> { X[z], rm, rp } }. \
- This example allows the user to work x[n] without every defining it. \
- The multidimensional case is more tricky: \
- ZTransform[ x[n1, n2], n, z, TransformLookup -> \
- { x[n1, n2] :> X[z1, n2], X[z1, n2] :> X[z1, z2] } ]. \
- A region of convergence can be specified for each dimension. \
- For the z- and Laplace transform rule bases, one can also specify \
- a region of convergence associated with the transform pairs."
-
- (* E N D U S A G E I N F O R M A T I O N *)
-
- If [ TrueQ[ $VersionNumber >= 2.0 ],
- Terms::usage = "An"; Protect[ Terms ],
- Terms::usage = Terms::usage ~StringJoin~ " It is also an" ]
-
- Terms::usage = Terms::usage ~StringJoin~
- " option for the inverse z- and Laplace transform rule bases, \
- InvZTransform and InvLaPlace, which specifies that the inverse \
- will be approximated by a series expansion if all other attempts \
- at the inverse have failed.";
-
-
- Begin["`Private`"]
-
-
- (* C O N S T A N T S *)
-
- axesdefaultvalue = If [ TrueQ[$VersionNumber >= 2.0], True, Automatic ]
-
-
- (* M E S S A G E S *)
-
- MagPhasePlot::unresolved =
- "These symbols were assigned a value of 1: ``."
- MagPhasePlot::nodeltas =
- "Delta functions are not shown on the graph"
- MultiDInvTransform::notenough =
- "Not enough non-transform (time) variables specified."
- MultiDTransform::notenough =
- "Not enough transform domain variables specified."
- PoleZeroPlot::invalidROC =
- "The poles `` are inside the specified region of convergence {``, ``}."
- PoleZeroPlot::multidimensions =
- "Can not produce a pole-zero plot for the multidimensional transform."
- PoleZeroPlot::notrational =
- "Transform is not a rational polynomial."
- PoleZeroPlot::noplot =
- "A pole-zero plot cannot be generated."
- PoleZeroPlot::posROC =
- "Possible Regions of Convergence are: "
- RootLocus::constant =
- "The function `` does not depend on the variable ``."
- RootLocus::notnum =
- "The min, max, or step value is not a number: ``."
- Transform::badvar =
- "The `` variable given to `` is not a symbol or list of symbols: ``"
- Transform::incomplete =
- "The rule base could not compute the `` of `` with respect to ``."
- Transform::integrate =
- "Integrating the function `` with respect to ``...."
- Transform::novariables =
- "No `` variables specified for transform. Possible variables are ``."
- Transform::notenough =
- "Not enough `` variables specified for the transform."
- Transform::twosided =
- "The two-sided transform was applied to ``."
- TransformLookup::nomatch
- TransformLookup::notrule =
- "The expression `` passed in the TransformLookup option of the `` \
- object is not a rule."
- TransformFixUp::notriplet =
- "The rule `` has been changed to `` because `` does not track \
- a region of convergence."
-
-
- (* A. B A S I C R O U T I N E S F O R T R A N S F O R M S *)
-
- (* AddT *)
- AddT[transq_, t1_, t2_, lowerlimit_:0] :=
- Transform[ TheFunction[t1] + TheFunction[t2],
- FindRMinus[ GetRMinus[t1], GetRMinus[t2], lowerlimit ],
- FindRPlus[ GetRPlus[t1], GetRPlus[t2], lowerlimit ] ] /;
- transq[t1] && transq[t2]
-
- Format[ AddT[transq_, t1_, t2_, lowerlimit_:0] ] := t1 + t2
-
- (* ConjT *)
- ConjT[transq_, t_, s_] :=
- Transform[ Conjugate[TheFunction[t]] /. s -> Conjugate[s],
- GetRMinus[t], GetRPlus[t] ] /;
- transq[t]
-
- Format[ ConjT[transq_, t_, s_] ] :=
- SequenceForm[ ToString[StringForm["{ Conj{``} }", t]],
- Subscript[s -> news] ]
-
- (* ConvolveT *)
- ConvolveT[transq_, convop_, t1_, t2_] :=
- Block [ {result},
- result = MultT[transq, t1, t2];
- Transform[ convop [t1, t2], GetRMinus[t], GetRPlus[t] ] ] /;
- transq[t1] && transq[t2]
-
- Format[ ConvolveT[transq_, convop_, t1_, t2_] ] :=
- ToString[StringForm["`` ** ``", t1, t2]]
-
- (* DerivativeT *)
- DerivativeT[transq_, x_, s_, m_] :=
- Transform[ D[TheFunction[x], {s, m}], GetRMinus[x], GetRPlus[x] ] /;
- transq[x]
-
- Format[ DerivativeT[transq_, x_, s_, m_] ] :=
- SequenceForm[ ColumnForm[{"D" Superscript[m],
- " " ~StringJoin~ ToString[s]}],
- { x } ]
-
- (* IntegrateT *)
- IntegrateT[transq_, t_, var_, lower_, upper_] :=
- Transform[ Integrate[ TheFunction[t], {var, lower, upper} ],
- GetRMinus[t], GetRPlus[t] ] /;
- transq[t]
-
- Format[ IntegrateT[transq_, t_, var_, l_, u_] ] :=
- ToString[StringForm["Integrate[``, {``, ``, ``}]", t, var, l, u]]
-
- (* LineImpulsemDT *)
- LineImpulsemDT[transq_, t_, s_, slist_, nleft_, fun_] :=
- Block [ {},
- Needs[ "SignalProcessing`Support`MDSupport`" ];
- lineImpulsemDT[transq, t, s, slist, nleft, fun] ] /;
- transq[t]
-
- Format[ SignalProcessing`ROCinfo[n_, rm_, rp_] ] :=
- Subscript[ "ROC", {rm, rp} ]
-
- (* MultiDInvTransform *)
- MultiDInvTransform[e_, s_, t_, op_, transq_, trans_, makeobject_, tdefault_] :=
- Block [ {},
- Needs[ "SignalProcessing`Support`MDSupport`" ];
- multiDInvTransform[e, s, t, op, transq,
- trans, makeobject, tdefault] ]
-
- (* MultiDLookup *)
- MultiDLookup[op_, nlist_, zlist_] :=
- Block [ {},
- Needs[ "SignalProcessing`Support`MDSupport`" ];
- multiDLookup[op, nlist, zlist] ]
-
- (* MultiDTransform *)
- MultiDTransform[makeobjectfun_, transform_, transq_, e_, time_, rest___] :=
- Block [ {},
- Needs[ "SignalProcessing`Support`MDSupport`" ];
- multiDTransform[makeobjectfun, transform,
- transq, e, time, rest] ]
-
- (* MultT *)
- MultT[transq_, t1_, t2_, lowerlimit_:0] :=
- Transform[ TheFunction[t1] TheFunction[t2],
- FindRMinus[ GetRMinus[t1], GetRMinus[t2], lowerlimit ],
- FindRPlus[ GetRPlus[t1], GetRPlus[t2], lowerlimit ] ] /;
- transq[t1] && transq[t2]
-
- Format[MultT[transq_, t1_, t2_, lowerlimit_:0]] := t1 t2
-
- (* ScaleT *)
- ScaleT[transq_, t_, c_] :=
- Transform[ c TheFunction[t], GetRMinus[t], GetRPlus[t] ] /;
- transq[t]
-
- ScaleT[transq_, ScaleT[transq_, t_, c_], d_] := ScaleT[transq, t, c d]
-
- Format[ScaleT[transq_, t_, c_]] := c t
-
- (* SubstituteForT *)
- SubstituteForT[transq_, t_, s_, news_] :=
- Transform[ TheFunction[t] /. s -> news, GetRMinus[t], GetRPlus[t] ] /;
- transq[t]
-
- Format[SubstituteForT[transq_, t_, s_, news_]] :=
- SequenceForm[ {t}, Subscript[s -> news] ]
-
- (* SubT *)
- SubT[transq_, t1_, t2_, lowerlimit_:0] :=
- Transform[ TheFunction[t1] - TheFunction[t2],
- FindRMinus[ GetRMinus[t1], GetRMinus[t2], lowerlimit ],
- FindRPlus[ GetRPlus[t1], GetRPlus[t2], lowerlimit ] ] /;
- transq[t1] && transq[t2]
-
- Format[SubT[transq_, t1_, t2_, lowerlimit_:0]] := t1 - t2
-
- (* Transform *)
- Transform/: TheFunction[ Transform[fun_, rest___] ] := fun
-
- (* TransformDialogue *)
- TransformDialogue[ Definition, options_, operator_, oldexpr_, trans_ ] :=
- Block [ {},
- If [ InformUserQ[options],
- Print[ "( after using ", operator,
- " to apply the definition to" ];
- Print[ " ", oldexpr ];
- Print[ " to find its transform"];
- Print[ " ", trans, " )" ] ];
- trans ]
-
-
- (* TransformFixUp: resolves functions according to user's transform pairs *)
- TransformFixUp[ n_, z_, {}, head_, triplet_, name_, low_, up_ ] := {}
-
- makefixuprules[n_, triplet_, name_] :=
- { (a_ -> b_) :> ToCollection[{}] /; FreeQ[a, n],
- (a_ :> b_) :> ToCollection[{}] /; FreeQ[a, n],
- If [ TrueQ[triplet],
- (a_ -> {b_, rm_, rp_}) :> (a -> Transform[b, rm, rp]),
- (a_ -> {b_, rm_, rp_}) :>
- MyMessage[ TransformFixUp::notriplet, a -> b,
- a -> {b, rm, rp}, a -> b, name ] ],
- If [ TrueQ[triplet],
- (a_ :> {b_, rm_, rp_}) :> (a :> Transform[b, rm, rp]),
- (a_ :> {b_, rm_, rp_}) :>
- MyMessage[ TransformFixUp::notriplet, a :> b,
- a :> {b, rm, rp}, a :> b, name ] ],
- x_ :> x }
-
- TransformFixUp[ n_, z_, options_, head_, triplet_, name_, low_, up_ ] :=
- Block [ {fixrules, nolookup, op, rules = {}, validop},
- op = Replace[TransformLookup, options];
- nolookup = SameQ[op, {}] || SameQ[op, TransformLookup];
- If [ ! nolookup,
- fixrules = makefixuprules[n, triplet, name];
- validop = Map[ Replace[#1, fixrules]&, ToList[op] ];
- If [ ! EmptyQ[validop],
- rules = makepostrules[ validop, n, z, head,
- triplet, name, low, up ] ] ];
-
- rules ]
-
- TransformFixUp[ trans_, n_, z_, options_, head_, triplet_, name_, low_, up_ ] :=
- Block [ {rules},
-
- rules = TransformFixUp[ n, z, options, head,
- triplet, name, low, up ];
- If [ EmptyQ[rules],
- trans,
- ReplaceRepeated[trans, rules] ] ]
-
-
- SetAttributes[makepostrules, Listable]
-
- (* rules for transforms returning triplets (forward z- and Laplace) *)
- makepostrules[ a_ -> b_Transform, n_, z_, h_, True, transname_, low_, up_ ] :=
- ( h[ a, n, z, __ ] :> b )
-
- makepostrules[ a_ -> b_, n_, z_, h_, True, transname_, low_, up_ ] :=
- ( h[ a, n, z, __ ] :> Transform[b, low, up] )
-
- makepostrules[ a_ :> b_Transform, n_, z_, h_, True, transname_, low_, up_ ] :=
- ( h[ a, n, z, __ ] :> b )
-
- makepostrules[ a_ :> b_, n_, z_, h_, True, transname_, low_, up_ ] :=
- ( h[ a, n, z, __ ] :> Transform[b, low, up] )
-
- (* rules for transforms not returning triplets *)
- makepostrules[ a_ -> b_, n_, z_, h_, False, transname_, low_, up_ ] :=
- ( h[ a, n, z, __ ] :> b )
-
- makepostrules[ a_ :> b_, n_, z_, h_, False, transname_, low_, up_ ] :=
- ( h[ a, n, z, __ ] :> b )
-
- (* invalid rule encountered *)
- makepostrules[ x_, n_, z_, h_, triplet_, transname_, low_, up_ ] :=
- Message[ TransformLookup::notrule, x, transname ]
-
-
-
-
- (* B. F R E Q U E N C Y R E S P O N S E *)
-
- (* getlocation -- Delta functions, a special case of continuous plotting *)
-
- getlocation[ c_. Delta[a_. t_ + b_.], t_ ] := N[ - b / a ]
-
-
- (* MagPhasePlot *)
-
- MagPhasePlot/: Options[MagPhasePlot] :=
- { DisplayFunction :> $DisplayFunction,
- Domain -> Continuous, DomainScale -> Linear,
- MagRangeScale -> Linear, PhaseRangeScale -> Degree,
- PlotRange -> All }
-
- badoptions[ MagPhasePlot ] =
- { Domain, DomainScale, MagRangeScale, PhaseRangeScale }
-
- (* Supporting local functions *)
-
- fmag[w0_Real, omega_, fresp_, logflag_] :=
- Block [ {value},
- value = Abs[ GetValue[fresp, omega, w0] ];
- If [ logflag, 20 Log[10, value], value ] ]
-
- fmag[w01_Real, omega1_, w02_Real, omega2_, fresp_, logflag_] :=
- Block [ {value},
- value = Abs[ GetValue[fresp, {omega1, omega2}, {w01, w02}] ];
- If [ logflag, 20 Log[10, value], value ] ]
-
- fphase[w0_Real, omega_, fresp_, indegrees_] :=
- Block [ {value},
- value = GetValue[fresp, omega, w0];
- If [ ZeroQ[value], Return[0.] ];
- value = N [ Arg[value] ];
- Chop[ If [ indegrees, value / Degree, value ] ] ]
-
- fphase[w01_Real, omega1_, w02_Real, omega2_, fresp_, indegrees_] :=
- Block [ {value},
- value = GetValue[fresp, {omega1, omega2}, {w01, w02}];
- If [ ZeroQ[value], Return[0.] ];
- value = N [ Arg[value] ];
- Chop[ If [ indegrees, value / Degree, value ] ] ]
-
- waxis[w_Real, logflag_] := If [ logflag, Log[10, w], w ]
-
- loglabel[w_] := ToString[StringForm["````", Subscripted[Log[10]], w]]
-
-
- (* Supporting rule bases for MagPhase Plot *)
- (* Interprets signal processing expressions so that they can be plotted *)
-
- ScaleAxisRules = {
- ScaleAxis[sc_, w_][x_] :>
- Block [ {i, result},
- result = x /. w -> sc w;
- For [ i = 1, i < sc, i++,
- result += x /. w -> (sc w + i 2 Pi) ];
- result ]
- }
-
- PeriodicRules = {
-
- Periodic[k_, w_Symbol][freqresp_] :>
- Block [ {fresp},
- fresp = TheFunction[freqresp /. ScaleAxisRules];
- fresp + ( fresp /. w -> w + k ) +
- ( fresp /. w -> w - k ) ],
-
- Periodic[{k1_, k2_}, {w1_Symbol, w2_Symbol}][freqresp_] :>
- Block [ {dim1, dim2, fresp, rules},
- fresp = TheFunction[freqresp /. ScaleAxisRules];
- rules = { w1 -> w1 + dim1 k1,
- w2 -> w2 + dim2 k2 };
- Apply[Plus,
- Table[fresp /. rules,
- {dim1, -1, 1}, {dim2, -1, 1}]] ]
-
- }
-
-
- (* Magnitude and phase plots for 2-D signals (must be defined before 1-D) *)
- MagPhasePlot[freqresp_, {w1_Symbol, wmin1_, wmax1_},
- {w2_Symbol, wmin2_, wmax2_}, op___] :=
- Block [ {},
- Needs[ "SignalProcessing`Support`MDPlotting`" ];
- magPhasePlot2D[ freqresp,
- {w1, wmin1, wmax1},
- {w2, wmin2, wmax2},
- op] ]
-
- (* Magnitude and phase plots for 1-D signals *)
- MagPhasePlot[ freqresp_, {w_Symbol, wminimum_, wmaximum_}, op___ ] :=
- Block [ {arglist, bogus, degrees, deltafuns, deltaplot, disp, fresp,
- ftemp, i, list, logdomain, logrange, magfun,
- magplot = NullPlot, max, maxdec, min, mindec, omega,
- omitplot, options, period, phasefun, phaseplot = NullPlot,
- plotlabel, plotoptions, plotrange, points, ticks,
- varlist, varlist2, wmin, wmax, wstr, xlabel},
-
- (* Check for errors in arguments *)
-
- If [ N [ wminimum > wmaximum ],
- Message[Plot::limits, {w, wminimum, wmaximum}];
- Return[Null] ];
-
- (* Set up for plotting *)
-
- Off[Power::infy, Infinity::indt];
- options = ToList[op] ~Join~ Options[MagPhasePlot];
- wstr = StripPackage[w];
- plotoptions = RemoveOptions[options, badoptions[MagPhasePlot]];
-
- (* Separate functions into delta functions and other *)
- (* functions keeping in mind the interval of interest *)
- {deltafuns, fresp} =
- SignalCleanup[freqresp, w, wminimum, wmaximum];
-
- (* If frequency response is zero, plot deltas and exit *)
-
- If [ ZeroQ[fresp],
- arglist = Join[ plotoptions,
- { Axes -> axesdefaultvalue,
- AxesLabel -> { wstr, " " },
- PlotLabel -> "Magnitude Response",
- PlotRange -> All,
- Ticks -> Automatic } ];
- PrependTo[ arglist,
- DeltaPlot[ deltafuns, w, N[wminimum],
- N[wmaximum], 0.0, 1.0 ] ];
- magplot = Apply[ Show, arglist ];
- Return[ { magplot, phaseplot } ] ];
-
- (* Rename the frequency variable *)
-
- fresp = fresp /. ( w -> omega );
-
- (* Find valueless symbols other than frequency variable *)
- (* The first parameter in Summation is a local symbol *)
-
- ftemp = fresp /.
- ( Summation[n_, l_, u_, inc_][expr_] :>
- bogus[l, u, inc, GetVariables[expr /. n -> l]] );
- varlist = Select[GetVariables[ftemp], (! SameQ[#1, omega])&];
- If [ ! EmptyQ[varlist],
- Message[ MagPhasePlot::unresolved, Sort[ varlist ] ];
- fresp = fresp /. Map[(#1 -> 1)&, varlist] ];
-
- (* Set up for plotting *)
-
- logrange = SameQ[Replace[MagRangeScale, options], Log];
- plotlabel = If [ logrange,
- "Magnitude Response (dB)",
- "Magnitude Response" ];
-
- logdomain = SameQ[Replace[DomainScale, options], Log];
- If [ ZeroQ[fresp], logdomain = False ];
- xlabel = If [ logdomain, loglabel[wstr], wstr ];
- If [ logdomain,
- Off[General::dby0];
- mindec = Floor[ N[ Log[10, wminimum] ] ];
- maxdec = Ceiling[ N[ Log[10, wmaximum] ] ];
- wmin = 10^mindec;
- wmax = 10^maxdec;
- ticks = {};
- For [ i = mindec, i < maxdec, i++,
- ticks = ticks ~Join~
- {i, i + 0.3, i + 0.7} ];
- ticks = { ticks, Automatic };
- On[General::dby0],
-
- wmin = wminimum;
- wmax = wmaximum;
- ticks = Automatic ];
-
- (* Magnitude plot with Delta functions (if any) *)
-
- omitplot = SameQ[Replace[MagRangeScale, options], Null];
- If [ ! omitplot,
- arglist = Join[ { { waxis[w, logdomain],
- fmag[w, omega, fresp, logrange] },
- { w, wmin, wmax },
- DisplayFunction -> Identity },
- plotoptions ];
- magplot = Apply[ ParametricPlot, arglist ];
-
- (* determine the range of the magnitude plot *)
-
- plotrange = Replace[PlotRange, options];
- list = Map[ Second, magplot[[1]] [[1]] [[1]] [[1]] ];
- list = Select[list, NumberQ]; (* remove infinities *)
- max = Max[list];
- min = Min[list];
-
- If [ logrange,
- max = 20 Ceiling[max / 20];
- min = 20 Floor[min / 20];
- If [ ! SameQ[deltafuns, Null] && max < 20,
- max = 20 ];
- If [ ( max - min ) > 100,
- min = max - 100 ];
- If [ SameQ[plotrange, All],
- plotrange = {min, max} ] ];
-
- If [ ! logrange && max == 0, max = 1 ];
-
- (* complete magnitude plot -- add in delta functions *)
-
- magplot =
- Apply[ Show,
- Join[ { DeltaPlot[deltafuns, omega, wmin, wmax,
- magplot, min, max] },
- plotoptions,
- { PlotLabel -> plotlabel,
- PlotRange -> plotrange,
- Ticks -> ticks,
- AxesLabel -> { xlabel, " " } } ] ] ];
-
- (* Phase plot *)
-
- omitplot = SameQ[Replace[PhaseRangeScale, options], Null];
- If [ ! omitplot,
- degrees = SameQ[Replace[PhaseRangeScale, options], Degree];
- plotlabel = If [ degrees,
- "Phase Response (degrees)",
- "Phase Response (radians)" ];
- arglist = Join[ { { waxis[w, logdomain],
- fphase[w, omega, fresp, degrees] },
- { w, wmin, wmax } },
- { PlotRange -> All },
- plotoptions,
- { PlotLabel -> plotlabel,
- Ticks -> ticks,
- AxesLabel -> { xlabel, " " } } ];
- phaseplot = Apply[ ParametricPlot, arglist ] ];
-
- (* Clean up and return the two plots as graphics objects *)
- On[Power::infy, Infinity::indt];
- { magplot, phaseplot } ]
-
-
- (* C. R O O T - L O C U S P L O T T I N G *)
-
- Options[RootLocus] :=
- { Axes -> axesdefaultvalue, AxesLabel -> { "Re", "" },
- DisplayFunction :> $DisplayFunction, PlotRange -> All }
-
- initPlot[ startpoly_, endpoly_, z_, text_, multtext_ ] :=
- Block [ {coords, endplot, rootlist, startplot},
- rootlist = GetRootList[ startpoly, z, N ];
- coords = ComplexTo2DCoordList[rootlist];
- startplot = PointwisePlot[ coords, text, multtext ];
- rootlist = GetRootList[ endpoly, z, N ];
- coords = ComplexTo2DCoordList[rootlist];
- endplot = PointwisePlot[ coords, text, multtext ];
- {startplot, endplot} ]
-
- localplot[ label_, oplist_, plots___ ] :=
- Apply[ Show, Join[ { plots }, { PlotLabel -> label }, oplist ] ]
-
- oneBranch[ rule_, ktemp_, 0, l_. Pi, kstep_, z_, psize_ ] :=
- Block [ {fun, simprule},
- simprule = simplifyrule[ktemp];
- fun = Replace[z, rule] /. simprule;
- fun = ( fun /. ktemp -> (Pi ktemp) ) /. simprule;
- ParametricPlot[ reImPair[fun],
- {ktemp, 0, l},
- PlotStyle -> Thickness[psize/3],
- DisplayFunction -> Identity ] ]
-
- oneBranch[ rule_, ktemp_, kmin_, kmax_, kstep_, z_, psize_ ] :=
- Block [ {fun},
- fun = Replace[z, rule] /. simplifyrule[ktemp];
- ParametricPlot[ reImPair[fun],
- {ktemp, kmin, kmax},
- PlotStyle -> Thickness[psize/3],
- DisplayFunction -> Identity ] ]
-
- oneLocus[ poly_, ktemp_, kmin_, kmax_, kstep_, z_, psize_ ] :=
- Block [ {coords, roots},
- roots = Table[ ToCollection[ GetRootList[poly, z, N] ],
- {ktemp, kmin, kmax, kstep} ];
- coords = Map[Point, ComplexTo2DCoordList[roots]];
- Show[ Prepend[ Map[ oneBranch[#, ktemp, kmin,
- kmax, kstep, z, psize]&,
- Select[ Solve[poly == 0, z], tractableQ ] ],
- Graphics[ Prepend[coords, PointSize[psize]] ] ],
- DisplayFunction -> Identity ] ]
-
- polyorder[poly_, z_] := Exponent[poly, z] + Exponent[poly /. z -> z^-1, z]
-
- reImPair[f_] := { Re[f], Im[f] }
-
- simplifyrule[ ktemp_ ] :=
- Block [ {},
- Exp[ Complex[0, b_] Pi c_. ktemp ] :>
- Exp[ I Mod[b c, 2] Pi ktemp ] /;
- RationalQ[b c] || RealQ[b c] ]
-
- (* Tractable means a closed-form solution of less than 30 terms *)
- tractableQ[ ToRules[x_] ] := False
- tractableQ[ {z_ -> a_} ] := ( Count[a, b_, Infinity] < 30 )
- tractableQ[ x_ ] := False
-
- RootLocus[ poly_, z_Symbol, {k_Symbol, start_, end_, step_:1}, op___ ] :=
- MyMessage[ RootLocus::constant,
- { NullPlot, NullPlot, NullPlot }, poly, z ] /;
- FreeQ[poly, z]
-
- RootLocus[ poly_, z_Symbol, {k_Symbol, start_, end_, step_:1}, op___ ] :=
- Block [ {Epplot = NullPlot, Ezplot = NullPlot, Szplot = NullPlot,
- Spplot = NullPlot, denom, expdenom, expnumer, factor,
- kmax, kmin, kstep, ktemp, newpoly, numer, options,
- order, pointthickness, pplot = NullPlot, zplot = NullPlot},
-
- (* Check numeric arguments *)
- If [ ! Apply[And, Map[RealValuedQ, N[{start, end, step}]]],
- Message[ RootLocus::notnum, {start, end, step} ];
- Return[ NullPlot ] ];
-
- (* Disable compilation warnings *)
- If [ TrueQ[ $VersionNumber >= 2.0 ],
- Off[ ParametricPlot::ppcom ] ];
-
- (* Determine the numeric range for the free param k *)
- options = Flatten[ToList[op]] ~Join~ Options[RootLocus];
- If [ N[start < end],
- kmin = start; kmax = end,
- kmin = end; kmax = start ];
- kstep = If [ Positive[step], step, -step ];
-
- (* Put the function over a common denominator *)
- newpoly = Together[poly] /. k -> ktemp;
-
- (* Get numerator and denominator-- expand them for speed *)
- (* note: expand also hides errors in Solve under Mma 1.2 *)
- expnumer = Expand[Numerator[newpoly]];
- expdenom = Expand[Denominator[newpoly]];
- If [ TrueQ[ $VersionNumber >= 2.0 ],
- numer = Factor[expnumer]; denom = Factor[expdenom],
- numer = expnumer; denom = expdenom ];
-
- (* Compute the thickness of the points in the locus *)
- order = 1;
- If [ MixedPolynomialQ[expnumer, z],
- order += polyorder[expnumer, z] ];
- If [ MixedPolynomialQ[expdenom, z],
- order += polyorder[expdenom, z] ];
- factor = N[ Log[10, order (kmax - kmin) / kstep] ];
- pointthickness = Min[0.015, Max[0.024 / factor, 0.003]];
-
- (* Compute the initial and final zero plot *)
- If [ ! FreeQ[numer, z],
- {Szplot, Ezplot} =
- initPlot[ (numer /. ktemp -> kmin),
- (numer /. ktemp -> kmax), z, "O", "*O*" ] ];
-
- (* Compute the initial and final pole plot *)
- If [ ! FreeQ[denom, z],
- {Spplot, Epplot} =
- initPlot[ (denom /. ktemp -> kmin),
- (denom /. ktemp -> kmax), z, "X", "*X*" ] ];
-
- (* Compute the zero locus *)
- If [ ! FreeQ[numer, z],
- zplot = oneLocus[ numer, ktemp, kmin, kmax, kstep,
- z, pointthickness ] ];
-
- (* Compute the pole locus *)
- If [ ! FreeQ[denom, z],
- pplot = oneLocus[ denom, ktemp, kmin, kmax, kstep,
- z, pointthickness ] ];
-
- (* Enable compilation warnings *)
- If [ TrueQ[ $VersionNumber >= 2.0 ],
- On[ ParametricPlot::ppcom ] ];
-
- { localplot[ "Pole-Zero Diagram at Start", options,
- Szplot, Spplot],
- localplot[ "Root Locus", options, Szplot, Spplot,
- Ezplot, Epplot, zplot, pplot ],
- localplot[ "Pole-Zero Diagram at End", options,
- Ezplot, Epplot ] } ]
-
-
- (* D. P O L E - Z E R O P L O T T I N G *)
-
- (* 1. S u p p o r t i n g F u n c t i o n s *)
-
- (* a. For graphing the region of convergence as an annulus *)
-
- (* findline *)
- findline[ycoord_, cos_, sin_] :=
- Block [ {xcoord},
- xcoord = N[ Sqrt[1 - ycoord^2] ];
- { rotate[xcoord, ycoord, cos, sin],
- rotate[-xcoord, ycoord, cos, sin] } ]
-
- makeannulus[{rm_, rp_}] :=
- ToCollection[ CirclePS[rp, Dashing[{0.05,0.05}]],
- ShadedAnnulus[rm, rp],
- CirclePS[rm, Dashing[{0.05,0.05}]] ]
-
- (* makefilllines *)
- (* Fill lines for a unit circle: tilt in degrees, *)
- (* and angular separation ~= 360 / numsamples *)
- makefilllines[tilt_, numsamples_] :=
- Block [ {costilt, maxi, sintilt},
- costilt = N[ Cos[ tilt Pi / 180 ] ];
- sintilt = N[ Sin[ tilt Pi / 180 ] ];
- maxi = Floor[numsamples / 4] - 1;
-
- Join[ Table[ findline[4 i / numsamples, costilt, sintilt],
- {i, 0, maxi} ],
- Table[ findline[-1 + 4 i / numsamples, costilt, sintilt],
- {i, 1, maxi} ] ] ]
-
- (* rotate *)
- rotate[x_, y_, cos_, sin_] := { x cos - y sin, x sin + y cos }
-
- (* splitline *)
- splitline[ap_, rm_, slope_, {point1_, point2_}] :=
- Block [ {b, sqrtterm, x1, x2, y1, y2},
- b = Second[point1] - slope First[point1];
- sqrtterm = Re[ N[ Sqrt[ rm^2 ap - b^2 ] ] ];
- x1 = ( - slope b + sqrtterm ) / ap;
- x2 = ( - slope b - sqrtterm ) / ap;
- y1 = slope x1 + b;
- y2 = slope x2 + b;
- {{point1, {x1, y1}}, {{x2, y2}, point2}} ]
-
- (* ShadedAnnulus *)
- AngularTilt = 20 (* default values *)
- NumSamples = 72
- FillLines = makefilllines[AngularTilt, NumSamples]
- FillLineSlope = N[ Tan[AngularTilt Pi / 180] ] (* slope of all fill lines *)
-
- ShadedAnnulus[rm_, rp_] :=
- ShadedAnnulus[ rm, rp, AngularTilt, FillLines, FillLineSlope ]
-
- ShadedAnnulus[rm_, rp_, tilt_] :=
- ShadedAnnulus[ rm, rp, tilt, makefilllines[tilt, NumSamples] ]
-
- ShadedAnnulus[rm_, rp_, tilt_, filllines_] :=
- ShadedAnnulus[ rm, rp, tilt, filllines, N[Tan[tilt Pi / 180]] ]
-
- ShadedAnnulus[0, rp_, tilt_, filllines_, slope_] :=
- Graphics[ Map[Line, rp filllines] ]
-
- ShadedAnnulus[rm_, rp_, tilt_, filllines_, slope_] :=
- Block [ {ap, fillpattern, i, intersections,
- maxi, newlines, numlines, split, xnew},
-
- fillpattern = (rp filllines);
- numlines = Length[filllines];
-
- (* Find intersection of lines with rminus circle *)
- (* Equations: x^2 + y^2 = r^2 and y = m x + b *)
- (* reduces to quadratic a' x^2 + b' x + c', with *)
- (* a' = (m^2 + 1), b' = 2 m b, c' = b^2 - r^2 *)
- ap = ( slope^2 + 1 );
-
- (* Intersection of first line, zero intercept *)
- xnew = N[ - rm / Sqrt[ap] ];
- newlines = { { {xnew, slope xnew},
- Second[ First[fillpattern] ] } };
- fillpattern[[1]] = { First[ First[fillpattern] ],
- {-xnew, - slope xnew} };
-
- (* Find intersections of other lines *)
- intersections = ( numlines + 2 ) rm / rp;
- maxi = Floor[ intersections / 2];
- For [ i = 1, i <= maxi, i++,
- split = splitline[ ap, rm, slope, fillpattern[[i + 1]] ];
- AppendTo[ newlines, First[split] ];
- fillpattern[[i + 1]] = Second[split];
- split = splitline[ ap, rm, slope,
- fillpattern[[numlines - i + 1]] ];
- AppendTo[ newlines, First[split] ];
- fillpattern[[numlines - i + 1]] = Second[split] ];
-
- Graphics[ Map[Line, Join[fillpattern, newlines]] ] ]
-
- (* ROCPlot *)
- Options[ROCPlot] :=
- { Axes -> axesdefaultvalue, AspectRatio -> 1,
- DisplayFunction :> $DisplayFunction }
-
- ROCPlot[{rm_, Infinity}, options___] :=
- Block [ {oplist, plotrange, pmax, rmax},
- oplist = ToList[options];
- plotrange = Replace[PlotRange, oplist];
- pmax = If [ SameQ[plotrange, PlotRange],
- N[ Sqrt[2] rm ],
- Max[ N[Sqrt[2] rm],
- Abs[Select[N[Flatten[plotrange]], NumberQ]] ] ];
- rmax = 1.75 pmax; (* pmax times some number > Sqrt[2] *)
- Apply [ Show,
- Join [ {makeannulus[{rm, rmax}]},
- oplist,
- Options[ROCPlot],
- PlotRange -> {{-pmax, pmax}, {-pmax, pmax}} ] ] ]
-
- ROCPlot[{rm_, rp_}, options___] :=
- Apply[ Show,
- Join[{makeannulus[{rm, rp}]},
- ToList[options],
- Options[ROCPlot]] ]
-
- (* b. other supporting functions *)
-
-
-
- (* printInvalidPoles *)
- printInvalidPoles[invalidROClist_, polelist_, rm_, rp_, op_, smallestPole_] :=
- Block [ {rocs, pairs},
- Message[PoleZeroPlot::invalidROC, invalidROClist, rm, rp];
- roclist = Sort[ Map[ op, polelist ] ];
- pairs = Transpose[ { Prepend[roclist, smallestPole],
- Append[roclist, Infinity] } ];
- Print[ PoleZeroPlot::posROC, Rationalize[pairs] ] ]
-
- (* InvalidZPoleQ *)
- InvalidZPoleQ[rminus_, r_, rplus_] := TrueQ[rminus < Abs[N[r]] < rplus]
-
- (* InvalidSPoleQ *)
- InvalidSPoleQ[rminus_, r_, rplus_] := TrueQ[rminus < Re[N[r]] < rplus]
-
- (* MyRationalPolyQ *)
- MyRationalPolyQ[f_, z_] :=
- MixedPolynomialQ[ Expand[Numerator[f]], z ] &&
- MixedPolynomialQ[ Expand[Denominator[f]], z ]
-
- (* pzOptions *)
- pzOptions[ op___ ] :=
- Join[ ToList[op],
- ToList[ Options[PoleZeroPlot] ],
- { DisplayFunction :> $DisplayFunction } ]
-
- (* SeparableQ *)
- Separate[x_ y_, {var1_, var2_}] := { x, y } /; FreeQ[x, var2] && FreeQ[y, var1]
- Separable[x_ y_, {var1_, var2_}] := True /; FreeQ[x, var2] && FreeQ[y, var1]
- SeparableQ[p_, vars_] := TrueQ[ Separable[p, vars] ]
-
- (* 2. D r i v e r *)
-
- Options[ PoleZeroPlot ] :=
- { Dialogue -> True, DisplayFunction :> $DisplayFunction }
-
- (* One-dimensional pole-zero plotting for z-transform objects *)
- PoleZeroPlot[ ZTransData[f_, Rminus[rm_], Rplus[rp_], ZVariables[z_]], op___ ] :=
- If [ Length[z] <= 2,
- PoleZeroPlot[ f, z, rm, rp, True, op ],
- Message[PoleZeroPlot::multidimensions] ]
-
- (* One-dimensional pole-zero plotting for Laplace transform objects *)
- PoleZeroPlot[ LTransData[f_, Rminus[rm_], Rplus[rp_], LVariables[s_]], op___ ] :=
- If [ Length[s] <= 2,
- PoleZeroPlot[ f, s, rm, rp, False, op ],
- Message[PoleZeroPlot::multidimensions] ]
-
- (* Two-dimensional pole-zero plotting driver *)
- (* -- separable transforms generate two separable pole-zero plots *)
- (* -- symmetric transfer function f only requires one plot *)
- (* -- otherwise, project z1 onto z2 and z2 onto z1 *)
- PoleZeroPlot[f_, {z1_Symbol, z2_Symbol}, {rm1_, rm2_}, {rp1_, rp2_}, rest__ ] :=
- Block [ {},
- Needs[ "SignalProcessing`Support`MDPlotting`" ];
- poleZeroPlot2D[f, {z1, z2}, {rm1, rm2}, {rp1, rp2}, rest ] ]
-
-
- (* 3. P l o t t i n g R o u t i n e s f o r z - d o m a i n *)
-
- PoleZeroPlot[f_, z_Symbol, rm_, rp_, True, op___] :=
- Block [ {abspolelist, fillplot, invalidROClist, options,
- polelist, poleplot, rmax, rminus, rplus, rminuscircle,
- rpluscircle, unitcircle, xlabel, ylabel, zeroplot,
- zstr, ztrans},
-
- options = pzOptions[op];
-
- rmax = 1; (* default values *)
- unitcircle = CirclePS[1];
-
- rminus = N[rm]; (* numeric approximation *)
- rplus = N[rp];
-
- (* Analyze the z-transform function *)
-
- ztrans = Together[f];
- If [ ! MyRationalPolyQ[ztrans, z],
- Message[ PoleZeroPlot::notrational ];
- Message[ PoleZeroPlot::noplot ];
- Return[{}] ];
-
- (* Find the poles and zeroes *)
-
- zerolist = GetRootList[Numerator[ztrans], z];
- polelist = GetRootList[Denominator[ztrans], z];
- If [ InformUserQ[options],
- Print[ " " ];
- Print[ "The zeroes are: ", zerolist ];
- Print[ "The poles are: ", polelist ] ];
-
- (* Check consistency of poles with region of convergence *)
- (* Exit function if inconsistency encountered *)
-
- invalidROClist = Select[ polelist,
- InvalidZPoleQ[rminus, #1, rplus]& ];
- If [ ! SameQ[invalidROClist, {}],
- printInvalidPoles[ invalidROClist, polelist,
- rm, rp, Abs, 0 ];
- Return[polelist] ];
-
- (* Compute plot of poles and zeroes *)
- (* Determine the plot's its circular extent *)
-
- rmax = Max[ Join[ {rmax},
- Map[Abs, ToList[N[zerolist]]],
- Map[Abs, ToList[N[polelist]]] ] ];
-
- zeroplot = If [ EmptyQ[zerolist],
- NullPlot,
- PointwisePlot[ComplexTo2DCoordList[zerolist],
- "O", "*O*"] ];
-
- poleplot = If [ EmptyQ[polelist],
- NullPlot,
- PointwisePlot[ComplexTo2DCoordList[polelist],
- "X", "*X*"] ];
-
- (* Build region of convergence plot *)
-
- If [ NumberQ[rminus],
- rminuscircle = CirclePS[ rminus, Dashing[{0.05,0.05}] ];
- rmax = Max[rmax, Abs[rminus]],
- rminuscircle = NullPlot ];
-
- If [ NumberQ[rplus],
- rpluscircle = CirclePS[ rplus, Dashing[{0.05,0.05}] ];
- rmax = Max[rmax, Abs[rplus]],
- rpluscircle = NullPlot ];
-
- rplus = N[ If[ SameQ[rp, Infinity], 2 rmax, rp] ]; (* kludge *)
-
- fillplot = If [ NumberQ[rminus] && NumberQ[rplus],
- ShadedAnnulus[rminus, rplus],
- NullPlot ];
-
- (* Send the contour plot to the screen *)
- rmax *= 1.25;
- zstr = StripPackage[z];
- xlabel = "Re " ~StringJoin~ zstr;
- ylabel = "Im " ~StringJoin~ zstr;
-
- options = Join[ RemoveOptions[options, {Dialogue}];
- { PlotRange -> {{-rmax, rmax}, {-rmax, rmax}},
- AspectRatio -> 1,
- AxesLabel -> { xlabel, ylabel },
- Axes -> axesdefaultvalue } ];
-
- Show [unitcircle, rminuscircle, rpluscircle,
- fillplot, zeroplot, poleplot, options];
-
- polelist ]
-
-
- (* 4. P l o t t i n g R o u t i n e s f o r s - d o m a i n *)
-
- (* One-dimensional pole-zero plotting *)
- PoleZeroPlot[f_, s_Symbol, rm_, rp_, False, op___] :=
- Block [ {invalidROClist, jj, ltrans, numericlist, options,
- points, rminus, rminusline, rplus, rplusline, polelist,
- poleplot, shading, skew, sstr, x1, x2, xlabel, xmin,
- xmax, w, ylabel, ymin, ymax, ystep, zerolist, zeroplot},
-
- options = pzOptions[op];
-
- ltrans = Together[f]; (* default values *)
- rminusline = rplusline = shading = NullPlot;
-
- rminus = N[rm]; (* numeric approximation *)
- rplus = N[rp];
-
- If [ ! RationalPolynomialQ[ltrans, s],
- Message[ PoleZeroPlot::notrational ];
- Message[ PoleZeroPlot::noplot ];
- Return[{}] ];
-
- zerolist = GetRootList[Numerator[ltrans], s];
- polelist = GetRootList[Denominator[ltrans], s];
- If [ InformUserQ[options],
- Print[ " " ];
- Print[ "The zeroes are: ", zerolist ];
- Print[ "The poles are: ", polelist ] ];
-
- (* Check consistency of poles with region of convergence *)
- (* Exit function if inconsistency encountered *)
-
- invalidROClist = Select[ polelist,
- InvalidSPoleQ[rminus, #1, rplus]& ];
- If [ ! SameQ[invalidROClist, {}],
- printInvalidPoles[ invalidROClist, polelist,
- rm, rp, Re, -Infinity ];
- Return[polelist] ];
-
- numericlist = ToList[N[zerolist]] ~Join~ ToList[N[polelist]];
-
- (* Determine extent of pole-zero plot *)
- (* Mathematica does not do this automatically *)
-
- xmin = Min[Re[numericlist]];
- xmax = Max[Re[numericlist]];
-
- If [ NumberQ[rminus], xmin = Min[xmin, rminus] ];
- If [ NumberQ[rplus], xmax = Max[xmax, rplus] ];
-
- xmin = ( 1.0 - 0.25 Sign[xmin] ) xmin; (* move toward -Inf *)
- xmax = ( 1.0 + 0.25 Sign[xmax] ) xmax; (* move toward +Inf *)
- If [ ZeroQ[xmax], xmax = -.1 xmin ];
-
- ymin = Min[Im[numericlist]];
- ymax = Max[Im[numericlist]];
-
- If [ SameQ[ymin, ymax],
- ymin = ymin - 1;
- ymax = ymax + 1,
- ymin = ( 1.0 - 0.25 Sign[ymin] ) ymin;
- ymax = ( 1.0 + 0.25 Sign[ymax] ) ymax ];
-
- (* Build convergence boundaries (lines) *)
-
- If [ NumberQ[rminus],
- rminusline = Graphics[ { Dashing[{0.05, 0.05}],
- Line[{{rminus, ymin},
- {rminus, ymax}}] } ] ];
-
- If [ NumberQ[rplus],
- rplusline = Graphics[ { Dashing[{0.05, 0.05}],
- Line[{{rplus, ymin},
- {rplus, ymax}}] } ] ];
-
- If [ (NumberQ[rminus] || SameQ[rm, -Infinity]) &&
- (NumberQ[rplus] || SameQ[rp, Infinity]),
- x1 = Max[rminus, xmin];
- x2 = Min[rplus, xmax];
- ystep = ( ymax - ymin ) / 15;
- skew = 4 ystep;
- points = Table[ Line[{{x1,jj}, {x2,jj+skew}}],
- {jj, ymin - skew, ymax, ystep} ];
- shading = Graphics[ points ] ];
-
- (* Build plots of zeroes as O's and poles as X's *)
-
- If [ EmptyQ[zerolist],
- zeroplot = NullPlot,
- zeroplot = PointwisePlot[ ComplexTo2DCoordList[zerolist],
- "O", "*O*" ] ];
-
- If [ EmptyQ[polelist],
- poleplot = NullPlot,
- poleplot = PointwisePlot[ ComplexTo2DCoordList[polelist],
- "X", "*X*" ] ];
-
- (* Plot pole-zero diagram, all together now *)
- sstr = StripPackage[s];
- xlabel = "Re " ~StringJoin~ sstr;
- ylabel = "Im " ~StringJoin~ sstr;
-
- options = Join[ RemoveOptions[options, {Dialogue}];
- { PlotRange -> {{xmin, xmax}, {ymin, ymax}},
- AxesLabel -> { xlabel, ylabel },
- Axes -> axesdefaultvalue } ];
-
- Show [ rminusline, rplusline, shading,
- zeroplot, poleplot, options ];
-
- polelist ]
-
-
-
- (* E. S T A B I L I T Y A N A L Y S I S *)
-
- (*
- The trick here for m-D transforms is to replace any occurrence of
- the transform variables in the ROC with the value of the boundary at
- which the ROC becomes unstable. For the Laplace transform, this is
- the line Re(s) = 0. For the z-transform, this is the m-D unit circle;
- because the ROC in the z-transform are real numbers (absolute values
- of complex-valued quantities), we let z1 = 1, z2 = 1, etc., instead
- of z1 = Exp[I w1], z2 = Exp[I w2], etc.
- *)
-
- unknown[ condexpr_ ] :=
- Block [ {simplified},
- simplified = Simplify[condexpr] /. SPLessGreaterRules;
- If [ N[simplified], True, False, simplified ] ]
-
- LTransData/: Stable[ LTransData[t_, rm_, rp_, v_] ] :=
- Block [ {inrange, vars, ltrans},
- ltrans = LTransData[t, rm, rp, v];
- inrange = InRange[GetRMinus[ltrans], 0, GetRPlus[ltrans],
- -Infinity, Infinity, Less, Less];
- vars = First[v];
- If [ ListQ[vars], (* evaluate along imag. axes *)
- inrange = inrange /. ( Map[Re, vars] ~ReplaceWith~ 0 ) ];
- If [ inrange, True, False, unknown[inrange] ] ]
-
- ZTransData/: Stable[ ZTransData[t_, rm_, rp_, v_] ] :=
- Block [ {inrange, vars, ztrans},
- ztrans = ZTransData[t, rm, rp, v];
- inrange = InRange[GetRMinus[ztrans], 1, GetRPlus[ztrans],
- 0, Infinity, Less, Less];
- vars = First[v];
- If [ ListQ[vars], (* evaluate along unit circles *)
- inrange = inrange /. ( vars ~ReplaceWith~ 1 ) ];
- If [ inrange, True, False, unknown[inrange] ] ]
-
-
-
-
- (* E N D P A C K A G E *)
-
- End[]
- EndPackage[]
-
-
- (* A L I A S E S *)
-
- Unprotect[ MagnitudePhasePlot ]
- Clear[ MagnitudePhasePlot ]
- MagnitudePhasePlot = MagPhasePlot
- MagnitudePhasePlot::usage = MagPhasePlot::usage
- Protect[ MagnitudePhasePlot ]
-
- Unprotect[ RootLocusPlot ]
- Clear[ RootLocusPlot ]
- RootLocusPlot = RootLocus
- RootLocusPlot::usage = RootLocus::usage
- Protect[ RootLocusPlot ]
-
- If [ TrueQ[ $VersionNumber >= 2.0 ],
- On[ General::spell ];
- On[ General::spell1 ] ]
-
-
- (* H E L P I N F O R M A T I O N *)
-
- Block [ {newfuns},
- newfuns =
- { AddT, ConjT, ConvolveT,
- DerivativeT, IntegrateT, LineImpulsemDT,
- MagPhasePlot, MultiDInvTransform, MultiDLookup,
- MultiDTransform, MultT, PoleZeroPlot,
- ROCPlot, RootLocus, ScaleT,
- ShadedAnnulus, Stable, SubstituteForT,
- SubT, TransformDialogue, TransformFixUp };
- Combine[ SPfunctions, newfuns ];
- Apply[ Protect, newfuns ] ]
-
-
- (* E N D I N G M E S S A G E *)
-
- Print["Supporting routines for transform rule bases are loaded."]
- Null
-