@@ -153,6 +153,19 @@ def nichols_grid(cl_mags=None, cl_phases=None, line_style='dotted', ax=None,
153
153
Axes to add grid to. If ``None``, use ``plt.gca()``.
154
154
label_cl_phases: bool, optional
155
155
If True, closed-loop phase lines will be labelled.
156
+
157
+ Returns
158
+ -------
159
+ cl_mag_lines: list of `matplotlib.line.Line2D`
160
+ The constant closed-loop gain contours
161
+ cl_phase_lines: list of `matplotlib.line.Line2D`
162
+ The constant closed-loop phase contours
163
+ cl_mag_labels: list of `matplotlib.text.Text`
164
+ mcontour labels; each entry corresponds to the respective entry
165
+ in ``cl_mag_lines``
166
+ cl_phase_labels: list of `matplotlib.text.Text`
167
+ ncontour labels; each entry corresponds to the respective entry
168
+ in ``cl_phase_lines``
156
169
"""
157
170
if ax is None :
158
171
ax = plt .gca ()
@@ -163,8 +176,8 @@ def nichols_grid(cl_mags=None, cl_phases=None, line_style='dotted', ax=None,
163
176
ol_mag_min = - 40.0
164
177
ol_mag_max = default_ol_mag_max = 50.0
165
178
166
- # Find bounds of the current dataset, if there is one.
167
179
if ax .has_data ():
180
+ # Find extent of intersection the current dataset or view
168
181
ol_phase_min , ol_mag_min , ol_phase_max , ol_mag_max = _inner_extents (ax )
169
182
170
183
# M-circle magnitudes.
@@ -184,20 +197,22 @@ def nichols_grid(cl_mags=None, cl_phases=None, line_style='dotted', ax=None,
184
197
ol_mag_min + cl_mag_step , cl_mag_step )
185
198
cl_mags = np .concatenate ((extended_cl_mags , key_cl_mags ))
186
199
187
- phase_offset_min = 360.0 * np .ceil (ol_phase_min / 360.0 )
188
- phase_offset_max = 360.0 * np .ceil (ol_phase_max / 360.0 ) + 360.0
200
+ # a minimum 360deg extent containing the phases
201
+ phase_round_max = 360.0 * np .ceil (ol_phase_max / 360.0 )
202
+ phase_round_min = min (phase_round_max - 360 ,
203
+ 360.0 * np .floor (ol_phase_min / 360.0 ))
189
204
190
205
# N-circle phases (should be in the range -360 to 0)
191
206
if cl_phases is None :
192
207
# aim for 9 lines, but always show (-360+eps, -180, -eps)
193
208
# smallest spacing is 45, biggest is 180
194
- phase_span = phase_offset_max - phase_offset_min
209
+ phase_span = phase_round_max - phase_round_min
195
210
spacing = np .clip (round (phase_span / 8 / 45 ) * 45 , 45 , 180 )
196
211
key_cl_phases = np .array ([- 0.25 , - 359.75 ])
197
212
other_cl_phases = np .arange (- spacing , - 360.0 , - spacing )
198
213
cl_phases = np .unique (np .concatenate ((key_cl_phases , other_cl_phases )))
199
- else :
200
- assert (( - 360.0 < np . min ( cl_phases )) and ( np . max ( cl_phases ) < 0.0 ) )
214
+ elif not (( - 360 < np . min ( cl_phases )) and ( np . max ( cl_phases ) < 0.0 )) :
215
+ raise ValueError ( ' cl_phases must between -360 and 0, exclusive' )
201
216
202
217
# Find the M-contours
203
218
m = m_circles (cl_mags , phase_min = np .min (cl_phases ),
@@ -216,21 +231,29 @@ def nichols_grid(cl_mags=None, cl_phases=None, line_style='dotted', ax=None,
216
231
# over the range -360 < phase < 0. Given the range
217
232
# the base chart is computed over, the phase offset should be 0
218
233
# for -360 < ol_phase_min < 0.
219
- phase_offsets = np .arange (phase_offset_min , phase_offset_max , 360.0 )
234
+ phase_offsets = 360 + np .arange (phase_round_min , phase_round_max , 360.0 )
235
+
236
+ cl_mag_lines = []
237
+ cl_phase_lines = []
238
+ cl_mag_labels = []
239
+ cl_phase_labels = []
220
240
221
241
for phase_offset in phase_offsets :
222
242
# Draw M and N contours
223
- ax .plot (m_phase + phase_offset , m_mag , color = 'lightgray' ,
224
- linestyle = line_style , zorder = 0 )
225
- ax .plot (n_phase + phase_offset , n_mag , color = 'lightgray' ,
226
- linestyle = line_style , zorder = 0 )
243
+ cl_mag_lines .extend (
244
+ ax .plot (m_phase + phase_offset , m_mag , color = 'lightgray' ,
245
+ linestyle = line_style , zorder = 0 ))
246
+ cl_phase_lines .extend (
247
+ ax .plot (n_phase + phase_offset , n_mag , color = 'lightgray' ,
248
+ linestyle = line_style , zorder = 0 ))
227
249
228
250
# Add magnitude labels
229
251
for x , y , m in zip (m_phase [:][- 1 ] + phase_offset , m_mag [:][- 1 ],
230
252
cl_mags ):
231
253
align = 'right' if m < 0.0 else 'left'
232
- ax .text (x , y , str (m ) + ' dB' , size = 'small' , ha = align ,
233
- color = 'gray' , clip_on = True )
254
+ cl_mag_labels .append (
255
+ ax .text (x , y , str (m ) + ' dB' , size = 'small' , ha = align ,
256
+ color = 'gray' , clip_on = True ))
234
257
235
258
# phase labels
236
259
if label_cl_phases :
@@ -243,20 +266,23 @@ def nichols_grid(cl_mags=None, cl_phases=None, line_style='dotted', ax=None,
243
266
align = 'center'
244
267
else :
245
268
align = 'left'
246
- ax .text (x , y , f'{ round (p )} \N{DEGREE SIGN} ' ,
247
- size = 'small' ,
248
- ha = align ,
249
- va = 'bottom' ,
250
- color = 'gray' ,
251
- clip_on = True )
269
+ cl_phase_labels .append (
270
+ ax .text (x , y , f'{ round (p )} \N{DEGREE SIGN} ' ,
271
+ size = 'small' ,
272
+ ha = align ,
273
+ va = 'bottom' ,
274
+ color = 'gray' ,
275
+ clip_on = True ))
252
276
253
277
254
278
# Fit axes to generated chart
255
- ax .axis ([phase_offset_min - 360.0 ,
256
- phase_offset_max - 360.0 ,
279
+ ax .axis ([phase_round_min ,
280
+ phase_round_max ,
257
281
np .min (np .concatenate ([cl_mags ,[ol_mag_min ]])),
258
282
np .max ([ol_mag_max , default_ol_mag_max ])])
259
283
284
+ return cl_mag_lines , cl_phase_lines , cl_mag_labels , cl_phase_labels
285
+
260
286
#
261
287
# Utility functions
262
288
#
0 commit comments