Skip to content

Commit f25e625

Browse files
committed
fix timebase processing in frd, zpk
1 parent d810d5a commit f25e625

4 files changed

Lines changed: 38 additions & 6 deletions

File tree

control/frdata.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,22 @@ def __init__(self, *args, **kwargs):
124124
To construct frequency response data for an existing LTI
125125
object, other than an FRD, call FRD(sys, omega).
126126
127+
The timebase for the frequency response can be provided using an
128+
optional third argument or the 'dt' keyword.
129+
127130
"""
128-
# TODO: discrete-time FRD systems?
129131
smooth = kwargs.pop('smooth', False)
130132

131133
#
132134
# Process positional arguments
133135
#
136+
if len(args) == 3:
137+
# Discrete time transfer function
138+
if 'dt' in kwargs:
139+
raise TypeError(
140+
"timebase specified as positional argument and keyword")
141+
kwargs['dt'] = args.pop()
142+
134143
if len(args) == 2:
135144
if not isinstance(args[0], FRD) and isinstance(args[0], LTI):
136145
# not an FRD, but still a system, second argument should be
@@ -200,11 +209,11 @@ def __init__(self, *args, **kwargs):
200209

201210
# Process iosys keywords
202211
defaults = {
203-
'inputs': self.fresp.shape[1], 'outputs': self.fresp.shape[0],
204-
'dt': None}
212+
'inputs': self.fresp.shape[1], 'outputs': self.fresp.shape[0]}
213+
if arg_dt is not None:
214+
defaults['dt'] = arg_dt # choose compatible timebase
205215
name, inputs, outputs, states, dt = _process_iosys_keywords(
206216
kwargs, defaults, end=True)
207-
dt = common_timebase(dt, arg_dt) # choose compatible timebase
208217

209218
# Process signal names
210219
InputOutputSystem.__init__(

control/tests/docstrings_test.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
control.bode_plot: ['sharex', 'sharey', 'margin_info'], # deprecated
5555
control.eigensys_realization: ['arg'], # quasi-positional
5656
control.find_operating_point: ['method'], # internal use
57+
control.zpk: ['args'] # 'dt' (manual)
5758
}
5859

5960
# Decide on the level of verbosity (use -rP when running pytest)

control/tests/timebase_test.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,25 @@ def test_composition_override(dt):
9797
with pytest.raises(ValueError, match="incompatible timebases"):
9898
sys3 = ct.interconnect(
9999
[sys1, sys2], inputs='u1', outputs='y2', dt=dt)
100+
101+
102+
# Make sure all system creation functions treat timebases uniformly
103+
@pytest.mark.parametrize(
104+
"fcn, args", [
105+
(ct.ss, [-1, 1, 1, 1]),
106+
(ct.tf, [[1, 2], [3, 4, 5]]),
107+
(ct.zpk, [[-1], [-2, -3], 1]),
108+
(ct.frd, [[1, 1, 1], [1, 2, 3]]),
109+
(ct.nlsys, [lambda t, x, u, params: -x, None]),
110+
])
111+
@pytest.mark.parametrize(
112+
"kwargs, expected", [
113+
({}, 0),
114+
({'dt': 0}, 0),
115+
({'dt': 0.1}, 0.1),
116+
({'dt': True}, True),
117+
({'dt': None}, None),
118+
])
119+
def test_default(fcn, args, kwargs, expected):
120+
sys = fcn(*args, **kwargs)
121+
assert sys.dt == expected

control/xferfcn.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1677,7 +1677,7 @@ def tf(*args, **kwargs):
16771677
raise ValueError("Needs 1 or 2 arguments; received %i." % len(args))
16781678

16791679

1680-
def zpk(zeros, poles, gain, dt=None, **kwargs):
1680+
def zpk(zeros, poles, gain, *args, **kwargs):
16811681
"""zpk(zeros, poles, gain[, dt])
16821682
16831683
Create a transfer function from zeros, poles, gain.
@@ -1732,7 +1732,7 @@ def zpk(zeros, poles, gain, dt=None, **kwargs):
17321732
17331733
"""
17341734
num, den = zpk2tf(zeros, poles, gain)
1735-
return TransferFunction(num, den, dt=dt, **kwargs)
1735+
return TransferFunction(num, den, *args, **kwargs)
17361736

17371737

17381738
def ss2tf(*args, **kwargs):

0 commit comments

Comments
 (0)