Skip to content

Commit 7f5d66c

Browse files
committed
TST:
- added a test with figure superposition and nyquist frequency check - shortened some lines of code in freqplot.py for PEP8
1 parent fbafc6c commit 7f5d66c

2 files changed

Lines changed: 67 additions & 28 deletions

File tree

control/freqplot.py

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -190,15 +190,17 @@ def bode_plot(syslist, omega=None,
190190
grid = config._get_param('bode', 'grid', kwargs, _bode_defaults, pop=True)
191191
plot = config._get_param('bode', 'grid', plot, True)
192192
margins = config._get_param('bode', 'margins', margins, False)
193-
wrap_phase = config._get_param('bode', 'wrap_phase', kwargs, _bode_defaults, pop=True)
194-
initial_phase = config._get_param('bode', 'initial_phase', kwargs, None, pop=True)
193+
wrap_phase = config._get_param(
194+
'bode', 'wrap_phase', kwargs, _bode_defaults, pop=True)
195+
initial_phase = config._get_param(
196+
'bode', 'initial_phase', kwargs, None, pop=True)
195197
omega_num = config._get_param('freqplot', 'number_of_samples', omega_num)
196-
197198
# If argument was a singleton, turn it into a tuple
198199
if not hasattr(syslist, '__iter__'):
199200
syslist = (syslist,)
200201

201-
omega, omega_range_given = _determine_frequency_range(syslist, omega, omega_limits, omega_num, Hz)
202+
omega, omega_range_given = _determine_omega_vector(
203+
syslist, omega, omega_limits, omega_num, Hz)
202204

203205
if plot:
204206
# Set up the axes with labels so that multiple calls to
@@ -1086,17 +1088,23 @@ def singular_values_plot(syslist, omega=None,
10861088
kwargs = dict(kwargs)
10871089

10881090
# Get values for params (and pop from list to allow keyword use in plot)
1089-
dB = config._get_param('singular_values_plot', 'dB', kwargs, singular_values_plot, pop=True)
1090-
Hz = config._get_param('singular_values_plot', 'Hz', kwargs, _bode_defaults, pop=True)
1091-
grid = config._get_param('singular_values_plot', 'grid', kwargs, _bode_defaults, pop=True)
1092-
plot = config._get_param('singular_values_plot', 'grid', plot, True)
1091+
dB = config._get_param(
1092+
'singular_values_plot', 'dB', kwargs, _singular_values_plot_default, pop=True)
1093+
Hz = config._get_param(
1094+
'singular_values_plot', 'Hz', kwargs, _singular_values_plot_default, pop=True)
1095+
grid = config._get_param(
1096+
'singular_values_plot', 'grid', kwargs, _singular_values_plot_default, pop=True)
1097+
plot = config._get_param(
1098+
'singular_values_plot', 'grid', plot, True)
10931099
omega_num = config._get_param('freqplot', 'number_of_samples', omega_num)
10941100

10951101
# If argument was a singleton, turn it into a tuple
10961102
if not hasattr(syslist, '__iter__'):
10971103
syslist = (syslist,)
10981104

1099-
omega, omega_range_given = _determine_frequency_range(syslist, omega, omega_limits, omega_num, Hz)
1105+
omega, omega_range_given = _determine_omega_vector(
1106+
syslist, omega, omega_limits, omega_num, Hz)
1107+
11001108
omega = np.atleast_1d(omega)
11011109

11021110
if plot:
@@ -1122,7 +1130,12 @@ def singular_values_plot(syslist, omega=None,
11221130
nyquistfrq = math.pi / sys.dt
11231131
if not omega_range_given:
11241132
# limit up to and including nyquist frequency
1125-
omega_sys = np.hstack((omega_sys[omega_sys < nyquistfrq], nyquistfrq))
1133+
omega_sys = np.hstack((
1134+
omega_sys[omega_sys < nyquistfrq], nyquistfrq))
1135+
else:
1136+
if np.max(omega_sys) >= nyquistfrq:
1137+
warnings.warn("Specified frequency range is above Nyquist limit!")
1138+
11261139
omega_complex = np.exp(1j * omega_sys * sys.dt)
11271140
else:
11281141
nyquistfrq = None
@@ -1152,9 +1165,11 @@ def singular_values_plot(syslist, omega=None,
11521165
sigma_plot = sigma
11531166

11541167
if dB:
1155-
ax_sigma.semilogx(omega_plot, 20 * np.log10(sigma_plot), color=color, *args, **kwargs)
1168+
ax_sigma.semilogx(omega_plot, 20 * np.log10(sigma_plot),
1169+
color=color, *args, **kwargs)
11561170
else:
1157-
ax_sigma.loglog(omega_plot, sigma_plot, color=color, *args, **kwargs)
1171+
ax_sigma.loglog(omega_plot, sigma_plot,
1172+
color=color, *args, **kwargs)
11581173

11591174
if nyquistfrq_plot is not None:
11601175
ax_sigma.axvline(x=nyquistfrq_plot, color=color)
@@ -1178,16 +1193,20 @@ def singular_values_plot(syslist, omega=None,
11781193

11791194

11801195
# Determine the frequency range to be used
1181-
def _determine_frequency_range(syslist, omega_in, omega_limits, omega_num, Hz):
1182-
"""Determine the frequency range to be used for a frequency-domain plot according to a standard logic.
1196+
def _determine_omega_vector(syslist, omega_in, omega_limits, omega_num, Hz):
1197+
"""Determine the frequency range for a frequency-domain plot
1198+
according to a standard logic.
11831199
1184-
If omega_in and omega_limits are both None, then omega_out is computed on omega_num points
1185-
according to a default logic defined by _default_frequency_range and tailored for the list of systems syslist, and
1200+
If omega_in and omega_limits are both None, then omega_out is computed
1201+
on omega_num points according to a default logic defined by
1202+
_default_frequency_range and tailored for the list of systems syslist, and
11861203
omega_range_given is set to False.
1187-
If omega_in is None but omega_limits is an array-like of 2 elements, then omega_out is computed with the function
1188-
np.logspace on omega_num points within the interval [min, max] = [omega_limits[0], omega_limits[1]], and
1204+
If omega_in is None but omega_limits is an array-like of 2 elements, then
1205+
omega_out is computed with the function np.logspace on omega_num points
1206+
within the interval [min, max] = [omega_limits[0], omega_limits[1]], and
11891207
omega_range_given is set to True.
1190-
If omega_in is not None, then omega_out is set to omega_in, and omega_range_given is set to True
1208+
If omega_in is not None, then omega_out is set to omega_in,
1209+
and omega_range_given is set to True
11911210
11921211
Parameters
11931212
----------
@@ -1198,15 +1217,17 @@ def _determine_frequency_range(syslist, omega_in, omega_limits, omega_num, Hz):
11981217
omega_limits : 1D array_like or None
11991218
Frequency limits specified by the user
12001219
omega_num : int
1201-
Number of points to be used for the frequency range (if not user-specified)
1220+
Number of points to be used for the frequency
1221+
range (if the frequency range is not user-specified)
12021222
12031223
Returns
12041224
-------
12051225
omega_out : 1D array
12061226
Frequency range to be used
12071227
omega_range_given : bool
1208-
True if the frequency range was specified by the user, either through omega_in or through omega_limits.
1209-
False if both omega_in and omega_limits are None.
1228+
True if the frequency range was specified by the user, either through
1229+
omega_in or through omega_limits. False if both omega_in
1230+
and omega_limits are None.
12101231
"""
12111232

12121233
# Decide whether to go above Nyquist frequency
@@ -1216,7 +1237,7 @@ def _determine_frequency_range(syslist, omega_in, omega_limits, omega_num, Hz):
12161237
if omega_limits is None:
12171238
# Select a default range if none is provided
12181239
omega_out = _default_frequency_range(syslist,
1219-
number_of_samples=omega_num)
1240+
number_of_samples=omega_num)
12201241
else:
12211242
omega_range_given = True
12221243
omega_limits = np.asarray(omega_limits)
@@ -1225,7 +1246,8 @@ def _determine_frequency_range(syslist, omega_in, omega_limits, omega_num, Hz):
12251246
if Hz:
12261247
omega_limits *= 2. * math.pi
12271248
omega_out = np.logspace(np.log10(omega_limits[0]),
1228-
np.log10(omega_limits[1]), num=omega_num, endpoint=True)
1249+
np.log10(omega_limits[1]),
1250+
num=omega_num, endpoint=True)
12291251
else:
12301252
omega_out = omega_in
12311253

control/tests/freqresp_test.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -615,18 +615,35 @@ def test_singular_values_plot(tsystem):
615615
np.testing.assert_almost_equal(sigma, sigma_ref)
616616

617617

618-
def test_singular_values_plot_mpl(ss_mimo_ct):
619-
sys = ss_mimo_ct.sys
618+
def test_singular_values_plot_mpl_base(ss_mimo_ct, ss_mimo_dt):
619+
sys_ct = ss_mimo_ct.sys
620+
sys_dt = ss_mimo_dt.sys
620621
plt.figure()
621622
omega_all = np.logspace(-3, 2, 1000)
622-
singular_values_plot(sys, omega_all, plot=True)
623+
singular_values_plot(sys_ct, omega_all, plot=True)
623624
fig = plt.gcf()
624625
allaxes = fig.get_axes()
625626
assert(len(allaxes) == 1)
626627
assert(allaxes[0].get_label() == 'control-sigma')
627628
plt.figure()
628-
singular_values_plot(sys, plot=True, Hz=True, dB=True, grid=False) # non-default settings
629+
singular_values_plot([sys_ct, sys_dt], plot=True, Hz=True, dB=True, grid=False)
629630
fig = plt.gcf()
630631
allaxes = fig.get_axes()
631632
assert(len(allaxes) == 1)
632633
assert(allaxes[0].get_label() == 'control-sigma')
634+
635+
636+
def test_singular_values_plot_mpl_superimpose_nyq(ss_mimo_ct, ss_mimo_dt):
637+
sys_ct = ss_mimo_ct.sys
638+
sys_dt = ss_mimo_dt.sys
639+
plt.figure()
640+
singular_values_plot(sys_ct, plot=True)
641+
singular_values_plot(sys_dt, plot=True)
642+
fig = plt.gcf()
643+
allaxes = fig.get_axes()
644+
assert(len(allaxes) == 1)
645+
assert (allaxes[0].get_label() == 'control-sigma')
646+
nyquist_line = allaxes[0].lines[-1].get_data()
647+
assert(len(nyquist_line[0]) == 2)
648+
assert(nyquist_line[0][0] == nyquist_line[0][1])
649+
assert(nyquist_line[0][0] == np.pi/sys_dt.dt)

0 commit comments

Comments
 (0)