8000 Merge pull request #589 from bnavigator/drss-dt · lmartinp/python-control@8b900ca · GitHub
[go: up one dir, main page]

Skip to content

Commit 8b900ca

Browse files
authored
Merge pull request python-control#589 from bnavigator/drss-dt
Return a discrete time system with drss()
2 parents ce4c2b6 + e50ce23 commit 8b900ca

File tree

2 files changed

+57
-15
lines changed

2 files changed

+57
-15
lines changed

control/statesp.py

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1434,11 +1434,11 @@ def _convert_to_statespace(sys, **kw):
14341434

14351435

14361436
# TODO: add discrete time option
1437-
def _rss_generate(states, inputs, outputs, type, strictly_proper=False):
1437+
def _rss_generate(states, inputs, outputs, cdtype, strictly_proper=False):
14381438
"""Generate a random state space.
14391439
14401440
This does the actual random state space generation expected from rss and
1441-
drss. type is 'c' for continuous systems and 'd' for discrete systems.
1441+
drss. cdtype is 'c' for continuous systems and 'd' for discrete systems.
14421442
14431443
"""
14441444

@@ -1465,6 +1465,8 @@ def _rss_generate(states, inputs, outputs, type, strictly_proper=False):
14651465
if outputs < 1 or outputs % 1:
14661466
raise ValueError("outputs must be a positive integer. outputs = %g." %
14671467
outputs)
1468+
if cdtype not in ['c', 'd']:
1469+
raise ValueError("cdtype must be `c` or `d`")
14681470

14691471
# Make some poles for A. Preallocate a complex array.
14701472
poles = zeros(states) + zeros(states) * 0.j
@@ -1484,16 +1486,16 @@ def _rss_generate(states, inputs, outputs, type, strictly_proper=False):
14841486
i += 2
14851487
elif rand() < pReal or i == states - 1:
14861488
# No-oscillation pole.
1487-
if type == 'c':
1489+
if cdtype == 'c':
14881490
poles[i] = -exp(randn()) + 0.j
1489-
elif type == 'd':
1491+
else:
14901492
poles[i] = 2. * rand() - 1.
14911493
i += 1
14921494
else:
14931495
# Complex conjugate pair of oscillating poles.
1494-
if type == 'c':
1496+
if cdtype == 'c':
14951497
poles[i] = complex(-exp(randn()), 3. * exp(randn()))
1496-
elif type == 'd':
1498+
else:
14971499
mag = rand()
14981500
phase = 2. * math.pi * rand()
14991501
poles[i] = complex(mag * cos(phase), mag * sin(phase))
@@ -1546,7 +1548,11 @@ def _rss_generate(states, inputs, outputs, type, strictly_proper=False):
15461548
C = C * Cmask
15471549
D = D * Dmask if not strictly_proper else zeros(D.shape)
15481550

1549-
return StateSpace(A, B, C, D)
1551+
if cdtype == 'c':
1552+
ss_args = (A, B, C, D)
1553+
else:
1554+
ss_args = (A, B, C, D, True)
1555+
return StateSpace(*ss_args)
15501556

15511557

15521558
# Convert a MIMO system to a SISO system
@@ -1825,15 +1831,14 @@ def rss(states=1, outputs=1, inputs=1, strictly_proper=False):
18251831
18261832
Parameters
18271833
----------
1828-
states : integer
1834+
states : int
18291835
Number of state variables
1830-
inputs : integer
1836+
inputs : int
18311837
Number of system inputs
1832-
outputs : integer
1838+
outputs : int
18331839
Number of system outputs
18341840
strictly_proper : bool, optional
1835-
If set to 'True', returns a proper system (no direct term). Default
1836-
value is 'False'.
1841+
If set to 'True', returns a proper system (no direct term).
18371842
18381843
Returns
18391844
-------
@@ -1867,12 +1872,15 @@ def drss(states=1, outputs=1, inputs=1, strictly_proper=False):
18671872
18681873
Parameters
18691874
----------
1870-
states : integer
1875+
states : int
18711876
Number of state variables
18721877
inputs : integer
18731878
Number of system inputs
1874-
outputs : integer
1879+
outputs : int
18751880
Number of system outputs
1881+
strictly_proper: bool, optional
1882+
If set to 'True', returns a proper system (no direct term).
1883+
18761884
18771885
Returns
18781886
-------

control/tests/statesp_test.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from control.dtime import sample_system
2020
from control.lti import evalfr
2121
from control.statesp import (StateSpace, _convert_to_statespace, drss,
22-
rss, ss, tf2ss, _statesp_defaults)
22+
rss, ss, tf2ss, _statesp_defaults, _rss_generate)
2323
from control.tests.conftest import ismatarrayout, slycotonly
2424
from control.xferfcn import TransferFunction, ss2tf
2525

@@ -855,6 +855,28 @@ def test_pole(self, states, outputs, inputs):
855855
for z in p:
856856
assert z.real < 0
857857

858+
@pytest.mark.parametrize('strictly_proper', [True, False])
859+
def test_strictly_proper(self, strictly_proper):
860+
"""Test that the strictly_proper argument returns a correct D."""
861+
for i in range(100):
862+
# The probability that drss(..., strictly_proper=False) returns an
863+
# all zero D 100 times in a row is 0.5**100 = 7.89e-31
864+
sys = rss(1, 1, 1, strictly_proper=strictly_proper)
865+
if np.all(sys.D == 0.) == strictly_proper:
866+
break
867+
assert np.all(sys.D == 0.) == strictly_proper
868+
869+
@pytest.mark.parametrize('par, errmatch',
870+
[((-1, 1, 1, 'c'), 'states must be'),
871+
((1, -1, 1, 'c'), 'inputs must be'),
872+
((1, 1, -1, 'c'), 'outputs must be'),
873+
((1, 1, 1, 'x'), 'cdtype must be'),
874+
])
875+
def test_rss_invalid(self, par, errmatch):
876+
"""Test invalid inputs for rss() and drss()."""
877+
with pytest.raises(ValueError, match=errmatch):
878+
_rss_generate(*par)
879+
858880

859881
class TestDrss:
860882
"""These are tests for the proper functionality of statesp.drss."""
@@ -873,6 +895,7 @@ def test_shape(self, states, outputs, inputs):
873895
assert sys.nstates == states
874896
assert sys.ninputs == inputs
875897
assert sys.noutputs == outputs
898+
assert sys.dt is True
876899

877900
@pytest.mark.parametrize('states', range(1, maxStates))
878901
@pytest.mark.parametrize('outputs', range(1, maxIO))
@@ -884,6 +907,17 @@ def test_pole(self, states, outputs, inputs):
884907
for z in p:
885908
assert abs(z) < 1
886909

910+
@pytest.mark.parametrize('strictly_proper', [True, False])
911+
def test_strictly_proper(self, strictly_proper):
912+
"""Test that the strictly_proper argument returns a correct D."""
913+
for i in range(100):
914+
# The probability that drss(..., strictly_proper=False) returns an
915+
# all zero D 100 times in a row is 0.5**100 = 7.89e-31
916+
sys = drss(1, 1, 1, strictly_proper=strictly_proper)
917+
if np.all(sys.D == 0.) == strictly_proper:
918+
break
919+
assert np.all(sys.D == 0.) == strictly_proper
920+
887921

888922
class TestLTIConverter:
889923
"""Test returnScipySignalLTI method"""

0 commit comments

Comments
 (0)
0