4545from .ctrlutil import unwrap
4646from .freqplot import default_frequency_range
4747
48- __all__ = ['nichols_plot' , 'nichols' ]
48+ __all__ = ['nichols_plot' , 'nichols' , 'nichols_grid' ]
4949
50- # Nichols plot
51- def nichols_plot (syslist , omega = None , grid = True ):
50+
51+ def nichols_plot (sys_list , omega = None , grid = True ):
5252 """Nichols plot for a system
5353
5454 Plots a Nichols plot for the system over a (optional) frequency range.
5555
5656 Parameters
5757 ----------
58- syslist : list of LTI, or LTI
58+ sys_list : list of LTI, or LTI
5959 List of linear input/output systems (single system is OK)
6060 omega : array_like
6161 Range of frequencies (list or bounds) in rad/sec
@@ -68,14 +68,14 @@ def nichols_plot(syslist, omega=None, grid=True):
6868 """
6969
7070 # If argument was a singleton, turn it into a list
71- if ( not getattr (syslist , '__iter__' , False ) ):
72- syslist = (syslist ,)
71+ if not getattr (sys_list , '__iter__' , False ):
72+ sys_list = (sys_list ,)
7373
7474 # Select a default range if none is provided
7575 if omega is None :
76- omega = default_frequency_range (syslist )
76+ omega = default_frequency_range (sys_list )
7777
78- for sys in syslist :
78+ for sys in sys_list :
7979 # Get the magnitude and phase of the system
8080 mag_tmp , phase_tmp , omega = sys .freqresp (omega )
8181 mag = np .squeeze (mag_tmp )
@@ -100,8 +100,8 @@ def nichols_plot(syslist, omega=None, grid=True):
100100 if grid :
101101 nichols_grid ()
102102
103- # Nichols grid
104- #! TODO: Consider making linestyle configurable
103+
104+ # TODO: Consider making line style configurable
105105def nichols_grid (cl_mags = None , cl_phases = None ):
106106 """Nichols chart grid
107107
@@ -137,12 +137,12 @@ def nichols_grid(cl_mags=None, cl_phases=None):
137137 # The key set of magnitudes are always generated, since this
138138 # guarantees a recognizable Nichols chart grid.
139139 key_cl_mags = np .array ([- 40.0 , - 20.0 , - 12.0 , - 6.0 , - 3.0 , - 1.0 , - 0.5 , 0.0 ,
140- 0.25 , 0.5 , 1.0 , 3.0 , 6.0 , 12.0 ])
140+ 0.25 , 0.5 , 1.0 , 3.0 , 6.0 , 12.0 ])
141141 # Extend the range of magnitudes if necessary. The extended arange
142142 # will end up empty if no extension is required. Assumes that closed-loop
143143 # magnitudes are approximately aligned with open-loop magnitudes beyond
144144 # the value of np.min(key_cl_mags)
145- cl_mag_step = - 20.0 # dB
145+ cl_mag_step = - 20.0 # dB
146146 extended_cl_mags = np .arange (np .min (key_cl_mags ),
147147 ol_mag_min + cl_mag_step , cl_mag_step )
148148 cl_mags = np .concatenate ((extended_cl_mags , key_cl_mags ))
@@ -163,12 +163,12 @@ def nichols_grid(cl_mags=None, cl_phases=None):
163163 # Find the M-contours
164164 m = m_circles (cl_mags , phase_min = np .min (cl_phases ), phase_max = np .max (cl_phases ))
165165 m_mag = 20 * sp .log10 (np .abs (m ))
166- m_phase = sp .mod (sp .degrees (sp .angle (m )), - 360.0 ) # Unwrap
166+ m_phase = sp .mod (sp .degrees (sp .angle (m )), - 360.0 ) # Unwrap
167167
168168 # Find the N-contours
169169 n = n_circles (cl_phases , mag_min = np .min (cl_mags ), mag_max = np .max (cl_mags ))
170170 n_mag = 20 * sp .log10 (np .abs (n ))
171- n_phase = sp .mod (sp .degrees (sp .angle (n )), - 360.0 ) # Unwrap
171+ n_phase = sp .mod (sp .degrees (sp .angle (n )), - 360.0 ) # Unwrap
172172
173173 # Plot the contours behind other plot elements.
174174 # The "phase offset" is used to produce copies of the chart that cover
@@ -203,7 +203,7 @@ def nichols_grid(cl_mags=None, cl_phases=None):
203203# generating Nichols plots
204204#
205205
206- # Compute contours of a closed-loop transfer function
206+
207207def closed_loop_contours (Gcl_mags , Gcl_phases ):
208208 """Contours of the function Gcl = Gol/(1+Gol), where
209209 Gol is an open-loop transfer function, and Gcl is a corresponding
@@ -229,7 +229,7 @@ def closed_loop_contours(Gcl_mags, Gcl_phases):
229229 # Invert Gcl = Gol/(1+Gol) to map the contours into the open-loop space
230230 return Gcl / (1.0 - Gcl )
231231
232- # M-circle
232+
233233def m_circles (mags , phase_min = - 359.75 , phase_max = - 0.25 ):
234234 """Constant-magnitude contours of the function Gcl = Gol/(1+Gol), where
235235 Gol is an open-loop transfer function, and Gcl is a corresponding
@@ -255,7 +255,7 @@ def m_circles(mags, phase_min=-359.75, phase_max=-0.25):
255255 Gcl_mags , Gcl_phases = sp .meshgrid (10.0 ** (mags / 20.0 ), phases )
256256 return closed_loop_contours (Gcl_mags , Gcl_phases )
257257
258- # N-circle
258+
259259def n_circles (phases , mag_min = - 40.0 , mag_max = 12.0 ):
260260 """Constant-phase contours of the function Gcl = Gol/(1+Gol), where
261261 Gol is an open-loop transfer function, and Gcl is a corresponding
@@ -281,5 +281,6 @@ def n_circles(phases, mag_min=-40.0, mag_max=12.0):
281281 Gcl_phases , Gcl_mags = sp .meshgrid (sp .radians (phases ), mags )
282282 return closed_loop_contours (Gcl_mags , Gcl_phases )
283283
284+
284285# Function aliases
285286nichols = nichols_plot
0 commit comments