visualdynamics.core.classical_pulse¶
classical_pulse
¶
Judge a measured classical shock pulse against the pulse it was meant to be.
A classical pulse is written as three things: a shape, a peak and a duration. Three shapes are in common use. The half-sine is half a cycle of a sine, the shape a drop onto a resilient pad makes. The terminal-peak sawtooth rises in a straight line over the duration and falls back to zero quickly; the sharp fall sets every resonator ringing, which is why it is chosen where a flat spectrum is wanted. The trapezoid rises, holds a flat top and falls. A sawtooth's fall, and a trapezoid's rise and fall, are its edges, and they are part of what is asked for.
A test document states the pulse with its tolerance, and the measurement is judged against that, point by point and as a whole. What test documents commonly ask for, and the defaults here (each is an argument, and the document the procedure cites decides its values):
- The band on the pulse. The measured pulse stays inside a band
drawn around the ideal shape, a fifth of the peak either side of it
(
BAND), so a 40 g pulse peaks between 32 and 48 g. - The duration, within a tenth of nominal either way
(
DURATION_TOLERANCE): 9.9 to 12.1 ms for an 11 ms pulse. - The edges. A sawtooth's fall, and a trapezoid's rise and fall,
take no longer than a tenth of the duration (
EDGE_LIMIT). - The quiet either side. For two and a half durations before the
pulse and after it (
QUIET), the acceleration stays inside the same fifth of the peak. Before the pulse is where a shaker puts its compensation; after it is where a fixture's ringing shows. - The velocity change, the area under the pulse, integrated from
0.4 durations before the pulse to 0.1 after it (
VELOCITY_WINDOW), within a fifth of the nominal pulse's (VELOCITY_TOLERANCE). The window is part of the rule: a shaker's compensated pulse changes velocity by nothing over the whole record, by design, so the area is taken around the main pulse only. The fifth is MIL-STD-810H's; documents and their revisions differ on this tolerance, so it is set from the one the procedure cites.
The nominal velocity change is the area formula of each shape, with
A the peak and T the duration: 2AT/π for the half-sine,
AT/2 for the sawtooth (with its fall taken as instant, as the
formula is usually stated) and A(T - e) for a trapezoid whose rise
and fall each take e. A sawtooth whose fall takes its whole
allowance therefore changes velocity by about a tenth more than its
nominal, half of a fifth's tolerance spent before anything else varies,
and the check reports it as it is rather than moving the nominal.
How the envelope is drawn
Documents draw the envelope as a figure, and the shapes of their lines
at the start and end of the pulse differ. Here it is built one way for
all three shapes, so that it agrees with the separate duration and
edge tolerances rather than contradicting them: every pulse of the
shape whose duration is inside the duration tolerance and whose edges
are inside the edge limit is admissible, and the envelope is the
highest and lowest of them at each instant, widened by the band. A
pulse a tenth long is then inside the envelope and inside the duration
tolerance, and a sawtooth that falls in a tenth of its duration is
inside both as well. Away from the pulse every admissible shape is
zero, and the envelope is the band alone. The admissible family is
sampled (FAMILY durations by FAMILY_EDGES edges); the largest error
that leaves, at a half-sine's crest, is a few parts in a hundred
thousand of the peak.
How the measured pulse is read
The pulse is found by its largest excursion in the nominal's direction,
and its start, end and edges by where it crosses a tenth and nine
tenths of that peak on either side, walking out from the peak so that
noise before and after the pulse is not mistaken for it. The edges of a
sawtooth and a trapezoid are straight, so each is extended through its
two crossings to zero: the start, the end, and how long each edge took.
A half-sine has no straight edge, and its duration is the span between
its two tenth-of-peak crossings over the share of a half-sine's
duration that span covers, 1 - 2 asin(0.1)/π. The measurement
cannot resolve a time finer than a sample, so the duration and edge
tolerances are read with half a sample's grace.
The velocity change is integrated raw, by the trapezoid rule with
nothing removed (filters.integrate with drift_corner=None), over
the window placed on the measured pulse: from 0.4 nominal durations
before its start to 0.1 after its end. A mean removed or a drift
high-pass applied first would take out exactly the net area being
measured. The integration needs evenly spaced samples, so a record
whose samples are not evenly spaced is refused, as is one holding a
sample that is not a number.
The velocity change here is the area over the window: the velocity
where the window closes, from rest where it opens. The shock check
(core.shock_check) reads a different number under the same name,
the span from the lowest velocity to the highest across its whole
window. For a lone one-signed pulse the two agree. They part where the
record swings the other way: an overshoot of the other sign after the
pulse, inside the window, takes from the area here and leaves the span
alone, and compensation that runs into the window is counted here and
not by the span.
References
The check is a standard one, and its tolerances are the test document's; these set out the pulses, their envelopes and their velocity change.
- Department of Defense (2019). MIL-STD-810H, Environmental Engineering Considerations and Laboratory Tests. Method 516.8, Figures 516.8-3 to 516.8-5: the classical pulses' tolerance envelopes, a band of a fifth of the peak on the pulse and for 2.5 durations either side of it, the duration within a tenth, a sawtooth's fall and a trapezoid's rise and fall within a tenth of the duration, and the velocity change within a fifth over a window from 0.4 durations before the pulse to 0.1 after it.
- Piersol, A. G., & Paez, T. L. (Eds.) (2010). Harris' Shock and Vibration Handbook, 6th ed. McGraw-Hill. Shock testing machines and the pulses they are asked to make.
Classes:
| Name | Description |
|---|---|
ClassicalPulse |
A nominal classical pulse: its shape, peak, duration and edge. |
PulseMeasurement |
Where a measured pulse is and what shape it took, on the record's |
PulseCheck |
A measured pulse judged against its nominal. |
Functions:
| Name | Description |
|---|---|
admissible_reach |
The latest instant, in seconds from the start, any admissible |
pulse_envelope |
The tolerance envelope of |
measure_pulse |
Find a classical pulse in a record and read its start, end, |
suggest_pulse |
The classical pulse of |
judge_pulse |
Judge a measured pulse against |
check_pulse |
Judge one acceleration channel of a record against |
pulse_channel |
The row of |
Classes¶
ClassicalPulse
dataclass
¶
A nominal classical pulse: its shape, peak, duration and edge.
peak is signed, in SI (m/s² for an acceleration pulse), so a pulse
in the negative direction is a negative peak. duration is in
seconds. edge is the sawtooth's fall, after its rise over the
duration, or the trapezoid's rise and its fall, each, inside the
duration; a half-sine has none. A sawtooth's fall is instant unless
given, and a trapezoid's edge has to be given.
Methods:
| Name | Description |
|---|---|
at |
The nominal pulse at |
describe |
The pulse in words, as '50 g, 11 ms half-sine'. |
Attributes:
| Name | Type | Description |
|---|---|---|
extent |
float
|
From the pulse's start to its end, in seconds: the duration, |
velocity_change |
float
|
The nominal velocity change, in SI (m/s), signed. |
Attributes¶
extent
property
¶
From the pulse's start to its end, in seconds: the duration, and the fall after it for a sawtooth.
velocity_change
property
¶
The nominal velocity change, in SI (m/s), signed.
2AT/π for a half-sine, AT/2 for a sawtooth with its fall
taken as instant (the formula as it is usually stated, so a fall
that takes time adds to the measured change and not to this),
and A(T - e) for a trapezoid with edges of e.
Methods:¶
at
¶
The nominal pulse at time, in seconds from its start.
Zero before the start and after the end.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
time
|
array_like
|
Times, in seconds, measured from the pulse's start. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
The pulse, in the units of |
Source code in src/visualdynamics/core/classical_pulse.py
describe
¶
The pulse in words, as '50 g, 11 ms half-sine'.
factor scales the SI peak to unit (1/9.80665 for g).
Source code in src/visualdynamics/core/classical_pulse.py
PulseMeasurement
dataclass
¶
PulseMeasurement(start: float, end: float, duration: float, peak: float, peak_time: float, rise: float | None = None, fall: float | None = None)
Where a measured pulse is and what shape it took, on the record's clock (seconds) and in the record's SI units.
rise is the trapezoid's rise and fall the sawtooth's or the
trapezoid's fall; each is None where the shape has no such edge.
peak is signed, in the direction of the nominal pulse.
PulseCheck
dataclass
¶
PulseCheck(pulse: ClassicalPulse, measured: PulseMeasurement, time: ndarray, acceleration: ndarray, lower: ndarray, upper: ndarray, outside: ndarray, velocity_time: ndarray, velocity: ndarray, span: tuple[float, float], window: tuple[float, float], band: float = BAND, duration_tolerance: float = DURATION_TOLERANCE, edge_limit: float = EDGE_LIMIT, velocity_tolerance: float = VELOCITY_TOLERANCE, sample_time: float = 0.0, record: tuple[float, float] = (0.0, 0.0))
A measured pulse judged against its nominal.
The arrays are on the record's clock and in SI. time,
acceleration, lower, upper and outside cover the judged
span, from QUIET durations before the measured start to as long
after the latest admissible end, as far as the record reaches; the
envelope is placed on the measured start. velocity_time and
velocity cover the velocity window, the velocity from rest at its
first sample. The verdicts are read from them.
Methods:
| Name | Description |
|---|---|
failures |
What was not met, a phrase apiece; empty for a pass. |
describe |
The verdict in a sentence or two, as the view and the panel |
Attributes:
| Name | Type | Description |
|---|---|---|
points |
int
|
How many samples were judged against the envelope. |
outside_count |
int
|
How many judged samples are outside the envelope. |
covered |
bool
|
Whether the record reaches across the whole judged span and |
velocity_change |
float
|
The measured velocity change over the window, SI, signed. |
nominal_velocity_change |
float
|
The nominal pulse's velocity change, SI, signed. |
velocity_ratio |
float
|
The measured velocity change over the nominal. |
peak_ratio |
float
|
The measured peak over the nominal. |
duration_ratio |
float
|
The measured duration over the nominal. |
edge_allowance |
float
|
The longest an edge may take, in seconds. |
envelope_ok |
bool
|
Every judged sample is inside the envelope. |
peak_ok |
bool
|
The measured peak is inside the band about the nominal's. |
duration_ok |
bool
|
The measured duration is inside its tolerance, with half a |
rise_ok |
bool
|
The trapezoid's rise takes no longer than the limit, with half |
fall_ok |
bool
|
The sawtooth's or the trapezoid's fall takes no longer than |
edges_ok |
bool
|
Every edge the shape has is inside the limit. |
velocity_ok |
bool
|
The velocity change is inside its tolerance of nominal. |
passed |
bool
|
Every part of the check is met, over a record that covers it. |
Attributes¶
covered
property
¶
Whether the record reaches across the whole judged span and the whole velocity window.
velocity_change
property
¶
The measured velocity change over the window, SI, signed.
The area under the record across the window, the velocity where
the window closes. Not the span from the lowest velocity to the
highest that core.shock_check.ShockCheck.velocity_change
reads; the module docstring says where the two differ.
nominal_velocity_change
property
¶
The nominal pulse's velocity change, SI, signed.
duration_ok
property
¶
The measured duration is inside its tolerance, with half a sample's grace.
rise_ok
property
¶
The trapezoid's rise takes no longer than the limit, with half a sample's grace; true where the shape has no rise to hold.
fall_ok
property
¶
The sawtooth's or the trapezoid's fall takes no longer than the limit, with half a sample's grace; true for a half-sine.
Methods:¶
failures
¶
What was not met, a phrase apiece; empty for a pass.
Source code in src/visualdynamics/core/classical_pulse.py
describe
¶
The verdict in a sentence or two, as the view and the panel state it.
Source code in src/visualdynamics/core/classical_pulse.py
Functions:¶
admissible_reach
¶
admissible_reach(pulse: ClassicalPulse, duration_tolerance: float = DURATION_TOLERANCE, edge_limit: float = EDGE_LIMIT) -> float
The latest instant, in seconds from the start, any admissible pulse is still away from zero: the longest duration, and the longest fall after it for a sawtooth.
Source code in src/visualdynamics/core/classical_pulse.py
pulse_envelope
¶
pulse_envelope(pulse: ClassicalPulse, time: ArrayLike, *, band: float = BAND, duration_tolerance: float = DURATION_TOLERANCE, edge_limit: float = EDGE_LIMIT) -> tuple[ndarray, ndarray]
The tolerance envelope of pulse at time.
The highest and the lowest of every admissible pulse at each
instant, widened by band of the peak either way: admissible is the
shape at any duration inside duration_tolerance and, for the
sawtooth and the trapezoid, any edge from instant to edge_limit of
the nominal duration (the module docstring says why it is built
this way). Away from the pulse it is the band either side of zero.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pulse
|
ClassicalPulse
|
The nominal pulse. |
required |
time
|
array_like
|
Times, in seconds, measured from the pulse's start. |
required |
band
|
float
|
The band's half-width, as a share of the peak. |
`BAND`
|
duration_tolerance
|
float
|
How far the duration may sit from nominal, as a share of it. |
`DURATION_TOLERANCE`
|
edge_limit
|
float
|
The longest an edge may take, as a share of the duration. |
`EDGE_LIMIT`
|
Returns:
| Type | Description |
|---|---|
(ndarray, ndarray)
|
The lower and the upper edge of the envelope, in the units of the peak. |
Source code in src/visualdynamics/core/classical_pulse.py
measure_pulse
¶
measure_pulse(time: ArrayLike, acceleration: ArrayLike, shape: str, direction: float = 1.0) -> PulseMeasurement
Find a classical pulse in a record and read its start, end, duration, peak and edges.
The pulse is the record's largest excursion in direction, and its
timing comes from where it crosses a tenth and nine tenths of that
peak, walking out from the peak; the module docstring says how each
shape's duration and edges follow from them.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
time
|
array_like
|
Each sample's time, in seconds. |
required |
acceleration
|
array_like
|
The record, in SI. |
required |
shape
|
str
|
One of |
required |
direction
|
float
|
The sign of the nominal pulse; the pulse looked for is the largest excursion this way. |
1.0
|
Returns:
| Type | Description |
|---|---|
PulseMeasurement
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/visualdynamics/core/classical_pulse.py
431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 | |
suggest_pulse
¶
suggest_pulse(time: ArrayLike, acceleration: ArrayLike, shape: str = 'half-sine') -> ClassicalPulse
The classical pulse of shape a record most resembles: its
largest excursion either way, with the duration (and the edge) read
from it by measure_pulse.
A starting point for a reading that has no nominal yet, as the app's Pulse Check opens on a record; the nominal a test is judged against is the one its document states, never one read off the record being judged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
time
|
array_like
|
Each sample's time, in seconds. |
required |
acceleration
|
array_like
|
The record, in SI. |
required |
shape
|
str
|
One of |
'half-sine'
|
Returns:
| Type | Description |
|---|---|
ClassicalPulse
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
As |
Source code in src/visualdynamics/core/classical_pulse.py
judge_pulse
¶
judge_pulse(time: ArrayLike, acceleration: ArrayLike, pulse: ClassicalPulse, *, band: float = BAND, duration_tolerance: float = DURATION_TOLERANCE, edge_limit: float = EDGE_LIMIT, velocity_tolerance: float = VELOCITY_TOLERANCE, quiet: float = QUIET, window: tuple[float, float] = VELOCITY_WINDOW) -> PulseCheck
Judge a measured pulse against pulse, point by point and as a
whole.
The pulse is found and measured (measure_pulse), the envelope is
placed on its measured start (pulse_envelope) and every sample
from quiet durations before the start to as long after the latest
admissible end is judged against it, and the velocity change is
integrated raw over window (filters.integrate, no drift
corner) and compared with the nominal's.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
time
|
array_like
|
Each sample's time, in seconds, evenly spaced and in order. |
required |
acceleration
|
array_like
|
The record, in SI. |
required |
pulse
|
ClassicalPulse
|
The pulse the record was meant to be. |
required |
band
|
float
|
The band either side of the shape and of zero, a share of the peak; it holds the peak as well. |
`BAND`
|
duration_tolerance
|
float
|
How far the duration may sit from nominal, as a share. |
`DURATION_TOLERANCE`
|
edge_limit
|
float
|
The longest an edge may take, a share of the nominal duration. |
`EDGE_LIMIT`
|
velocity_tolerance
|
float
|
How far the velocity change may sit from nominal, as a share. |
`VELOCITY_TOLERANCE`
|
quiet
|
float
|
The quiet judged before and after the pulse, in durations. |
`QUIET`
|
window
|
(float, float)
|
The velocity window, in durations before the start and after the end. |
`VELOCITY_WINDOW`
|
Returns:
| Type | Description |
|---|---|
PulseCheck
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If a tolerance is not a positive share, the samples are not
evenly spaced, or the pulse cannot be found in the record
( |
Source code in src/visualdynamics/core/classical_pulse.py
756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 | |
check_pulse
¶
check_pulse(history: TimeHistory, pulse: ClassicalPulse, channel: int | str | None = None, *, band: float = BAND, duration_tolerance: float = DURATION_TOLERANCE, edge_limit: float = EDGE_LIMIT, velocity_tolerance: float = VELOCITY_TOLERANCE, quiet: float = QUIET, window: tuple[float, float] = VELOCITY_WINDOW) -> PulseCheck
Judge one acceleration channel of a record against pulse
(judge_pulse).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
history
|
TimeHistory
|
The shock record. |
required |
pulse
|
ClassicalPulse
|
The pulse it was meant to be, its peak in SI (m/s²). |
required |
channel
|
int or str
|
The record's row, or its DOF; the first acceleration channel by default. |
None
|
band
|
float
|
As for |
BAND
|
duration_tolerance
|
float
|
As for |
BAND
|
edge_limit
|
float
|
As for |
BAND
|
velocity_tolerance
|
float
|
As for |
BAND
|
quiet
|
float
|
As for |
BAND
|
window
|
float
|
As for |
BAND
|
Returns:
| Type | Description |
|---|---|
PulseCheck
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If the channel is not in the record or is not an acceleration,
or as |
Source code in src/visualdynamics/core/classical_pulse.py
pulse_channel
¶
pulse_channel(history: TimeHistory, channel: int | str | None = None) -> int
The row of history a pulse is read from: channel by index or
DOF, or the first acceleration channel whose unit is declared
(core.shaker.acceleration_channel, the one rule for the stroke
and the replication fit too); refused otherwise, since a pulse's
velocity change is the area under an acceleration in a known scale
and nothing else.