Source code for suboptimumg.log_analysis.utils
import numpy as np
from numpy.typing import NDArray
[docs]
def estimate_fs(t_s: NDArray[np.float64]) -> float:
"""Median-step sample-rate estimate (Hz) from a seconds time vector."""
if len(t_s) < 2:
return 0.0
dt = float(np.median(np.diff(t_s)))
return 1.0 / dt if dt > 0 else 0.0
[docs]
def safe_gradient(signal: NDArray[np.float64], dt: NDArray[np.float64]) -> NDArray[np.float64]:
"""Compute a time derivative that is robust to zero-width time steps,
so that duplicate timestamps do not produce inf or NaN values.
Parameters
----------
signal : NDArray[np.float64]
The signal to differentiate.
dt : NDArray[np.float64]
Time step array. Must be the same length as ``signal``.
Returns
-------
NDArray[np.float64]
``d(signal)/dt`` with NaNs from zero-width steps filled by
linear interpolation between the nearest valid neighbours.
"""
# Compute the raw gradient, masking out any points where dt == 0
# to avoid division-by-zero producing inf or incorrect values.
result = np.full_like(signal, np.nan)
mask = dt != 0
result[mask] = np.gradient(signal[mask]) / dt[mask]
nans = np.isnan(result)
if nans.any():
# Fill masked positions by linearly interpolating from valid neighbours.
idx = np.arange(len(result))
result[nans] = np.interp(idx[nans], idx[~nans], result[~nans])
return result