Merge pull request #668 from sawyerbfuller/nyquist-discrete-indent · python-control/python-control@affe4d3
@@ -714,41 +714,53 @@ def nyquist_plot(syslist, omega=None, plot=True, omega_limits=None,
714714if np.any(omega_sys * sys.dt > np.pi) and warn_nyquist:
715715warnings.warn("evaluation above Nyquist frequency")
716716717-# Transform frequencies to continuous domain
718-contour = np.exp(1j * omega * sys.dt)
719-else:
720-contour = 1j * omega_sys
717+# do indentations in s-plane where it is more convenient
718+splane_contour = 1j * omega_sys
721719722720# Bend the contour around any poles on/near the imaginary axis
721+# TODO: smarter indent radius that depends on dcgain of system
722+# and timebase of discrete system.
723723if isinstance(sys, (StateSpace, TransferFunction)) \
724-and sys.isctime() and indent_direction != 'none':
725-poles = sys.pole()
726-if contour[1].imag > indent_radius \
727-and 0. in poles and not omega_range_given:
724+and indent_direction != 'none':
725+if sys.isctime():
726+splane_poles = sys.pole()
727+else:
728+# map z-plane poles to s-plane, ignoring any at the origin
729+# because we don't need to indent for them
730+zplane_poles = sys.pole()
731+zplane_poles = zplane_poles[~np.isclose(abs(zplane_poles), 0.)]
732+splane_poles = np.log(zplane_poles)/sys.dt
733+734+if splane_contour[1].imag > indent_radius \
735+and np.any(np.isclose(abs(splane_poles), 0)) \
736+and not omega_range_given:
728737# add some points for quarter circle around poles at origin
729-contour = np.concatenate(
738+splane_contour = np.concatenate(
730739 (1j * np.linspace(0., indent_radius, 50),
731- contour[1:]))
732-for i, s in enumerate(contour):
740+splane_contour[1:]))
741+for i, s in enumerate(splane_contour):
733742# Find the nearest pole
734-p = poles[(np.abs(poles - s)).argmin()]
735-743+p = splane_poles[(np.abs(splane_poles - s)).argmin()]
736744# See if we need to indent around it
737745if abs(s - p) < indent_radius:
738-if p.real < 0 or \
739-(p.real == 0 and indent_direction == 'right'):
746+if p.real < 0 or (np.isclose(p.real, 0) \
747+ and indent_direction == 'right'):
740748# Indent to the right
741-contour[i] += \
749+splane_contour[i] += \
742750np.sqrt(indent_radius ** 2 - (s-p).imag ** 2)
743-elif p.real > 0 or \
744-(p.real == 0 and indent_direction == 'left'):
751+elif p.real > 0 or (np.isclose(p.real, 0) \
752+ and indent_direction == 'left'):
745753# Indent to the left
746-contour[i] -= \
754+splane_contour[i] -= \
747755np.sqrt(indent_radius ** 2 - (s-p).imag ** 2)
748756else:
749757ValueError("unknown value for indent_direction")
750758751-# TODO: add code to indent around discrete poles on unit circle
759+# change contour to z-plane if necessary
760+if sys.isctime():
761+contour = splane_contour
762+else:
763+contour = np.exp(splane_contour * sys.dt)
752764753765# Compute the primary curve
754766resp = sys(contour)