Merge pull request #997 from wueestry/nyquist_improvements · python-control/python-control@4acc78b
@@ -1060,7 +1060,9 @@ def gen_zero_centered_series(val_min, val_max, period):
10601060'nyquist.max_curve_magnitude': 20, # clip large values
10611061'nyquist.max_curve_offset': 0.02, # offset of primary/mirror
10621062'nyquist.start_marker': 'o', # marker at start of curve
1063-'nyquist.start_marker_size': 4, # size of the maker
1063+'nyquist.start_marker_size': 4, # size of the marker
1064+'nyquist.circle_style': # style for unit circles
1065+ {'color': 'black', 'linestyle': 'dashed', 'linewidth': 1}
10641066}
1065106710661068@@ -1477,8 +1479,8 @@ def nyquist_response(
1477147914781480def nyquist_plot(
14791481data, omega=None, plot=None, label_freq=0, color=None, label=None,
1480-return_contour=None, title=None, legend_loc='upper right',
1481-ax=None, **kwargs):
1482+return_contour=None, title=None, legend_loc='upper right', ax=None,
1483+unit_circle=False, mt_circles=None, ms_circles=None, **kwargs):
14821484"""Nyquist plot for a system.
1483148514841486 Generates a Nyquist plot for the system over a (optional) frequency
@@ -1501,7 +1503,13 @@ def nyquist_plot(
15011503 ``omega_limits``.
15021504 color : string, optional
15031505 Used to specify the color of the line and arrowhead.
1504-1506+ unit_circle : bool, optional
1507+ If ``True``, display the unit circle, to read gain crossover frequency.
1508+ mt_circles : array_like, optional
1509+ Draw circles corresponding to the given magnitudes of sensitivity.
1510+ ms_circles : array_like, optional
1511+ Draw circles corresponding to the given magnitudes of complementary
1512+ sensitivity.
15051513 **kwargs : :func:`matplotlib.pyplot.plot` keyword properties, optional
15061514 Additional keywords (passed to `matplotlib`)
15071515@@ -1856,7 +1864,48 @@ def _parse_linestyle(style_name, allow_false=False):
18561864# Mark the -1 point
18571865plt.plot([-1], [0], 'r+')
185818661859-# Label the frequencies of the points
1867+#
1868+# Draw circles for gain crossover and sensitivity functions
1869+#
1870+theta = np.linspace(0, 2*np.pi, 100)
1871+cos = np.cos(theta)
1872+sin = np.sin(theta)
1873+label_pos = 15
1874+1875+# Display the unit circle, to read gain crossover frequency
1876+if unit_circle:
1877+plt.plot(cos, sin, **config.defaults['nyquist.circle_style'])
1878+1879+# Draw circles for given magnitudes of sensitivity
1880+if ms_circles is not None:
1881+for ms in ms_circles:
1882+pos_x = -1 + (1/ms)*cos
1883+pos_y = (1/ms)*sin
1884+plt.plot(
1885+pos_x, pos_y, **config.defaults['nyquist.circle_style'])
1886+plt.text(pos_x[label_pos], pos_y[label_pos], ms)
1887+1888+# Draw circles for given magnitudes of complementary sensitivity
1889+if mt_circles is not None:
1890+for mt in mt_circles:
1891+if mt != 1:
1892+ct = -mt**2/(mt**2-1) # Mt center
1893+rt = mt/(mt**2-1) # Mt radius
1894+pos_x = ct+rt*cos
1895+pos_y = rt*sin
1896+plt.plot(
1897+pos_x, pos_y,
1898+**config.defaults['nyquist.circle_style'])
1899+plt.text(pos_x[label_pos], pos_y[label_pos], mt)
1900+else:
1901+_, _, ymin, ymax = plt.axis()
1902+pos_y = np.linspace(ymin, ymax, 100)
1903+plt.vlines(
1904+-0.5, ymin=ymin, ymax=ymax,
1905+**config.defaults['nyquist.circle_style'])
1906+plt.text(-0.5, pos_y[label_pos], 1)
1907+1908+# Label the frequencies of the points on the Nyquist curve
18601909if label_freq:
18611910ind = slice(None, None, label_freq)
18621911omega_sys = np.imag(splane_contour[np.real(splane_contour) == 0])