Skip to content

Commit 9c409eb

Browse files
authored
Merge branch 'master' into iosys-nonproper-tf
2 parents 8c707dd + d1502f4 commit 9c409eb

14 files changed

+498
-335
lines changed

control/config.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
# Package level default values
1717
_control_defaults = {
18-
# No package level defaults (yet)
18+
'control.default_dt':0
1919
}
2020
defaults = dict(_control_defaults)
2121

@@ -59,6 +59,9 @@ def reset_defaults():
5959
from .statesp import _statesp_defaults
6060
defaults.update(_statesp_defaults)
6161

62+
from .iosys import _iosys_defaults
63+
defaults.update(_iosys_defaults)
64+
6265

6366
def _get_param(module, param, argval=None, defval=None, pop=False):
6467
"""Return the default value for a configuration option.
@@ -208,8 +211,11 @@ def use_legacy_defaults(version):
208211
# Go backwards through releases and reset defaults
209212
#
210213

211-
# Version 0.9.0: switched to 'array' as default for state space objects
214+
# Version 0.9.0:
212215
if major == 0 and minor < 9:
216+
# switched to 'array' as default for state space objects
213217
set_defaults('statesp', use_numpy_matrix=True)
218+
# switched to 0 (=continuous) as default timestep
219+
set_defaults('control', default_dt=None)
214220

215221
return (major, minor, patch)

control/dtime.py

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -53,21 +53,19 @@
5353

5454
# Sample a continuous time system
5555
def sample_system(sysc, Ts, method='zoh', alpha=None, prewarp_frequency=None):
56-
"""Convert a continuous time system to discrete time
57-
58-
Creates a discrete time system from a continuous time system by
59-
sampling. Multiple methods of conversion are supported.
56+
"""
57+
Convert a continuous time system to discrete time by sampling
6058
6159
Parameters
6260
----------
63-
sysc : linsys
61+
sysc : LTI (StateSpace or TransferFunction)
6462
Continuous time system to be converted
65-
Ts : real
63+
Ts : real > 0
6664
Sampling period
6765
method : string
68-
Method to use for conversion: 'matched', 'tustin', 'zoh' (default)
66+
Method to use for conversion, e.g. 'bilinear', 'zoh' (default)
6967
70-
prewarp_frequency : float within [0, infinity)
68+
prewarp_frequency : real within [0, infinity)
7169
The frequency [rad/s] at which to match with the input continuous-
7270
time system's magnitude and phase
7371
@@ -78,13 +76,13 @@ def sample_system(sysc, Ts, method='zoh', alpha=None, prewarp_frequency=None):
7876
7977
Notes
8078
-----
81-
See `TransferFunction.sample` and `StateSpace.sample` for
79+
See :meth:`StateSpace.sample` or :meth:`TransferFunction.sample`` for
8280
further details.
8381
8482
Examples
8583
--------
8684
>>> sysc = TransferFunction([1], [1, 2, 1])
87-
>>> sysd = sample_system(sysc, 1, method='matched')
85+
>>> sysd = sample_system(sysc, 1, method='bilinear')
8886
"""
8987

9088
# Make sure we have a continuous time system
@@ -95,35 +93,39 @@ def sample_system(sysc, Ts, method='zoh', alpha=None, prewarp_frequency=None):
9593

9694

9795
def c2d(sysc, Ts, method='zoh', prewarp_frequency=None):
98-
'''
99-
Return a discrete-time system
96+
"""
97+
Convert a continuous time system to discrete time by sampling
10098
10199
Parameters
102100
----------
103-
sysc: LTI (StateSpace or TransferFunction), continuous
104-
System to be converted
101+
sysc : LTI (StateSpace or TransferFunction)
102+
Continuous time system to be converted
103+
Ts : real > 0
104+
Sampling period
105+
method : string
106+
Method to use for conversion, e.g. 'bilinear', 'zoh' (default)
105107
106-
Ts: number
107-
Sample time for the conversion
108+
prewarp_frequency : real within [0, infinity)
109+
The frequency [rad/s] at which to match with the input continuous-
110+
time system's magnitude and phase
108111
109-
method: string, optional
110-
Method to be applied,
111-
'zoh' Zero-order hold on the inputs (default)
112-
'foh' First-order hold, currently not implemented
113-
'impulse' Impulse-invariant discretization, currently not implemented
114-
'tustin' Bilinear (Tustin) approximation, only SISO
115-
'matched' Matched pole-zero method, only SISO
112+
Returns
113+
-------
114+
sysd : linsys
115+
Discrete time system, with sampling rate Ts
116+
117+
Notes
118+
-----
119+
See :meth:`StateSpace.sample` or :meth:`TransferFunction.sample`` for
120+
further details.
116121
117-
prewarp_frequency : float within [0, infinity)
118-
The frequency [rad/s] at which to match with the input continuous-
119-
time system's magnitude and phase
122+
Examples
123+
--------
124+
>>> sysc = TransferFunction([1], [1, 2, 1])
125+
>>> sysd = sample_system(sysc, 1, method='bilinear')
126+
"""
120127

121-
'''
122128
# Call the sample_system() function to do the work
123129
sysd = sample_system(sysc, Ts, method, prewarp_frequency)
124130

125-
# TODO: is this check needed? If sysc is StateSpace, sysd is too?
126-
if isinstance(sysc, StateSpace) and not isinstance(sysd, StateSpace):
127-
return _convertToStateSpace(sysd) # pragma: no cover
128-
129131
return sysd

control/iosys.py

Lines changed: 45 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,15 @@
3838

3939
from .statesp import StateSpace, tf2ss
4040
from .timeresp import _check_convert_array
41-
from .lti import isctime, isdtime, _find_timebase
41+
from .lti import isctime, isdtime, common_timebase
42+
from . import config
4243

4344
__all__ = ['InputOutputSystem', 'LinearIOSystem', 'NonlinearIOSystem',
4445
'InterconnectedSystem', 'input_output_response', 'find_eqpt',
4546
'linearize', 'ss2io', 'tf2io']
4647

48+
# Define module default parameter values
49+
_iosys_defaults = {}
4750

4851
class InputOutputSystem(object):
4952
"""A class for representing input/output systems.
@@ -69,9 +72,11 @@ class for a set of subclasses that are used to implement specific
6972
states : int, list of str, or None
7073
Description of the system states. Same format as `inputs`.
7174
dt : None, True or float, optional
72-
System timebase. None (default) indicates continuous time, True
73-
indicates discrete time with undefined sampling time, positive number
74-
is discrete time with specified sampling time.
75+
System timebase. 0 (default) indicates continuous
76+
time, True indicates discrete time with unspecified sampling
77+
time, positive number is discrete time with specified
78+
sampling time, None indicates unspecified timebase (either
79+
continuous or discrete time).
7580
params : dict, optional
7681
Parameter values for the systems. Passed to the evaluation functions
7782
for the system as default values, overriding internal defaults.
@@ -87,9 +92,11 @@ class for a set of subclasses that are used to implement specific
8792
Dictionary of signal names for the inputs, outputs and states and the
8893
index of the corresponding array
8994
dt : None, True or float
90-
System timebase. None (default) indicates continuous time, True
91-
indicates discrete time with undefined sampling time, positive number
92-
is discrete time with specified sampling time.
95+
System timebase. 0 (default) indicates continuous
96+
time, True indicates discrete time with unspecified sampling
97+
time, positive number is discrete time with specified
98+
sampling time, None indicates unspecified timebase (either
99+
continuous or discrete time).
93100
params : dict, optional
94101
Parameter values for the systems. Passed to the evaluation functions
95102
for the system as default values, overriding internal defaults.
@@ -118,7 +125,7 @@ def name_or_default(self, name=None):
118125
return name
119126

120127
def __init__(self, inputs=None, outputs=None, states=None, params={},
121-
dt=None, name=None):
128+
name=None, **kwargs):
122129
"""Create an input/output system.
123130
124131
The InputOutputSystem contructor is used to create an input/output
@@ -143,10 +150,11 @@ def __init__(self, inputs=None, outputs=None, states=None, params={},
143150
states : int, list of str, or None
144151
Description of the system states. Same format as `inputs`.
145152
dt : None, True or float, optional
146-
System timebase. None (default) indicates continuous
147-
time, True indicates discrete time with undefined sampling
153+
System timebase. 0 (default) indicates continuous
154+
time, True indicates discrete time with unspecified sampling
148155
time, positive number is discrete time with specified
149-
sampling time.
156+
sampling time, None indicates unspecified timebase (either
157+
continuous or discrete time).
150158
params : dict, optional
151159
Parameter values for the systems. Passed to the evaluation
152160
functions for the system as default values, overriding internal
@@ -162,9 +170,13 @@ def __init__(self, inputs=None, outputs=None, states=None, params={},
162170
163171
"""
164172
# Store the input arguments
165-
self.params = params.copy() # default parameters
166-
self.dt = dt # timebase
167-
self.name = self.name_or_default(name) # system name
173+
174+
# default parameters
175+
self.params = params.copy()
176+
# timebase
177+
self.dt = kwargs.get('dt', config.defaults['control.default_dt'])
178+
# system name
179+
self.name = self.name_or_default(name)
168180

169181
# Parse and store the number of inputs, outputs, and states
170182
self.set_inputs(inputs)
@@ -210,9 +222,7 @@ def __mul__(sys2, sys1):
210222
"inputs and outputs")
211223

212224
# Make sure timebase are compatible
213-
dt = _find_timebase(sys1, sys2)
214-
if dt is False:
215-
raise ValueError("System timebases are not compabile")
225+
dt = common_timebase(sys1.dt, sys2.dt)
216226

217227
inplist = [(0,i) for i in range(sys1.ninputs)]
218228
outlist = [(1,i) for i in range(sys2.noutputs)]
@@ -464,12 +474,11 @@ def feedback(self, other=1, sign=-1, params={}):
464474
"inputs and outputs")
465475

466476
# Make sure timebases are compatible
467-
dt = _find_timebase(self, other)
468-
if dt is False:
469-
raise ValueError("System timebases are not compabile")
477+
dt = common_timebase(self.dt, other.dt)
470478

471479
inplist = [(0,i) for i in range(self.ninputs)]
472480
outlist = [(0,i) for i in range(self.noutputs)]
481+
473482
# Return the series interconnection between the systems
474483
newsys = InterconnectedSystem((self, other), inplist=inplist, outlist=outlist,
475484
params=params, dt=dt)
@@ -580,10 +589,11 @@ def __init__(self, linsys, inputs=None, outputs=None, states=None,
580589
states : int, list of str, or None, optional
581590
Description of the system states. Same format as `inputs`.
582591
dt : None, True or float, optional
583-
System timebase. None (default) indicates continuous
584-
time, True indicates discrete time with undefined sampling
592+
System timebase. 0 (default) indicates continuous
593+
time, True indicates discrete time with unspecified sampling
585594
time, positive number is discrete time with specified
586-
sampling time.
595+
sampling time, None indicates unspecified timebase (either
596+
continuous or discrete time).
587597
params : dict, optional
588598
Parameter values for the systems. Passed to the evaluation
589599
functions for the system as default values, overriding internal
@@ -650,7 +660,8 @@ class NonlinearIOSystem(InputOutputSystem):
650660
651661
"""
652662
def __init__(self, updfcn, outfcn=None, inputs=None, outputs=None,
653-
states=None, params={}, dt=None, name=None):
663+
states=None, params={},
664+
name=None, **kwargs):
654665
"""Create a nonlinear I/O system given update and output functions.
655666
656667
Creates an `InputOutputSystem` for a nonlinear system by specifying a
@@ -702,10 +713,10 @@ def __init__(self, updfcn, outfcn=None, inputs=None, outputs=None,
702713
operating in continuous or discrete time. It can have the
703714
following values:
704715
705-
* dt = None No timebase specified
706-
* dt = 0 Continuous time system
707-
* dt > 0 Discrete time system with sampling time dt
708-
* dt = True Discrete time with unspecified sampling time
716+
* dt = 0: continuous time system (default)
717+
* dt > 0: discrete time system with sampling period 'dt'
718+
* dt = True: discrete time with unspecified sampling period
719+
* dt = None: no timebase specified
709720
710721
name : string, optional
711722
System name (used for specifying signals). If unspecified, a generic
@@ -722,6 +733,7 @@ def __init__(self, updfcn, outfcn=None, inputs=None, outputs=None,
722733
self.outfcn = outfcn
723734

724735
# Initialize the rest of the structure
736+
dt = kwargs.get('dt', config.defaults['control.default_dt'])
725737
super(NonlinearIOSystem, self).__init__(
726738
inputs=inputs, outputs=outputs, states=states,
727739
params=params, dt=dt, name=name
@@ -871,10 +883,10 @@ def __init__(self, syslist, connections=[], inplist=[], outlist=[],
871883
operating in continuous or discrete time. It can have the
872884
following values:
873885
874-
* dt = None No timebase specified
875-
* dt = 0 Continuous time system
876-
* dt > 0 Discrete time system with sampling time dt
877-
* dt = True Discrete time with unspecified sampling time
886+
* dt = 0: continuous time system (default)
887+
* dt > 0: discrete time system with sampling period 'dt'
888+
* dt = True: discrete time with unspecified sampling period
889+
* dt = None: no timebase specified
878890
879891
name : string, optional
880892
System name (used for specifying signals). If unspecified, a generic
@@ -888,20 +900,14 @@ def __init__(self, syslist, connections=[], inplist=[], outlist=[],
888900
# Check to make sure all systems are consistent
889901
self.syslist = syslist
890902
self.syslist_index = {}
891-
dt = None
892903
nstates = 0; self.state_offset = []
893904
ninputs = 0; self.input_offset = []
894905
noutputs = 0; self.output_offset = []
895906
sysobj_name_dct = {}
896907
sysname_count_dct = {}
897908
for sysidx, sys in enumerate(syslist):
898909
# Make sure time bases are consistent
899-
# TODO: Use lti._find_timebase() instead?
900-
if dt is None and sys.dt is not None:
901-
# Timebase was not specified; set to match this system
902-
dt = sys.dt
903-
elif dt != sys.dt:
904-
raise TypeError("System timebases are not compatible")
910+
dt = common_timebase(dt, sys.dt)
905911

906912
# Make sure number of inputs, outputs, states is given
907913
if sys.ninputs is None or sys.noutputs is None or \

0 commit comments

Comments
 (0)