8000 Move damp, c2d, rlocus from matlab into control · XiangPiCha/python-control@a3fb4f5 · GitHub
[go: up one dir, main page]

Skip to content

Commit a3fb4f5

Browse files
committed
Move damp, c2d, rlocus from matlab into control
1 parent 689c59d commit a3fb4f5

File tree

7 files changed

+124
-123
lines changed

7 files changed

+124
-123
lines changed

control/__init__.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,17 @@
4848
# Should probably only import the exact functions we use...
4949
from .bdalg import series, parallel, negate, feedback
5050
from .delay import pade
51-
from .dtime import sample_system
51+
from .dtime import *
5252
from .freqplot import bode_plot, nyquist_plot, gangof4_plot
5353
from .freqplot import bode, nyquist, gangof4
54-
from .lti import issiso, timebase, timebaseEqual, isdtime, isctime
55-
from .margins import stability_margins, phase_crossover_frequencies
54+
from .lti import *
55+
from .margins import *
5656
from .mateqn import lyap, dlyap, care, dare
5757
from .modelsimp import hsvd, modred, balred, era, markov, minreal
5858
from .nichols import *
5959
from .phaseplot import phase_plot, box_grid
6060
from .pzmap import pzmap
61-
from .rlocus import root_locus
61+
from .rlocus import *
6262
from .statefbk import place, lqr, ctrb, obsv, gram, acker
6363
from .statesp import *
6464
from .timeresp import forced_response, initial_response, step_response, \
@@ -86,10 +86,6 @@
8686
#! of defaults from the main package. At that point, the matlab module will
8787
#! allow provide compatibility with MATLAB but no package functionality.
8888
#!
89-
from .matlab import dcgain
90-
from .matlab import nichols, rlocus, margin
91-
# bode and nyquist come directly from freqplot.py
92-
from .matlab import step, impulse, initial, lsim
9389

9490
# The following is to use Numpy's testing framework
9591
# Tests go under directory tests/, benchmarks under directory benchmarks/

control/dtime.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@
4747
"""
4848

4949
from .lti import isctime
50+
from .statesp import StateSpace, _convertToStateSpace
51+
52+
__all__ = ['sample_system', 'c2d']
5053

5154
# Sample a continuous time system
5255
def sample_system(sysc, Ts, method='zoh', alpha=None):
@@ -85,3 +88,30 @@ def sample_system(sysc, Ts, method='zoh', alpha=None):
8588
raise ValueError("First argument must be continuous time system")
8689

8790
return sysc.sample(Ts, method, alpha)
91+
92+
93+
def c2d(sysc, Ts, method='zoh'):
94+
'''
95+
Return a discrete-time system
96+
97+
Parameters
98+
----------
99+
sysc: LTI (StateSpace or TransferFunction), continuous
100+
System to be converted
101+
102+
Ts: number
103+
Sample time for the conversion
104+
105+
method: string, optional
106+
Method to be applied,
107+
'zoh' Zero-order hold on the inputs (default)
108+
'foh' First-order hold, currently not implemented
109+
'impulse' Impulse-invariant discretization, currently not implemented
110+
'tustin' Bilinear (Tustin) approximation, only SISO
111+
'matched' Matched pole-zero method, only SISO
112+
'''
113+
# Call the sample_system() function to do the work
114+
sysd = sample_system(sysc, Ts, method)
115+
if isinstance(sysc, StateSpace) and not isinstance(sysd, StateSpace):
116+
return _convertToStateSpace(sysd)
117+
return sysd

control/lti.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from numpy import absolute, real
1616

1717
__all__ = ['issiso', 'timebase', 'timebaseEqual', 'isdtime', 'isctime',
18-
'pole', 'zero', 'evalfr', 'freqresp']
18+
'pole', 'zero', 'damp', 'evalfr', 'freqresp']
1919

2020
class LTI:
2121
"""LTI is a parent class to linear time-invariant (LTI) system objects.
@@ -258,6 +258,44 @@ def zero(sys):
258258

259259
return sys.zero()
260260

261+
def damp(sys, doprint=True):
262+
'''
263+
Compute natural frequency, damping ratio, and poles of a system
264+
265+
The function takes 1 or 2 parameters
266+
267+
Parameters
268+
----------
269+
sys: LTI (StateSpace or TransferFunction)
270+
A linear system object
271+
doprint:
272+
if true, print table with values
273+
274+
Returns
275+
-------
276+
wn: array
277+
Natural frequencies of the poles
278+
damping: array
279+
Damping values
280+
poles: array
281+
Pole locations
282+
283+
See Also
284+
--------
285+
pole
286+
'''
287+
wn, damping, poles = sys.damp()
288+
if doprint:
289+
print('_____Eigenvalue______ Damping___ Frequency_')
290+
for p, d, w in zip(poles, damping, wn) :
291+
if abs(p.imag) < 1e-12:
292+
print("%10.4g %10.4g %10.4g" %
293+
(p.real, 1.0, -p.real))
294+
else:
295+
print("%10.4g%+10.4gj %10.4g %10.4g" %
296+
(p.real, p.imag, d, w))
297+
return wn, damping, poles
298+
261299
def evalfr(sys, x):
262300
"""
263301
Evaluate the transfer function of an LTI system for a single complex

control/margins.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@
5656
from . import frdata
5757
import scipy as sp
5858

59+
__all__ = ['stability_margins', 'phase_crossover_frequencies', 'margin']
60+
5961
# helper functions for stability_margins
6062
def _polyimsplit(pol):
6163
"""split a polynomial with (iw) applied into a real and an
@@ -271,3 +273,43 @@ def phase_crossover_frequencies(sys):
271273
gain = np.real(np.asarray([tf.evalfr(f)[0][0] for f in realposfreq]))
272274

273275
return realposfreq, gain
276+
277+
278+
def margin(*args):
279+
"""Calculate gain and phase margins and associated crossover frequencies
280+
281+
Function ``margin`` takes either 1 or 3 parameters.
282+
283+
Parameters
284+
----------
285+
sys : StateSpace or TransferFunction
286+
Linear SISO system
287+
mag, phase, w : array_like
288+
Input magnitude, phase (in deg.), and frequencies (rad/sec) from
289+
bode frequency response data
290+
291+
Returns
292+
-------
293+
gm, pm, Wcg, Wcp : float
294+
Gain margin gm, phase margin pm (in deg), gain crossover frequency
295+
(corresponding to phase margin) and phase crossover frequency
296+
(corresponding to gain margin), in rad/sec of SISO open-loop.
297+
If more than one crossover frequency is detected, returns the lowest
298+
corresponding margin.
299+
300+
Examples
301+
--------
302+
>>> sys = tf(1, [1, 2, 1, 0])
303+
>>> gm, pm, Wcg, Wcp = margin(sys)
304+
305+
"""
306+
if len(args) == 1:
307+
sys = args[0]
308+
margin = stability_margins(sys)
309+
elif len(args) == 3:
310+
margin = stability_margins(args)
311+
else:
312+
raise ValueError("Margin needs 1 or 3 arguments; received %i."
313+
% len(args))
314+
315+
return margin[0], margin[1], margin[4], margin[3]

control/matlab/__init__.py

Lines changed: 4 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@
5454
import scipy as sp # SciPy library (used all over)
5555
import numpy as np # NumPy library
5656
import re # regular expressions
57-
from copy import deepcopy
5857

5958
# Import MATLAB-like functions that are defined in other packages
6059
from scipy.signal import zpk2ss, ss2zpk, tf2zpk, zpk2tf
@@ -81,7 +80,7 @@
8180
from ..exception import ControlArgument
8281

8382
# Import MATLAB-like functions that can be used as-is
84-
from ..ctrlutil import unwrap
83+
from ..ctrlutil import *
8584
from ..freqplot import nyquist, gangof4
8685
from ..nichols import nichols, nichols_grid
8786
from ..bdalg import series, parallel, negate, feedback, append, connect
@@ -90,6 +89,9 @@
9089
from ..delay import pade
9190
from ..modelsimp import hsvd, balred, modred, minreal
9291
from ..mateqn import lyap, dlyap, dare, care
92+
from ..margins import margin
93+
from ..rlocus import rlocus
94+
from ..dtime import c2d
9395

9496
# Import functions specific to Matlab compatibility package
9597
from .timeresp import *
@@ -386,47 +388,6 @@
386388
387389
"""
388390

389-
from ..margins import stability_margins
390-
391-
def margin(*args):
392-
"""Calculate gain and phase margins and associated crossover frequencies
393-
394-
Function ``margin`` takes either 1 or 3 parameters.
395-
396-
Parameters
397-
----------
398-
sys : StateSpace or TransferFunction
399-
Linear SISO system
400-
mag, phase, w : array_like
401-
Input magnitude, phase (in deg.), and frequencies (rad/sec) from
402-
bode frequency response data
403-
404-
Returns
405-
-------
406-
gm, pm, Wcg, Wcp : float
407-
Gain margin gm, phase margin pm (in deg), gain crossover frequency
408-
(corresponding to phase margin) and phase crossover frequency
409-
(corresponding to gain margin), in rad/sec of SISO open-loop.
410-
If more than one crossover frequency is detected, returns the lowest
411-
corresponding margin.
412-
413-
Examples
414-
--------
415-
>>> sys = tf(1, [1, 2, 1, 0])
416-
>>> gm, pm, Wcg, Wcp = margin(sys)
417-
418-
"""
419-
if len(args) == 1:
420-
sys = args[0]
421-
margin = stability_margins(sys)
422-
elif len(args) == 3:
423-
margin = stability_margins(args)
424-
else:
425-
raise ValueError("Margin needs 1 or 3 arguments; received %i."
426-
% len(args))
427-
428-
return margin[0], margin[1], margin[4], margin[3]
429-
430391
def dcgain(*args):
431392
'''
432393
Compute the gain of the system in steady state.
@@ -480,68 +441,3 @@ def dcgain(*args):
480441
#gain = - C * A**-1 * B + D
481442
gain = sys.D - sys.C * sys.A.I * sys.B
482443
return gain
483-
484-
def damp(sys, doprint=True):
485-
'''
486-
Compute natural frequency, damping and poles of a system
487-
488-
The function takes 1 or 2 parameters
489-
490-
Parameters
491-
----------
492-
sys: LTI (StateSpace or TransferFunction)
493-
A linear system object
494-
doprint:
495-
if true, print table with values
496-
497-
Returns
498-
-------
499-
wn: array
500-
Natural frequencies of the poles
501-
damping: array
502-
Damping values
503-
poles: array
504-
Pole locations
505-
506-
See Also
507-
--------
508-
pole
509-
'''
510-
wn, damping, poles = sys.damp()
511-
if doprint:
512-
print('_____Eigenvalue______ Damping___ Frequency_')
513-
for p, d, w in zip(poles, damping, wn) :
514-
if abs(p.imag) < 1e-12:
515-
print("%10.4g %10.4g %10.4g" %
516-
(p.real, 1.0, -p.real))
517-
else:
518-
print("%10.4g%+10.4gj %10.4g %10.4g" %
519-
(p.real, p.imag, d, w))
520-
return wn, damping, poles
521-
522-
# Convert a continuous time system to a discrete time system 10000
523-
def c2d(sysc, Ts, method='zoh'):
524-
'''
525-
Return a discrete-time system
526-
527-
Parameters
528-
----------
529-
sysc: LTI (StateSpace or TransferFunction), continuous
530-
System to be converted
531-
532-
Ts: number
533-
Sample time for the conversion
534-
535-
method: string, optional
536-
Method to be applied,
537-
'zoh' Zero-order hold on the inputs (default)
538-
'foh' First-order hold, currently not implemented
539-
'impulse' Impulse-invariant discretization, currently not implemented
540-
'tustin' Bilinear (Tustin) approximation, only SISO
541-
'matched' Matched pole-zero method, only SISO
542-
'''
543-
# Call the sample_system() function to do the work
544-
sysd = sample_system(sysc, Ts, method)
545-
if isinstance(sysc, StateSpace) and not isinstance(sysd, StateSpace):
546-
return _convertToStateSpace(sysd)
547-
return sysd

control/matlab/plots.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import numpy as np
66

7-
__all__ = ['bode', 'rlocus', 'ngrid']
7+
__all__ = ['bode', 'ngrid']
88

99
def bode(*args, **keywords):
1010
"""Bode plot of the frequency response
@@ -103,8 +103,3 @@ def bode(*args, **keywords):
103103
def ngrid():
104104
return nichols_grid()
105105
ngrid.__doc__ = nichols_grid.__doc__
106-
107-
from ..rlocus import root_locus
108-
def rlocus(*args, **kwargs):
109-
return root_locus(*args, **kwargs)
110-
rlocus.__doc__ = root_locus.__doc__

control/rlocus.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@
5454
from .exception import ControlMIMONotImplemented
5555
from functools import partial
5656

57+
__all__ = ['root_locus', 'rlocus']
58+
5759
# Main function: compute a root locus diagram
5860
def root_locus(sys, kvect=None, xlim=None, ylim=None, plotstr='-', Plot=True,
5961
PrintGain=True):
@@ -208,3 +210,5 @@ def _RLFeedbackClicks(event, sys):
208210
if abs(K.real) > 1e-8 and abs(K.imag/K.real) < 0.04:
209211
print("Clicked at %10.4g%+10.4gj gain %10.4g damp %10.4g" %
210212
(s.real, s.imag, K.real, -1*s.real/abs(s)))
213+
214+
rlocus = root_locus

0 commit comments

Comments
 (0)
0