Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default dt is 0 #431

Closed
wants to merge 12 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion control/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

# Package level default values
_control_defaults = {
# No package level defaults (yet)
'control.default_dt':0
}
defaults = dict(_control_defaults)

Expand Down Expand Up @@ -59,6 +59,9 @@ def reset_defaults():
from .statesp import _statesp_defaults
defaults.update(_statesp_defaults)

from .iosys import _iosys_defaults
defaults.update(_iosys_defaults)


def _get_param(module, param, argval=None, defval=None, pop=False):
"""Return the default value for a configuration option.
Expand Down Expand Up @@ -170,5 +173,6 @@ def use_legacy_defaults(version):
"""
if version == '0.8.3':
use_numpy_matrix(True) # alternatively: set_defaults('statesp', use_numpy_matrix=True)
set_defaults('control', default_dt=None)
else:
raise ValueError('''version number not recognized. Possible values are: ['0.8.3']''')
64 changes: 33 additions & 31 deletions control/dtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,21 +53,19 @@

# Sample a continuous time system
def sample_system(sysc, Ts, method='zoh', alpha=None, prewarp_frequency=None):
"""Convert a continuous time system to discrete time

Creates a discrete time system from a continuous time system by
sampling. Multiple methods of conversion are supported.
"""
Convert a continuous time system to discrete time by sampling

Parameters
----------
sysc : linsys
sysc : LTI (StateSpace or TransferFunction)
Continuous time system to be converted
Ts : real
Ts : real > 0
Sampling period
method : string
Method to use for conversion: 'matched', 'tustin', 'zoh' (default)
Method to use for conversion, e.g. 'bilinear', 'zoh' (default)

prewarp_frequency : float within [0, infinity)
prewarp_frequency : real within [0, infinity)
The frequency [rad/s] at which to match with the input continuous-
time system's magnitude and phase

Expand All @@ -78,13 +76,13 @@ def sample_system(sysc, Ts, method='zoh', alpha=None, prewarp_frequency=None):

Notes
-----
See `TransferFunction.sample` and `StateSpace.sample` for
See :meth:`StateSpace.sample` or :meth:`TransferFunction.sample`` for
further details.

Examples
--------
>>> sysc = TransferFunction([1], [1, 2, 1])
>>> sysd = sample_system(sysc, 1, method='matched')
>>> sysd = sample_system(sysc, 1, method='bilinear')
"""

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


def c2d(sysc, Ts, method='zoh', prewarp_frequency=None):
'''
Return a discrete-time system
"""
Convert a continuous time system to discrete time by sampling

Parameters
----------
sysc: LTI (StateSpace or TransferFunction), continuous
System to be converted
sysc : LTI (StateSpace or TransferFunction)
Continuous time system to be converted
Ts : real > 0
Sampling period
method : string
Method to use for conversion, e.g. 'bilinear', 'zoh' (default)

Ts: number
Sample time for the conversion
prewarp_frequency : real within [0, infinity)
The frequency [rad/s] at which to match with the input continuous-
time system's magnitude and phase

method: string, optional
Method to be applied,
'zoh' Zero-order hold on the inputs (default)
'foh' First-order hold, currently not implemented
'impulse' Impulse-invariant discretization, currently not implemented
'tustin' Bilinear (Tustin) approximation, only SISO
'matched' Matched pole-zero method, only SISO
Returns
-------
sysd : linsys
Discrete time system, with sampling rate Ts

Notes
-----
See :meth:`StateSpace.sample` or :meth:`TransferFunction.sample`` for
further details.

prewarp_frequency : float within [0, infinity)
The frequency [rad/s] at which to match with the input continuous-
time system's magnitude and phase
Examples
--------
>>> sysc = TransferFunction([1], [1, 2, 1])
>>> sysd = sample_system(sysc, 1, method='bilinear')
"""

'''
# Call the sample_system() function to do the work
sysd = sample_system(sysc, Ts, method, prewarp_frequency)

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

return sysd
80 changes: 41 additions & 39 deletions control/iosys.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,15 @@

from .statesp import StateSpace, tf2ss
from .timeresp import _check_convert_array
from .lti import isctime, isdtime, _find_timebase
from .lti import isctime, isdtime, common_timebase
from . import config

__all__ = ['InputOutputSystem', 'LinearIOSystem', 'NonlinearIOSystem',
'InterconnectedSystem', 'input_output_response', 'find_eqpt',
'linearize', 'ss2io', 'tf2io']

# Define module default parameter values
_iosys_defaults = {}

class InputOutputSystem(object):
"""A class for representing input/output systems.
Expand All @@ -69,9 +72,11 @@ class for a set of subclasses that are used to implement specific
states : int, list of str, or None
Description of the system states. Same format as `inputs`.
dt : None, True or float, optional
System timebase. None (default) indicates continuous time, True
indicates discrete time with undefined sampling time, positive number
is discrete time with specified sampling time.
System timebase. 0 (default) indicates continuous
time, True indicates discrete time with unspecified sampling
time, positive number is discrete time with specified
sampling time, None indicates unspecified timebase (either
continuous or discrete time).
params : dict, optional
Parameter values for the systems. Passed to the evaluation functions
for the system as default values, overriding internal defaults.
Expand All @@ -86,9 +91,11 @@ class for a set of subclasses that are used to implement specific
Dictionary of signal names for the inputs, outputs and states and the
index of the corresponding array
dt : None, True or float
System timebase. None (default) indicates continuous time, True
indicates discrete time with undefined sampling time, positive number
is discrete time with specified sampling time.
System timebase. 0 (default) indicates continuous
time, True indicates discrete time with unspecified sampling
time, positive number is discrete time with specified
sampling time, None indicates unspecified timebase (either
continuous or discrete time).
params : dict, optional
Parameter values for the systems. Passed to the evaluation functions
for the system as default values, overriding internal defaults.
Expand All @@ -109,7 +116,7 @@ class for a set of subclasses that are used to implement specific

"""
def __init__(self, inputs=None, outputs=None, states=None, params={},
dt=None, name=None):
name=None, **kwargs):
"""Create an input/output system.

The InputOutputSystem contructor is used to create an input/output
Expand All @@ -134,10 +141,11 @@ def __init__(self, inputs=None, outputs=None, states=None, params={},
states : int, list of str, or None
Description of the system states. Same format as `inputs`.
dt : None, True or float, optional
System timebase. None (default) indicates continuous
time, True indicates discrete time with undefined sampling
System timebase. 0 (default) indicates continuous
time, True indicates discrete time with unspecified sampling
time, positive number is discrete time with specified
sampling time.
sampling time, None indicates unspecified timebase (either
continuous or discrete time).
params : dict, optional
Parameter values for the systems. Passed to the evaluation
functions for the system as default values, overriding internal
Expand All @@ -153,7 +161,7 @@ def __init__(self, inputs=None, outputs=None, states=None, params={},
"""
# Store the input arguments
self.params = params.copy() # default parameters
self.dt = dt # timebase
self.dt = kwargs.get('dt', config.defaults['control.default_dt']) # timebase
self.name = name # system name

# Parse and store the number of inputs, outputs, and states
Expand Down Expand Up @@ -200,10 +208,8 @@ def __mul__(sys2, sys1):
"inputs and outputs")

# Make sure timebase are compatible
dt = _find_timebase(sys1, sys2)
if dt is False:
raise ValueError("System timebases are not compabile")

dt = common_timebase(sys1.dt, sys2.dt)

# Return the series interconnection between the systems
newsys = InterconnectedSystem((sys1, sys2))

Expand Down Expand Up @@ -478,10 +484,8 @@ def feedback(self, other=1, sign=-1, params={}):
"inputs and outputs")

# Make sure timebases are compatible
dt = _find_timebase(self, other)
if dt is False:
raise ValueError("System timebases are not compabile")

dt = common_timebase(self.dt, other.dt)

# Return the series interconnection between the systems
newsys = InterconnectedSystem((self, other), params=params, dt=dt)

Expand Down Expand Up @@ -601,10 +605,11 @@ def __init__(self, linsys, inputs=None, outputs=None, states=None,
states : int, list of str, or None, optional
Description of the system states. Same format as `inputs`.
dt : None, True or float, optional
System timebase. None (default) indicates continuous
time, True indicates discrete time with undefined sampling
System timebase. 0 (default) indicates continuous
time, True indicates discrete time with unspecified sampling
time, positive number is discrete time with specified
sampling time.
sampling time, None indicates unspecified timebase (either
continuous or discrete time).
params : dict, optional
Parameter values for the systems. Passed to the evaluation
functions for the system as default values, overriding internal
Expand Down Expand Up @@ -670,7 +675,8 @@ class NonlinearIOSystem(InputOutputSystem):

"""
def __init__(self, updfcn, outfcn=None, inputs=None, outputs=None,
states=None, params={}, dt=None, name=None):
states=None, params={},
name=None, **kwargs):
"""Create a nonlinear I/O system given update and output functions.

Creates an `InputOutputSystem` for a nonlinear system by specifying a
Expand Down Expand Up @@ -722,10 +728,10 @@ def __init__(self, updfcn, outfcn=None, inputs=None, outputs=None,
operating in continuous or discrete time. It can have the
following values:

* dt = None No timebase specified
* dt = 0 Continuous time system
* dt > 0 Discrete time system with sampling time dt
* dt = True Discrete time with unspecified sampling time
* dt = 0: continuous time system (default)
* dt > 0: discrete time system with sampling period 'dt'
* dt = True: discrete time with unspecified sampling period
* dt = None: no timebase specified

name : string, optional
System name (used for specifying signals).
Expand All @@ -741,6 +747,7 @@ def __init__(self, updfcn, outfcn=None, inputs=None, outputs=None,
self.outfcn = outfcn

# Initialize the rest of the structure
dt = kwargs.get('dt', config.defaults['control.default_dt'])
super(NonlinearIOSystem, self).__init__(
inputs=inputs, outputs=outputs, states=states,
params=params, dt=dt, name=name
Expand Down Expand Up @@ -865,10 +872,10 @@ def __init__(self, syslist, connections=[], inplist=[], outlist=[],
operating in continuous or discrete time. It can have the
following values:

* dt = None No timebase specified
* dt = 0 Continuous time system
* dt > 0 Discrete time system with sampling time dt
* dt = True Discrete time with unspecified sampling time
* dt = 0: continuous time system (default)
* dt > 0: discrete time system with sampling period 'dt'
* dt = True: discrete time with unspecified sampling period
* dt = None: no timebase specified

name : string, optional
System name (used for specifying signals).
Expand All @@ -881,19 +888,14 @@ def __init__(self, syslist, connections=[], inplist=[], outlist=[],
# Check to make sure all systems are consistent
self.syslist = syslist
self.syslist_index = {}
dt = None
nstates = 0; self.state_offset = []
ninputs = 0; self.input_offset = []
noutputs = 0; self.output_offset = []
system_count = 0

for sys in syslist:
# Make sure time bases are consistent
# TODO: Use lti._find_timebase() instead?
if dt is None and sys.dt is not None:
# Timebase was not specified; set to match this system
dt = sys.dt
elif dt != sys.dt:
raise TypeError("System timebases are not compatible")
dt = common_timebase(dt, sys.dt)

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