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,

714714

if np.any(omega_sys * sys.dt > np.pi) and warn_nyquist:

715715

warnings.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.

723723

if 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

737745

if 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] += \

742750

np.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] -= \

747755

np.sqrt(indent_radius ** 2 - (s-p).imag ** 2)

748756

else:

749757

ValueError("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

754766

resp = sys(contour)