visualdynamics.core.srs¶
srs
¶
Shock response spectra.
An SRS is not a spectrum of the shock. It is the answer to a question asked of it: hang a single-degree-of-freedom oscillator off the base, shake the base with the measured transient, and record the largest response the oscillator ever reaches. Sweep the oscillator's natural frequency across the band of interest and the curve of those peaks is the shock response spectrum.
That is why two shocks with the same SRS can look nothing alike, and why an SRS cannot be inverted back to a time history: every peak is one number taken from one whole run of one filter, and the phase that produced it is gone.
The filter is Smallwood's ramp-invariant recursion (Smallwood 1981), which is the method the shock community settled on. Multiplying by the continuous transfer function in the frequency domain is the obvious alternative and it is wrong near Nyquist: the sampled input is a ramp between samples, not a train of impulses, and the ramp-invariant filter is the one that gets that right. The recursion below is exact for that assumption rather than an approximation of it.
Damping is quoted either as a ratio or as Q = 1/(2*zeta). Q = 10 — 5% of critical — is the near-universal default, and the value a specification is assumed to be written at unless it says otherwise.
Functions:
| Name | Description |
|---|---|
damping_for |
The damping ratio an amplification factor means. |
q_for |
The amplification factor a damping ratio means. |
octave_frequencies |
Natural frequencies from |
ramp_invariant |
Smallwood's filter coefficients, one row per natural frequency. |
peaks |
(highest, lowest) the oscillators reach, one per frequency. |
maximax |
The peak absolute response, whichever way it went. |
Functions:¶
damping_for
¶
q_for
¶
octave_frequencies
¶
Natural frequencies from low to high, per_octave to an octave.
Geometric, because an SRS is read on a log axis and a linear grid would crowd the top of the band and starve the bottom.
Source code in src/visualdynamics/core/srs.py
ramp_invariant
¶
ramp_invariant(
frequencies: ArrayLike,
sample_rate: float,
damping: float,
) -> tuple[ndarray, ndarray]
Smallwood's filter coefficients, one row per natural frequency.
Returns (b, a), each (F, 3), for the absolute acceleration of the oscillator given the acceleration of its base.
The gain at DC is exactly one — b.sum() / a.sum() == 1 — which is
the statement that an infinitely stiff oscillator goes wherever its
base goes. It is the cheapest check that the coefficients are the
right ones, and test_srs makes it.
Source code in src/visualdynamics/core/srs.py
peaks
¶
peaks(
signal: ArrayLike,
frequencies: ArrayLike,
sample_rate: float,
damping: float | None = None,
q: float = DEFAULT_Q,
) -> tuple[ndarray, ndarray]
(highest, lowest) the oscillators reach, one per frequency.
The filter runs over the record once for every natural frequency at once — the state is a vector across frequencies and the loop is over time — so nothing of length (frequencies x samples) is ever held. Only the running extremes are kept, which is all an SRS is.
Source code in src/visualdynamics/core/srs.py
maximax
¶
maximax(
signal: ArrayLike,
frequencies: ArrayLike,
sample_rate: float,
damping: float | None = None,
q: float = DEFAULT_Q,
) -> ndarray
The peak absolute response, whichever way it went.
The reading a shock specification is written against: a shock that only ever pushes one way is no gentler for it.