diff --git a/control/margins.py b/control/margins.py index 301baaf57..019c866be 100644 --- a/control/margins.py +++ b/control/margins.py @@ -81,11 +81,16 @@ def _poly_iw_sqr(pol_iw): def _poly_iw_real_crossing(num_iw, den_iw, epsw): # Return w where imag(H(iw)) == 0 + + # Compute the imaginary part of H = (num.r + j num.i)/(den.r + j den.i) test_w = np.polysub(np.polymul(num_iw.imag, den_iw.real), np.polymul(num_iw.real, den_iw.imag)) + + # Find the real-valued w > 0 where imag(H(iw)) = 0 w = np.roots(test_w) w = np.real(w[np.isreal(w)]) w = w[w >= epsw] + return w @@ -471,7 +476,7 @@ def phase_crossover_frequencies(sys): omega : ndarray 1d array of (non-negative) frequencies where Nyquist plot intersects the real axis - gain : ndarray + gains : ndarray 1d array of corresponding gains Examples @@ -493,13 +498,13 @@ def phase_crossover_frequencies(sys): omega = _poly_iw_real_crossing(num_iw, den_iw, 0.) # using real() to avoid rounding errors and results like 1+0j - gain = np.real(evalfr(sys, 1J * omega)) + gains = np.real(sys(omega * 1j, warn_infinite=False)) else: zargs = _poly_z_invz(sys) z, omega = _poly_z_real_crossing(*zargs, epsw=0.) - gain = np.real(evalfr(sys, z)) + gains = np.real(sys(z, warn_infinite=False)) - return omega, gain + return omega, gains def margin(*args): diff --git a/control/tests/margin_test.py b/control/tests/margin_test.py index 07e21114f..43cd68ae3 100644 --- a/control/tests/margin_test.py +++ b/control/tests/margin_test.py @@ -12,12 +12,9 @@ from numpy import inf, nan from numpy.testing import assert_allclose -from control.frdata import FrequencyResponseData -from control.margins import (margin, phase_crossover_frequencies, - stability_margins) -from control.statesp import StateSpace -from control.xferfcn import TransferFunction -from control.exception import ControlMIMONotImplemented +from control import ControlMIMONotImplemented, FrequencyResponseData, \ + StateSpace, TransferFunction, margin, phase_crossover_frequencies, \ + stability_margins s = TransferFunction.s @@ -111,7 +108,6 @@ def test_margin_3input(tsys): out = margin((mag, phase*180/np.pi, omega_)) assert_allclose(out, np.array(refout)[[0, 1, 3, 4]], atol=1.5e-3) - @pytest.mark.parametrize( 'tfargs, omega_ref, gain_ref', [(([1], [1, 2, 3, 4]), [1.7325, 0.], [-0.5, 0.25]), @@ -119,7 +115,10 @@ def test_margin_3input(tsys): (([2], [1, 3, 3, 1]), [1.732, 0.], [-0.25, 2.]), ((np.array([3, 11, 3]) * 1e-4, [1., -2.7145, 2.4562, -0.7408], .1), [1.6235, 0.], [-0.28598, 1.88889]), + (([200.0], [1.0, 21.0, 20.0, 0.0]), + [4.47213595, 0], [-0.47619048, inf]), ]) +@pytest.mark.filterwarnings("error") def test_phase_crossover_frequencies(tfargs, omega_ref, gain_ref): """Test phase_crossover_frequencies() function""" sys = TransferFunction(*tfargs)