Help with tfest function. Resulting transfer function deviates from input on either side of freq range.

Requesting help with tfest options to resolve output transfer function issue where the middle of the range matches input values well, but deviates at both low and high ends of the input frequency range. Could this be due to optional inputs to tfest command?
code:
clear
tic
% create logspaced frequency vector
Fvec = logspace( log10(0.001), log10(1000), 10000 ); % 0.1 - 250 hz
% Equations/Constants to Calculate Ci Gain from frequency vector
% % Lee Pradko Vertical (Z) constants and Gain equations:
K0 = 4.35373;
K1 = 1.356;
Wi = 2*pi*Fvec;
F1 = -0.10245296*10^-9*Wi.^6 + 0.17583343*10^-5*Wi.^4 - 0.44600722*10^-2*Wi.^2 + 1;
F2 = +0.12881887*10^-7*Wi.^4 - 0.93394367*10^-4*Wi.^2 + 0.10543059;
F3 = -0.45416156*10^-9*Wi.^6 + 0.37667129*10^-5*Wi.^4 - 0.56104406*10^-2*Wi.^2 + 1;
F4 = -0.21179193*10^-11*Wi.^6 + 0.5172811*10^-7*Wi.^4 - 0.17946748*10^-3*Wi.^2 + 0.10543059;
Ci = K1.*K0.*(F1.*F4 - F2.*F3) ./ (F3.^2 + Wi.^2.*F4.^2);
sin_phi = (F2.*F3 - F1.*F4).*Wi ./ sqrt( (Wi.^2.*F4.^2+F3.^2).*(F1.^2+Wi.^2.*F2.^2));
phi = asin( sin_phi );
phase = phi* 180/pi;
%create frequency response data
data = frd(Ci.*exp(1j * phi ), Wi);
% Assuming 'data' is your iddata object and 'targetFit' is your desired percentage
targetFit = 99;
np = 1; % Start with 1 pole
nz = 0; % Start with 0 zeros
while true
% Estimate transfer function with current np and nz
sys = tfest(data, np, nz);
% Retrieve the Fit percentage (Normalized RMSE)
currentFit = sys.Report.Fit.FitPercent;
fprintf('Poles: %d, Zeros: %d | Current Fit: %.2f%%\n', np, nz, currentFit);
% Check if target fit is reached
if currentFit >= targetFit
fprintf('Desired fit of %.2f%% reached! Stopping.\n', targetFit);
break;
end
% Update your logic for iterating poles and zeros here.
% Example: Increment zeros up to np-1, then increment poles.
if nz < max(np - 1, 0)
nz = nz + 1;
else
np = np + 1;
nz = 0; % Reset zeros for the new pole count
end
% Set a safe break condition to avoid infinite loops
if np > 15
fprintf('Maximum iterations reached without meeting target fit.\n');
break;
end
end
Poles: 1, Zeros: 0 | Current Fit: 1.08% Poles: 2, Zeros: 0 | Current Fit: 13.17% Poles: 2, Zeros: 1 | Current Fit: 40.45% Poles: 3, Zeros: 0 | Current Fit: 12.24% Poles: 3, Zeros: 1 | Current Fit: 46.53% Poles: 3, Zeros: 2 | Current Fit: 75.45% Poles: 4, Zeros: 0 | Current Fit: 36.93% Poles: 4, Zeros: 1 | Current Fit: 59.46% Poles: 4, Zeros: 2 | Current Fit: 84.00% Poles: 4, Zeros: 3 | Current Fit: 88.83% Poles: 5, Zeros: 0 | Current Fit: 37.99% Poles: 5, Zeros: 1 | Current Fit: 65.46% Poles: 5, Zeros: 2 | Current Fit: 92.35% Poles: 5, Zeros: 3 | Current Fit: 92.44% Poles: 5, Zeros: 4 | Current Fit: 94.08% Poles: 6, Zeros: 0 | Current Fit: 48.72% Poles: 6, Zeros: 1 | Current Fit: 65.00% Poles: 6, Zeros: 2 | Current Fit: 92.37% Poles: 6, Zeros: 3 | Current Fit: 92.57% Poles: 6, Zeros: 4 | Current Fit: 94.17% Poles: 6, Zeros: 5 | Current Fit: 95.70% Poles: 7, Zeros: 0 | Current Fit: 49.63% Poles: 7, Zeros: 1 | Current Fit: 67.54% Poles: 7, Zeros: 2 | Current Fit: 92.84% Poles: 7, Zeros: 3 | Current Fit: 93.06% Poles: 7, Zeros: 4 | Current Fit: 95.46% Poles: 7, Zeros: 5 | Current Fit: 96.29% Poles: 7, Zeros: 6 | Current Fit: 97.09% Poles: 8, Zeros: 0 | Current Fit: 58.73% Poles: 8, Zeros: 1 | Current Fit: 67.43% Poles: 8, Zeros: 2 | Current Fit: 92.89% Poles: 8, Zeros: 3 | Current Fit: 93.12% Poles: 8, Zeros: 4 | Current Fit: 95.84% Poles: 8, Zeros: 5 | Current Fit: 97.19% Poles: 8, Zeros: 6 | Current Fit: 97.44% Poles: 8, Zeros: 7 | Current Fit: 98.36% Poles: 9, Zeros: 0 | Current Fit: 58.91% Poles: 9, Zeros: 1 | Current Fit: 72.49% Poles: 9, Zeros: 2 | Current Fit: 93.71% Poles: 9, Zeros: 3 | Current Fit: 93.89% Poles: 9, Zeros: 4 | Current Fit: 95.91% Poles: 9, Zeros: 5 | Current Fit: 97.36% Poles: 9, Zeros: 6 | Current Fit: 97.39% Poles: 9, Zeros: 7 | Current Fit: 98.77% Poles: 9, Zeros: 8 | Current Fit: 98.88% Poles: 10, Zeros: 0 | Current Fit: 64.05% Poles: 10, Zeros: 1 | Current Fit: 74.52% Poles: 10, Zeros: 2 | Current Fit: 93.74% Poles: 10, Zeros: 3 | Current Fit: 93.73% Poles: 10, Zeros: 4 | Current Fit: 96.81% Poles: 10, Zeros: 5 | Current Fit: 97.37% Poles: 10, Zeros: 6 | Current Fit: 97.95% Poles: 10, Zeros: 7 | Current Fit: 98.87% Poles: 10, Zeros: 8 | Current Fit: 98.89% Poles: 10, Zeros: 9 | Current Fit: 98.89% Poles: 11, Zeros: 0 | Current Fit: 63.91% Poles: 11, Zeros: 1 | Current Fit: 76.68% Poles: 11, Zeros: 2 | Current Fit: 93.90% Poles: 11, Zeros: 3 | Current Fit: 94.04% Poles: 11, Zeros: 4 | Current Fit: 96.83% Poles: 11, Zeros: 5 | Current Fit: 97.45% Poles: 11, Zeros: 6 | Current Fit: 97.99% Poles: 11, Zeros: 7 | Current Fit: 98.91% Poles: 11, Zeros: 8 | Current Fit: 99.14%
Desired fit of 99.00% reached! Stopping.
sys
sys =
-1.589e05 s^8 - 6.901e06 s^7 - 1.182e09 s^6 + 6.519e10 s^5 + 1.904e12 s^4 + 2.731e14 s^3 + 4.038e15 s^2 + 1.103e15 s + 4.216e14 ---------------------------------------------------------------------------------------------------------------------------------------------------------- s^11 + 181 s^10 - 7.282e04 s^9 - 9.436e06 s^8 - 9.538e08 s^7 - 4.838e10 s^6 - 1.691e12 s^5 - 4.387e13 s^4 - 1.185e15 s^3 - 4.614e16 s^2 - 1.11e18 s - 1.837e19 Continuous-time identified transfer function. Parameterization: Number of poles: 11 Number of zeros: 8 Number of free coefficients: 20 Use "tfdata", "getpvec", "getcov" for parameters and their uncertainties. Status: Estimated using TFEST on frequency response data "data". Fit to estimation data: 99.14% FPE: 1.38e-07, MSE: 1.375e-07
sys_zpk = zpk(sys)
sys_zpk =
-1.5892e05 (s-68.41) (s+15.08) (s^2 + 0.2709s + 0.1063) (s^2 + 24.2s + 2618) (s^2 + 72.28s + 9235) --------------------------------------------------------------------------------------------------------------------- (s+335.9) (s-271.9) (s+37.95) (s^2 + 34.22s + 815) (s^2 - 39.08s + 919.4) (s^2 + 37.58s + 1460) (s^2 + 46.35s + 4840) Continuous-time zero/pole/gain model.
[mag_raw, phase_raw, wout] = bode(sys,Wi);
mag = squeeze(mag_raw);
phase = squeeze(phase_raw);
Frequency_Hz = wout / (2 * pi);
bodeTable = table(wout, Frequency_Hz, mag, phase);
%writetable(bodeTable, 'BodeDataLeePradkotest.xlsx');
wout_Hz = wout / (2 * pi);
% interpolated magnitude
MAGvec = interp1( wout_Hz, mag, Fvec, 'pchip' );
title_str = 'Magnitude Gain, Z-Vertical (Up-Down) Axis';
lim_y = 15;
% Overlay both curves
set( groot, 'Units', 'inches' )
screensize = get(groot, 'ScreenSize' );
FD = min( screensize(3), screensize(4) );
f1 = figure( 'Units', 'inches', 'Position', [0, 0, FD+1.5, FD] );
movegui( 'center' )
th = get( f1, 'Theme' );
subplot( 2, 1, 1 )
plot( Fvec, Ci, 'r*' )
hold on
plot( Fvec, mag, 'b', 'LineWidth', 3 )
%plot( Fvec, MAGvecL, 'g', 'LineWidth', 3 )
grid on
title( title_str )
xlabel( 'Frequency (Hz)' )
ylabel( 'Gain ( Watts/(ft/s²)² )')
legend( 'Input Data', 'TF Output' )
xlim( [ 0 lim_y ] )
subplot( 2, 1, 2 )
loglog( Fvec, Ci, 'r*' )
hold on
loglog( Fvec, MAGvec, 'b', 'LineWidth', 3 )
%loglog( Fvec, MAGvecL, 'g', 'LineWidth', 3 )
grid on
xlabel( 'Frequency (Hz)' )
ylabel( 'Gain ( Watts/(ft/s²)² )')
legend( 'Input Data', 'TF Output')
toc
Elapsed time is 25.000515 seconds.
Warning: Graphics acceleration hardware is unavailable. Graphics quality and performance might be diminished. See MATLAB System Requirements.
Warning: Hardware-accelerated graphics is unavailable. Displaying fewer markers to preserve interactivity.

2 Comments

I should have mentioned this upfront. The first plot is shorter 0-15hz, the second plot is log scale, full 0-1000hz range. I thought part of the problem may have been the way I was interpolating the mag (magnitude of gain) output, so I tried plotting that mag output and got the exact same deviations shown in the second plot.
Paul,
Thank you. Visually, that's an improvement, but I need to figure out the fit convergence issue now. Definitely helpful. I need to explore more of the tfest options and what they impact.
Appreciate the help!
Jared

Sign in to comment.

 Accepted Answer

Hi Jared,
Maybe look into the WeightingFilter opton of tfestOptions. I tried with 'inv' below and seemed to get a result more in line with what's desired, but it doesn't reach the TargetFit (I don't know what that meansn nor if the TargetFit interacts with the WeightingFilter) and there was some other warning, but the results look pretty good. Also, I didn't undersand why interp1 was used, so I got rid of that in the code below, but the interpolated magnitude did look fine when I checked.
clear
tic
% create logspaced frequency vector
Fvec = logspace( log10(0.001), log10(1000), 10000 ); % 0.1 - 250 hz
% Equations/Constants to Calculate Ci Gain from frequency vector
% % Lee Pradko Vertical (Z) constants and Gain equations:
K0 = 4.35373;
K1 = 1.356;
Wi = 2*pi*Fvec;
F1 = -0.10245296*10^-9*Wi.^6 + 0.17583343*10^-5*Wi.^4 - 0.44600722*10^-2*Wi.^2 + 1;
F2 = +0.12881887*10^-7*Wi.^4 - 0.93394367*10^-4*Wi.^2 + 0.10543059;
F3 = -0.45416156*10^-9*Wi.^6 + 0.37667129*10^-5*Wi.^4 - 0.56104406*10^-2*Wi.^2 + 1;
F4 = -0.21179193*10^-11*Wi.^6 + 0.5172811*10^-7*Wi.^4 - 0.17946748*10^-3*Wi.^2 + 0.10543059;
Ci = K1.*K0.*(F1.*F4 - F2.*F3) ./ (F3.^2 + Wi.^2.*F4.^2);
sin_phi = (F2.*F3 - F1.*F4).*Wi ./ sqrt( (Wi.^2.*F4.^2+F3.^2).*(F1.^2+Wi.^2.*F2.^2));
phi = asin( sin_phi );
phase = phi* 180/pi;
%create frequency response data
data = frd(Ci.*exp(1j * phi ), Wi);
% Assuming 'data' is your iddata object and 'targetFit' is your desired percentage
targetFit = 99;
np = 1; % Start with 1 pole
nz = 0; % Start with 0 zeros
while true
% Estimate transfer function with current np and nz
sys = tfest(data, np, nz,tfestOptions('WeightingFilter','inv'));
% Retrieve the Fit percentage (Normalized RMSE)
currentFit = sys.Report.Fit.FitPercent;
%fprintf('Poles: %d, Zeros: %d | Current Fit: %.2f%%\n', np, nz, currentFit);
% Check if target fit is reached
if currentFit >= targetFit
fprintf('Desired fit of %.2f%% reached! Stopping.\n', targetFit);
break;
end
% Update your logic for iterating poles and zeros here.
% Example: Increment zeros up to np-1, then increment poles.
if nz < max(np - 1, 0)
nz = nz + 1;
else
np = np + 1;
nz = 0; % Reset zeros for the new pole count
end
% Set a safe break condition to avoid infinite loops
if np > 15
fprintf('Maximum iterations reached without meeting target fit.\n');
break;
end
end
Warning: Estimated model has a lower order than requested.
Maximum iterations reached without meeting target fit.
sys
sys =
0.02774 s^14 + 89.17 s^13 - 7.827e05 s^12 - 2.008e08 s^11 + 3.085e11 s^10 - 3.45e12 s^9 + 5.727e15 s^8 + 1.453e16 s^7 + 4.06e19 s^6 - 9.747e20 s^5 + 2.451e21 s^4 - 4.853e24 s^3 - 9.774e25 s^2 - 1.591e20 s - 2.186e19 ----------------------------------------------------------------------------------------------------------------------------------------------------------- s^15 + 759.9 s^14 - 2.773e06 s^13 - 4.476e08 s^12 + 1.109e11 s^11 + 3.84e12 s^10 + 2.088e15 s^9 + 2.26e17 s^8 + 1.993e19 s^7 + 1.278e21 s^6 + 4.113e22 s^5 + 1.411e24 s^4 + 2.505e25 s^3 + 1.122e27 s^2 + 2.186e28 s + 4.689e29 Continuous-time identified transfer function. Parameterization: Number of poles: 15 Number of zeros: 14 Number of free coefficients: 30 Use "tfdata", "getpvec", "getcov" for parameters and their uncertainties. Status: Estimated using TFEST on frequency response data "data". Fit to estimation data: 92.17% (data prefiltered) FPE: 6.595e-07, MSE: 0.002292
%sys_zpk = zpk(sys)
[mag_raw, phase_raw, wout] = bode(sys,Wi);
mag = squeeze(mag_raw);
phase = squeeze(phase_raw);
Frequency_Hz = wout / (2 * pi);
wout_Hz = wout / (2 * pi);
% interpolated magnitude
%MAGvec = interp1( wout_Hz, mag, Fvec, 'pchip' );
title_str = 'Magnitude Gain, Z-Vertical (Up-Down) Axis';
lim_y = 15;
% Overlay both curves
figure
subplot( 2, 1, 1 )
plot( Fvec, Ci, 'r*' )
hold on
plot( Fvec, mag, 'b', 'LineWidth', 3 )
%plot( Fvec, MAGvecL, 'g', 'LineWidth', 3 )
grid on
title( title_str )
xlabel( 'Frequency (Hz)' )
ylabel( 'Gain ( Watts/(ft/s²)² )')
legend( 'Input Data', 'TF Output' )
xlim( [ 0 lim_y ] )
subplot( 2, 1, 2 )
loglog( Fvec, Ci, 'r*' )
hold on
%loglog( Fvec, MAGvec, 'b', 'LineWidth', 3 )
loglog( Fvec, mag, 'b', 'LineWidth', 3 )
%loglog( Fvec, MAGvecL, 'g', 'LineWidth', 3 )
grid on
xlabel( 'Frequency (Hz)' )
ylabel( 'Gain ( Watts/(ft/s²)² )')
legend( 'Input Data', 'TF Output')
toc
Elapsed time is 57.072219 seconds.
Warning: Hardware-accelerated graphics is unavailable. Displaying fewer markers to preserve interactivity.

More Answers (0)

Products

Release

R2026a

Tags

Asked:

on 25 Sep 2026 at 17:02

Commented:

on 26 Sep 2026 at 2:08

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!