Skip to content

Commit 1390e37

Browse files
committed
remove sisotool dependencies in rlocus
1 parent 1451f3a commit 1390e37

File tree

5 files changed

+77
-57
lines changed

5 files changed

+77
-57
lines changed

control/pzmap.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,8 +273,12 @@ def pole_zero_plot(
273273
label=response.sysname)
274274

275275
# Compute the axis limits to use
276-
xlim = (min(xlim[0], response.xlim[0]), max(xlim[1], response.xlim[1]))
277-
ylim = (min(ylim[0], response.ylim[0]), max(ylim[1], response.ylim[1]))
276+
if response.xlim is not None:
277+
xlim = (min(xlim[0], response.xlim[0]),
278+
max(xlim[1], response.xlim[1]))
279+
if response.ylim is not None:
280+
ylim = (min(ylim[0], response.ylim[0]),
281+
max(ylim[1], response.ylim[1]))
278282

279283
# Set up the limits for the plot
280284
ax.set_xlim(xlim if xlim_user is None else xlim_user)

control/rlocus.py

Lines changed: 30 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
from .iosys import isdtime
2626
from .xferfcn import _convert_to_transfer_function
2727
from .exception import ControlMIMONotImplemented
28-
from .sisotool import _SisotoolUpdate
2928
from .grid import sgrid, zgrid
3029
from . import config
3130
import warnings
@@ -76,7 +75,7 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None,
7675
ax : :class:`matplotlib.axes.Axes`
7776
Axes on which to create root locus plot
7877
initial_gain : float, optional
79-
Used by :func:`sisotool` to indicate initial gain.
78+
Specify the initial gain to use when marking current gain. [TODO: update]
8079
8180
Returns
8281
-------
@@ -100,17 +99,12 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None,
10099
print_gain = config._get_param(
101100
'rlocus', 'print_gain', print_gain, _rlocus_defaults)
102101

103-
# Check for sisotool mode
104-
sisotool = kwargs.get('sisotool', False)
105-
106-
# make sure siso. sisotool has different requirements
107-
if not sys.issiso() and not sisotool:
102+
if not sys.issiso():
108103
raise ControlMIMONotImplemented(
109104
'sys must be single-input single-output (SISO)')
110105

111-
sys_loop = sys[0,0]
112106
# Convert numerator and denominator to polynomials if they aren't
113-
(nump, denp) = _systopoly1d(sys_loop)
107+
nump, denp = _systopoly1d(sys)
114108

115109
# if discrete-time system and if xlim and ylim are not given,
116110
# that we a view of the unit circle
@@ -128,31 +122,30 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None,
128122
root_array = _RLSortRoots(root_array)
129123
recompute_on_zoom = False
130124

131-
if sisotool:
132-
start_roots = _RLFindRoots(nump, denp, initial_gain)
133-
134125
# Make sure there were no extraneous keywords
135-
if not sisotool and kwargs:
126+
if kwargs:
136127
raise TypeError("unrecognized keywords: ", str(kwargs))
137128

138129
# Create the Plot
139130
if plot:
140-
if sisotool:
141-
fig = kwargs['fig']
142-
ax = fig.axes[1]
143-
else:
144-
if ax is None:
145-
ax = plt.gca()
146-
fig = ax.figure
131+
if ax is None:
132+
ax = plt.gca()
147133
ax.set_title('Root Locus')
134+
fig = ax.figure
135+
136+
# TODO: get rid of extra variable start_roots
137+
if initial_gain is not None:
138+
start_roots = _RLFindRoots(nump, denp, initial_gain)
139+
else:
140+
start_roots = None
148141

149-
if print_gain and not sisotool:
142+
if print_gain and start_roots is None:
150143
fig.canvas.mpl_connect(
151144
'button_release_event',
152145
partial(_RLClickDispatcher, sys=sys, fig=fig,
153-
ax_rlocus=fig.axes[0], plotstr=plotstr))
154-
elif sisotool:
155-
fig.axes[1].plot(
146+
ax_rlocus=ax, plotstr=plotstr))
147+
elif start_roots is not None:
148+
ax.plot(
156149
[root.real for root in start_roots],
157150
[root.imag for root in start_roots],
158151
marker='s', markersize=6, zorder=20, color='k', label='gain_point')
@@ -165,14 +158,6 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None,
165158
"Clicked at: %10.4g%+10.4gj gain: %10.4g damp: %10.4g" %
166159
(s.real, s.imag, initial_gain, zeta),
167160
fontsize=12 if int(mpl.__version__[0]) == 1 else 10)
168-
fig.canvas.mpl_connect(
169-
'button_release_event',
170-
partial(_RLClickDispatcher, sys=sys, fig=fig,
171-
ax_rlocus=fig.axes[1], plotstr=plotstr,
172-
sisotool=sisotool,
173-
bode_plot_params=kwargs['bode_plot_params'],
174-
tvect=kwargs['tvect']))
175-
176161

177162
if recompute_on_zoom:
178163
# update gains and roots when xlim/ylim change. Only then are
@@ -526,7 +511,7 @@ def _RLZoomDispatcher(event, sys, ax_rlocus, plotstr):
526511
scalex=False, scaley=False)
527512

528513

529-
def _RLClickDispatcher(event, sys, fig, ax_rlocus, plotstr, sisotool=False,
514+
def _RLClickDispatcher(event, sys, fig, ax_rlocus, plotstr,
530515
bode_plot_params=None, tvect=None):
531516
"""Rootlocus plot click dispatcher"""
532517

@@ -536,15 +521,14 @@ def _RLClickDispatcher(event, sys, fig, ax_rlocus, plotstr, sisotool=False,
536521
{'zoom rect', 'pan/zoom'}:
537522

538523
# if a point is clicked on the rootlocus plot visually emphasize it
539-
K = _RLFeedbackClicksPoint(event, sys, fig, ax_rlocus, sisotool)
540-
if sisotool and K is not None:
541-
_SisotoolUpdate(sys, fig, K, bode_plot_params, tvect)
524+
K = _RLFeedbackClicksPoint(
525+
event, sys, fig, ax_rlocus, show_clicked=False)
542526

543527
# Update the canvas
544528
fig.canvas.draw()
545529

546530

547-
def _RLFeedbackClicksPoint(event, sys, fig, ax_rlocus, sisotool=False):
531+
def _RLFeedbackClicksPoint(event, sys, fig, ax_rlocus, show_clicked=False):
548532
"""Display root-locus gain feedback point for clicks on root-locus plot"""
549533
sys_loop = sys[0,0]
550534
(nump, denp) = _systopoly1d(sys_loop)
@@ -591,16 +575,20 @@ def _RLFeedbackClicksPoint(event, sys, fig, ax_rlocus, sisotool=False):
591575
# Remove the previous line
592576
_removeLine(label='gain_point', ax=ax_rlocus)
593577

594-
# Visualise clicked point, display all roots for sisotool mode
595-
if sisotool:
578+
if show_clicked:
579+
# Visualise clicked point, display all roots
596580
root_array = _RLFindRoots(nump, denp, K.real)
597581
ax_rlocus.plot(
598582
[root.real for root in root_array],
599583
[root.imag for root in root_array],
600-
marker='s', markersize=6, zorder=20, label='gain_point', color='k')
584+
marker='s', markersize=6, zorder=20, label='gain_point',
585+
color='k')
601586
else:
602-
ax_rlocus.plot(s.real, s.imag, 'k.', marker='s', markersize=8,
603-
zorder=20, label='gain_point')
587+
# Just show the clicked point
588+
# TODO: should we keep this?
589+
ax_rlocus.plot(
590+
s.real, s.imag, 'k.', marker='s', markersize=8, zorder=20,
591+
label='gain_point')
604592

605593
return K.real
606594

control/sisotool.py

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
__all__ = ['sisotool', 'rootlocus_pid_designer']
22

3+
import numpy as np
4+
import matplotlib.pyplot as plt
5+
import warnings
6+
from functools import partial
7+
38
from control.exception import ControlMIMONotImplemented
49
from .freqplot import bode_plot
510
from .timeresp import step_response
@@ -11,9 +16,6 @@
1116
from .nlsys import interconnect
1217
from control.statesp import _convert_to_statespace
1318
from . import config
14-
import numpy as np
15-
import matplotlib.pyplot as plt
16-
import warnings
1719

1820
_sisotool_defaults = {
1921
'sisotool.initial_gain': 1
@@ -123,13 +125,39 @@ def sisotool(sys, initial_gain=None, xlim_rlocus=None, ylim_rlocus=None,
123125
initial_gain = config._get_param('sisotool', 'initial_gain',
124126
initial_gain, _sisotool_defaults)
125127

126-
# First time call to setup the bode and step response plots
128+
# First time call to setup the Bode and step response plots
127129
_SisotoolUpdate(sys, fig, initial_gain, bode_plot_params)
128130

129-
# Setup the root-locus plot window
130-
root_locus(sys, initial_gain=initial_gain, xlim=xlim_rlocus,
131+
root_locus(
132+
sys[0, 0], initial_gain=initial_gain, xlim=xlim_rlocus,
131133
ylim=ylim_rlocus, plotstr=plotstr_rlocus, grid=rlocus_grid,
132-
fig=fig, bode_plot_params=bode_plot_params, tvect=tvect, sisotool=True)
134+
ax=fig.axes[1])
135+
136+
# Reset the button release callback so that we can update all plots
137+
fig.canvas.mpl_connect(
138+
'button_release_event',
139+
partial(_click_dispatcher, sys=sys, fig=fig,
140+
ax_rlocus=fig.axes[1], plotstr=plotstr_rlocus,
141+
bode_plot_params=bode_plot_params, tvect=tvect))
142+
143+
144+
def _click_dispatcher(event, sys, fig, ax_rlocus, plotstr,
145+
bode_plot_params=None, tvect=None):
146+
from .rlocus import _RLFeedbackClicksPoint
147+
148+
# Zoom is handled by specialized callback in rlocus, only handle gain plot
149+
if event.inaxes == ax_rlocus.axes and \
150+
plt.get_current_fig_manager().toolbar.mode not in \
151+
{'zoom rect', 'pan/zoom'}:
152+
# if a point is clicked on the rootlocus plot visually emphasize it
153+
K = _RLFeedbackClicksPoint(
154+
event, sys, fig, ax_rlocus, show_clicked=True)
155+
if K is not None:
156+
_SisotoolUpdate(sys, fig, K, bode_plot_params, tvect)
157+
158+
# Update the canvas
159+
fig.canvas.draw()
160+
133161

134162
def _SisotoolUpdate(sys, fig, K, bode_plot_params, tvect=None):
135163

control/tests/pzmap_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def test_pzmap(kwargs, setdefaults, dt, editsdefaults, mplcleanup):
6060
fig, ax = plt.gcf(), plt.gca()
6161

6262
assert fig._suptitle.get_text().startswith(
63-
kwargs.get('title', 'Pole/zero map'))
63+
kwargs.get('title', 'Pole/zero plot'))
6464

6565
# FIXME: This won't work when zgrid and sgrid are unified
6666
children = ax.get_children()

control/tests/sisotool_test.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import pytest
88

99
from control.sisotool import sisotool, rootlocus_pid_designer
10-
from control.rlocus import _RLClickDispatcher
10+
from control.sisotool import _click_dispatcher
1111
from control.xferfcn import TransferFunction
1212
from control.statesp import StateSpace
1313
from control import c2d
@@ -93,8 +93,8 @@ def test_sisotool(self, tsys):
9393
event = type('test', (object,), {'xdata': 2.31206868287,
9494
'ydata': 15.5983051046,
9595
'inaxes': ax_rlocus.axes})()
96-
_RLClickDispatcher(event=event, sys=tsys, fig=fig,
97-
ax_rlocus=ax_rlocus, sisotool=True, plotstr='-',
96+
_click_dispatcher(event=event, sys=tsys, fig=fig,
97+
ax_rlocus=ax_rlocus, plotstr='-',
9898
bode_plot_params=bode_plot_params, tvect=None)
9999

100100
# Check the moved root locus plot points
@@ -143,8 +143,8 @@ def test_sisotool_tvect(self, tsys):
143143
event = type('test', (object,), {'xdata': 2.31206868287,
144144
'ydata': 15.5983051046,
145145
'inaxes': ax_rlocus.axes})()
146-
_RLClickDispatcher(event=event, sys=tsys, fig=fig,
147-
ax_rlocus=ax_rlocus, sisotool=True, plotstr='-',
146+
_click_dispatcher(event=event, sys=tsys, fig=fig,
147+
ax_rlocus=ax_rlocus, plotstr='-',
148148
bode_plot_params=dict(), tvect=tvect)
149149
assert_array_almost_equal(tvect, ax_step.lines[0].get_data()[0])
150150

0 commit comments

Comments
 (0)