Skip to content

v-code01/bwsign

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

bwsign

python-control's LTISystem.bandwidth returns infinity for any stable system with a negative DC gain, even though its magnitude response drops normally. The bandwidth is the frequency where the magnitude has dropped a given number of decibels below its value at zero frequency, so it depends only on the magnitude and the sign of the DC gain is irrelevant. The function finds where the magnitude has dropped by testing mag - dcgain * 10**(dbdrop/20) < 0 with the signed DC gain, but bisects with abs(dcgain). For a negative DC gain the threshold is negative, so mag - (negative) is never below zero, the drop set is empty, and the function returns infinity, reporting that the gain never drops. The bisection step in the same function uses abs(dcgain), so the drop test contradicts it.

The code

control, control/lti.py, LTISystem.bandwidth, 0.10.2 and current main (identical), lines 205 and 215/221:

idx_dropped = np.nonzero(mag - dcgain*10**(dbdrop/20) < 0)[0]     # signed dcgain
if idx_dropped.shape[0] == 0:
    ... return inf ...
else:
    ... bracket via
    lambda w: np.abs(self(w*1j)) - np.abs(dcgain)*10**(dbdrop/20)  # abs(dcgain)

For a negative DC gain, dcgain * 10**(dbdrop/20) is negative, so mag - (negative) is always positive and idx_dropped is empty. The bisection lambda uses abs(dcgain), so the drop test is inconsistent with the function's own bisection. The fix is to use abs(dcgain) in the drop test.

What it does

The minus-three-decibel bandwidth from the drop test with the signed or absolute DC gain, on a first-order system:

dc gain +1: signed drop test 0.9976311356937564, absolute drop test 0.9976  (magnitude identical to the +1 case: True)
dc gain -1: signed drop test inf, absolute drop test 0.9976  (magnitude identical to the +1 case: True)

Driving the real library:

first-order: bandwidth pos 0.9976 (correct), neg inf (brute-force true 0.9976)
magnitude responses identical: True (dc gain pos 1.0, neg -1.0)
second-order: pos 2.0178, neg inf (brute-force true 2.0178)
dbdrop=-6: neg bandwidth inf (inf: True)

The systems 1/(s+1) and -1/(s+1) have the same magnitude response, 0.894, 0.707, 0.447 at w = 0.5, 1, 2, so they must have the same bandwidth, but the real bandwidth returns 0.9976 for the positive one and infinity for the negative one. The base case isolates the fault: the only difference between the two systems is the sign of the numerator, and the correctly written bisection uses abs(dcgain). A second-order system with negative DC gain likewise returns infinity against a finite brute-force bandwidth of 2.0178, and the fault is not specific to the default drop, breaking at dbdrop=-6 as well.

Scope

bandwidth runs with the default drop of three decibels and standard SISO input, and negative-DC-gain systems are the ordinary case for any sign-inverted plant or controller. The failure is silent: infinity is returned with no warning, documented as the gain not dropping for any frequency, which is false. The error is a signed DC gain where the magnitude is intended, not floating-point round-off, and the bisection in the same function uses the absolute value. The fix is np.abs(dcgain) in the drop test.

Layout

  • excerpt.py: the drop test and the bisection quoted with the flags that name the fault, and the correct absolute-value drop test.
  • bandwidth.py: the drop test with the signed or absolute DC gain over a magnitude response, so the signed test returns infinity on a negative DC gain and the absolute test returns the correct bandwidth.
  • consequence.py: the real control.bandwidth returning infinity on a negative DC gain, the identical magnitude responses, the second-order case, and the non-default drop.
  • test_bwsign.py: the model signed test is infinite and the absolute test is correct while a positive DC gain agrees, the real negative-DC-gain bandwidth is infinite against a finite true value, the magnitudes are identical, and the second-order and non-default cases break.

Reproduce

python bandwidth.py
python consequence.py
python test_bwsign.py

The drop test is quoted from current main; the bandwidths are produced by the real control.bandwidth. The fix is to use the absolute DC gain in the drop test.

About

python-control's `LTISystem.bandwidth` returns infinity for any stable system with a negative DC gain, even though its magnitude response drops normally.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages