visualdynamics.core.sine_frequency¶
sine_frequency
¶
The sine frequency check: the tone's frequency on the channel against the frequency the profile asks for, along the sweep.
A sine test is held to its frequency as well as its level. Test
documents write the band as a share of the frequency; MIL-STD-810H holds
it to plus or minus 0.1 percent. The default here is 2 percent
(FREQUENCY_TOLERANCE), for the governing document's own figure to
replace, and the band can be given a floor in hertz (floor) for a
document that writes one. At a sharp resonance the frequency band is also
a level band: a tone held 2 percent off a mode with a Q of 40 drives it
to about half the response it would at the center, since the one-mass
response falls as 1 / sqrt(1 + (2 Q d)**2) for a fractional offset d.
The check reads the tone's frequency off the record. The tones the
extraction tracked are rebuilt on the record's own samples by the
estimator the extraction uses (core.sine.vold_kalman), each a complex
envelope riding on its sweep's argument, the phase the specification
gives at each sample. A tone off its specified frequency turns its
envelope: the envelope's phase gains 2 pi times the offset every second.
So the tone's own phase on the record is its argument plus its
envelope's unwrapped phase, and its frequency over a span is that phase's
advance over the span divided by 2 pi times the span's length. With
psi the tone's phase and phi the specification's,
f_tone = (psi(t1) - psi(t0)) / (2 pi (t1 - t0)),
f_spec = (phi(t1) - phi(t0)) / (2 pi (t1 - t0)),
read over cycles cycles of the tone centered on each line of the
extraction (its smoothing by default), the same spans the distortion
check reads (core.sine_distortion). The error is f_tone - f_spec, in
hertz and as a share of f_spec, and a line is flagged where it is wider
than the band: the tolerance times f_spec, or the floor if that is
wider. Before the phase is read each envelope's slow turning is fitted
and taken into its argument and the tone solved again, as the extraction
corrects a drifted sweep clock, so a tone well off its frequency is held
inside the estimator's band rather than read through its skirt; the
frequency read is the same either way, since the correction is added to
the argument and taken out of the envelope. On a linear sweep or a dwell
the turning is fitted as a quadratic in time. On any other sweep, a log
sweep among them, it is fitted on the sweep's own argument and angular
frequency, which is how a tone run at a share off its schedule, or late
on it, turns its envelope whatever the law.
What the check reads, and what it does not:
- The specified frequency is the specification's, on the clock the
extraction found the sweep on (
SineLevel.onset). A sweep is a schedule of frequency against time, so a sweep run at the wrong rate arrives at each frequency early or late, and the check reads that as a frequency error that grows along the sweep. A rate within its own tolerance can carry a frequency outside a tight band by the end of a long sweep; the sweep's rate is read bycore.sweep_rate, and a dwell, held at one frequency, is where the frequency band is read most directly. - The record's own phase is read, and a channel's phase moves when the sweep passes a resonance of what it sits on: through a mode of quality factor Q at natural frequency f_n, swept at r Hz/s, the phase turns at up to 2 Q r / f_n radians per second, which reads as an offset of Q r / (pi f_n) Hz. At 84 Hz, a Q of 21 and 2 oct/min (1.9 Hz/s) that is 0.15 Hz, 0.18 percent. A control channel rarely sits on a sharp mode, and where it does the offset is real: it is the frequency the channel carried.
- A line under the noise floor (
SineLevel.below_floor), a line whose span reaches a tone of the specification the levels do not hold, a line with no tone on the channel (a dead channel) and every line of a channel whose stretch holds a sample that is not a finite number are not judged; the notes count them and a channel with nothing judged does not pass.
References
The estimator of the tone and the frequency band it is judged against are set out in these.
- Brandt, A. (2011). Noise and Vibration Analysis: Signal Analysis and Experimental Procedures. Wiley. Order tracking, the Vold-Kalman filter among it: a component on a known frequency history read as a slowly varying complex amplitude by least squares, whose phase carries the component's departure from that history.
- Department of Defense (2019). MIL-STD-810H, Environmental Engineering Considerations and Laboratory Tests. Method 514.8, Table 514.8-III: the frequency of a sinusoidal test held to plus or minus 0.1 percent.
Classes:
| Name | Description |
|---|---|
FrequencyCheck |
One tone's frequency along its sweep, on each channel read: what |
Functions:
| Name | Description |
|---|---|
allowed |
The frequency band at each specified frequency, Hz either side: |
check_frequency |
Check one tone's frequency along its sweep: the tone's own |
Classes¶
FrequencyCheck
dataclass
¶
FrequencyCheck(*, tone: str, dofs: list[str], frequency: ndarray, seconds: ndarray, specified: ndarray, measured: ndarray, judged: ndarray, tolerance: float, floor: float, cycles: float, notes: tuple[str, ...] = ())
One tone's frequency along its sweep, on each channel read: what
check_frequency returns.
frequency and seconds are the extraction's lines, ascending in
frequency, each with the second it was read from the recording's
first sample. specified is the specification's frequency over each
line's span, Hz, and measured channels x lines of the tone's own
frequency over the same span, not a number where it could not be
read. judged is channels x lines, False where a line cannot be
judged (under the noise floor, beside a tone the levels do not hold,
no tone on the channel, or a sample that is not a finite number).
tolerance and floor set the band (allowed). notes says what
was not judged and why. Two of these compare equal only if they are
the same object, since the arrays have no single truth value.
Methods:
| Name | Description |
|---|---|
row |
The row of channel |
worst |
The judged line whose error takes the most of its band, on |
band_words |
The band in words: '±2 %' or '±2 % or 1 Hz, whichever is |
describe |
The verdict in one line, such as 'Up at 101Z+: worst +1.52 % |
Attributes:
| Name | Type | Description |
|---|---|---|
error |
ndarray
|
Channels x lines: the tone's frequency less the specified, |
relative |
ndarray
|
Channels x lines: the error as a share of the specified |
allowed |
ndarray
|
Lines: the band either side, Hz ( |
share |
ndarray
|
Channels x lines: the error as a share of the band, its size |
over |
ndarray
|
Channels x lines, True where a judged line's error is wider |
passed |
bool
|
Whether every channel read has a judged line and no judged |
Attributes¶
relative
property
¶
Channels x lines: the error as a share of the specified frequency (0.02 is 2 %).
share
property
¶
Channels x lines: the error as a share of the band, its size over the band's half-width; over one is outside.
over
property
¶
Channels x lines, True where a judged line's error is wider than the band.
passed
property
¶
Whether every channel read has a judged line and no judged line is outside the band. A channel with nothing judged has not passed: there is no reading to pass.
Methods:¶
row
¶
The row of channel dof; the first when None. Raises
ValueError when the check did not read it.
Source code in src/visualdynamics/core/sine_frequency.py
worst
¶
The judged line whose error takes the most of its band, on
channel dof or, when None, on any channel: (specified Hz, error
Hz, error as a share of the frequency), or None when no line was
judged.
Source code in src/visualdynamics/core/sine_frequency.py
band_words
¶
The band in words: '±2 %' or '±2 % or 1 Hz, whichever is greater'.
Source code in src/visualdynamics/core/sine_frequency.py
describe
¶
The verdict in one line, such as 'Up at 101Z+: worst +1.52 % (+1.52 Hz) at 100 Hz, 40 of 40 lines outside ±1.25 %'.
Source code in src/visualdynamics/core/sine_frequency.py
Functions:¶
allowed
¶
The frequency band at each specified frequency, Hz either side:
tolerance times the frequency, or floor where that is wider ('2 %
or 1 Hz, whichever is greater' is tolerance 0.02, floor 1.0).
Raises ValueError unless tolerance is a finite number more than
zero and floor one of zero or more.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
frequency
|
float or array - like
|
Hz. |
required |
tolerance
|
float
|
A share of the frequency. |
`FREQUENCY_TOLERANCE`
|
floor
|
float
|
Hz. |
0.0
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Hz, the shape of |
Source code in src/visualdynamics/core/sine_frequency.py
check_frequency
¶
check_frequency(history: Any, specification: SineSweepSpecification, levels: SineLevelSet | None = None, *, tone: str | None = None, channels: Sequence[str] | None = None, tolerance: float = FREQUENCY_TOLERANCE, floor: float = 0.0, cycles: float | None = None) -> FrequencyCheck
Check one tone's frequency along its sweep: the tone's own frequency on each channel against the specification's, at every line of the extraction.
The tones come from levels, a SineLevelSet that extract_sine
read from this recording against this specification; with none, the
extraction is run here with the recording's own sine_extraction
setting (or its suggestion), as the project verb runs it. Every tone
the levels hold is rebuilt on each channel's stretch by the joint
Vold-Kalman solve with the extraction's smoothing, each envelope's
slow turning fitted into its argument and the solve repeated, and the
checked tone's phase (argument plus envelope phase) is read over
cycles cycles about each line. A line is flagged where the error is
wider than the band (allowed).
Raises ValueError when tolerance is not more than zero, floor is
under zero, cycles is not more than zero, the recording is not
evenly sampled, the levels hold no such tone or channel, the
recording carries no record of a channel in the quantity its levels
were read in, the levels do not say when each line was read or were
read at times the recording does not hold, or the recording holds
less of the tone than one span.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
history
|
TimeHistory
|
The recording, evenly sampled. |
required |
specification
|
SineSweepSpecification
|
The sweep the drive followed. |
required |
levels
|
SineLevelSet
|
The extracted levels; extracted here when omitted. |
None
|
tone
|
str
|
Which tone, by name; the first the levels hold by default. |
None
|
channels
|
sequence of str
|
Which control channels; every channel the levels hold by default. |
None
|
tolerance
|
float
|
The band, a share of the specified frequency either side. |
`FREQUENCY_TOLERANCE`
|
floor
|
float
|
The band's least half-width, Hz. |
0.0
|
cycles
|
float
|
The span each frequency is read over, in cycles of the tone; the extraction's smoothing by default. |
None
|
Returns:
| Type | Description |
|---|---|
FrequencyCheck
|
The specified and measured frequency at every line on every channel read, which lines could be judged, and the band. |
Source code in src/visualdynamics/core/sine_frequency.py
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 | |