Correct the framing and the guards of the perception and noise-control fiches (#355)
A review pass over the psychoacoustics, hearing, speech, environmental and
noise-control fiches. The numerics held up; what did not were the statements
printed around them, plus one wrong table lookup.
ISO 1999 validated domain. nipts()/htlan() applied Formula (2) wherever they
were asked to: L_EX,8h = 130 dB over 60 years at the 0,99 fractile returned a
357,9 dB threshold shift with nothing marking it as an extrapolation. Since the
formula is quadratic in (L_EX,8h - L0) that number is meaningless, not merely
uncertain. Both now warn (NoiseInducedHearingLossWarning) outside the stated
ranges while still computing: durations outside 1-40 years (clause 6.3.1
validates Formula (2) over 10-40 and Formula (3) over 1-10), fractiles in the
tails clause 6.3.2 says "should not be estimated" (Q below 5 % or above 95 %),
and levels above the 100 dB of Annex D that the Scope's NOTE 4 restricts
validity to. The fiches print the caveat when the conditions sit outside.
ISO 1999 Q. The fiches printed the library's fractile under ISO 1999's symbol,
inverting its meaning: in Formulae (4)/(5) Q is the percentage with worse
hearing, so the most-susceptible tenth is Q = 10 %, not the "Q = 0.9" the fiche
showed while citing 6.3.2. They now print ISO's Q as ISO defines it; the guides
note the difference from the fractile argument. Two further disclosures on the
same fiches: the age component H is evaluated from ISO 7029:2017 (the edition
ISO 1999 references undated in 6.2.2), which departs from the illustrative
Table A.3 selection by up to about 7 dB at the median, and the 2/3/4 kHz
average is the user's own choice, the Scope's NOTE 1 specifying no frequency
combination. The footer states a population scope rather than "the results
relate only to the tested specimen", which describes a laboratory specimen.
ISO 1999 Annex C is wired in as an independent oracle: its worked example (male
population aged 50, 90 dB for 30 years, 1/2/4 kHz at Q = 10 %) reproduces the
Table D.2 shifts it consumes (0, 9, 19 dB), the 13,3 dB left of the 4 kHz shift
after the Formula (1) compression (C.5) and the 31,1 dB combined threshold
(C.11). The composition it exercises is now exposed as combine_age_and_noise(),
which clause 6.2 wants available on its own: 6.2.3 recommends a database B
collected on a control population of the country under consideration, and this
applies Formula (1) to whichever database the user brings.
Verdict precision. The STI and SII fiches printed the requirement with one
decimal beside a measured value at full precision, so a verdict could contradict
itself: "STI = 0.50, required >= 0.5 -> FAIL" against a 0,52 requirement, and an
SII minimum of 0,75 printed as "0.8" (the committed example said so). Both now
print the requirement at the quantity's own precision. IEC 60268-16 Annex M
joins the conformance report as an end-to-end oracle: its printed MTF matrix,
speech spectrum and ambient noise give back its published MTI row and
STI = 0,76, exercising the whole A.5.3 to A.5.8 chain.
Noise-control prediction framing. The enclosure, silencer and HVAC fiches
compute design models but read as measurements: no prediction statement,
measurement headers, a footer scoped to a tested specimen, and committed
examples citing instrumentation that never existed ("Two-microphone
transfer-matrix bench"). They now follow the EN/ISO 12354 prediction exemplar
already in the repo, with a prediction-basis line, a statement naming what each
model does not represent, and a prediction footer. Their verdicts also decided
on the half-away-from-zero display rounding while printing Python's
round-half-to-even, so 0,25 printed 0.2 against a verdict taken on 0,3; both
now use the display rounding.
ISO/PAS 1996-3. With every onset rate at or below 10 dB/s the fiche withheld
the adjustment on the onset-rate gate (clauses 4.5/8) but justified it with "no
prominent impulse is present (governing P = 6.01 <= 5)", contradicting its own
boxed P. The note now states whichever gate governs. The assessment period is
read from the result instead of being hardcoded: 30 min is the Clause 5
*default*, now the default of the new assessment_period_min argument.
Smaller corrections:
- the HVAC elbow insertion loss took the wrong row of Bies Table 8.11 for a
W/lambda exactly on a bin edge; the rows read "a <= W/lambda < b", so an edge
opens its row (1 dB at 0,14, not 0 dB);
- the wind-turbine tonality fiche cited "subclauses 9.5.2-9.5.5" of
IEC 61400-11 for a chain running through 9.5.6 to 9.5.8 (Formulae 31-34);
- the ISO 1996-2 tonal fiche notes that Table J.1 keys on the mean audibility
of the J assessed spectra (ISO/PAS 20065 Clause 5.3.9), which a
single-spectrum assessment stands in for;
- the shared 10-90 % fractile band carried a hardcoded English legend in
Spanish figures, and the Spanish silencer and HVAC fiches printed the device
kind and duct-element label in English.
docs/ERRATA.md records two more source defects found on the way: ISO/PAS
1996-3:2022 Clause 5 swaps the 3.4/3.5 cross-references of the onset rate and
the level difference (its own units contradict the clause numbers printed with
them), and ISO 9613-2:1996 Table 2 prints 4,1 dB/km at 15 C / 80 % / 1 kHz
where ISO 9613-1 gives 4,1511, the neighbouring cells rounding correctly. The
library computes that coefficient from ISO 9613-1 directly, so it is unaffected.
Committed example fiches, the conformance report, the API reference and the
llms files are regenerated.
refactor: reorganize the package into twelve domain subpackages (3.2) (#162)
* refactor: private core to _internal, module-path shims, _plot skeleton (C1)
Foundation of the 3.2 package reorganization:
- _validation/_types/_warnings/_levels_math/utils move (git mv) into the
private phonometry/_internal/ package; ~90 import sites retargeted.
- New phonometry/_compat.py: dynamic sys.modules shims keep every moved
public module path importable for one deprecation cycle (silent import,
DeprecationWarning on attribute access); generalizes and absorbs the
former loudness.py PEP 562 shim (its 3.1 wording preserved).
- _plotting.py moves (git mv) to phonometry/_plot/common.py; a silent
explicit re-export shim keeps the old private path and the 84 lazy
.plot() call sites working until each domain retargets them.
- _warn_renamed gains a 'since' parameter (3.2 for the package moves).
- tests: new test_package_architecture.py (ast edge whitelist +
fresh-interpreter subpackage imports); test_deprecated_aliases gains the
frozen 85-path pre-move snapshot and the _MOVED shim behavior test;
pytest pythonpath=tests for the upcoming mirrored test tree.
- .git-blame-ignore-revs scaffold (hashes appended at the end of Phase 1).
* refactor: metrology subpackage (C2)
core, filter_design, frequencies, parametric_filters, levels, calibration,
compliance and uncertainty move (git mv) into phonometry/metrology/ with a
curated re-export __init__; the flat API and the old module paths keep
working (facade re-exports + _compat shims). Uncertainty renderers carve out
of _plot/common.py into _plot/metrology.py; the 14 metrology test files move
to tests/metrology/ and deep imports across tests/scripts point at the new
canonical paths.
* refactor: psychoacoustics subpackage (C3)
The thirteen psychoacoustics modules (Zwicker/ECMA/Moore-Glasberg loudness,
contours, sharpness, tonality, roughness, fluctuation strength, annoyance,
tonal audibility, private Zwicker tables) move into
phonometry/psychoacoustics/; renderers carve into _plot/psychoacoustics.py;
twelve test files move to tests/psychoacoustics/. The phonometry.loudness
alias now resolves to the relocated canonical module in a single hop.
* refactor: hearing and emission subpackages (C4+C5)
hearing/ gains threshold (renamed from hearing.py), noise-induced hearing
loss, occupational exposure, SII and STI; emission/ gains the ISO 3740
sound-power family, ISO 9614 intensity and ISO/TS 7849 vibration-based
power. Renderers carve into _plot/hearing.py and _plot/emission.py; the
eleven domain test files move under tests/hearing/ and tests/emission/.
* refactor: materials, room and building subpackages (C6-C8)
materials/ collects ISO 354/11654/12999-2 absorption, scattering-diffusion,
road absorption, impedance tube, airflow resistance and EN 29052-1 dynamic
stiffness; room/ the room-acoustics, impulse-response, open-plan, room-noise,
reverberation-prediction and EN 12354-6 modules; building/ the eleven-module
EN 12354 / ISO 717 / ISO 16283 insulation family with EN 15657 and
EN 12354-5 structure-borne sound. Renderers carve into the matching _plot
modules; twenty-six test files move under their domain directories.
* fix: complete the building plot TYPE_CHECKING imports (C8 follow-up)
* refactor: vibration and environmental subpackages (C9+C10)
vibration/ collects human vibration (ISO 8041-1/2631/5349), multiple-shock
(ISO 2631-5), mechanical mobility (ISO 7626-1) and transfer stiffness
(ISO 10846); environmental/ collects the renamed rating (Lden/Ldn) and
measurement (ISO 1996-2) modules plus outdoor propagation (ISO 9613-2),
air absorption (ISO 9613-1), wind-turbine noise (IEC 61400-11) and NT
ACOU 112 impulse prominence. Renderers carve into their _plot modules and
ten test files move under the domain directories.
* refactor: aircraft, underwater and electroacoustics subpackages (C11-C13)
aircraft/ collects EPNL, the renamed atmospheric absorption, ECAC Doc 29
airport contours and ECAC Doc 32 rotorcraft hemispheres; underwater/ the
renamed acoustics/propagation/sound_speed modules with ship noise, pile
driving, ambient noise, sonar equation, seabed reflection and the numerical
solvers; electroacoustics/ distortion and frequency response. Renderers
carve into their _plot modules; seventeen test files move under the domain
directories. All twelve domain subpackages are now in place.
* refactor: finalize the 3.2 package reorganization (C14)
- api-reference gains a Namespaces section (the twelve subpackages with
scope and the 'from phonometry import aircraft as air' idiom); README
quick-start notes the namespaces; CHANGELOG records the added namespaces,
the deprecated flat module paths and the pickle note.
- plot_excitation moves from _plot/common.py to _plot/room.py and the
facade imports it from there; _plot/common.py now holds only shared
helpers and infrastructure.
- hearing/ and environmental/ package __init__ re-export the full public
surface of their renamed modules (threshold, rating) so the pre-move
package-path imports stay silent, as promised by the migration contract.
- SonarCloud cpd exclusion follows core.py to metrology/; llms.txt
regenerated; .git-blame-ignore-revs lists the move commits and the local
blame config registers it.
Full suite 2647 passed; conformance 194/194; golden baseline byte-stable;
import time unchanged vs main (1.09 s vs 1.17 s).
* fix: re-export the measurement surface on phonometry.environmental
Subagent-review finding: the generated environmental/__init__.py missed the
ISO 1996-2 measurement module (its facade import block was momentarily
path-corrupted when the subpackage exports were collected), so
phonometry.environmental.assess_tonal_audibility and 15 sibling names
raised AttributeError while every other domain namespace was complete. Adds
the missing re-exports (including EnvironmentalMeasurementWarning on the
domain namespace) and a new architecture invariant test asserting that
every name the facade imports from a domain submodule is reachable on that
subpackage, so this class of gap cannot recur.
* test: forbid absolute self-imports in the architecture check
Review feedback: the ast edge extraction only saw relative imports, so an
absolute 'from phonometry.x import y' inside the package would bypass the
cross-package whitelist. Such imports now fail the architecture test
directly (the codebase has none; relative imports are the convention).
Correct the framing and the guards of the perception and noise-control fiches (#355)
A review pass over the psychoacoustics, hearing, speech, environmental and
noise-control fiches. The numerics held up; what did not were the statements
printed around them, plus one wrong table lookup.
ISO 1999 validated domain. nipts()/htlan() applied Formula (2) wherever they
were asked to: L_EX,8h = 130 dB over 60 years at the 0,99 fractile returned a
357,9 dB threshold shift with nothing marking it as an extrapolation. Since the
formula is quadratic in (L_EX,8h - L0) that number is meaningless, not merely
uncertain. Both now warn (NoiseInducedHearingLossWarning) outside the stated
ranges while still computing: durations outside 1-40 years (clause 6.3.1
validates Formula (2) over 10-40 and Formula (3) over 1-10), fractiles in the
tails clause 6.3.2 says "should not be estimated" (Q below 5 % or above 95 %),
and levels above the 100 dB of Annex D that the Scope's NOTE 4 restricts
validity to. The fiches print the caveat when the conditions sit outside.
ISO 1999 Q. The fiches printed the library's fractile under ISO 1999's symbol,
inverting its meaning: in Formulae (4)/(5) Q is the percentage with worse
hearing, so the most-susceptible tenth is Q = 10 %, not the "Q = 0.9" the fiche
showed while citing 6.3.2. They now print ISO's Q as ISO defines it; the guides
note the difference from the fractile argument. Two further disclosures on the
same fiches: the age component H is evaluated from ISO 7029:2017 (the edition
ISO 1999 references undated in 6.2.2), which departs from the illustrative
Table A.3 selection by up to about 7 dB at the median, and the 2/3/4 kHz
average is the user's own choice, the Scope's NOTE 1 specifying no frequency
combination. The footer states a population scope rather than "the results
relate only to the tested specimen", which describes a laboratory specimen.
ISO 1999 Annex C is wired in as an independent oracle: its worked example (male
population aged 50, 90 dB for 30 years, 1/2/4 kHz at Q = 10 %) reproduces the
Table D.2 shifts it consumes (0, 9, 19 dB), the 13,3 dB left of the 4 kHz shift
after the Formula (1) compression (C.5) and the 31,1 dB combined threshold
(C.11). The composition it exercises is now exposed as combine_age_and_noise(),
which clause 6.2 wants available on its own: 6.2.3 recommends a database B
collected on a control population of the country under consideration, and this
applies Formula (1) to whichever database the user brings.
Verdict precision. The STI and SII fiches printed the requirement with one
decimal beside a measured value at full precision, so a verdict could contradict
itself: "STI = 0.50, required >= 0.5 -> FAIL" against a 0,52 requirement, and an
SII minimum of 0,75 printed as "0.8" (the committed example said so). Both now
print the requirement at the quantity's own precision. IEC 60268-16 Annex M
joins the conformance report as an end-to-end oracle: its printed MTF matrix,
speech spectrum and ambient noise give back its published MTI row and
STI = 0,76, exercising the whole A.5.3 to A.5.8 chain.
Noise-control prediction framing. The enclosure, silencer and HVAC fiches
compute design models but read as measurements: no prediction statement,
measurement headers, a footer scoped to a tested specimen, and committed
examples citing instrumentation that never existed ("Two-microphone
transfer-matrix bench"). They now follow the EN/ISO 12354 prediction exemplar
already in the repo, with a prediction-basis line, a statement naming what each
model does not represent, and a prediction footer. Their verdicts also decided
on the half-away-from-zero display rounding while printing Python's
round-half-to-even, so 0,25 printed 0.2 against a verdict taken on 0,3; both
now use the display rounding.
ISO/PAS 1996-3. With every onset rate at or below 10 dB/s the fiche withheld
the adjustment on the onset-rate gate (clauses 4.5/8) but justified it with "no
prominent impulse is present (governing P = 6.01 <= 5)", contradicting its own
boxed P. The note now states whichever gate governs. The assessment period is
read from the result instead of being hardcoded: 30 min is the Clause 5
*default*, now the default of the new assessment_period_min argument.
Smaller corrections:
- the HVAC elbow insertion loss took the wrong row of Bies Table 8.11 for a
W/lambda exactly on a bin edge; the rows read "a <= W/lambda < b", so an edge
opens its row (1 dB at 0,14, not 0 dB);
- the wind-turbine tonality fiche cited "subclauses 9.5.2-9.5.5" of
IEC 61400-11 for a chain running through 9.5.6 to 9.5.8 (Formulae 31-34);
- the ISO 1996-2 tonal fiche notes that Table J.1 keys on the mean audibility
of the J assessed spectra (ISO/PAS 20065 Clause 5.3.9), which a
single-spectrum assessment stands in for;
- the shared 10-90 % fractile band carried a hardcoded English legend in
Spanish figures, and the Spanish silencer and HVAC fiches printed the device
kind and duct-element label in English.
docs/ERRATA.md records two more source defects found on the way: ISO/PAS
1996-3:2022 Clause 5 swaps the 3.4/3.5 cross-references of the onset rate and
the level difference (its own units contradict the clause numbers printed with
them), and ISO 9613-2:1996 Table 2 prints 4,1 dB/km at 15 C / 80 % / 1 kHz
where ISO 9613-1 gives 4,1511, the neighbouring cells rounding correctly. The
library computes that coefficient from ISO 9613-1 directly, so it is unaffected.
Committed example fiches, the conformance report, the API reference and the
llms files are regenerated.
Correct the framing and the guards of the perception and noise-control fiches (#355)
A review pass over the psychoacoustics, hearing, speech, environmental and
noise-control fiches. The numerics held up; what did not were the statements
printed around them, plus one wrong table lookup.
ISO 1999 validated domain. nipts()/htlan() applied Formula (2) wherever they
were asked to: L_EX,8h = 130 dB over 60 years at the 0,99 fractile returned a
357,9 dB threshold shift with nothing marking it as an extrapolation. Since the
formula is quadratic in (L_EX,8h - L0) that number is meaningless, not merely
uncertain. Both now warn (NoiseInducedHearingLossWarning) outside the stated
ranges while still computing: durations outside 1-40 years (clause 6.3.1
validates Formula (2) over 10-40 and Formula (3) over 1-10), fractiles in the
tails clause 6.3.2 says "should not be estimated" (Q below 5 % or above 95 %),
and levels above the 100 dB of Annex D that the Scope's NOTE 4 restricts
validity to. The fiches print the caveat when the conditions sit outside.
ISO 1999 Q. The fiches printed the library's fractile under ISO 1999's symbol,
inverting its meaning: in Formulae (4)/(5) Q is the percentage with worse
hearing, so the most-susceptible tenth is Q = 10 %, not the "Q = 0.9" the fiche
showed while citing 6.3.2. They now print ISO's Q as ISO defines it; the guides
note the difference from the fractile argument. Two further disclosures on the
same fiches: the age component H is evaluated from ISO 7029:2017 (the edition
ISO 1999 references undated in 6.2.2), which departs from the illustrative
Table A.3 selection by up to about 7 dB at the median, and the 2/3/4 kHz
average is the user's own choice, the Scope's NOTE 1 specifying no frequency
combination. The footer states a population scope rather than "the results
relate only to the tested specimen", which describes a laboratory specimen.
ISO 1999 Annex C is wired in as an independent oracle: its worked example (male
population aged 50, 90 dB for 30 years, 1/2/4 kHz at Q = 10 %) reproduces the
Table D.2 shifts it consumes (0, 9, 19 dB), the 13,3 dB left of the 4 kHz shift
after the Formula (1) compression (C.5) and the 31,1 dB combined threshold
(C.11). The composition it exercises is now exposed as combine_age_and_noise(),
which clause 6.2 wants available on its own: 6.2.3 recommends a database B
collected on a control population of the country under consideration, and this
applies Formula (1) to whichever database the user brings.
Verdict precision. The STI and SII fiches printed the requirement with one
decimal beside a measured value at full precision, so a verdict could contradict
itself: "STI = 0.50, required >= 0.5 -> FAIL" against a 0,52 requirement, and an
SII minimum of 0,75 printed as "0.8" (the committed example said so). Both now
print the requirement at the quantity's own precision. IEC 60268-16 Annex M
joins the conformance report as an end-to-end oracle: its printed MTF matrix,
speech spectrum and ambient noise give back its published MTI row and
STI = 0,76, exercising the whole A.5.3 to A.5.8 chain.
Noise-control prediction framing. The enclosure, silencer and HVAC fiches
compute design models but read as measurements: no prediction statement,
measurement headers, a footer scoped to a tested specimen, and committed
examples citing instrumentation that never existed ("Two-microphone
transfer-matrix bench"). They now follow the EN/ISO 12354 prediction exemplar
already in the repo, with a prediction-basis line, a statement naming what each
model does not represent, and a prediction footer. Their verdicts also decided
on the half-away-from-zero display rounding while printing Python's
round-half-to-even, so 0,25 printed 0.2 against a verdict taken on 0,3; both
now use the display rounding.
ISO/PAS 1996-3. With every onset rate at or below 10 dB/s the fiche withheld
the adjustment on the onset-rate gate (clauses 4.5/8) but justified it with "no
prominent impulse is present (governing P = 6.01 <= 5)", contradicting its own
boxed P. The note now states whichever gate governs. The assessment period is
read from the result instead of being hardcoded: 30 min is the Clause 5
*default*, now the default of the new assessment_period_min argument.
Smaller corrections:
- the HVAC elbow insertion loss took the wrong row of Bies Table 8.11 for a
W/lambda exactly on a bin edge; the rows read "a <= W/lambda < b", so an edge
opens its row (1 dB at 0,14, not 0 dB);
- the wind-turbine tonality fiche cited "subclauses 9.5.2-9.5.5" of
IEC 61400-11 for a chain running through 9.5.6 to 9.5.8 (Formulae 31-34);
- the ISO 1996-2 tonal fiche notes that Table J.1 keys on the mean audibility
of the J assessed spectra (ISO/PAS 20065 Clause 5.3.9), which a
single-spectrum assessment stands in for;
- the shared 10-90 % fractile band carried a hardcoded English legend in
Spanish figures, and the Spanish silencer and HVAC fiches printed the device
kind and duct-element label in English.
docs/ERRATA.md records two more source defects found on the way: ISO/PAS
1996-3:2022 Clause 5 swaps the 3.4/3.5 cross-references of the onset rate and
the level difference (its own units contradict the clause numbers printed with
them), and ISO 9613-2:1996 Table 2 prints 4,1 dB/km at 15 C / 80 % / 1 kHz
where ISO 9613-1 gives 4,1511, the neighbouring cells rounding correctly. The
library computes that coefficient from ISO 9613-1 directly, so it is unaffected.
Committed example fiches, the conformance report, the API reference and the
llms files are regenerated.
fix: tonal-audibility single-line correction, verifier coverage and environmental corrections (#174)
* fix: single-line tones take Formula (7) without the Hanning bandwidth correction (DIN 45681/ISO PAS 20065)
The tone level of a K = 1 run is the line level itself (Formula (7));
the 10 lg(delta f / delta fe) correction applies only to sums of K > 1
lines (Formula (8)), matching the DIN 45681:2005-03 Annex J reference
program. The unconditional -1.76 dB understated every single-line
audibility and could flip weak near-bin-centred tones to inaudible;
regression test pins the demonstrated flip case (+0.90 dB, audible).
* feat: automate Clause 5.3.8 Step 3 same-band tone combination and pin the DIN 45681 Anhang I oracle
analyze_spectrum now energy-sums the tone levels of audible tones
sharing a critical band (Formula (17), shared lines counted once) into
an FG entry rated at the most audible member, honouring the
exactly-two-tones-below-1000-Hz separation exception (Formulae
(18)/(19)); group_sizes on the result tells single tones from FG
entries and the decisive audibility now follows Step 4 as printed.
New oracle: DIN 45681:2005-03 Anhang I wind-energy-plant example --
Tabelle I.9 spectrum reproduces the decisive I.10 row (LS 41.71,
LT 68.10, dL 12.52, u 3.18) end to end, I.11 rows j = 45/48 pin the
Formulae (12)-(14) chain, the printed 5 FG row pins the two-tone
combination decision, Tabelle I.6 6 FG pins the Formula (17) sum, the
6.38 dB mean maps to KT = 4, and Tabelle A.1 pins Formula (2) at 24
printed bandwidths (250 Hz print quirk documented).
* fix: weighting verifier at exact base-10 frequencies, 5.5.7 between-nominals sweep and range-limited flag (IEC 61672-1)
verify_weighting_class now evaluates the SOS at the exact frequencies
1000*10^(n/10) behind the Table 3 nominal labels (Table 3 NOTE; IEC
61672-3 13.3), removing the up-to-0.27 dB pairing bias, and the
conformance report's A/C branch follows the same convention its G
branch already used. A dense logarithmic sweep enforces subclause
5.5.7 between the nominal frequencies (analytic Annex E goal, larger
of the adjacent limits), so a resonance or notch between nominals can
no longer pass; adversarial iirnotch test included. When Table 3 rows
with finite lower limits fall beyond Nyquist the result carries
range_limited=True, marking the verdict as checked-range-only.
verify_filter_class evaluates the Table 1 breakpoints exactly with
sosfreqz instead of interpolating off the grid (the 16-point floor now
reproduces the dense-grid verdict), and references the mid-band
attenuation exactly. Docstrings: plain-bilinear class threshold
corrected (class 1 holds at 44.1/48 kHz, degrades at fs <= 32 kHz) and
the 1995 band-edge single-point convention documented.
* fix: anchor wind-turbine tone classification to the highest tone line and expose the identified-tone status (IEC 61400-11)
Subclauses 9.5.3/9.5.4: lines are classified as tone when within 10 dB
of the highest line above the L_pn,avg + 6 dB threshold, and the tone
frequency (also the f of Formula 34) is that highest line, not the
probed candidate; hand-derived two-tone case pinned (dL_a = 17.066 dB
at 500 Hz). The 9.5.2 possible-tone screening (local maximum more than
6 dB above the band energy average excluding the maximum and adjacent
lines) now runs, and has_identified_tone reports whether a tone was
identified at all: spectra without one carry non-standard fallback
numbers and must be excluded from the 9.5.1 bin averaging, and
is_audible requires an identified tone. Truncated critical bands warn
(WindTurbineNoiseWarning), candidates below 20 Hz are rejected,
slant_distance gains the vertical-axis Formula 2 (R0 = H + D), and the
background-correction scope note cross-references the differing ISO
1996-2 rule set.
* fix: ISO 1996-2 repeated-measurement uncertainty by the primary Formulae (17)+(19) route and residual-correction reporting
uncertainty_from_repeated_measurements now propagates the sample
standard deviation of the energy values (Formula (17)) back to level
(Formula (19)) as the standard uncertainty; the Note 2 level-domain
substitute (Formula (20)) stays available as approximate_uncertainty
with a warning when the levels spread beyond 3 dB, where it grossly
inflates ([50, 60, 70] dB: 3.94 dB primary vs 12.18 dB approximate).
residual_sound_correction: with a margin of 3 dB or less no correction
is allowed and the UNCORRECTED measured level is the reportable upper
bound (the previous wording inverted the bound); the result exposes it
as reportable_upper_bound. gaussian_residual_level rejects inverted
percentile orderings (swapped arguments were squared away silently),
and the Annex K survey notes its 25 Hz-10 kHz threshold span.
* fix: signed barrier path difference and exact-midband atmospheric absorption (ISO 9613-2)
Barrier gains line_of_sight_clear: when the sight line passes above the
top edge, z takes the negative sign the standard prescribes (text after
Eq. (16)), Kmet = 1 (Eq. (18)) and Eq. (14) is still evaluated with the
log argument floored at 1, so Dz falls continuously from 10 lg 3 =
4.8 dB at grazing to 0 for deeper geometry instead of granting full
screening credit (up to ~20 dB spurious attenuation before, plus a
4.8 dB discontinuity across grazing: z = 0 now yields 10 lg 3, not 0).
Hand-derived below-sight-line geometry pinned per band.
Aatm now evaluates the ISO 9613-1 coefficient at the exact base-10
midband behind each nominal band (the ISO 9613-2 Table 2 convention;
at 8 kHz the nominal evaluation ran ~1.3 % high). The nearest-nominal
snapping of the Table 3 ground lookup is now stated in the public
docstrings.
* fix: GUM correlated-budget dof fallback and robust finite-difference step
Welch-Satterthwaite (Annex G.4) is derived for independent inputs
only: with a non-identity correlation matrix combine_uncertainty now
takes effective_dof = inf (GUM 6.3.3 fallback, normal coverage factor)
and warns when finite input dof are present, instead of producing a
meaningless effective dof (r = 0.999 with nu = 5 gave veff = 1e-5 and
k95 = 2e151). An explicit identity matrix still propagates the dof.
The sensitivity step is now max(u(xi), sqrt(eps)|xi|) with a spacing
guard (GUM 5.1.4 NOTE 2 suggests dxi = u(xi)); a 1e-6 uncertainty on a
1e9 value no longer underflows below the float spacing and silently
reports uc = 0 (regression pinned: uc = 1.41e-6). A model output that
does not respond to an input's perturbation raises UncertaintyWarning.
monte_carlo documents its independence assumption and fixed-trials
scope (Supplement 1 6.4.8 / 7.9 / 5.3.4 not implemented) and rejects
trials < 2 (ddof = 1 returned NaN with a raw numpy warning).
* fix: NT ACOU 112 onset-rate gate, tone-audibility application range and calibration input validation
impulse_prominence enforces the clause 4.5/8 qualification: level
rises with onset rates at or below 10 dB/s are not impulses, cannot
set the governing prominence or produce a KI adjustment (0 dB when no
event qualifies) and are reported via ImpulseProminenceWarning; the
per-event qualifies mask is exposed on the result.
The DIN 45681/ISO PAS 20065 module documents the stated application
ranges (DIN fT >= 90 Hz, ISO scope >= 50 Hz, Formula (19) printed for
fT < 1000 Hz) so out-of-range use reads as extrapolation.
sensitivity() rejects non-finite reference samples (a NaN propagated
silently to a NaN calibration factor) and its docstring now matches
the IEC 60942:2017 Table 2 rows (0.20 dB spans 31,5-63 Hz; below the
table's span the strict 0.07 dB applies).
* test: promote published oracles -- GUM H.1/H.2, Supplement 1 Tables 2-3, ISO 9613-2 Table 2 grid, IEC 61260-1 Table F.1
GUM Annex H.1 end-gauge calibration now runs end to end through
combine_uncertainty (l = 50 000 838 nm, uc = 31.71, contributions
25/9.7/0/0/2.9/16.7, veff = 16.66, U99 = 92.1 nm, k(0.99,16) = 2.9208
vs Table G.2's 2.92). GUM Annex H.2 (Table H.3) pins the correlated
Equation (16) path from the five printed observation sets: R/X/Z =
127.732/219.847/254.260 ohm with uc = 0.071/0.295/0.236. Supplement 1
Table 2 pins the Gaussian sampler (+/-3.92) and a seeded Table 3
Monte Carlo run becomes a deterministic conformance entry (+/-3.88,
u = 2.00). The full ISO 9613-2 Table 2 grid (6 conditions x 8 octave
bands) is pinned at exact midbands to half a printed digit, with the
15 degC/80 %/1 kHz print quirk documented (4,1 printed vs 4,151
exact). IEC 61260-1 Table F.1 breakpoints and both E.3.4 rounding
examples are pinned to the printed decimals.
* docs: reflect the verifier, propagation, wind-turbine, tonality and uncertainty corrections
Guides (EN and ES mirrors): exact-frequency pairing, the 5.5.7
between-nominals sweep and the range-limited flag of the weighting
verifier; the signed barrier path difference (line_of_sight_clear,
10 lg 3 at grazing) and exact-midband Aatm; the 9.5.2 screening,
highest-tone-line anchoring and has_identified_tone of the wind-turbine
tonality; Formula (7) for single-line tones and the automated Step 3
FG combination with group_sizes; the primary Formulae (17)+(19)
repeated-measurement route and the reportable upper bound of the
residual correction; the NT ACOU 112 onset-rate gate; the GUM
correlated-dof fallback, Monte Carlo independence/fixed-trials scope
and the H.1/H.2 oracles. API-reference rows updated for the new flags
and fields; CHANGELOG Added/Fixed entries; ERRATA records the DIN
45681 Tabelle I.6 6 FG print contradiction.
Conformance report regenerated (251 checks, +7 new oracle entries).
Figures regenerated with justification: weighting_class_mask now plots
the deviations at the exact base-10 frequencies (IEC 61672-1 Table 3
NOTE) and outdoor_attenuation_breakdown carries the exact-midband Aatm
term (ISO 9613-2 Table 2 convention); all other figures verified
unchanged.
* fix: merge overlapping Step 3 candidate bands into one cluster
The critical bandwidth grows with frequency, so a chain of tones yields
different own-band candidate sets that the exact-tuple deduplication kept
as separate overlapping FG combinations; candidates sharing a member now
merge into one connected component, giving exactly one combined entry per
cluster, with a chained 900/950/1000 Hz regression test.
* refactor: extract the tone-cluster, narrowband-validation and sweep helpers
The analysis-flagged cognitive complexity moves into dedicated helpers
(Step 3 clustering, narrowband spectrum validation with the possible-tone
screen, and the 5.5.7 between-nominals sweep); the finite-difference step
guard avoids floating-point equality and the exception tests keep a single
throwing call per block.
* refactor: extract the per-band weighting verdicts and simplify the step guard
* fix: review follow-ups on the verifier, wind-turbine, propagation and uncertainty changes
The candidate tone frequency is validated before nearest-bin snapping and
the documented candidate fallback applies whenever identification fails;
the filter-class breakpoint cutoff uses the processing Nyquist rather than
the sweep grid's last point; any Table 3 row beyond Nyquist marks the
weighting verdict range-limited and the between-nominals sweep is
documented as sampled; the ISO 9613-2 conformance check fails on
non-finite residuals; the finite-difference step keeps locality (input
uncertainty with a 64-ULP floor, with a nonlinear regression); correlated
budgets with finite input dof carry undefined effective dof and expanded()
requires an explicit coverage factor; and the guides document the new
Barrier flag and the single-line tone-level formula.
* refactor: extract the candidate-peak selection of the wind-turbine tonality
fix: tonal-audibility single-line correction, verifier coverage and environmental corrections (#174)
* fix: single-line tones take Formula (7) without the Hanning bandwidth correction (DIN 45681/ISO PAS 20065)
The tone level of a K = 1 run is the line level itself (Formula (7));
the 10 lg(delta f / delta fe) correction applies only to sums of K > 1
lines (Formula (8)), matching the DIN 45681:2005-03 Annex J reference
program. The unconditional -1.76 dB understated every single-line
audibility and could flip weak near-bin-centred tones to inaudible;
regression test pins the demonstrated flip case (+0.90 dB, audible).
* feat: automate Clause 5.3.8 Step 3 same-band tone combination and pin the DIN 45681 Anhang I oracle
analyze_spectrum now energy-sums the tone levels of audible tones
sharing a critical band (Formula (17), shared lines counted once) into
an FG entry rated at the most audible member, honouring the
exactly-two-tones-below-1000-Hz separation exception (Formulae
(18)/(19)); group_sizes on the result tells single tones from FG
entries and the decisive audibility now follows Step 4 as printed.
New oracle: DIN 45681:2005-03 Anhang I wind-energy-plant example --
Tabelle I.9 spectrum reproduces the decisive I.10 row (LS 41.71,
LT 68.10, dL 12.52, u 3.18) end to end, I.11 rows j = 45/48 pin the
Formulae (12)-(14) chain, the printed 5 FG row pins the two-tone
combination decision, Tabelle I.6 6 FG pins the Formula (17) sum, the
6.38 dB mean maps to KT = 4, and Tabelle A.1 pins Formula (2) at 24
printed bandwidths (250 Hz print quirk documented).
* fix: weighting verifier at exact base-10 frequencies, 5.5.7 between-nominals sweep and range-limited flag (IEC 61672-1)
verify_weighting_class now evaluates the SOS at the exact frequencies
1000*10^(n/10) behind the Table 3 nominal labels (Table 3 NOTE; IEC
61672-3 13.3), removing the up-to-0.27 dB pairing bias, and the
conformance report's A/C branch follows the same convention its G
branch already used. A dense logarithmic sweep enforces subclause
5.5.7 between the nominal frequencies (analytic Annex E goal, larger
of the adjacent limits), so a resonance or notch between nominals can
no longer pass; adversarial iirnotch test included. When Table 3 rows
with finite lower limits fall beyond Nyquist the result carries
range_limited=True, marking the verdict as checked-range-only.
verify_filter_class evaluates the Table 1 breakpoints exactly with
sosfreqz instead of interpolating off the grid (the 16-point floor now
reproduces the dense-grid verdict), and references the mid-band
attenuation exactly. Docstrings: plain-bilinear class threshold
corrected (class 1 holds at 44.1/48 kHz, degrades at fs <= 32 kHz) and
the 1995 band-edge single-point convention documented.
* fix: anchor wind-turbine tone classification to the highest tone line and expose the identified-tone status (IEC 61400-11)
Subclauses 9.5.3/9.5.4: lines are classified as tone when within 10 dB
of the highest line above the L_pn,avg + 6 dB threshold, and the tone
frequency (also the f of Formula 34) is that highest line, not the
probed candidate; hand-derived two-tone case pinned (dL_a = 17.066 dB
at 500 Hz). The 9.5.2 possible-tone screening (local maximum more than
6 dB above the band energy average excluding the maximum and adjacent
lines) now runs, and has_identified_tone reports whether a tone was
identified at all: spectra without one carry non-standard fallback
numbers and must be excluded from the 9.5.1 bin averaging, and
is_audible requires an identified tone. Truncated critical bands warn
(WindTurbineNoiseWarning), candidates below 20 Hz are rejected,
slant_distance gains the vertical-axis Formula 2 (R0 = H + D), and the
background-correction scope note cross-references the differing ISO
1996-2 rule set.
* fix: ISO 1996-2 repeated-measurement uncertainty by the primary Formulae (17)+(19) route and residual-correction reporting
uncertainty_from_repeated_measurements now propagates the sample
standard deviation of the energy values (Formula (17)) back to level
(Formula (19)) as the standard uncertainty; the Note 2 level-domain
substitute (Formula (20)) stays available as approximate_uncertainty
with a warning when the levels spread beyond 3 dB, where it grossly
inflates ([50, 60, 70] dB: 3.94 dB primary vs 12.18 dB approximate).
residual_sound_correction: with a margin of 3 dB or less no correction
is allowed and the UNCORRECTED measured level is the reportable upper
bound (the previous wording inverted the bound); the result exposes it
as reportable_upper_bound. gaussian_residual_level rejects inverted
percentile orderings (swapped arguments were squared away silently),
and the Annex K survey notes its 25 Hz-10 kHz threshold span.
* fix: signed barrier path difference and exact-midband atmospheric absorption (ISO 9613-2)
Barrier gains line_of_sight_clear: when the sight line passes above the
top edge, z takes the negative sign the standard prescribes (text after
Eq. (16)), Kmet = 1 (Eq. (18)) and Eq. (14) is still evaluated with the
log argument floored at 1, so Dz falls continuously from 10 lg 3 =
4.8 dB at grazing to 0 for deeper geometry instead of granting full
screening credit (up to ~20 dB spurious attenuation before, plus a
4.8 dB discontinuity across grazing: z = 0 now yields 10 lg 3, not 0).
Hand-derived below-sight-line geometry pinned per band.
Aatm now evaluates the ISO 9613-1 coefficient at the exact base-10
midband behind each nominal band (the ISO 9613-2 Table 2 convention;
at 8 kHz the nominal evaluation ran ~1.3 % high). The nearest-nominal
snapping of the Table 3 ground lookup is now stated in the public
docstrings.
* fix: GUM correlated-budget dof fallback and robust finite-difference step
Welch-Satterthwaite (Annex G.4) is derived for independent inputs
only: with a non-identity correlation matrix combine_uncertainty now
takes effective_dof = inf (GUM 6.3.3 fallback, normal coverage factor)
and warns when finite input dof are present, instead of producing a
meaningless effective dof (r = 0.999 with nu = 5 gave veff = 1e-5 and
k95 = 2e151). An explicit identity matrix still propagates the dof.
The sensitivity step is now max(u(xi), sqrt(eps)|xi|) with a spacing
guard (GUM 5.1.4 NOTE 2 suggests dxi = u(xi)); a 1e-6 uncertainty on a
1e9 value no longer underflows below the float spacing and silently
reports uc = 0 (regression pinned: uc = 1.41e-6). A model output that
does not respond to an input's perturbation raises UncertaintyWarning.
monte_carlo documents its independence assumption and fixed-trials
scope (Supplement 1 6.4.8 / 7.9 / 5.3.4 not implemented) and rejects
trials < 2 (ddof = 1 returned NaN with a raw numpy warning).
* fix: NT ACOU 112 onset-rate gate, tone-audibility application range and calibration input validation
impulse_prominence enforces the clause 4.5/8 qualification: level
rises with onset rates at or below 10 dB/s are not impulses, cannot
set the governing prominence or produce a KI adjustment (0 dB when no
event qualifies) and are reported via ImpulseProminenceWarning; the
per-event qualifies mask is exposed on the result.
The DIN 45681/ISO PAS 20065 module documents the stated application
ranges (DIN fT >= 90 Hz, ISO scope >= 50 Hz, Formula (19) printed for
fT < 1000 Hz) so out-of-range use reads as extrapolation.
sensitivity() rejects non-finite reference samples (a NaN propagated
silently to a NaN calibration factor) and its docstring now matches
the IEC 60942:2017 Table 2 rows (0.20 dB spans 31,5-63 Hz; below the
table's span the strict 0.07 dB applies).
* test: promote published oracles -- GUM H.1/H.2, Supplement 1 Tables 2-3, ISO 9613-2 Table 2 grid, IEC 61260-1 Table F.1
GUM Annex H.1 end-gauge calibration now runs end to end through
combine_uncertainty (l = 50 000 838 nm, uc = 31.71, contributions
25/9.7/0/0/2.9/16.7, veff = 16.66, U99 = 92.1 nm, k(0.99,16) = 2.9208
vs Table G.2's 2.92). GUM Annex H.2 (Table H.3) pins the correlated
Equation (16) path from the five printed observation sets: R/X/Z =
127.732/219.847/254.260 ohm with uc = 0.071/0.295/0.236. Supplement 1
Table 2 pins the Gaussian sampler (+/-3.92) and a seeded Table 3
Monte Carlo run becomes a deterministic conformance entry (+/-3.88,
u = 2.00). The full ISO 9613-2 Table 2 grid (6 conditions x 8 octave
bands) is pinned at exact midbands to half a printed digit, with the
15 degC/80 %/1 kHz print quirk documented (4,1 printed vs 4,151
exact). IEC 61260-1 Table F.1 breakpoints and both E.3.4 rounding
examples are pinned to the printed decimals.
* docs: reflect the verifier, propagation, wind-turbine, tonality and uncertainty corrections
Guides (EN and ES mirrors): exact-frequency pairing, the 5.5.7
between-nominals sweep and the range-limited flag of the weighting
verifier; the signed barrier path difference (line_of_sight_clear,
10 lg 3 at grazing) and exact-midband Aatm; the 9.5.2 screening,
highest-tone-line anchoring and has_identified_tone of the wind-turbine
tonality; Formula (7) for single-line tones and the automated Step 3
FG combination with group_sizes; the primary Formulae (17)+(19)
repeated-measurement route and the reportable upper bound of the
residual correction; the NT ACOU 112 onset-rate gate; the GUM
correlated-dof fallback, Monte Carlo independence/fixed-trials scope
and the H.1/H.2 oracles. API-reference rows updated for the new flags
and fields; CHANGELOG Added/Fixed entries; ERRATA records the DIN
45681 Tabelle I.6 6 FG print contradiction.
Conformance report regenerated (251 checks, +7 new oracle entries).
Figures regenerated with justification: weighting_class_mask now plots
the deviations at the exact base-10 frequencies (IEC 61672-1 Table 3
NOTE) and outdoor_attenuation_breakdown carries the exact-midband Aatm
term (ISO 9613-2 Table 2 convention); all other figures verified
unchanged.
* fix: merge overlapping Step 3 candidate bands into one cluster
The critical bandwidth grows with frequency, so a chain of tones yields
different own-band candidate sets that the exact-tuple deduplication kept
as separate overlapping FG combinations; candidates sharing a member now
merge into one connected component, giving exactly one combined entry per
cluster, with a chained 900/950/1000 Hz regression test.
* refactor: extract the tone-cluster, narrowband-validation and sweep helpers
The analysis-flagged cognitive complexity moves into dedicated helpers
(Step 3 clustering, narrowband spectrum validation with the possible-tone
screen, and the 5.5.7 between-nominals sweep); the finite-difference step
guard avoids floating-point equality and the exception tests keep a single
throwing call per block.
* refactor: extract the per-band weighting verdicts and simplify the step guard
* fix: review follow-ups on the verifier, wind-turbine, propagation and uncertainty changes
The candidate tone frequency is validated before nearest-bin snapping and
the documented candidate fallback applies whenever identification fails;
the filter-class breakpoint cutoff uses the processing Nyquist rather than
the sweep grid's last point; any Table 3 row beyond Nyquist marks the
weighting verdict range-limited and the between-nominals sweep is
documented as sampled; the ISO 9613-2 conformance check fails on
non-finite residuals; the finite-difference step keeps locality (input
uncertainty with a 64-ULP floor, with a nonlinear regression); correlated
budgets with finite input dof carry undefined effective dof and expanded()
requires an explicit coverage factor; and the guides document the new
Barrier flag and the single-line tone-level formula.
* refactor: extract the candidate-peak selection of the wind-turbine tonality