visualdynamics.core.overload¶
overload
¶
Each channel of a record checked against its range.
Every input measures over a window, its range, and a signal larger than the window is clipped: each sample beyond the edge is written as the edge, and the tops of the waveform come out flat. The averaged spectrum does not show it. Clipping takes a little energy off the few peaks that cross the edge and spreads it across the band as harmonics, and over dozens of frames the result is a spectrum a fraction of a decibel low in band with a slightly raised floor above it. The kurtosis barely moves either: a Gaussian record clipped at 2.8 standard deviations reads about 2.86, inside the kurtosis bars' band of 2 to 4. The overload is in the time history and nowhere else, so this reads it there, channel by channel, against each channel's own range.
The reading of a record against a range is core.time_statistics's,
and this does not read it again: the samples on the rail (within
RAIL_MARGIN of the range, judged against RAIL_SAMPLES), the crest
factor, the peak and the kurtosis are that reading's, over the same span
the kurtosis bars read (core.kurtosis.analyzed_span). What this adds
is what a range makes of them:
- The range itself, read from the channel table for every channel at once, where the Statistics reading takes one range typed for one channel.
- The samples on the rail as a rate per second, beside the count and the share.
- The headroom of the range over the largest excursion, in dB: 20 log10(range / peak). At or below 0 dB the range was reached.
- The largest excursion scaled to the next level of the test, a stated step in dB up (6 dB, about twice the amplitude, by default), and its headroom there. A test is run up in steps, and a range that holds at one level is checked against the next level's peak before the step, not found wanting after it. The scaled peak is a starting point: structures at full level often do not behave as they did at the low level, so it is never a ceiling.
- Samples beyond the range: further out than the range and its margin. A converter cannot write a sample past its own range, so a record that holds one was not taken through the range the table states: the ranges or the Sensitivity in the table are not the ones the record was made with, and every other number here is against the wrong range.
The range comes from the channel table. The level a channel clips at is
ChannelTable.clip_ranges(), in volts: the smaller of its DAQ Range
(the input's range) and its Sensor Range (the sensor's own full-scale
output), whichever is stated. Its Sensitivity column is the chain's
sensitivity in mV per the unit beside it, so the range in that unit is
volts * 1000 / sensitivity, and in SI it is that times the unit's
scale. table_full_scales reads it for each channel of a time history
by the same match the role pairing uses (ChannelTable.identities). A
channel the table does not name, or names with no sensitivity, or in a
unit of some other quantity, has no range, and says why rather than
being judged against a guess. Two rows naming one channel give it a
range only when they agree, and say so. A range is a size either way,
so a unit with an offset (a temperature) converts by its scale alone. A
row that states neither range gives its channel none, and the channel
says so rather than being read against a range nobody stated.
overload_check reads every channel of a TimeHistory (also
TimeHistory.overload_check), channel_overload one plain array; each
channel's result is a ChannelOverload, which carries its
TimeStatistics, and the whole an OverloadCheck.
What is judged: the samples beyond the range (none), the samples on the rail (the time statistics' own judgment of them), and the peak scaled to the next level against the range. No headroom is asked for beyond that: how much a range should carry over its predicted peak is a choice a test procedure makes, and the procedures put no single number on it, so the headroom is reported and the step is set where the next level is.
References
What a channel is ranged against and what clipping costs are set out in the test standard this check follows.
- Department of Defense (2019). MIL-STD-810H, Environmental Engineering Considerations and Laboratory Tests. Method 517.3, paragraph 4.4.1.1c: a channel ranged for the peak it is expected to see, and a clipped signal from which no data can be recovered. Method 516.8, paragraph 4.5: clipping found in a record, and a record that shows it set aside.
Classes:
| Name | Description |
|---|---|
TableRanges |
Each channel's range as a channel table gives it. |
ChannelOverload |
One channel's record against its range. |
OverloadCheck |
Every channel read against its range, and the judgment. |
Functions:
| Name | Description |
|---|---|
range_in_unit |
The range in the channel's own unit: a range in volts over a |
table_full_scales |
Each channel's range in SI from a channel table, and why where |
channel_overload |
Read one record against its range. |
overload_check |
Read each channel of a time history against its range. |
headroom_rows |
[(label, headroom in dB)] for each channel, NaN where there is |
notes_for |
{row: why} for each channel with no range: the sentence the |
Classes¶
TableRanges
¶
Bases: NamedTuple
Each channel's range as a channel table gives it.
One entry per channel of the history, in order: full_scales in
SI, NaN where there is none; reasons, why there is none, '' where
the range was read; and notes, anything else to say about the
rows the range came from, ''.
ChannelOverload
dataclass
¶
ChannelOverload(record: int, label: str, full_scale: float, statistics: TimeStatistics | None, step_db: float = STEP_DB, reason: str = '', beyond: int = 0, note: str = '')
One channel's record against its range.
full_scale and the values read from statistics are in the
record's own units (SI for a channel of a TimeHistory). Without a
range, full_scale is NaN, reason says why, and nothing is
judged; without a reading at all (a channel with fewer than two
finite samples), statistics is None as well.
Methods:
| Name | Description |
|---|---|
flags |
Each judged item, by |
describe |
The channel's judgment in words. |
cautions |
What to doubt about the reading, in words: samples left out |
Attributes:
| Name | Type | Description |
|---|---|---|
has_range |
bool
|
Whether the channel was read against a range. |
peak |
float
|
The largest excursion either way, a size: NaN without a |
rms |
float
|
The rms over the span read; NaN without a reading. |
crest |
float
|
The crest factor: the largest excursion over the rms. |
rail |
int | None
|
The samples on the rail; None without a range. |
rail_share |
float | None
|
The samples on the rail as a share of those read; None |
rail_rate |
float | None
|
The samples on the rail per second of the span read; None |
headroom_db |
float
|
The range over the largest excursion in dB, 20 log10 of the |
next_peak |
float
|
The largest excursion scaled up by |
next_headroom_db |
float
|
The headroom at the next level, in dB: |
flagged |
list[str]
|
The keys flagged, in |
Attributes¶
rail_share
property
¶
The samples on the rail as a share of those read; None without a range.
rail_rate
property
¶
The samples on the rail per second of the span read; None without a range.
headroom_db
property
¶
The range over the largest excursion in dB, 20 log10 of the
time statistics' headroom ratio. At or below 0 the range was
reached; infinite for a record that never leaves zero; NaN
without a range.
next_peak
property
¶
The largest excursion scaled up by step_db, a size: what
the next level would put on the channel if the structure
behaves as it did at this one.
next_headroom_db
property
¶
The headroom at the next level, in dB: headroom_db less the
step.
Methods:¶
flags
¶
Each judged item, by OVERLOADS key, and whether it is
flagged; empty for a channel with no range. The rail is the time
statistics' own judgment (TimeStatistics.flags). The next
level is judged only where the range has not already been
reached, so a clipped channel is flagged for its clip and not
again for a scaled peak that predicts nothing.
Source code in src/visualdynamics/core/overload.py
describe
¶
The channel's judgment in words.
scale divides the values (SI for a channel of a time history)
into unit for the words, as a unit system's transform scale
does: 9.80665 and 'g' state an acceleration in g.
Source code in src/visualdynamics/core/overload.py
cautions
¶
What to doubt about the reading, in words: samples left out because they are not finite, and any note on the rows the range came from. Empty when there is nothing.
Source code in src/visualdynamics/core/overload.py
OverloadCheck
dataclass
¶
OverloadCheck(channels: tuple[ChannelOverload, ...], step_db: float = STEP_DB, margin: float = RAIL_MARGIN)
Every channel read against its range, and the judgment.
Methods:
| Name | Description |
|---|---|
with_step |
This check with the step to the next level at |
with_flag |
The channels flagged for |
describe |
The judgment in words: which channels are on the rail, |
Attributes:
| Name | Type | Description |
|---|---|---|
checked |
list[ChannelOverload]
|
The channels read against a range. |
unranged |
list[ChannelOverload]
|
The channels with no range, or not read at all, each saying |
unread |
list[ChannelOverload]
|
The channels not read at all (a dead channel, or one with too |
flagged |
list[ChannelOverload]
|
The channels flagged for anything. |
worst |
ChannelOverload | None
|
The channel that most wants looking at: the most samples on |
Attributes¶
unranged
property
¶
unranged: list[ChannelOverload]
The channels with no range, or not read at all, each saying why.
unread
property
¶
unread: list[ChannelOverload]
The channels not read at all (a dead channel, or one with too few finite samples), each saying why.
worst
property
¶
worst: ChannelOverload | None
The channel that most wants looking at: the most samples on the rail, then the least headroom at the next level. None when no channel has a range.
Methods:¶
with_step
¶
with_step(step_db: float) -> OverloadCheck
This check with the step to the next level at step_db, the
readings kept: nothing read from the record depends on the
step, so dragging the headroom floor does not read every
channel's statistics again (2.2 s for eight channels of a
five-minute record, on every drag).
Source code in src/visualdynamics/core/overload.py
with_flag
¶
with_flag(key: str) -> list[ChannelOverload]
The channels flagged for key (an OVERLOADS key).
Source code in src/visualdynamics/core/overload.py
describe
¶
The judgment in words: which channels are on the rail, which reach their range at the next level, which hold samples beyond it, and how many had no range.
Source code in src/visualdynamics/core/overload.py
Functions:¶
range_in_unit
¶
The range in the channel's own unit: a range in volts over a
sensitivity in mV per unit, volts * 1000 / sensitivity.
A 10 V input on a 100 mV/g chain reads to 100 g. NaN when the sensitivity is not a positive number, since there is then no range to speak of.
Source code in src/visualdynamics/core/overload.py
table_full_scales
¶
table_full_scales(table: ChannelTable, history: TimeHistory) -> TableRanges
Each channel's range in SI from a channel table, and why where there is none.
The table's row for a channel is the one that names it
(ChannelTable.identities): its DOF and its channel type, or its
DOF alone when the type is blank and the history has one channel
there. The range is the level the row's channel clips at
(ChannelTable.clip_ranges, the smaller of its DAQ Range and its
Sensor Range that are stated) in volts over its Sensitivity in mV
per the row's unit (range_in_unit), put into SI by the unit's
scale. A row that states neither range gives none, and the reason
says so. Where two or more rows name one channel, it has a range
only when they all give the same one, and a note says which rows;
where they differ it has none, and the reason says so.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
ChannelTable
|
The channel table describing the recording. |
required |
history
|
TimeHistory
|
The recording. |
required |
Returns:
| Type | Description |
|---|---|
TableRanges
|
Per channel of |
Source code in src/visualdynamics/core/overload.py
channel_overload
¶
channel_overload(values: ArrayLike, sample_rate: float, full_scale: float | None, *, step_db: float = STEP_DB, margin: float = RAIL_MARGIN, rail_samples: int = RAIL_SAMPLES, label: str = '', record: int = 0, start: float = 0.0, spans: Sequence[tuple[int, int]] | None = None) -> ChannelOverload
Read one record against its range.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
values
|
array_like
|
The record, one channel, evenly sampled. |
required |
sample_rate
|
float
|
Samples per second. |
required |
full_scale
|
float or None
|
The range either way, in the units of |
required |
step_db
|
float
|
The step to the next level, in dB. |
`STEP_DB`
|
margin
|
float
|
How close to the range a sample counts as on it, as a share. |
`core.time_statistics.RAIL_MARGIN`
|
rail_samples
|
int
|
The most samples allowed on the rail. |
`core.time_statistics.RAIL_SAMPLES`
|
label
|
str
|
What the channel is called in the words. |
''
|
record
|
int
|
The channel's index, carried for the caller. |
0
|
start
|
float
|
As |
0.0
|
spans
|
float
|
As |
0.0
|
Returns:
| Type | Description |
|---|---|
ChannelOverload
|
The numbers and the flags. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the range is not above zero, or as
|
Source code in src/visualdynamics/core/overload.py
overload_check
¶
overload_check(history: TimeHistory, ranges: ChannelTable | ArrayLike | float, *, records: Sequence[int] | None = None, step_db: float = STEP_DB, margin: float = RAIL_MARGIN, rail_samples: int = RAIL_SAMPLES) -> OverloadCheck
Read each channel of a time history against its range.
Over the span core.kurtosis.analyzed_span names, as the kurtosis
bars and the Statistics reading read it, so a channel's rail count
here is that reading's for the same range and margin.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
history
|
TimeHistory
|
The record. |
required |
ranges
|
(ChannelTable, array_like or float)
|
Where the ranges come from: the channel table describing the
record ( |
required |
records
|
sequence of int
|
Which channels, counting from 0; every channel by default. |
None
|
step_db
|
float
|
The step to the next level, in dB. |
`STEP_DB`
|
margin
|
float
|
How close to the range a sample counts as on it, as a share. |
`core.time_statistics.RAIL_MARGIN`
|
rail_samples
|
int
|
The most samples allowed on the rail. |
`core.time_statistics.RAIL_SAMPLES`
|
Returns:
| Type | Description |
|---|---|
OverloadCheck
|
Every channel's numbers and flags, and the judgment. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If a channel named is not in the record, none is named, the ranges given are not one per channel, a range is zero or negative, the step is not finite, or the margin is not a share. |
Source code in src/visualdynamics/core/overload.py
626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 | |
headroom_rows
¶
headroom_rows(check: OverloadCheck) -> list[tuple[str, float]]
[(label, headroom in dB)] for each channel, NaN where there is no range: the rows the headroom bars draw.
Source code in src/visualdynamics/core/overload.py
notes_for
¶
notes_for(check: OverloadCheck) -> dict[int, str]
{row: why} for each channel with no range: the sentence the headroom bars write along its row in place of a bar.