Skip to content

Commit 78a03c2

Browse files
authored
Merge pull request #126 from roryyorke/rory/discr-time-dcgain-fix
BugFix: DC gain for discrete-time systems
2 parents 32f13bc + 310f580 commit 78a03c2

File tree

4 files changed

+79
-7
lines changed

4 files changed

+79
-7
lines changed

control/statesp.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -612,11 +612,14 @@ def dcgain(self):
612612
at the origin
613613
"""
614614
try:
615-
gain = np.asarray(self.D -
616-
self.C.dot(np.linalg.solve(self.A, self.B)))
615+
if self.isctime():
616+
gain = np.asarray(self.D -
617+
self.C.dot(np.linalg.solve(self.A, self.B)))
618+
else:
619+
gain = self.horner(1)
617620
except LinAlgError:
618-
# zero eigenvalue: singular matrix
619-
return np.nan
621+
# eigenvalue at DC
622+
gain = np.tile(np.nan,(self.outputs,self.inputs))
620623
return np.squeeze(gain)
621624

622625

control/tests/statesp_test.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,8 @@ def testArrayAccessSS(self):
228228

229229
assert sys1.dt == sys1_11.dt
230230

231-
def test_dcgain(self):
231+
def test_dcgain_cont(self):
232+
"""Test DC gain for continuous-time state-space systems"""
232233
sys = StateSpace(-2.,6.,5.,0)
233234
np.testing.assert_equal(sys.dcgain(), 15.)
234235

@@ -239,6 +240,43 @@ def test_dcgain(self):
239240
sys3 = StateSpace(0., 1., 1., 0.)
240241
np.testing.assert_equal(sys3.dcgain(), np.nan)
241242

243+
def test_dcgain_discr(self):
244+
"""Test DC gain for discrete-time state-space systems"""
245+
# static gain
246+
sys = StateSpace([], [], [], 2, True)
247+
np.testing.assert_equal(sys.dcgain(), 2)
248+
249+
# averaging filter
250+
sys = StateSpace(0.5, 0.5, 1, 0, True)
251+
np.testing.assert_almost_equal(sys.dcgain(), 1)
252+
253+
# differencer
254+
sys = StateSpace(0, 1, -1, 1, True)
255+
np.testing.assert_equal(sys.dcgain(), 0)
256+
257+
# summer
258+
sys = StateSpace(1, 1, 1, 0, True)
259+
np.testing.assert_equal(sys.dcgain(), np.nan)
260+
261+
def test_dcgain_integrator(self):
262+
"""DC gain when eigenvalue at DC returns appropriately sized array of nan"""
263+
# the SISO case is also tested in test_dc_gain_{cont,discr}
264+
import itertools
265+
# iterate over input and output sizes, and continuous (dt=None) and discrete (dt=True) time
266+
for inputs,outputs,dt in itertools.product(range(1,6),range(1,6),[None,True]):
267+
states = max(inputs,outputs)
268+
269+
# a matrix that is singular at DC, and has no "useless" states as in _remove_useless_states
270+
a = np.triu(np.tile(2,(states,states)))
271+
# eigenvalues all +2, except for ...
272+
a[0,0] = 0 if dt is None else 1
273+
b = np.eye(max(inputs,states))[:states,:inputs]
274+
c = np.eye(max(outputs,states))[:outputs,:states]
275+
d = np.zeros((outputs,inputs))
276+
sys = StateSpace(a,b,c,d,dt)
277+
dc = np.squeeze(np.tile(np.nan,(outputs,inputs)))
278+
np.testing.assert_array_equal(dc, sys.dcgain())
279+
242280

243281
def test_scalarStaticGain(self):
244282
"""Regression: can we create a scalar static gain?"""

control/tests/xferfcn_test.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,8 @@ def testMatrixMult(self):
524524
np.testing.assert_array_almost_equal(H.num[1][0], H2.num[0][0])
525525
np.testing.assert_array_almost_equal(H.den[1][0], H2.den[0][0])
526526

527-
def test_dcgain(self):
527+
def test_dcgain_cont(self):
528+
"""Test DC gain for continuous-time transfer functions"""
528529
sys = TransferFunction(6, 3)
529530
np.testing.assert_equal(sys.dcgain(), 2)
530531

@@ -540,6 +541,26 @@ def test_dcgain(self):
540541
expected = [[5, 7, 11], [2, 2, 2]]
541542
np.testing.assert_array_equal(sys4.dcgain(), expected)
542543

544+
def test_dcgain_discr(self):
545+
"""Test DC gain for discrete-time transfer functions"""
546+
# static gain
547+
sys = TransferFunction(6, 3, True)
548+
np.testing.assert_equal(sys.dcgain(), 2)
549+
550+
# averaging filter
551+
sys = TransferFunction(0.5, [1, -0.5], True)
552+
np.testing.assert_almost_equal(sys.dcgain(), 1)
553+
554+
# differencer
555+
sys = TransferFunction(1, [1, -1], True)
556+
np.testing.assert_equal(sys.dcgain(), np.inf)
557+
558+
# summer
559+
# causes a RuntimeWarning due to the divide by zero
560+
sys = TransferFunction([1,-1], [1], True)
561+
np.testing.assert_equal(sys.dcgain(), 0)
562+
563+
543564
def suite():
544565
return unittest.TestLoader().loadTestsFromTestCase(TestXferFcn)
545566

control/xferfcn.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -945,13 +945,23 @@ def sample(self, Ts, method='zoh', alpha=None):
945945
def dcgain(self):
946946
"""Return the zero-frequency (or DC) gain
947947
948-
For a transfer function G(s), the DC gain is G(0)
948+
For a continous-time transfer function G(s), the DC gain is G(0)
949+
For a discrete-time transfer function G(z), the DC gain is G(1)
949950
950951
Returns
951952
-------
952953
gain : ndarray
953954
The zero-frequency gain
954955
"""
956+
if self.isctime():
957+
return self._dcgain_cont()
958+
else:
959+
return self(1)
960+
961+
def _dcgain_cont(self):
962+
"""_dcgain_cont() -> DC gain as matrix or scalar
963+
964+
Special cased evaluation at 0 for continuous-time systems"""
955965
gain = np.empty((self.outputs, self.inputs), dtype=float)
956966
for i in range(self.outputs):
957967
for j in range(self.inputs):

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy