feat: laboratory flanking transmission (ISO 10848 vibration reduction index) (#132)
ISO 10848:2006/2010 is the laboratory measurement counterpart of the EN 12354
flanking-transmission prediction: it measures the junction vibration reduction
index Kij that the prediction takes as an input, plus the overall flanking
descriptors Dn,f and Ln,f.
New module phonometry.flanking_transmission:
* vibration_reduction_index() -- Kij (Formula (13), or the simplified (14) for
lightweight well-damped elements), with the equivalent absorption length
(Formula (12)), the direction-averaged velocity level difference (Formula
(11), making Kij symmetric), octave-band combination and the single-number
mean Kij over 200-1250 Hz (Annex A). Returns a plottable
VibrationReductionResult.
* velocity_level_difference() / direction_averaged_level_difference() /
total_loss_factor() / equivalent_absorption_length() -- the building blocks.
* normalized_flanking_level_difference() (Dn,f, Formula (4)) and
normalized_flanking_impact_level() (Ln,f, Formula (5)), rated through the
verified ISO 717-1/-2 engines.
* vibration_reduction_index_from_flanking() -- the indirect Kij from Dn,f.
* Validity criteria: strong_coupling_satisfied() (Formula (15)),
critical_frequency() (Formula (20)), and the Part 4 modal-density /
band-mode-count / modal-overlap-factor checks (Formulas (5)/(4)/(6)).
ISO 10848 contains no worked numeric example, so conformance is anchored on
closed-form identities (simplified Kij, aj at f_ref, the total loss factor) --
three new checks in the conformance report. 29 unit tests cover the closed
forms, the Kij symmetry and simplified/full-formula relationship, the octave
conversion, the Dn,f/Ln,f ratings, the validity criteria and the plotting.
Docs (GitHub + site EN/ES) get a new section 8 with a figure and the API
reference rows; the measured Kij feeds the existing EN 12354 flanking model.
Audit pass 8: retrofit the house conventions onto the legacy guides (#117)
* docs: retrofit the house conventions onto the legacy guides
Audit batch 8 - the pre-convention guides catch up with the style the
newer ones follow, on all three surfaces (repo, site EN, site ES):
- Standards footers for the ten guides that cited clauses inline but
never closed with the normative scope (calibration, filter-banks,
intensity, levels, time-weighting, weighting, outdoor-propagation,
sound-power, psychoacoustics, plus materials synced from the site
style and a new room-acoustics footer); block-processing,
multichannel and getting-started verified as having no normative
citations to footer
- every touched snippet is now self-contained or carries an explicit
reuse annotation, and ~20 prints gained expected outputs EXECUTED
against the library (levels LN values re-verified, materials
# 222956, human-vibration VDV/MTVV/crest, getting-started ~91 dB
bands, Zwicker sone/phon values, enclosed-space RT spectrum)
- 12 figure-reproduction <details> blocks added (materials x3,
outdoor-propagation x2, time-weighting x2, weighting x3,
speech-intelligibility, getting-started - with an honest note where
the committed figure overlays extra context), all verified runnable
- structural reorders, content-preserving: the exponential detector now
precedes the F/S/I modes; the filter parameter table follows the
comparison it references; weighting opens with 'Where the curves come
from'; block-processing gains a motivating opening and drops its
duplicated constraints list; the raw LaTeX in human-vibration code
spans becomes proper math; F2/F3/F4 defined in prose in intensity
- sound-power SS1-3 gain result figures: three new deterministic
generators (12 PNG variants, ES translations, visually verified)
embedded with captions and <details> using each result's .plot()
* docs: address the review findings on the legacy-conventions batch
- NullFormatter imported from matplotlib.ticker in the SII snippet
(plt.NullFormatter does not exist) (Copilot)
- eps floor inside the log10 of the three FFT-magnitude plots in the
weighting snippets, verified warning-free with -W error (Copilot)
- the getting-started figure snippet now reproduces the committed
figure including the gray PSD overlay, note removed (CodeRabbit)
- 8-tau settling is ~99.97 %, not 99.8 % (CodeRabbit)
- the intensity result generator drops its blanket warning suppression
(this data fires none) and masks non-finite bands instead of
nan_to_num zero bars; regenerated variants are byte-identical
(Gemini, CodeRabbit)
- the outdoor attenuation stacks use separate positive/negative
baselines - the 63 Hz ground term (-4.65 dB) now hangs below zero in
BOTH the snippet and the committed generator figure, whose four
variants regenerate (CodeRabbit, Major)
- the IEC verification claim narrows to what is true: the example
verifies the 200 ms Fast row; CI covers Table 4 down to 1 ms for F
and 2 ms for S (CodeRabbit)
- freqs = res.frequencies added to the three sound-power by-hand blocks
(CodeRabbit)
feat: IEC 61260:1995 / ANSI S1.11-2004 class 0 filter verification (#126)
* feat: IEC 61260:1995 / ANSI S1.11-2004 class 0 filter verification
verify_filter_class and class_limits gain an `edition` argument. The default
"2014" path (IEC 61260-1:2014, classes 1 and 2) is unchanged; edition "1995"
selects the withdrawn IEC 61260:1995 / ANSI S1.11-2004 Table 1, which adds the
stricter laboratory-grade class 0 and whose class 1/2 masks differ slightly
from the 2014 edition.
The 1995/ANSI octave-band Table 1 was transcribed digit-for-digit and verified
identical between the two standards (dual-oracle clean-room). The fractional-
octave breakpoint mapping is shared: 1995 Annex B equation (10) is identical to
2014 Formula (9), so the existing _map_breakpoint is reused for both editions.
The library default (Butterworth order 6) already meets class 0 across the usual
configurations (octave/third-octave, 32-96 kHz); the non-Butterworth
architectures cannot reach the class mask by construction (ripple / flat group
delay), so defaults are unchanged and the class each architecture reaches is
documented instead.
Adds a class-0 conformance check (99/99), the EN/ES filter_class0_mask figure,
tests cross-checked against a shared reference_data copy, the "Class 0" doc
section (guide EN/ES + API refs EN/ES) and regenerated CONFORMANCE.md /
llms-full.txt.
* docs: address bot review — scope class-0 claims, fix English decimals
- compliance.py: English docstring tolerances use dot decimals and ± symbol
(were decimal commas, inconsistent in an English context).
- api-reference (docs + site EN/ES): qualify overall_class by edition
(1/2/None for "2014"; 0/1/2/None for "1995"); neutralize the ES headers that
still said "IEC 61260-1:2014" while documenting the edition switch.
- filter-banks (docs + site EN/ES): narrow the class-0 wording to the verified
order-6 Butterworth octave/third-octave banks at 48 kHz; drop the untested
32-96 kHz range and advise re-running verify_filter_class away from those
settings.
- generate_graphs.py: fix misleading "filter_class0_mask.png" print (output is
SVG).
- test_compliance.py: assert the class-0/1/2 minimum ordering the docstring
already promised (previously only the maximum ordering was checked).
feat: environmental-noise determination (ISO 1996-2 tonal adjustment + uncertainty) (#133)
* feat: environmental-noise determination (ISO 1996-2 tonal adjustment + uncertainty)
ISO 1996-2:2017 is the determination part of the ISO 1996 environmental-noise
family (the descriptors Lden/Ldn live in ISO 1996-1, already in
phonometry.environmental). New module phonometry.environmental_measurement:
* Tonal adjustment (engineering method, ISO 1996-2:2007 Annex C):
tonal_audibility() forms ΔLta = Lpt − Lpn + 2 + lg[1 + (fc/502)^2.5] dB
(Formula C.3) and tonal_adjustment() the piecewise Kt (Formulae C.4-C.6);
assess_tonal_audibility() bundles them with the critical bandwidth (Table
C.1) into a plottable result. tonal_seeking_survey() is the one-third-octave
15/8/5 dB screen (Annex K) and tonal_adjustment_from_mean_audibility() the
Table J.1 mean-audibility route.
* Residual-noise correction: residual_sound_correction() (Formula 16, flags a
<3 dB margin as an upper bound) and gaussian_residual_level() (Annex I).
* Measurement uncertainty: combined_standard_uncertainty() (Formula 2),
environmental_expanded_uncertainty() (k=2/1.3), residual_correction_uncertainty()
(Formulae F.7-F.9) and uncertainty_from_repeated_measurements() (Formulae 17-20).
Unlike the removed 2017 engineering method (which defers to ISO/PAS 20065), the
detailed 2007/2009 Annex C algorithm is self-contained and has worked numeric
examples: conformance is anchored on Annex C.5 (ΔLta and Kt) and Annex G.2 (the
combined uncertainty u = 2.18 dB) -- three new checks (115/115, byte-stable).
The raw-FFT tone-detection pipeline is intentionally out of scope: the standard
gives no raw input spectra, so it could not be verified. 25 unit tests; docs
(GitHub + site EN/ES) get a new levels section with a figure and API rows.
* test: anchor ISO 1996-2 residual/gaussian/uncertainty tests on hand-computed values
Review found four tests re-derived their expected value from the same
arithmetic expression as the implementation, so a transcription error would
go undetected. Replace them with independent hand-computed literals (residual
correction Formula 16, Gaussian residual I.1/I.2, residual-correction
uncertainty F.9, repeated-measurement mean/uncertainty 18/20).
* fix: avoid assert for type narrowing in gaussian_residual_level (bandit B101)
The CI quality job runs bandit -r src, which flags assert statements (stripped
under python -O). Replace the mypy-narrowing assert with an elif/else-raise so
the branch is both bandit-clean and correctly narrowed.
feat: reverberation-time prediction (Sabine, Eyring, Millington-Sette, Fitzroy, Arau-Puchades) (#134)
* feat: reverberation-time prediction (Sabine, Eyring, Millington-Sette, Fitzroy, Arau-Puchades)
Add a general reverberation-time prediction module complementing the
EN 12354-6 enclosed-space model and the ISO 3382 measurement code. Five
statistical-acoustics models predict T from a room's volume, boundary
areas and surface absorption:
- Sabine, Eyring (Norris-Eyring) and Millington-Sette over an arbitrary
surface list;
- Fitzroy (area-weighted arithmetic mean) and Arau-Puchades (geometric
mean, Acustica 65, 1988, Formula 18) over the three wall pairs of a
rectangular room;
- the air-absorption term 4mV throughout (m from air_attenuation_m);
- reverberation_time_models + ReverberationModelResult.plot() to compare
all five per band.
k = 24 ln10 / c0 (0.161 at c0 = 343 m/s). Conformance is anchored on a
real worked oracle — Everest, Master Handbook of Acoustics 4th ed,
Fig. 7-22 Example 1, reproduced to <= 0.02 s after ft -> SI conversion —
reinforced by hand-computed closed-form values and the model identities
(all reduce to Eyring for uniform absorption; Eyring -> Sabine as a -> 0).
Docs (repo + bilingual site guide + API reference), the reverberation_models
figure (4 variants), 6 conformance checks (120/120) and 26 tests.
* build: regenerate reverberation_models figures with pinned matplotlib 3.11.0
The four new SVGs were first rendered with the local matplotlib 3.10.1,
whose glyph/path layout differs structurally from the CI-pinned 3.11.0
(requirements-figures.txt), tripping the figure-staleness check. Regenerate
them under the pinned toolchain so they match a fresh 'make graphs' on CI.