forked from pool/python-control
- Backport patch to fix build on aarch64: * 688.patch OBS-URL: https://build.opensuse.org/request/show/953135 OBS-URL: https://build.opensuse.org/package/show/devel:languages:python:numeric/python-control?expand=0&rev=32
264 lines
11 KiB
Diff
264 lines
11 KiB
Diff
From 58d8cd6e4fe77fd5a18f91c791532e3e259b1949 Mon Sep 17 00:00:00 2001
|
|
From: Ben Greiner <code@bnavigator.de>
|
|
Date: Tue, 4 Jan 2022 20:09:23 +0100
|
|
Subject: [PATCH 1/4] round to nearest integer for default omega
|
|
|
|
---
|
|
control/freqplot.py | 16 +++++++---------
|
|
control/tests/sisotool_test.py | 4 ++--
|
|
2 files changed, 9 insertions(+), 11 deletions(-)
|
|
|
|
diff --git a/control/freqplot.py b/control/freqplot.py
|
|
index 881ec93d..7225afe9 100644
|
|
--- a/control/freqplot.py
|
|
+++ b/control/freqplot.py
|
|
@@ -1326,7 +1326,7 @@ def _default_frequency_range(syslist, Hz=None, number_of_samples=None,
|
|
features_ = np.concatenate((np.abs(sys.pole()),
|
|
np.abs(sys.zero())))
|
|
# Get rid of poles and zeros at the origin
|
|
- toreplace = features_ == 0.0
|
|
+ toreplace = np.isclose(features_, 0.0)
|
|
if np.any(toreplace):
|
|
features_ = features_[~toreplace]
|
|
elif sys.isdtime(strict=True):
|
|
@@ -1339,7 +1339,7 @@ def _default_frequency_range(syslist, Hz=None, number_of_samples=None,
|
|
# Get rid of poles and zeros on the real axis (imag==0)
|
|
# * origin and real < 0
|
|
# * at 1.: would result in omega=0. (logaritmic plot!)
|
|
- toreplace = (features_.imag == 0.0) & (
|
|
+ toreplace = np.isclose(features_.imag, 0.0) & (
|
|
(features_.real <= 0.) |
|
|
(np.abs(features_.real - 1.0) < 1.e-10))
|
|
if np.any(toreplace):
|
|
@@ -1360,15 +1360,13 @@ def _default_frequency_range(syslist, Hz=None, number_of_samples=None,
|
|
|
|
if Hz:
|
|
features /= 2. * math.pi
|
|
- features = np.log10(features)
|
|
- lsp_min = np.floor(np.min(features) - feature_periphery_decades)
|
|
- lsp_max = np.ceil(np.max(features) + feature_periphery_decades)
|
|
+ features = np.log10(features)
|
|
+ lsp_min = np.rint(np.min(features) - feature_periphery_decades)
|
|
+ lsp_max = np.rint(np.max(features) + feature_periphery_decades)
|
|
+ if Hz:
|
|
lsp_min += np.log10(2. * math.pi)
|
|
lsp_max += np.log10(2. * math.pi)
|
|
- else:
|
|
- features = np.log10(features)
|
|
- lsp_min = np.floor(np.min(features) - feature_periphery_decades)
|
|
- lsp_max = np.ceil(np.max(features) + feature_periphery_decades)
|
|
+
|
|
if freq_interesting:
|
|
lsp_min = min(lsp_min, np.log10(min(freq_interesting)))
|
|
lsp_max = max(lsp_max, np.log10(max(freq_interesting)))
|
|
diff --git a/control/tests/sisotool_test.py b/control/tests/sisotool_test.py
|
|
index 6b8c6d14..d5e9dd01 100644
|
|
--- a/control/tests/sisotool_test.py
|
|
+++ b/control/tests/sisotool_test.py
|
|
@@ -102,8 +102,8 @@ def test_sisotool(self, tsys):
|
|
|
|
# Check if the bode_mag line has moved
|
|
bode_mag_moved = np.array(
|
|
- [674.0242, 667.8354, 661.7033, 655.6275, 649.6074, 643.6426,
|
|
- 637.7324, 631.8765, 626.0742, 620.3252])
|
|
+ [69.0065, 68.6749, 68.3448, 68.0161, 67.6889, 67.3631, 67.0388,
|
|
+ 66.7159, 66.3944, 66.0743])
|
|
assert_array_almost_equal(ax_mag.lines[0].get_data()[1][10:20],
|
|
bode_mag_moved, 4)
|
|
|
|
|
|
From 19801e6deac560e72ee9593f81be2d24e8df14e6 Mon Sep 17 00:00:00 2001
|
|
From: Ben Greiner <code@bnavigator.de>
|
|
Date: Tue, 4 Jan 2022 21:07:35 +0100
|
|
Subject: [PATCH 2/4] split up nyquist indent tests
|
|
|
|
---
|
|
control/tests/nyquist_test.py | 36 ++++++++++++++++++++++++-----------
|
|
1 file changed, 25 insertions(+), 11 deletions(-)
|
|
|
|
diff --git a/control/tests/nyquist_test.py b/control/tests/nyquist_test.py
|
|
index 4667c621..c77d94c8 100644
|
|
--- a/control/tests/nyquist_test.py
|
|
+++ b/control/tests/nyquist_test.py
|
|
@@ -182,42 +182,56 @@ def test_nyquist_encirclements():
|
|
assert _Z(sys) == count + _P(sys)
|
|
|
|
|
|
-def test_nyquist_indent():
|
|
+@pytest.fixture
|
|
+def indentsys():
|
|
# FBS Figure 10.10
|
|
- s = ct.tf('s')
|
|
- sys = 3 * (s+6)**2 / (s * (s+1)**2)
|
|
# poles: [-1, -1, 0]
|
|
+ s = ct.tf('s')
|
|
+ return 3 * (s+6)**2 / (s * (s+1)**2)
|
|
|
|
+
|
|
+def test_nyquist_indent_default(indentsys):
|
|
plt.figure();
|
|
- count = ct.nyquist_plot(sys)
|
|
+ count = ct.nyquist_plot(indentsys)
|
|
plt.title("Pole at origin; indent_radius=default")
|
|
- assert _Z(sys) == count + _P(sys)
|
|
+ assert _Z(indentsys) == count + _P(indentsys)
|
|
|
|
+
|
|
+def test_nyquist_indent_dont(indentsys):
|
|
# first value of default omega vector was 0.1, replaced by 0. for contour
|
|
# indent_radius is larger than 0.1 -> no extra quater circle around origin
|
|
- count, contour = ct.nyquist_plot(sys, plot=False, indent_radius=.1007,
|
|
+ count, contour = ct.nyquist_plot(indentsys,
|
|
+ plot=False,
|
|
+ indent_radius=.1007,
|
|
return_contour=True)
|
|
np.testing.assert_allclose(contour[0], .1007+0.j)
|
|
# second value of omega_vector is larger than indent_radius: not indented
|
|
assert np.all(contour.real[2:] == 0.)
|
|
|
|
+
|
|
+def test_nyquist_indent_do(indentsys):
|
|
plt.figure();
|
|
- count, contour = ct.nyquist_plot(sys, indent_radius=0.01,
|
|
+ count, contour = ct.nyquist_plot(indentsys,
|
|
+ indent_radius=0.01,
|
|
return_contour=True)
|
|
plt.title("Pole at origin; indent_radius=0.01; encirclements = %d" % count)
|
|
- assert _Z(sys) == count + _P(sys)
|
|
+ assert _Z(indentsys) == count + _P(indentsys)
|
|
# indent radius is smaller than the start of the default omega vector
|
|
# check that a quarter circle around the pole at origin has been added.
|
|
np.testing.assert_allclose(contour[:50].real**2 + contour[:50].imag**2,
|
|
0.01**2)
|
|
|
|
+
|
|
+def test_nyquist_indent_left(indentsys):
|
|
plt.figure();
|
|
- count = ct.nyquist_plot(sys, indent_direction='left')
|
|
+ count = ct.nyquist_plot(indentsys, indent_direction='left')
|
|
plt.title(
|
|
"Pole at origin; indent_direction='left'; encirclements = %d" % count)
|
|
- assert _Z(sys) == count + _P(sys, indent='left')
|
|
+ assert _Z(indentsys) == count + _P(indentsys, indent='left')
|
|
+
|
|
|
|
- # System with poles on the imaginary axis
|
|
+def test_nyquist_indent_im():
|
|
+ """Test system with poles on the imaginary axis."""
|
|
sys = ct.tf([1, 1], [1, 0, 1])
|
|
|
|
# Imaginary poles with standard indentation
|
|
|
|
From c44b901d3af030187d4c2c75dbee75f2bedcf29a Mon Sep 17 00:00:00 2001
|
|
From: Ben Greiner <code@bnavigator.de>
|
|
Date: Tue, 4 Jan 2022 21:08:06 +0100
|
|
Subject: [PATCH 3/4] passthrough Hz parameter for omega vector
|
|
|
|
---
|
|
control/freqplot.py | 18 ++++++++++++------
|
|
1 file changed, 12 insertions(+), 6 deletions(-)
|
|
|
|
diff --git a/control/freqplot.py b/control/freqplot.py
|
|
index 7225afe9..18b9a448 100644
|
|
--- a/control/freqplot.py
|
|
+++ b/control/freqplot.py
|
|
@@ -209,7 +209,7 @@ def bode_plot(syslist, omega=None,
|
|
syslist = (syslist,)
|
|
|
|
omega, omega_range_given = _determine_omega_vector(
|
|
- syslist, omega, omega_limits, omega_num)
|
|
+ syslist, omega, omega_limits, omega_num, Hz=Hz)
|
|
|
|
if plot:
|
|
# Set up the axes with labels so that multiple calls to
|
|
@@ -965,7 +965,7 @@ def gangof4_plot(P, C, omega=None, **kwargs):
|
|
# Select a default range if none is provided
|
|
# TODO: This needs to be made more intelligent
|
|
if omega is None:
|
|
- omega = _default_frequency_range((P, C, S))
|
|
+ omega = _default_frequency_range((P, C, S), Hz=Hz)
|
|
|
|
# Set up the axes with labels so that multiple calls to
|
|
# gangof4_plot will superimpose the data. See details in bode_plot.
|
|
@@ -1115,7 +1115,7 @@ def singular_values_plot(syslist, omega=None,
|
|
syslist = (syslist,)
|
|
|
|
omega, omega_range_given = _determine_omega_vector(
|
|
- syslist, omega, omega_limits, omega_num)
|
|
+ syslist, omega, omega_limits, omega_num, Hz=Hz)
|
|
|
|
omega = np.atleast_1d(omega)
|
|
|
|
@@ -1210,7 +1210,8 @@ def singular_values_plot(syslist, omega=None,
|
|
|
|
|
|
# Determine the frequency range to be used
|
|
-def _determine_omega_vector(syslist, omega_in, omega_limits, omega_num):
|
|
+def _determine_omega_vector(syslist, omega_in, omega_limits, omega_num,
|
|
+ Hz=None):
|
|
"""Determine the frequency range for a frequency-domain plot
|
|
according to a standard logic.
|
|
|
|
@@ -1236,6 +1237,10 @@ def _determine_omega_vector(syslist, omega_in, omega_limits, omega_num):
|
|
omega_num : int
|
|
Number of points to be used for the frequency
|
|
range (if the frequency range is not user-specified)
|
|
+ Hz : bool. optional
|
|
+ If True, the limits (first and last value) of the frequencies
|
|
+ are set to full decades in Hz so it fits plotting with logarithmic
|
|
+ scale in Hz otherwise in rad/s. Omega is always returned in rad/sec.
|
|
|
|
Returns
|
|
-------
|
|
@@ -1253,7 +1258,8 @@ def _determine_omega_vector(syslist, omega_in, omega_limits, omega_num):
|
|
omega_range_given = False
|
|
# Select a default range if none is provided
|
|
omega_out = _default_frequency_range(syslist,
|
|
- number_of_samples=omega_num)
|
|
+ number_of_samples=omega_num,
|
|
+ Hz=Hz)
|
|
else:
|
|
omega_limits = np.asarray(omega_limits)
|
|
if len(omega_limits) != 2:
|
|
@@ -1280,7 +1286,7 @@ def _default_frequency_range(syslist, Hz=None, number_of_samples=None,
|
|
----------
|
|
syslist : list of LTI
|
|
List of linear input/output systems (single system is OK)
|
|
- Hz : bool
|
|
+ Hz : bool. optional
|
|
If True, the limits (first and last value) of the frequencies
|
|
are set to full decades in Hz so it fits plotting with logarithmic
|
|
scale in Hz otherwise in rad/s. Omega is always returned in rad/sec.
|
|
|
|
From 021372f4cd821907fee913ceef6d46af899a9bcd Mon Sep 17 00:00:00 2001
|
|
From: Ben Greiner <code@bnavigator.de>
|
|
Date: Tue, 4 Jan 2022 21:37:10 +0100
|
|
Subject: [PATCH 4/4] docstring punctuation
|
|
|
|
---
|
|
control/freqplot.py | 4 ++--
|
|
1 file changed, 2 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/control/freqplot.py b/control/freqplot.py
|
|
index 18b9a448..a8324e06 100644
|
|
--- a/control/freqplot.py
|
|
+++ b/control/freqplot.py
|
|
@@ -1237,7 +1237,7 @@ def _determine_omega_vector(syslist, omega_in, omega_limits, omega_num,
|
|
omega_num : int
|
|
Number of points to be used for the frequency
|
|
range (if the frequency range is not user-specified)
|
|
- Hz : bool. optional
|
|
+ Hz : bool, optional
|
|
If True, the limits (first and last value) of the frequencies
|
|
are set to full decades in Hz so it fits plotting with logarithmic
|
|
scale in Hz otherwise in rad/s. Omega is always returned in rad/sec.
|
|
@@ -1286,7 +1286,7 @@ def _default_frequency_range(syslist, Hz=None, number_of_samples=None,
|
|
----------
|
|
syslist : list of LTI
|
|
List of linear input/output systems (single system is OK)
|
|
- Hz : bool. optional
|
|
+ Hz : bool, optional
|
|
If True, the limits (first and last value) of the frequencies
|
|
are set to full decades in Hz so it fits plotting with logarithmic
|
|
scale in Hz otherwise in rad/s. Omega is always returned in rad/sec.
|