Skip to content

Fix discrete-time frequency handling in describing_function_response - #1270

Closed
RoboX2020 wants to merge 1 commit into
python-control:mainfrom
RoboX2020:fix-describing-function-discrete-omega
Closed

RoboX2020 wants to merge 1 commit into
python-control:mainfrom
RoboX2020:fix-describing-function-discrete-omega

Conversation

@RoboX2020

Copy link
Copy Markdown

Fixes #1269.

For discrete-time systems, describing_function_response takes the imaginary part of the Nyquist contour as the frequency. That is sin(omega * dt), not the angular frequency. Refinement and returned positions also evaluate the transfer function at j * omega instead of on the unit circle.

This uses angle(contour) / dt for discrete-time frequencies and evaluates the response at exp(j * omega * dt) during refinement and when returning positions. The strict timebase check leaves continuous and unspecified (dt=None) systems on the existing path; dt=True uses radians per sample.

Regression tests cover sample times 1 and 0.5, unspecified discrete time, and continuous-compatible dt=None. For the issue's sign-nonlinearity example, the reported frequencies change from about 0.9703 for both sample times to 1.3265 and 2.6530. The analytic crossings are 1.3181 and 2.6362; the tests allow 2% numerical error rather than claiming exact refinement.

Validation:

  • Describing-function tests: 11 passed.
  • Describing-function, frequency-response and LTI tests: 211 passed, 123 skipped, 1 expected failure.
  • Baseline code with the added tests: 3 failed, 8 passed.
  • Also checked both refine=False and refine=True with an analytic relay nonlinearity at sample times 0.01, 0.5, 1 and 2.

For a discrete-time system, describing_function_response found limit
cycles with about the right amplitude but reported the wrong frequency:
it took the oscillation frequency as the imaginary part of the Nyquist
contour, which for a discrete system lies on the unit circle
exp(1j*w*dt), so it reported about sin(w*dt) - the same number whatever
dt is.  The refinement cost function and the returned positions also
evaluated H at z = 1j*w rather than on the unit circle, and refinement
typically failed with "not able to refine result; returning estimate".

For example, H(z) = 0.5/(z^2 - 0.5z) with dt = 1 crosses the negative
real axis at w = 1.3181, but the reported frequency was 0.9703, and
dt = 0.5 gave the same 0.9703 instead of 2.6362.

When H.isdtime(strict=True) - matching the timebase semantics used by
nyquist_response - take the frequencies as np.angle(contour) / dt and
evaluate H at exp(1j*w*dt) in the refinement cost function and for the
returned positions.  The strict check keeps dt=None (unspecified
timebase) on the continuous-time path; dt=True (discrete, unspecified
sample time) reports rad/sample.

Adds regression tests: a known unit-circle crossing for dt = 1 and
dt = 0.5, plus dt=None continuous-equivalence and dt=True coverage.
The discrete tests fail at baseline; continuous-time behavior is
unchanged.

Fixes python-control#1269
@slivingston

Copy link
Copy Markdown
Member

@RoboX2020 Please add AI tool disclosures, ensuring consistency with https://numpy.org/doc/stable/dev/ai_policy.html ? (Given how long and verbose your responses are and how quickly you are committing, I assume you used a chat bot for most or all of this...)

@RoboX2020 RoboX2020 closed this Nov 5, 2026
@slivingston

Copy link
Copy Markdown
Member

@gimballock This PR supposedly fixes the issue you opened. I guess @RoboX2020 is a bot who simply closed the PR in response to my request...

If you want, @gimballock , feel free to review this or open a PR that re-uses the code for this PR. I assume @RoboX2020 did not author any of it, so you can just claim authorship.

@RoboX2020

Copy link
Copy Markdown
Author

Hi,
I accept using a bot - the previous pr went without a proper review from me and seeing the ai policy, i had to take back the pr to do a review before request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

describing_function_response reports sin(w*dt), not w, for discrete-time systems, and refines at z = jw

2 participants