visualdynamics.core.replication_fit¶
replication_fit
¶
Whether a shaker can play a reference record, and what the high-pass that makes it playable costs.
A sine profile states its peak, velocity and displacement at every frequency, and a random profile states its rms and, through the Gaussian assumption, its peaks. A record to be replicated states nothing until it is worked: its largest peak may come once, in one second of a minute, and its largest excursion at another second entirely. So the fit check is made on the record itself, sample by sample, before the first pass.
The force is the moving mass (everything the shaker accelerates: the armature, the head expander or slip table, the fixture and the article) times the record's largest absolute acceleration, read from the record and not estimated from its rms. A record with impacts has a crest factor well past the three sigma a Gaussian test would suggest, and the force at that one peak is what goes against the rating. Each cutoff changes the largest peak a little, so each case judges the force at its own.
The velocity and the displacement come from integrating the record twice, and they live at the bottom of its band: a component of acceleration a at frequency f moves the table a/(2πf)², so the lowest content decides the stroke almost alone. That is also where a record is least trustworthy. A logger's offset of a thousandth of a g, held for a minute, integrates to hundreds of inches, and below the sensor's own low-frequency limit what the record holds is the chain's response, not the vehicle's. So the low end is edited before the integration: a zero-phase high-pass at a cutoff below the band of interest.
Each candidate cutoff is worked the same way. The record, with its
offset taken out, has its ends tapered: a half-cosine from zero over
two periods of the cutoff, 2/fc seconds, at each end (half the record
each when it is shorter than that). It is then high-passed (a
second-order Butterworth run forward and backward, filters.filtered,
so nothing moves in time), integrated raw to velocity, the velocity
high-passed again at the same cutoff, and integrated raw to
displacement. The velocity is filtered too because an integration from
rest cannot know the record's velocity at its first sample, and that
unknown constant walks the displacement off as a ramp of its own. The
integration is filters.integrate with drift_corner=None, the
trapezoid rule with nothing removed: the cutoff here is the edit being
priced, so the integrator adds no high-pass of its own.
The taper is there because a record cut out of a longer one does not start or end at rest. A tone at full amplitude from its first sample integrates to a velocity with a constant in it, and the high-pass's own start-up transient turns that constant into a displacement offset of about v0/(2π fc), which at a low cutoff is many times the tone's true motion. Eased in and out over two periods of the cutoff, the same tone moves the table A/ω² to a fifth of a percent.
Each cutoff has a price, paid by the article: the share of the record's mean square the filter removed, and the change in its largest peak. Both are measured against the same tapered record with its offset taken out, so neither the offset nor the taper is counted as content the filter removed. The record is also integrated as logged, with no taper and no cutoff, so that a logger offset shows as the drift it is, and the offset alone is integrated beside it.
The travel, the velocity and the force are judged by the rule in
core.shaker, which the stroke readout uses too: the armature's motion
against the body is the table's times one plus the body's share, the
travel is read each way from the middle against half the stroke, and
the force is the moving mass times the peak. A record that dips into a
hole and climbs out is not symmetric about zero, and its span alone
can fit a stroke its one-sided excursion does not.
References
The check is a standard one; this sets out what it asks for and why
integration needs the low end edited first. The zero-phase filter is
core.filters's, which cites its own source.
- Department of Defense (2019). MIL-STD-810H, Environmental Engineering Considerations and Laboratory Tests. Method 525.2, Time Waveform Replication, paragraph 4.5.1. Before the first pass the reference is band-limited, filtered of the low content that would over-travel the exciter, its force estimated from the peak acceleration and the total moving mass, and integrated to velocity and displacement, with the caution that the integration can give unreliable answers and that its drift is to be removed without a phase lag.
Classes:
| Name | Description |
|---|---|
FitCase |
The record worked at one cutoff, or as logged. |
ReplicationFit |
One channel of a reference record checked against a shaker. |
Functions:
| Name | Description |
|---|---|
replication_fit |
Check one channel of a reference record against a shaker. |
taper |
A window of |
Classes¶
FitCase
dataclass
¶
FitCase(cutoff: float | None, rating: ShakerRating, start: float, sample_rate: float, velocity: ndarray, displacement: ndarray, mean_square_removed: float, peak_change: float, peak_acceleration: float)
The record worked at one cutoff, or as logged.
The arrays are the table's velocity and displacement, in m/s and m,
from rest at the record's first sample. cutoff is the high-pass in
Hz, None for the record integrated as logged. mean_square_removed
and peak_change are the price of the cutoff, as shares of the
tapered record with its offset out; both are zero as logged, where
nothing was edited. The travel, the velocity and the force are
judged by core.shaker.ShakerRating's rule.
Methods:
| Name | Description |
|---|---|
first_past_stroke |
When the travel first leaves the room either way, in seconds, |
name |
'as logged', or the cutoff such as '3 Hz'. |
shortfalls |
Each rating this case is over, in words, such as 'the |
describe |
The verdict in words, as shares of the ratings, as the view |
Attributes:
| Name | Type | Description |
|---|---|---|
time |
ndarray
|
Each sample's time on the record's clock, in seconds. |
travel |
ndarray
|
The armature's travel against the body, in m: the table's |
travel_up |
float
|
The largest travel above the middle, in m; zero if it never |
travel_down |
float
|
The largest travel below the middle, in m, as a positive |
peak_to_peak |
float
|
The travel from its lowest to its highest, in m. |
excursion_time |
float
|
When the largest travel either way happens, in seconds. |
peak_velocity |
float
|
The table's largest velocity either way, in m/s. |
relative_velocity |
float
|
The armature's largest velocity against the body, in m/s: |
up_share |
float
|
The travel up as a share of the room, half the stroke. |
down_share |
float
|
The travel down as a share of the room, half the stroke. |
stroke_share |
float
|
The larger of |
velocity_share |
float
|
The peak velocity, counted against the body, as a share of |
force |
float
|
The moving mass times |
force_share |
float
|
The force as a share of the rating. |
fits_stroke |
bool
|
Whether the travel stays inside the room both ways. |
fits_velocity |
bool
|
Whether the peak velocity is inside the rating. |
fits_force |
bool
|
Whether the force at this cutoff's largest peak is inside the |
fits |
bool
|
Whether the record fits the stroke, the velocity and the |
Attributes¶
travel
property
¶
The armature's travel against the body, in m: the table's displacement times one plus the body's share.
travel_up
property
¶
The largest travel above the middle, in m; zero if it never rises.
travel_down
property
¶
The largest travel below the middle, in m, as a positive number; zero if it never falls.
excursion_time
property
¶
When the largest travel either way happens, in seconds.
relative_velocity
property
¶
The armature's largest velocity against the body, in m/s:
peak_velocity with the body's share counted, what the velocity
rating is judged against.
stroke_share
property
¶
The larger of up_share and down_share: one is at the
stop.
velocity_share
property
¶
The peak velocity, counted against the body, as a share of the rating.
fits_force
property
¶
Whether the force at this cutoff's largest peak is inside the rating.
fits
property
¶
Whether the record fits the stroke, the velocity and the force at this cutoff.
Methods:¶
first_past_stroke
¶
When the travel first leaves the room either way, in seconds, or None when it never does.
Source code in src/visualdynamics/core/replication_fit.py
name
¶
shortfalls
¶
Each rating this case is over, in words, such as 'the travel up is 140 % of the room'; empty when it fits.
Source code in src/visualdynamics/core/replication_fit.py
describe
¶
The verdict in words, as shares of the ratings, as the view and the panel state it.
Source code in src/visualdynamics/core/replication_fit.py
ReplicationFit
dataclass
¶
ReplicationFit(dof: str, rating: ShakerRating, peak_acceleration: float, peak_time: float, rms: float, offset: float, duration: float, as_logged: FitCase, cases: tuple[FitCase, ...])
One channel of a reference record checked against a shaker.
as_logged is the record integrated with no cutoff, and cases
the record worked at each candidate cutoff, lowest first. The force,
the offset and its drift are read from the record as logged; each
case also judges the force at its own largest peak.
Methods:
| Name | Description |
|---|---|
case |
The case at |
describe |
The whole check in a few sentences: the force, the drift as |
Attributes:
| Name | Type | Description |
|---|---|---|
force |
float
|
The moving mass times the largest peak, in N. |
force_share |
float
|
The force at the largest peak as a share of the rating. |
fits_force |
bool
|
Whether the force at the largest peak is inside the rating. |
crest_factor |
float
|
The largest peak over the rms: well past three for a record |
offset_velocity |
float
|
What the offset alone integrates to by the end, in m/s. |
offset_displacement |
float
|
What the offset alone integrates to by the end, in m: half |
lowest_fitting |
FitCase | None
|
The lowest cutoff at which the record fits the stroke, the |
Attributes¶
crest_factor
property
¶
The largest peak over the rms: well past three for a record with impacts, which is why the force is read from the peak.
offset_velocity
property
¶
What the offset alone integrates to by the end, in m/s.
offset_displacement
property
¶
What the offset alone integrates to by the end, in m: half the offset times the square of the record's length.
lowest_fitting
property
¶
lowest_fitting: FitCase | None
The lowest cutoff at which the record fits the stroke, the velocity and the force, the one that takes least out of the record; None when none does.
Methods:¶
case
¶
case(cutoff: float | None) -> FitCase
The case at cutoff in Hz, or the record as logged for None.
Raises:
| Type | Description |
|---|---|
KeyError
|
If |
Source code in src/visualdynamics/core/replication_fit.py
describe
¶
The whole check in a few sentences: the force, the drift as logged, and the lowest cutoff that fits.
Source code in src/visualdynamics/core/replication_fit.py
Functions:¶
replication_fit
¶
replication_fit(history: TimeHistory, rating: ShakerRating, *, cutoffs: Sequence[float] = CUTOFFS, channel: str | int | None = None) -> ReplicationFit
Check one channel of a reference record against a shaker.
The force at the record's largest peak, the record integrated as logged, and the record worked at each candidate cutoff: its ends tapered over two periods of the cutoff, high-passed at zero phase, integrated raw to velocity, high-passed again, integrated raw to displacement (the module docstring says why each step). Each case carries the travel each way with the body counted, the peak velocity, the force at its own largest peak, the share of the mean square the cutoff removed, the change in the largest peak, and its verdict.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
history
|
TimeHistory
|
The reference record. |
required |
rating
|
ShakerRating
|
The shaker's ratings and what it carries; the force, stroke and velocity ratings and the moving mass are all needed. |
required |
cutoffs
|
sequence of float
|
The candidate high-pass cutoffs, in Hz; worked lowest first. |
`CUTOFFS`
|
channel
|
str or int
|
The acceleration channel, by DOF or record index; the record's
first acceleration channel by default. Only a channel whose unit
is declared counts ( |
None
|
Returns:
| Type | Description |
|---|---|
ReplicationFit
|
The check, with a |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the record holds no acceleration channel, |
Source code in src/visualdynamics/core/replication_fit.py
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 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 | |
taper
¶
A window of samples that rises from zero over length samples
by a half-cosine at each end and is one between; each end is at
most half the window, where the two meet as a Hann window.