Skip to content

Commit f890a88

Browse files
authored
Merge pull request #171 from murrayrm/statesp_constructor_doc
Update docstrings for LTI class constructors
2 parents a5094e2 + 2635889 commit f890a88

13 files changed

+1398
-65
lines changed

control/bdalg.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,8 @@ def feedback(sys1, sys2=1, sign=-1):
247247
return sys1.feedback(sys2, sign)
248248

249249
def append(*sys):
250-
'''
250+
'''append(sys1, sys2, ... sysn)
251+
251252
Group models by appending their inputs and outputs
252253
253254
Forms an augmented system model, and appends the inputs and

control/ctrlutil.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,12 @@
4343
# Packages that we need access to
4444
from . import lti
4545
import numpy as np
46-
from numpy import pi
46+
import math
4747

4848
__all__ = ['unwrap', 'issys', 'db2mag', 'mag2db']
4949

5050
# Utility function to unwrap an angle measurement
51-
def unwrap(angle, period=2*pi):
51+
def unwrap(angle, period=2*math.pi):
5252
"""Unwrap a phase angle to give a continuous curve
5353
5454
Parameters

control/frdata.py

+10-5
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@
5858
__all__ = ['FRD', 'frd']
5959

6060
class FRD(LTI):
61-
"""A class for models defined by Frequency Response Data (FRD)
61+
"""FRD(d, w)
62+
63+
A class for models defined by frequency response data (FRD)
6264
6365
The FRD class is used to represent systems in frequency response data form.
6466
@@ -81,7 +83,9 @@ class FRD(LTI):
8183
epsw = 1e-8
8284

8385
def __init__(self, *args, **kwargs):
84-
"""Construct an FRD object
86+
"""FRD(d, w)
87+
88+
Construct an FRD object
8589
8690
The default constructor is FRD(d, w), where w is an iterable of
8791
frequency points, and d is the matching frequency data.
@@ -470,8 +474,9 @@ def _convertToFRD(sys, omega, inputs=1, outputs=1):
470474
sys.__class__)
471475

472476
def frd(*args):
473-
"""
474-
Construct a Frequency Response Data model, or convert a system
477+
"""frd(d, w)
478+
479+
Construct a frequency response data model
475480
476481
frd models store the (measured) frequency response of a system.
477482
@@ -501,6 +506,6 @@ def frd(*args):
501506
502507
See Also
503508
--------
504-
ss, tf
509+
FRD, ss, tf
505510
"""
506511
return FRD(*args)

control/freqplot.py

+12-11
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import matplotlib.pyplot as plt
4545
import scipy as sp
4646
import numpy as np
47+
import math
4748
from .ctrlutil import unwrap
4849
from .bdalg import feedback
4950

@@ -128,7 +129,7 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
128129
else:
129130
omega_limits = np.array(omega_limits)
130131
if Hz:
131-
omega_limits *= 2.*np.pi
132+
omega_limits *= 2.*math.pi
132133
if omega_num:
133134
omega = sp.logspace(np.log10(omega_limits[0]), np.log10(omega_limits[1]), num=omega_num, endpoint=True)
134135
else:
@@ -142,7 +143,7 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
142143
else:
143144
omega_sys = np.array(omega)
144145
if sys.isdtime(True):
145-
nyquistfrq = 2. * np.pi * 1. / sys.dt / 2.
146+
nyquistfrq = 2. * math.pi * 1. / sys.dt / 2.
146147
omega_sys = omega_sys[omega_sys < nyquistfrq]
147148
# TODO: What distance to the Nyquist frequency is appropriate?
148149
else:
@@ -154,9 +155,9 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
154155
phase = unwrap(phase)
155156
nyquistfrq_plot = None
156157
if Hz:
157-
omega_plot = omega_sys / (2. * np.pi)
158+
omega_plot = omega_sys / (2. * math.pi)
158159
if nyquistfrq:
159-
nyquistfrq_plot = nyquistfrq / (2. * np.pi)
160+
nyquistfrq_plot = nyquistfrq / (2. * math.pi)
160161
else:
161162
omega_plot = omega_sys
162163
if nyquistfrq:
@@ -187,7 +188,7 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
187188
# Phase plot
188189
ax_phase = plt.subplot(212, sharex=ax_mag);
189190
if deg:
190-
phase_plot = phase * 180. / np.pi
191+
phase_plot = phase * 180. / math.pi
191192
else:
192193
phase_plot = phase
193194
ax_phase.semilogx(omega_plot, phase_plot, *args, **kwargs)
@@ -208,8 +209,8 @@ def genZeroCenteredSeries(val_min, val_max, period):
208209
ax_phase.set_yticks(genZeroCenteredSeries(ylim[0], ylim[1], 15.), minor=True)
209210
else:
210211
ylim = ax_phase.get_ylim()
211-
ax_phase.set_yticks(genZeroCenteredSeries(ylim[0], ylim[1], np.pi / 4.))
212-
ax_phase.set_yticks(genZeroCenteredSeries(ylim[0], ylim[1], np.pi / 12.), minor=True)
212+
ax_phase.set_yticks(genZeroCenteredSeries(ylim[0], ylim[1], math.pi / 4.))
213+
ax_phase.set_yticks(genZeroCenteredSeries(ylim[0], ylim[1], math.pi / 12.), minor=True)
213214
ax_phase.grid(True, which='both')
214215
# ax_mag.grid(which='minor', alpha=0.3)
215216
# ax_mag.grid(which='major', alpha=0.9)
@@ -449,7 +450,7 @@ def default_frequency_range(syslist, Hz=None, number_of_samples=None, feature_pe
449450
features_ = features_[features_ != 0.0];
450451
features = np.concatenate((features, features_))
451452
elif sys.isdtime(strict=True):
452-
fn = np.pi * 1. / sys.dt
453+
fn = math.pi * 1. / sys.dt
453454
# TODO: What distance to the Nyquist frequency is appropriate?
454455
freq_interesting.append(fn * 0.9)
455456

@@ -475,12 +476,12 @@ def default_frequency_range(syslist, Hz=None, number_of_samples=None, feature_pe
475476
features = np.array([1.]);
476477

477478
if Hz:
478-
features /= 2.*np.pi
479+
features /= 2.*math.pi
479480
features = np.log10(features)
480481
lsp_min = np.floor(np.min(features) - feature_periphery_decade)
481482
lsp_max = np.ceil(np.max(features) + feature_periphery_decade)
482-
lsp_min += np.log10(2.*np.pi)
483-
lsp_max += np.log10(2.*np.pi)
483+
lsp_min += np.log10(2.*math.pi)
484+
lsp_max += np.log10(2.*math.pi)
484485
else:
485486
features = np.log10(features)
486487
lsp_min = np.floor(np.min(features) - feature_periphery_decade)

control/margins.py

+11-10
Original file line numberDiff line numberDiff line change
@@ -48,21 +48,21 @@
4848
Date: 14 July 2011
4949
5050
$Id$
51-
5251
"""
5352

53+
import math
5454
import numpy as np
55+
import scipy as sp
5556
from . import xferfcn
5657
from .lti import issiso
5758
from . import frdata
58-
import scipy as sp
5959

6060
__all__ = ['stability_margins', 'phase_crossover_frequencies', 'margin']
6161

6262
# helper functions for stability_margins
6363
def _polyimsplit(pol):
6464
"""split a polynomial with (iw) applied into a real and an
65-
imaginary part with w applied"""
65+
imaginary part with w applied"""
6666
rpencil = np.zeros_like(pol)
6767
ipencil = np.zeros_like(pol)
6868
rpencil[-1::-4] = 1.
@@ -141,7 +141,7 @@ def stability_margins(sysdata, returnall=False, epsw=0.0):
141141
sys = sysdata
142142
elif getattr(sysdata, '__iter__', False) and len(sysdata) == 3:
143143
mag, phase, omega = sysdata
144-
sys = frdata.FRD(mag * np.exp(1j * phase * np.pi/180),
144+
sys = frdata.FRD(mag * np.exp(1j * phase * math.pi/180),
145145
omega, smooth=True)
146146
else:
147147
sys = xferfcn._convertToTransferFunction(sysdata)
@@ -294,8 +294,7 @@ def dstab(w):
294294
# Contributed by Steffen Waldherr <waldherr@ist.uni-stuttgart.de>
295295
#! TODO - need to add test functions
296296
def phase_crossover_frequencies(sys):
297-
"""
298-
Compute frequencies and gains at intersections with real axis
297+
"""Compute frequencies and gains at intersections with real axis
299298
in Nyquist plot.
300299
301300
Call as:
@@ -338,11 +337,13 @@ def phase_crossover_frequencies(sys):
338337

339338

340339
def margin(*args):
341-
"""Calculate gain and phase margins and associated crossover frequencies
340+
"""margin(sysdata)
341+
342+
Calculate gain and phase margins and associated crossover frequencies
342343
343344
Parameters
344345
----------
345-
sysdata: LTI system or (mag, phase, omega) sequence
346+
sysdata : LTI system or (mag, phase, omega) sequence
346347
sys : StateSpace or TransferFunction
347348
Linear SISO system
348349
mag, phase, omega : sequence of array_like
@@ -360,8 +361,8 @@ def margin(*args):
360361
Wcp : float
361362
Phase crossover frequency (corresponding to gain margin) (in rad/sec)
362363
363-
Margins are of SISO open-loop. If more than one crossover frequency is
364-
detected, returns the lowest corresponding margin.
364+
Margins are of SISO open-loop. If more than one crossover frequency is
365+
detected, returns the lowest corresponding margin.
365366
366367
Examples
367368
--------

control/matlab/wrappers.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
__all__ = ['bode', 'ngrid', 'dcgain']
1111

1212
def bode(*args, **keywords):
13-
"""Bode plot of the frequency response
13+
"""bode(syslist[, omega, dB, Hz, deg, ...])
14+
15+
Bode plot of the frequency response
1416
1517
Plots a bode gain and phase diagram
1618

control/robust.py

+5-6
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ def h2syn(P,nmeas,ncon):
7272
>>> K = h2syn(P,nmeas,ncon)
7373
7474
"""
75-
7675
#Check for ss system object, need a utility for this?
7776

7877
#TODO: Check for continous or discrete, only continuous supported right now
@@ -116,11 +115,11 @@ def hinfsyn(P,nmeas,ncon):
116115
CL: closed loop system (State-space sys)
117116
gam: infinity norm of closed loop system
118117
rcond: 4-vector, reciprocal condition estimates of:
119-
1: control transformation matrix
120-
2: measurement transformation matrix
121-
3: X-Ricatti equation
122-
4: Y-Ricatti equation
123-
TODO: document significance of rcond
118+
1: control transformation matrix
119+
2: measurement transformation matrix
120+
3: X-Ricatti equation
121+
4: Y-Ricatti equation
122+
TODO: document significance of rcond
124123
125124
Raises
126125
------

control/statefbk.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,9 @@ def acker(A, B, poles):
144144
return K
145145

146146
def lqr(*args, **keywords):
147-
"""Linear quadratic regulator design
147+
"""lqr(A, B, Q, R[, N])
148+
149+
Linear quadratic regulator design
148150
149151
The lqr() function computes the optimal state feedback controller
150152
that minimizes the quadratic cost

control/statesp.py

+22-13
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,10 @@
5151
$Id$
5252
"""
5353

54+
import math
5455
import numpy as np
5556
from numpy import all, angle, any, array, asarray, concatenate, cos, delete, \
56-
dot, empty, exp, eye, matrix, ones, pi, poly, poly1d, roots, shape, sin, \
57+
dot, empty, exp, eye, matrix, ones, poly, poly1d, roots, shape, sin, \
5758
zeros, squeeze
5859
from numpy.random import rand, randn
5960
from numpy.linalg import solve, eigvals, matrix_rank
@@ -69,7 +70,9 @@
6970
__all__ = ['StateSpace', 'ss', 'rss', 'drss', 'tf2ss', 'ssdata']
7071

7172
class StateSpace(LTI):
72-
"""A class for representing state-space models
73+
"""StateSpace(A, B, C, D[, dt])
74+
75+
A class for representing state-space models
7376
7477
The StateSpace class is used to represent state-space realizations of linear
7578
time-invariant (LTI) systems:
@@ -89,15 +92,19 @@ class StateSpace(LTI):
8992
means the system timebase is not specified. If 'dt' is set to True, the
9093
system will be treated as a discrete time system with unspecified
9194
sampling time.
92-
9395
"""
9496

9597
def __init__(self, *args):
96-
"""Construct a state space object.
98+
"""
99+
StateSpace(A, B, C, D[, dt])
100+
101+
Construct a state space object.
97102
98-
The default constructor is StateSpace(A, B, C, D), where A, B, C, D are
99-
matrices or equivalent objects. To call the copy constructor, call
100-
StateSpace(sys), where sys is a StateSpace object.
103+
The default constructor is StateSpace(A, B, C, D), where A, B, C, D
104+
are matrices or equivalent objects. To create a discrete time system,
105+
use StateSpace(A, B, C, D, dt) where 'dt' is the sampling time (or
106+
True for unspecified sampling time). To call the copy constructor,
107+
call StateSpace(sys), where sys is a StateSpace object.
101108
102109
"""
103110

@@ -111,8 +118,7 @@ def __init__(self, *args):
111118
elif len(args) == 1:
112119
# Use the copy constructor.
113120
if not isinstance(args[0], StateSpace):
114-
raise TypeError("The one-argument constructor can only take in \
115-
a StateSpace object. Recived %s." % type(args[0]))
121+
raise TypeError("The one-argument constructor can only take in a StateSpace object. Received %s." % type(args[0]))
116122
A = args[0].A
117123
B = args[0].B
118124
C = args[0].C
@@ -363,7 +369,7 @@ def evalfr(self, omega):
363369
if isdtime(self, strict=True):
364370
dt = timebase(self)
365371
s = exp(1.j * omega * dt)
366-
if (omega * dt > pi):
372+
if (omega * dt > math.pi):
367373
warnings.warn("evalfr: frequency evaluation above Nyquist frequency")
368374
else:
369375
s = omega * 1.j
@@ -793,7 +799,7 @@ def _rss_generate(states, inputs, outputs, type):
793799
poles[i] = complex(-exp(randn()), 3. * exp(randn()))
794800
elif type == 'd':
795801
mag = rand()
796-
phase = 2. * pi * rand()
802+
phase = 2. * math.pi * rand()
797803
poles[i] = complex(mag * cos(phase),
798804
mag * sin(phase))
799805
poles[i+1] = complex(poles[i].real, -poles[i].imag)
@@ -956,7 +962,8 @@ def _mimo2simo(sys, input, warn_conversion=False):
956962
return sys
957963

958964
def ss(*args):
959-
"""
965+
"""ss(A, B, C, D[, dt])
966+
960967
Create a state space system.
961968
962969
The function accepts either 1, 4 or 5 parameters:
@@ -1014,6 +1021,7 @@ def ss(*args):
10141021
10151022
See Also
10161023
--------
1024+
StateSpace
10171025
tf
10181026
ss2tf
10191027
tf2ss
@@ -1045,7 +1053,8 @@ def ss(*args):
10451053
raise ValueError("Needs 1 or 4 arguments; received %i." % len(args))
10461054

10471055
def tf2ss(*args):
1048-
"""
1056+
"""tf2ss(sys)
1057+
10491058
Transform a transfer function to a state space system.
10501059
10511060
The function accepts either 1 or 2 parameters:

0 commit comments

Comments
 (0)