vyges-meas — closed measurement kernels
Part of the Vyges Loom suite. Install once with
vyges install loom, then runvyges loom meas. It’s also a standalonevyges-measbinary on your PATH (the integration contract for flow authors).
vyges-meas answers: what does this capture actually measure? Two families of scalars —
coherent single-tone SNR, SINAD, THD, SFDR from a captured time series, and gain,
bandwidth, unity-gain frequency, phase margin from an AC sweep.
Why the definition is the product
SNR names a ratio, not a measurement. Whether the fundamental is excluded from noise, how many
harmonics are counted, whether DC is in the band, where the band ends, what happens to a harmonic
that aliases back below Nyquist — every one of those changes the number, and two honest tools can
differ by several dB while both being “right”. A converter datasheet is comparable to a
simulation only if both state the same choices.
So each kernel fixes one method, documents every choice, and refuses inputs it cannot measure that way. A refusal is a result: it says the number would have been meaningless.
Run it
vyges install loom # one-time
# spectral — one scalar per call, each independently checked
vyges loom meas spectral capture.samples --fundamental-bin 37 \
--metric thd --harmonics 2,3,4,5
# AC transfer
vyges loom meas transfer opamp.ac --metric phase-margin --target 45
SERIES is one sample per line in capture order. SWEEP is hz gain_db phase_deg per line,
strictly increasing. Both accept # comments. Add --json for machine output; -o FILE writes
a report while the JSON still goes to stdout, so asking for the file never costs you the parsed
result.
The spectral method
| choice | this kernel |
|---|---|
| record length | power of two, 8 to 65,536 samples |
| sampling | coherent — the fundamental lands exactly on a DFT bin, and you say which |
| window | none. A rectangular window is exact for a coherent capture and wrong otherwise, so a non-coherent capture is refused, not smeared |
| DC | the mean is removed and the DC bin is excluded from every partition |
| harmonics | folded into the first Nyquist zone — an aliased harmonic’s power is really in the record |
| integration width | zero bins: each component is exactly one bin, never a skirt |
| collisions | a harmonic landing on DC, the fundamental, or another harmonic is refused — counting one bin twice would double-count its power |
| clipping | a clipped record is refused: it describes the acquisition, not the device |
SNR = 10 log10(p_f / p_n) noise only, declared harmonics excluded
SINAD = 10 log10(p_f / p_r) everything that is not the fundamental
THD = 10 log10(p_h / p_f) harmonics against the fundamental (negative dB)
SFDR = 10 log10(p_f / p_s) distance to the worst single spur
The AC method
Values between swept points are interpolated in (log10 f, dB) and (log10 f, degrees) — the space these are plotted in, so a coarse sweep does not read low. Nothing is extrapolated: a crossing outside the swept range is reported as absent, because a sweep that stopped too early is a fixable mistake and a guessed number is not.
Bandwidth is referenced to the peak gain, not the first point. A response that peaks before rolling off has its −3 dB corner relative to that peak, and referencing the first point would be wrong for exactly the circuits where the number matters most.
The verdict
With --target, the result carries a pass/fail: SNR, SINAD, SFDR and the AC metrics want
at least the target; THD wants at most it. Without a target there is no claim to make, so
met is null and the result envelope reports unknown rather than a pass
nobody asked for.
A refused input also resolves to unknown — the engine ran, and the evidence does not support a
conclusion.
How much a result claims
A number and a standard’s name printed near each other read as a conformance claim whether or not one was meant. So every result carries a machine-readable statement of exactly how much it claims:
| level | means |
|---|---|
vyges-definition | the method is ours, complete and versioned. No external standard is claimed. |
candidate | the application lies inside a named standard’s published scope, but no clause-level review has been done |
reviewed | a crosswalk records the exact edition, clauses, choices, deviations, reviewer and artifact |
conformant | an independently reviewed profile and a conformance suite |
--application decides which scope a result may name. It defaults to generic, because the tool
cannot infer from a list of numbers what device produced them and guessing would manufacture a
standards claim out of nothing:
--application | reaches | against |
|---|---|---|
generic (default) | vyges-definition | — |
adc | candidate | IEEE 1241-2023 |
dac | candidate | IEEE 1658-2023 |
recorder | candidate | IEEE 1057-2017 |
"alignment": {
"level": "candidate",
"edition": "IEEE 1241-2023",
"application": "adc",
"statement": "candidate (IEEE 1241-2023) — … NO clause-level review has been performed, so this is not a conformance claim"
}
The ladder is enforced, not merely documented. reviewed and conformant can only be built
from a crosswalk, and a crosswalk cannot exist without every field of the evidence it stands for —
edition, clauses, choices, deviations, reviewer, artifact. A claim stronger than the evidence has
no constructor. Nothing here is reviewed or conformant, and nothing becomes so by editing
a label.
The published scopes of those editions are the limit of what is asserted; the normative clauses
needed for a conformance claim are not public, so candidate is the honest ceiling today.
IEEE 519 is not authority here. It governs harmonic control in electric power systems at a point of common coupling — a different quantity, measured elsewhere, for a different purpose. A THD figure from this engine must never be cited as 519-anything. Common enough on datasheets that the exclusion is recorded in code.