Skip to content

freq_etfe

MATLAB equivalent: sidFreqETFE

freq_etfe

Empirical Transfer Function Estimate (ETFE) for frequency response estimation.

freq_etfe

freq_etfe(y: ndarray | list, u: ndarray | list | None = None, *, smoothing: int = 1, frequencies: ndarray | None = None, sample_time: float = 1.0) -> FreqResult

Estimate the frequency response via the Empirical Transfer Function Estimate.

Computes the frequency response as the ratio of the output and input discrete Fourier transforms. Provides maximum frequency resolution but high variance. Optional smoothing reduces variance at the cost of resolution.

This is an open-source replacement for the System Identification Toolbox function etfe.

Parameters:

Name Type Description Default
y ndarray or list of ndarray, shape (N, ny) or (N, ny, L)

Output data. A 1-D array is treated as a single-channel signal. For multiple trajectories pass a 3-D array (N, ny, L) or a list of 1-D / 2-D arrays (variable-length trajectories are trimmed to the shortest). Cross-periodograms are ensemble-averaged across trajectories.

required
u ndarray, list of ndarray, or None

Input data. Same shape conventions as y. Pass None for time-series mode (output periodogram only). Default is None.

None
smoothing int

Smoothing window length S. Must be a positive integer; when S > 1 it must be odd. A length-S boxcar (moving average) filter is applied to the raw ETFE. Default is 1 (no smoothing).

1
frequencies (ndarray, shape(nf))

Frequency vector in rad/sample. All values must lie in the interval (0, pi]. Default is 128 linearly spaced values k * pi / 128 for k = 1, ..., 128.

None
sample_time float

Sample time in seconds. Must be positive. Default is 1.0.

1.0

Returns:

Type Description
FreqResult

Frozen dataclass with fields:

  • frequency (ndarray, shape (nf,)) -- Frequency vector, rad/sample.
  • frequency_hz (ndarray, shape (nf,)) -- Frequency vector, Hz.
  • response (ndarray or None) -- Complex frequency response. Shape (nf,) for SISO, (nf, ny, nu) for MIMO, or None in time-series mode.
  • response_std (ndarray or None) -- Standard deviation of response, same shape. Filled with NaN (ETFE has no closed-form asymptotic variance). None in time-series mode.
  • noise_spectrum (ndarray) -- Noise power spectrum (or output periodogram in time-series mode). Shape (nf,) for SISO / single-channel time-series, (nf, ny, ny) for MIMO or multi-channel time-series.
  • noise_spectrum_std (ndarray) -- Standard deviation of noise_spectrum, same shape. Filled with NaN.
  • coherence (None) -- Always None for ETFE.
  • sample_time (float) -- Sample time in seconds.
  • window_size (int) -- Data length N.
  • data_length (int) -- Number of samples N per trajectory.
  • num_trajectories (int) -- Number of trajectories.
  • method (str) -- 'freq_etfe'.

Raises:

Type Description
SidError

If sample_time is not positive (code: 'bad_ts').

SidError

If smoothing is not a positive integer, or is even and > 1 (code: 'bad_smoothing').

SidError

If any frequency is outside (0, pi] (code: 'bad_freqs').

SidError

If data contains NaN/Inf (code: 'non_finite'), is complex (code: 'complex_data'), or is too short (code: 'too_short'). These are raised by the data validation layer.

Examples:

Basic SISO system identification:

>>> import numpy as np
>>> import sid
>>> N = 1000; rng = np.random.default_rng(0)
>>> u = rng.standard_normal(N)
>>> from scipy.signal import lfilter
>>> y = lfilter([1], [1, -0.9], u) + 0.1 * rng.standard_normal(N)
>>> result = sid.freq_etfe(y, u, smoothing=5)
>>> result.response.shape
(128,)

Time-series periodogram:

>>> y = rng.standard_normal(500)
>>> result = sid.freq_etfe(y)
>>> result.noise_spectrum.shape
(128,)

Custom frequencies:

>>> w = np.linspace(0.01, np.pi, 256)
>>> result = sid.freq_etfe(y, u, frequencies=w)

Multi-trajectory (ensemble-averaged):

>>> L = 5; N = 1000
>>> u3d = rng.standard_normal((N, 1, L))
>>> y3d = np.zeros_like(u3d)
>>> for l in range(L):
...     y3d[:, 0, l] = lfilter([1], [1, -0.9], u3d[:, 0, l]) + 0.1 * rng.standard_normal(N)
>>> result = sid.freq_etfe(y3d, u3d)
Notes

Algorithm:

  1. Validate and orient input data; detect time-series vs. SISO vs. MIMO.
  2. Compute discrete Fourier transforms Y(w) and U(w) of the data.
  3. Form the raw ETFE: G(w) = Y(w) / U(w) (SISO) or G(w) = Phi_yu(w) * Phi_u(w)^{-1} (MIMO), using the H1 estimator for multi-trajectory data.
  4. Optionally smooth G with a length-S boxcar (moving average) window.
  5. Noise spectrum: Phi_v(w) = (1/N) * |Y(w) - G(w) * U(w)|^2. Time-series mode: periodogram Phi_y(w) = (1/N) * |Y(w)|^2.
  6. ETFE has no closed-form asymptotic variance; standard deviations are returned as NaN arrays.

Specification: SPEC.md S4 -- Empirical Transfer Function Estimate

References

.. [1] Ljung, L., "System Identification: Theory for the User", 2nd ed., Prentice Hall, 1999. Sections 2.3, 6.3.

See Also

sid.freq_bt : Blackman-Tukey spectral analysis (lower variance). sid.freq_btfdr : Blackman-Tukey with frequency-dependent resolution. sid.bode_plot : Bode magnitude/phase plot of a FreqResult. sid.spectrum_plot : Plot the noise/output spectrum of a FreqResult.

Changelog

2026-04-08 : First version (Python port) by Pedro Lourenco.