@@ -620,7 +620,7 @@ def nyquist_plot(syslist, omega=None, plot=True, omega_limits=None,
620
620
621
621
2. If a continuous-time system contains poles on or near the imaginary
622
622
axis, a small indentation will be used to avoid the pole. The radius
623
- of the indentation is given by `indent_radius` and it is taken the the
623
+ of the indentation is given by `indent_radius` and it is taken to the
624
624
right of stable poles and the left of unstable poles. If a pole is
625
625
exactly on the imaginary axis, the `indent_direction` parameter can be
626
626
used to set the direction of indentation. Setting `indent_direction`
@@ -675,13 +675,11 @@ def nyquist_plot(syslist, omega=None, plot=True, omega_limits=None,
675
675
if not hasattr (syslist , '__iter__' ):
676
676
syslist = (syslist ,)
677
677
678
- omega , omega_range_given = _determine_omega_vector (syslist ,
679
- omega ,
680
- omega_limits ,
681
- omega_num )
678
+ omega , omega_range_given = _determine_omega_vector (
679
+ syslist , omega , omega_limits , omega_num )
682
680
if not omega_range_given :
683
- # Replace first point with the origin
684
- omega [0 ] = 0
681
+ # Start contour at zero frequency
682
+ omega [0 ] = 0.
685
683
686
684
# Go through each system and keep track of the results
687
685
counts , contours = [], []
@@ -713,9 +711,15 @@ def nyquist_plot(syslist, omega=None, plot=True, omega_limits=None,
713
711
contour = 1j * omega_sys
714
712
715
713
# Bend the contour around any poles on/near the imaginary axis
716
- if isinstance (sys , (StateSpace , TransferFunction )) and \
717
- sys .isctime () and indent_direction != 'none' :
714
+ if isinstance (sys , (StateSpace , TransferFunction )) \
715
+ and sys .isctime () and indent_direction != 'none' :
718
716
poles = sys .pole ()
717
+ if contour [1 ].imag > indent_radius \
718
+ and 0. in poles and not omega_range_given :
719
+ # add some points for quarter circle around poles at origin
720
+ contour = np .concatenate (
721
+ (1j * np .linspace (0. , indent_radius , 50 ),
722
+ contour [1 :]))
719
723
for i , s in enumerate (contour ):
720
724
# Find the nearest pole
721
725
p = poles [(np .abs (poles - s )).argmin ()]
@@ -1221,7 +1225,6 @@ def _determine_omega_vector(syslist, omega_in, omega_limits, omega_num):
1221
1225
omega_in or through omega_limits. False if both omega_in
1222
1226
and omega_limits are None.
1223
1227
"""
1224
-
1225
1228
omega_range_given = True
1226
1229
1227
1230
if omega_in is None :
@@ -1245,11 +1248,12 @@ def _determine_omega_vector(syslist, omega_in, omega_limits, omega_num):
1245
1248
# Compute reasonable defaults for axes
1246
1249
def _default_frequency_range (syslist , Hz = None , number_of_samples = None ,
1247
1250
feature_periphery_decades = None ):
1248
- """Compute a reasonable default frequency range for frequency
1249
- domain plots.
1251
+ """Compute a default frequency range for frequency domain plots.
1250
1252
1251
- Finds a reasonable default frequency range by examining the features
1252
- (poles and zeros) of the systems in syslist.
1253
+ This code looks at the poles and zeros of all of the systems that
1254
+ we are plotting and sets the frequency range to be one decade above
1255
+ and below the min and max feature frequencies, rounded to the nearest
1256
+ integer. If no features are found, it returns logspace(-1, 1)
1253
1257
1254
1258
Parameters
1255
1259
----------
@@ -1280,12 +1284,6 @@ def _default_frequency_range(syslist, Hz=None, number_of_samples=None,
1280
1284
>>> omega = _default_frequency_range(sys)
1281
1285
1282
1286
"""
1283
- # This code looks at the poles and zeros of all of the systems that
1284
- # we are plotting and sets the frequency range to be one decade above
1285
- # and below the min and max feature frequencies, rounded to the nearest
1286
- # integer. It excludes poles and zeros at the origin. If no features
1287
- # are found, it turns logspace(-1, 1)
1288
-
1289
1287
# Set default values for options
1290
1288
number_of_samples = config ._get_param (
1291
1289
'freqplot' , 'number_of_samples' , number_of_samples )
@@ -1307,30 +1305,31 @@ def _default_frequency_range(syslist, Hz=None, number_of_samples=None,
1307
1305
features_ = np .concatenate ((np .abs (sys .pole ()),
1308
1306
np .abs (sys .zero ())))
1309
1307
# Get rid of poles and zeros at the origin
1310
- features_ = features_ [features_ != 0.0 ]
1311
- features = np .concatenate ((features , features_ ))
1308
+ toreplace = features_ == 0.0
1309
+ if np .any (toreplace ):
1310
+ features_ = features_ [~ toreplace ]
1312
1311
elif sys .isdtime (strict = True ):
1313
1312
fn = math .pi * 1. / sys .dt
1314
1313
# TODO: What distance to the Nyquist frequency is appropriate?
1315
1314
freq_interesting .append (fn * 0.9 )
1316
1315
1317
1316
features_ = np .concatenate ((sys .pole (),
1318
1317
sys .zero ()))
1319
- # Get rid of poles and zeros
1320
- # * at the origin and real <= 0 & imag==0: log!
1318
+ # Get rid of poles and zeros on the real axis (imag==0)
1319
+ # * origin and real < 0
1321
1320
# * at 1.: would result in omega=0. (logaritmic plot!)
1322
- features_ = features_ [
1323
- ( features_ . imag != 0.0 ) | (features_ .real > 0. )]
1324
- features_ = features_ [
1325
- np .bitwise_not (( features_ . imag == 0.0 ) &
1326
- ( np . abs ( features_ . real - 1.0 ) < 1.e-10 )) ]
1321
+ toreplace = ( features_ . imag == 0.0 ) & (
1322
+ (features_ .real <= 0. ) |
1323
+ ( np . abs ( features_ . real - 1.0 ) < 1.e-10 ))
1324
+ if np .any ( toreplace ):
1325
+ features_ = features_ [ ~ toreplace ]
1327
1326
# TODO: improve
1328
- features__ = np .abs (np .log (features_ ) / (1.j * sys .dt ))
1329
- features = np .concatenate ((features , features__ ))
1327
+ features_ = np .abs (np .log (features_ ) / (1.j * sys .dt ))
1330
1328
else :
1331
1329
# TODO
1332
1330
raise NotImplementedError (
1333
1331
"type of system in not implemented now" )
1332
+ features = np .concatenate ((features , features_ ))
1334
1333
except NotImplementedError :
1335
1334
pass
1336
1335
0 commit comments