Skip to content

Commit 346bc40

Browse files
authored
Merge pull request #863 from henklaak/main
Solve #862 and #864: bode_plot phase wrapping incorrect for multiple systems
2 parents c4d1764 + 4c4b3fc commit 346bc40

File tree

2 files changed

+29
-11
lines changed

2 files changed

+29
-11
lines changed

control/freqplot.py

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -276,18 +276,21 @@ def bode_plot(syslist, omega=None,
276276
if initial_phase is None:
277277
# Start phase in the range 0 to -360 w/ initial phase = -180
278278
# If wrap_phase is true, use 0 instead (phase \in (-pi, pi])
279-
initial_phase = -math.pi if wrap_phase is not True else 0
279+
initial_phase_value = -math.pi if wrap_phase is not True else 0
280280
elif isinstance(initial_phase, (int, float)):
281281
# Allow the user to override the default calculation
282282
if deg:
283-
initial_phase = initial_phase/180. * math.pi
283+
initial_phase_value = initial_phase/180. * math.pi
284+
else:
285+
initial_phase_value = initial_phase
286+
284287
else:
285288
raise ValueError("initial_phase must be a number.")
286289

287290
# Shift the phase if needed
288-
if abs(phase[0] - initial_phase) > math.pi:
291+
if abs(phase[0] - initial_phase_value) > math.pi:
289292
phase -= 2*math.pi * \
290-
round((phase[0] - initial_phase) / (2*math.pi))
293+
round((phase[0] - initial_phase_value) / (2*math.pi))
291294

292295
# Phase wrapping
293296
if wrap_phase is False:
@@ -1021,9 +1024,11 @@ def _parse_linestyle(style_name, allow_false=False):
10211024
# Plot the scaled sections of the curve (changing linestyle)
10221025
x_scl = np.ma.masked_where(scale_mask, resp.real)
10231026
y_scl = np.ma.masked_where(scale_mask, resp.imag)
1024-
plt.plot(
1025-
x_scl * (1 + curve_offset), y_scl * (1 + curve_offset),
1026-
primary_style[1], color=c, **kwargs)
1027+
if x_scl.count() >= 1 and y_scl.count() >= 1:
1028+
plt.plot(
1029+
x_scl * (1 + curve_offset),
1030+
y_scl * (1 + curve_offset),
1031+
primary_style[1], color=c, **kwargs)
10271032

10281033
# Plot the primary curve (invisible) for setting arrows
10291034
x, y = resp.real.copy(), resp.imag.copy()
@@ -1041,10 +1046,11 @@ def _parse_linestyle(style_name, allow_false=False):
10411046
# Plot the regular and scaled segments
10421047
plt.plot(
10431048
x_reg, -y_reg, mirror_style[0], color=c, **kwargs)
1044-
plt.plot(
1045-
x_scl * (1 - curve_offset),
1046-
-y_scl * (1 - curve_offset),
1047-
mirror_style[1], color=c, **kwargs)
1049+
if x_scl.count() >= 1 and y_scl.count() >= 1:
1050+
plt.plot(
1051+
x_scl * (1 - curve_offset),
1052+
-y_scl * (1 - curve_offset),
1053+
mirror_style[1], color=c, **kwargs)
10481054

10491055
# Add the arrows (on top of an invisible contour)
10501056
x, y = resp.real.copy(), resp.imag.copy()

control/tests/freqresp_test.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,18 @@ def test_phase_wrap(TF, wrap_phase, min_phase, max_phase):
375375
assert(max(phase) <= max_phase)
376376

377377

378+
def test_phase_wrap_multiple_systems():
379+
sys_unstable = ctrl.zpk([],[1,1], gain=1)
380+
381+
mag, phase, omega = ctrl.bode(sys_unstable, plot=False)
382+
assert(np.min(phase) >= -2*np.pi)
383+
assert(np.max(phase) <= -1*np.pi)
384+
385+
mag, phase, omega = ctrl.bode((sys_unstable, sys_unstable), plot=False)
386+
assert(np.min(phase) >= -2*np.pi)
387+
assert(np.max(phase) <= -1*np.pi)
388+
389+
378390
def test_freqresp_warn_infinite():
379391
"""Test evaluation warnings for transfer functions w/ pole at the origin"""
380392
sys_finite = ctrl.tf([1], [1, 0.01])

0 commit comments

Comments
 (0)