visualdynamics.core.sweep_rate¶
sweep_rate
¶
Whether a sine sweep is slow enough for the modes it passes.
A mode needs about Q cycles at its own frequency to build to its full
response. A sweep does not hold it there: it gives the mode a short time
inside its half-power band (the band fn/Q wide around fn, where
the response is within 3 dB of its peak) and moves on. When that time
holds fewer than about Q of the mode's cycles, the swept response reads
the peak low, places it late in the direction of the sweep, and spreads
it, so a Q read from its half-power width comes out low as well. All
three at once, and all three are properties of the rate, not of the
article.
The arithmetic is short. On a logarithmic sweep at R octaves per
minute the band, 1/(Q ln 2) octaves wide, is crossed in
60 / (ln 2 Q R) seconds, about 86.6 / (Q R), whatever the mode's
frequency; the mode completes fn times as many cycles in that time.
Setting those cycles equal to the Q the mode needs gives the rate at
which a sweep only just lets it build:
R = (60 / ln 2) fn / Q**2, about 87 fn / Q**2 oct/min.
A linear sweep at r Hz/s crosses the band, fn/Q Hz wide, in
fn / (Q r) seconds, and the same equation gives r = fn**2 / Q**2
Hz/s. At the rule's rate a mode still reads only about 94 % of its
steady height, and the Q read from its width comes out about 20 % low,
so the rule is a ceiling to stay well under, not a target. That is the
reason the rule here flags a rate above it rather than near it, and the
reason each check also reports what a mode of that Q would actually
read at that rate.
The rule is this package's criterion, worked out as above from the cycles a mode needs to build. It is not taken from a published standard; the references below are for the effect it guards against and for the simulation, not for the rule.
What a mode reads at a rate is found by simulating it: the one-mass
resonator (srs.ramp_invariant, the shock spectrum's filter) driven
through the sweep at the segment's rate and law, its response read once
per cycle of the drive as that cycle's rms reported as the peak of a
sine (the rms detector a controller uses), and the peak and the
half-power width of that swept curve read the way they would be read
off a plot (half_power.read_half_power). The simulation is made at a
natural frequency of 1 Hz, since a sweep's effect on a mode depends on the rate only through
R / fn (log) or r / fn**2 (linear), so its cost does not grow
with the mode's frequency.
check_sweep_rate takes a SineSweepSpecification (or one SineTone)
and a list of modes, each a natural frequency and a Q, and returns one
SweepRateCheck per pass of a tone through a mode's band: the time and
the cycles inside the band, the rule's rate for that mode, the rate as
a multiple of it, the swept reading, and whether it is too fast. A tone
swept up and back passes each mode twice and gives two rows. A mode
whose band no tone reaches gives none.
swept_q and read_swept_q answer the reverse question, for a Q read
off a level that was itself swept (a tracked sine level, the usual
source of a Q in a sine test): whether the rate was slow enough for the
Q it reads, and what Q a mode would need to read that low at that rate.
References
The effect is a standard result of the response of a linear oscillator
to a tone of changing frequency, which the review below credits to
Lewis (1932) and Cronin (1965); this is where it is set out. The
one-mass simulation runs through core.srs's filter, which cites its
source.
- Reed, C. C., & Kabe, A. M. (2019). "Peak Response of Single-Degree-of-Freedom Systems to Swept-Frequency Excitation." The Mathematica Journal. The response of a one-mass system to linear and octave sweeps in closed form: the faster the sweep, the lower its peak and the further the peak moves in the sweep's direction.
Classes:
| Name | Description |
|---|---|
SweptReading |
What a sweep at one rate makes of one mode: |
SweepRateCheck |
One pass of one tone through one mode's half-power band: |
SweptQ |
A Q read from a swept level, judged against the rate it was |
Functions:
| Name | Description |
|---|---|
half_power_band |
The mode's half-power band, |
rule_rate |
The rule's rate for a mode: the sweep rate at which the time in |
rate_unit |
'oct/min' for a logarithmic segment, 'Hz/s' for a linear one: the |
simulate_sweep |
What a mode reads when a sweep passes it at |
check_sweep_rate |
Check a sine specification's sweep rate against a list of modes. |
swept_q |
Judge a Q read from a level swept at |
read_swept_q |
Read the peak nearest |
Classes¶
SweptReading
dataclass
¶
SweptReading(frequency: float, q: float, peak_frequency: float, peak_share: float, q_read: float | None, simulated: bool = True)
What a sweep at one rate makes of one mode: simulate_sweep's
result.
peak_frequency is where the swept peak sits, Hz; peak_share is
its height over the mode's steady height at fn,
Q sqrt(1 + 1/Q**2) (NaN where the sweep passed the mode in too
few cycles to read a peak at all); q_read is the peak frequency over the
swept curve's half-power width, None where the curve did not fall
to 0.707 of its peak inside the simulated stretch. simulated is
False where the rate was under STEADY_RATIO of the rule's and the
steady curve, the transmissibility, was read the same way instead.
Attributes:
| Name | Type | Description |
|---|---|---|
under_read |
float
|
How far the swept peak is under the steady one, a fraction: |
q_bias |
float | None
|
How far the Q read is from the mode's, a fraction: -0.46 for |
SweepRateCheck
dataclass
¶
SweepRateCheck(tone: str, frequency: float, q: float, segment_type: int, rate: float, start: float, seconds: float, cycles: float, crossed: bool, limit: float, reading: SweptReading, margin: float = MARGIN)
One pass of one tone through one mode's half-power band:
check_sweep_rate's row.
rate is the specification's rate on the pass's fastest segment
(the largest multiple of the rule's), as written (oct/min for a log
segment, Hz/s for a linear one, negative
sweeping down); limit is the rule's rate for the mode in the same
unit. start is when the sweep enters the band, in seconds from
the tone's start; seconds and cycles are how long it stays and
how many of the drive's cycles that is. crossed is False where the
sweep starts, ends or turns inside the band, so the time counted is
not a full crossing. reading is what a mode of this Q reads at
this rate (simulate_sweep).
Methods:
| Name | Description |
|---|---|
describe |
The check in a line, as the window's status bar gives it: |
Attributes:
| Name | Type | Description |
|---|---|---|
unit |
str
|
'oct/min' or 'Hz/s', the unit of |
direction |
str
|
'up' or 'down'. |
ratio |
float
|
The rate as a multiple of the rule's rate for this mode. |
too_fast |
bool
|
Whether the rate is above |
peak_share |
float
|
The swept peak's height over the steady height. |
q_read |
float | None
|
The Q a swept reading of this mode gives at this rate. |
verdict |
str
|
'too fast' or 'builds', the word the table and the plot use. |
Attributes¶
Methods:¶
describe
¶
The check in a line, as the window's status bar gives it: '22 Hz, Q 40, up at 4 oct/min: 0.54 s and 12 cycles in the band (40 to build), 3.3 times the rule's 1.2 oct/min; reads 81 % of the peak and a Q of 21.6; too fast'.
Source code in src/visualdynamics/core/sweep_rate.py
SweptQ
dataclass
¶
SweptQ(frequency: float, q_read: float, rate: float, segment_type: int, q_true: float | None, margin: float = MARGIN, height: float | None = None)
A Q read from a swept level, judged against the rate it was
swept at: what swept_q and read_swept_q return.
frequency is the peak's, Hz; q_read the Q read from its width;
rate and segment_type the sweep's at the peak. q_true is the
Q a mode would need to read q_read at this rate, by inverting
simulate_sweep; None where no Q up to Q_SEARCH times the one
read reads it, which happens when the rate alone sets the width. height is the peak's height in the level's own units
where it was read from a level, else None.
Methods:
| Name | Description |
|---|---|
describe |
The judgment in a line: 'Q 21.6 read at 22.6 Hz from a sweep |
Attributes:
| Name | Type | Description |
|---|---|---|
unit |
str
|
'oct/min' or 'Hz/s'. |
limit |
float
|
The rule's rate for the Q the reading implies ( |
ratio |
float
|
The sweep's rate as a multiple of |
flagged |
bool
|
Whether the Q read cannot be taken as the mode's: no Q could |
Attributes¶
limit
property
¶
The rule's rate for the Q the reading implies (q_true), or
for the Q read where no Q could be inferred.
flagged
property
¶
Whether the Q read cannot be taken as the mode's: no Q could
be inferred at all, the sweep was too fast for the Q it implies,
or the Q it implies is more than Q_TOLERANCE above the one
read. The last catches a sweep under the rule's rate that still
spreads the width: at the rule's rate a peak reads about 94 % of
its height but its Q reads about 20 % low.
Methods:¶
describe
¶
The judgment in a line: 'Q 21.6 read at 22.6 Hz from a sweep at 4 oct/min: reads low; a mode reads this low at a Q of about 40, for which the rate is 3.4 times the rule's'.
Source code in src/visualdynamics/core/sweep_rate.py
Functions:¶
half_power_band
¶
The mode's half-power band, fn (1 - 1/(2Q)) to
fn (1 + 1/(2Q)) in Hz: fn/Q wide, centered on the mode.
The band the sweep's time and cycles are counted in. It is the width the mode's Q defines, not the crossings of any measured curve, which a fast sweep moves.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
frequency
|
float
|
The natural frequency, Hz. |
required |
q
|
float
|
The mode's Q, |
required |
Returns:
| Type | Description |
|---|---|
tuple of float
|
(lower, upper) in Hz. |
Source code in src/visualdynamics/core/sweep_rate.py
rule_rate
¶
The rule's rate for a mode: the sweep rate at which the time in its half-power band holds just the Q cycles it needs to build.
(60 / ln 2) fn / Q**2 oct/min for a logarithmic sweep (about
87 fn / Q**2), and fn**2 / Q**2 Hz/s for a linear one. This
is the package's own criterion (see the module's notes). A sweep
should run well below it: at this rate a mode reads about 94 % of
its steady height and a Q about 20 % low.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
frequency
|
float
|
The natural frequency, Hz. |
required |
q
|
float
|
The mode's Q. |
required |
segment_type
|
int
|
|
LOG
|
Returns:
| Type | Description |
|---|---|
float
|
oct/min for LOG, Hz/s for LINEAR; always positive. |
Source code in src/visualdynamics/core/sweep_rate.py
rate_unit
¶
'oct/min' for a logarithmic segment, 'Hz/s' for a linear one: the units the specification writes each rate in.
simulate_sweep
¶
simulate_sweep(frequency: float, q: float, rate: float, segment_type: int = LOG) -> SweptReading
What a mode reads when a sweep passes it at rate, by
simulation of the one-mass resonator.
The resonator at frequency with q is driven from rest by a unit
tone swept through its band at the constant rate and law
(segment_type); a negative rate sweeps down. Its response is read
once per cycle of the drive, each cycle's rms reported as the peak
of a sine, and the swept curve's peak and half-power width are read
from those readings. A 22 Hz, Q 40 mode swept up at 1, 4 and
8 oct/min reads 95, 81 and 71 % of its steady height, with a Q of
about 33, 22 and 17.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
frequency
|
float
|
The natural frequency, Hz. |
required |
q
|
float
|
The mode's Q. |
required |
rate
|
float
|
oct/min for a LOG sweep, Hz/s for a LINEAR one; negative sweeps down. |
required |
segment_type
|
int
|
|
LOG
|
Returns:
| Type | Description |
|---|---|
SweptReading
|
The peak's frequency, its height as a share of the steady height, and the Q read from its width. |
Source code in src/visualdynamics/core/sweep_rate.py
check_sweep_rate
¶
check_sweep_rate(specification: SineSweepSpecification | SineTone, modes: Iterable[tuple[float, float]] | Any, *, tone: str | None = None, margin: float = MARGIN) -> list[SweepRateCheck]
Check a sine specification's sweep rate against a list of modes.
For every tone (or the one named) and every mode, each pass of the
sweep through the mode's half-power band (half_power_band) gives
a row: when it enters, how long it stays, how many of the drive's
cycles that is against the Q the mode needs, the rule's rate for
the mode (rule_rate) and the specification's as a multiple of it,
what a mode of that Q reads at that rate (simulate_sweep), and
whether the rate is too fast. A band that straddles a breakpoint is
one pass, counted across both segments, and judged on the faster of
them: the one whose rate is the larger multiple of the rule's.
checks = check_sweep_rate(spec, [(22.0, 40.0), (180.0, 20.0)])
for check in checks:
print(check.describe())
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
specification
|
SineSweepSpecification or SineTone
|
The sweep. |
required |
modes
|
iterable of (frequency, Q), or ShapeSet
|
Natural frequencies in Hz with their Q; a |
required |
tone
|
str
|
Check only the tone of this name. |
None
|
margin
|
float
|
The multiple of the rule's rate above which a pass is flagged too fast; 1 by default, where a mode reads about 94 %. |
MARGIN
|
Returns:
| Type | Description |
|---|---|
list of SweepRateCheck
|
In tone order, then mode order, then pass order. A mode whose band the sweep never reaches has no row. |
Source code in src/visualdynamics/core/sweep_rate.py
swept_q
¶
swept_q(frequency: float, q_read: float, rate: float, segment_type: int = LOG, *, margin: float = MARGIN, height: float | None = None) -> SweptQ
Judge a Q read from a level swept at rate: was the rate slow
enough for it, and what Q would read that low?
A swept reading's Q comes out low (high only by the fraction of a
percent its per-cycle readings scatter by), so the Q read is in
effect a lower bound on the mode's and the rule's rate worked out
from it an upper bound. The Q a mode needs to read q_read at this
rate is found on simulate_sweep: the swept Q rises with the true Q
towards a ceiling the rate sets, so past that ceiling no Q reads
that low and none is returned. The reading is flagged when the rate
is above margin times the rule's rate for that Q, or when that Q
is more than Q_TOLERANCE above the one read.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
frequency
|
float
|
The peak's frequency, Hz. |
required |
q_read
|
float
|
The Q read from the swept level's half-power width. |
required |
rate
|
float
|
The sweep's rate at the peak, oct/min (LOG) or Hz/s (LINEAR). |
required |
segment_type
|
int
|
|
LOG
|
margin
|
float
|
As for |
MARGIN
|
height
|
float
|
The peak's height, carried through for display. |
None
|
Returns:
| Type | Description |
|---|---|
SweptQ
|
The reading, the Q it implies (None where none can be inferred), and the flag. |
Source code in src/visualdynamics/core/sweep_rate.py
718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 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 | |
read_swept_q
¶
read_swept_q(level: Any, specification: SineSweepSpecification | SineTone, near: float, *, record: int = 0, within: float = 0.2, margin: float = MARGIN) -> SweptQ | None
Read the peak nearest near on a tracked sine level and judge its
Q against the rate the level was swept at.
The level's readings are put back in sweep order by their clock
(level.seconds), and lines under the noise floor (level.resolved)
are not read. The peak is the tallest reading within within (a
fraction) of near, read on the one pass of the sweep that holds
it (half_power.read_half_power): its top placed between readings
and its half-power crossings found by walking out along that pass.
The rate is the one on the segment the tone was sweeping when that
reading was made. Returns None where the window holds no peak (no
readings, or the tallest at its edge, the flank of a peak
elsewhere) or a crossing is missing, since then there is no Q to
judge; and for a level kept without its clock, read off a tone that
sweeps both ways, whose two passes cannot be told apart.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
level
|
SineLevel
|
A tracked or extracted level. |
required |
specification
|
SineSweepSpecification or SineTone
|
The sweep the level was read from. A specification's tone is
the level's own ( |
required |
near
|
float
|
Hz, the mode looked for. |
required |
record
|
int
|
The level's record (channel) to read. |
0
|
within
|
float
|
The window around |
0.2
|
margin
|
float
|
As for |
MARGIN
|
Returns:
| Type | Description |
|---|---|
SweptQ or None
|
The judgment, with the peak's height in the level's units. |
Source code in src/visualdynamics/core/sweep_rate.py
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 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 | |