# freqresp gives wrong output for purely real inputs

9 views (last 30 days)
Will Robertson on 17 May 2020
Edited: Paul on 4 Jun 2020
I am stumped by the behaviour of evalfr vs freqresp in the control systems toolbox. They claim to give the same results, but when their arguments are purely real there is a (large!) discrepancy. I am inclined to believe the results from evalfr; for all sane transfer functions a real value of s should result in a real value of the transfer function.
With the following code:
G = tf([1],[1 1]);
wc = 3+1e-10*1i;
[evalfr(G,wc);freqresp(G,wc)]
Both give the answer 0.2500-0.0000i.
When the input is purely real, however:
G = tf([1],[1 1]);
wc = 3;
[evalfr(G,wc);freqresp(G,wc)]
ans =
0.2500 + 0.0000i
0.1000 - 0.3000i
I'm using Matlab 2020a and I believe this behaviour has been around for a while; I came across an old code comment that said something like "don't use freqresp -- evalfr gives proper results here". Fine for me, but harder to advise when teaching students...
##### 2 CommentsShowHide 1 older comment
Will Robertson on 22 May 2020
Sorry, I was unclear. That was my own code comment written to myself a few years back :)
FWIW the correct answer is definitely 0.25 here — it's literally as simple as 1/(s+1) for s=3.

Devineni Aslesha on 26 May 2020
Hi Will,
I have brought the issue of 'freqresp accepting frequency input with non-zero imaginary part' to the notice of our developers. They will investigate the matter further.
Paul on 3 Jun 2020
1. I agree with Will on point 1. The documenation is crystal clear. The input w must be real and the output is defined using the standard definition of freuqency response for either a continous or discrete system. I'm not sure why there's any reason for freqresp to accept an input with non-zero imaginary part. One might think that it should for backwards compatibility with the old version of freqresp (which is still usable, see my example below). But IMO that argument doesn't work because freqresp as currently implemented already has a different behavior than the old version for a real input. So that backwards compatibilty was lost anyway. At the very least, if freqresp is going to continue to accept complex inputs, it should generate a warning that that's unodocumented behavior, and it should be deprecated. And the same goes for the old version of freqresp.
2. Zhao, I can't comment on Will's point. But freqresp does return inconsistent results. Please check my example below that shows freqresp (the current version) returns different outputs for the same input. Surely that can't be acceptable.
3. As to Will's point 3, I agree that the documentation for evalfr is misleading. In fact, I think the name of the function is misleading. It should be evaltf. The very description says: "evaluates the transfer function ..." (emphasis added).
4. As has been well established we have for continuous time systems: evalfr(G,1j*w) == freqresp(G,w) with w real. I'd like to add that if G is a model for a discrete time system then we have: evalfr(G,exp(1j*w*G.Ts) == freqresp(G,w) if G.Ts is defined and w is real, and evalfr(G,exp(1j*w) == freqresp(G,w) if G.Ts is undefined.
5. The doc pages for evalfr and freqresp are pretty weak at explaining how these functions relate to each other, and the "More About" and "Algorithm" sections of doc freqresp are weak at explaining the discrete time case.

### More Answers (1)

Paul on 24 May 2020
evalfr evalutes the transfer function at the value of the input argument, which can be an arbitrary complex number. For your example, evalfr(G,wc) = G(wc).
freqresp evaluates the frequency response at the value of the input argument, which must be real (doc freqresp). Because it's the frequency response, freqresp(G,wc) = G(1j*wc) (as long as wc is real).
So if you want to compute the frequency response of G using evalfr, you have to multiply the (real) input by 1j:
>> wc = 3; [evalfr(G,1j*wc); freqresp(G,wc)]
ans =
0.1000 - 0.3000i
0.1000 - 0.3000i
If you want to evaluate the transfer function of G at a complex number with non-zero real part, then your only choice is evalfr(G,s).
Check the doc pages for both functions.
Having said that, in 2019a the doc page for evalfr is missing the sections for the details on the input and output arguments. But it's all pretty clear from what is shown on that doc page.
Will Robertson on 25 May 2020
Sorry I misunderstood your statement about 3 being complex!
I guess it's a tricky to balance sensible behaviour with backwards compatibility. In a pragmatic world we probably can't change anything to avoid breaking old code...