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).
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