Spanish docs: UNE terminology, decimal commas, localized snippets and theory figures (#365)
* Normalize Spanish docs prose to UNE acoustic terminology
Sweep the Spanish pages for the non-UNE forms "presion sonora",
"potencia sonora" and "intensidad sonora" and normalize the prose to
the wording used by the official Spanish editions of the standards:
"presion acustica" (UNE-EN ISO 16283-2:2016, UNE-EN ISO 10052:2005,
UNE-EN ISO 3746:2011, UNE-ISO 226:2013), "potencia acustica"
(UNE-EN ISO 3746:2011, UNE-EN ISO 4871:2010, UNE-EN ISO 9614-1:2010),
"intensidad acustica" (UNE-EN 61043:1999, UNE-EN ISO 9614-1:2010) and
"nivel de presion acustica de impactos" (UNE-EN ISO 12354-2:2018,
UNE-EN ISO 717-2:2013). 92 replacements across 32 pages, prose only:
code snippets keep the labels they share with the generated figures.
Rename the intensity guide to "Intensidad acustica (p-p)" and the
structure-borne power guide to "Potencia acustica estructural de
equipos (EN 15657)", updating every cross-link text and the theory
anchor intensidad-sonora-iec-61043 accordingly. The API sidebar group
label becomes "Potencia acustica e intensidad" via api_taxonomy.py
with the generated sidebar regenerated.
* Use decimal commas in Spanish docs prose quantities
Sweep every Spanish page for decimal points leaking into prose numbers
and switch them to the comma the locale convention calls for: the
rotorcraft validation prose (fourteen tolerances such as 0,08 dB(A) and
0,5 s), the insulation-prediction worked example (53,6 dB / 53,8 dB),
the tone-audibility Annex E tone (137,3 Hz), the Mackenzie check value
(1550,744 m/s), the EPNL, loudness and absorption fiche descriptions
(98,3 EPNdB; 8,2 sonos and 70,4 fonios; 0,60 y 0,55), the junction
constants table and the third-octave band-edge table in the signal
analysis theory page. Code snippets, inline code, math, clause numbers
and quoted source text keep their points.
* Pass language="es" in Spanish figure-reproduction snippets
The <details> blocks under each localized figure in the Spanish guides
are meant to reproduce the _es variant shown on the page, but 82 of the
one-line result .plot() calls omitted the language="es" argument and
would render English axis labels instead. Add the argument across 33
guides, matching the pattern the intensity field-indicators snippet
already used. The three lab-insulation results whose plot() does not
take a language argument (lab airborne, lab impact and the
element-normalized intensity difference) are left as they were.
* Extend the UNE terminology sweep to sound absorption
Normalize "absorción sonora" to "absorción acústica" across the
Spanish pages (36 occurrences in 14 pages, including the EN 12354-6
guide title and the theory headings), the wording of UNE-EN ISO
3746:2011 and of the equivalent absorption area in UNE-EN ISO
10140-3:2011, UNE-EN ISO 12354-2:2018 and UNE-EN ISO 16283-2:2016, and
the same term the accredited report strings already use. Also restore
the accent in the theory index anchor to the renamed intensity heading,
since heading slugs keep accented characters.
* Theory pages: illustrate each section family with existing result figures
The seven theory pages carried no figures at all. Embed sixteen of the
existing generated figures (light and dark variants, Spanish variants on
the Spanish mirrors) where the concept matches the prose: weighting
curves, time integration and the p-p intensity estimator in the signal
analysis page; equal-loudness contours, the specific-loudness pattern
and the hearing-threshold panels in perception; NC/RC rating, the
Schroeder decay and the ISO 717-1 shifted reference in rooms and
buildings; the Lden profile, ISO 9613-1 absorption and the three sound
power routes in environment and transport; the scattering coefficient
and ISO 11654 rating in materials and surfaces; and the Wk weighting and
the resonator mobility triplet in vibration. Each embed reuses the alt
text of the guide that owns the figure and adds a one-line caption.
Also give the wave-simulation section hub the reading-order paragraph
its siblings already have, linking the closed-form pages the FDTD solver
cross-checks, in both languages.
* Address review findings from the Spanish locale sweeps
Second pass fixes flagged by review: five terminology occurrences the
line-based sweep missed because the noun and adjective were split by a
line wrap or emphasis markup (sound-power, rotorcraft-noise and two
section hubs); six Spanish pages that still embedded the English figure
variant although the _es variant exists (junction-transmission,
aircraft-noise airport contour, miso-coherence,
objective-intelligibility, psychoacoustic-annoyance twice); and 19 more
language="es" arguments on figure-reproduction plot calls the first
pass missed, chained-call receivers such as room.noise_criterion(spl)
.plot() and the axes-creating first call of the two-profile
atmospheric-refraction snippet among them. The lab-insulation plot
wrappers forward keyword arguments to the rating plot, so the three
calls excluded earlier now take the argument too. All Spanish python
fences re-checked to compile and EN/ES parity re-verified.
* Carry the UNE terminology into the generated-asset Spanish strings
Apply the same evidence-based decisions to every Spanish string source
that feeds generated assets: the translation tables of
generate_graphs.py and generate_diagrams.py, the _STRINGS tables of the
plot modules and the report i18n values. 35 strings change: "presión
acústica" (UNE-EN ISO 16283-2:2016, 10052:2005, 3746:2011),
"potencia acústica" (UNE-EN ISO 3746:2011, 4871:2010, 9614-1:2010),
"intensidad acústica" (UNE-EN 61043:1999, 9614-1:2010), "absorción
acústica" (UNE-EN ISO 3746:2011) and "nivel de presión acústica de
impactos" (UNE-EN ISO 12354-2:2018). Strings the UNE editions keep are
untouched: "emisión sonora", "exposición sonora", "campo sonoro",
"fuente sonora". Only Spanish values change; the English keys stay.
The two plot-i18n test assertions that pin the axis label follow, and
the eight Spanish doc fences that deliberately mirrored the old figure
labels now mirror the new ones.
* Regenerate the Spanish figure variants with the UNE terminology
make graphs after the string-source change: 48 files, all of them _es
or _es_dark variants (24 figure/diagram pairs), picking up "presión
acústica", "potencia acústica", "intensidad acústica", "absorción
acústica" and "nivel de presión acústica de impactos" in titles and
axis labels. The English variants are untouched. The committed example
fiches need no regeneration: every example in generate_reports.py
renders in English, so no changed Spanish report string reaches them.
* Align the intensity-fiche Spanish assertions with the UNE terminology
Spanish docs: UNE terminology, decimal commas, localized snippets and theory figures (#365)
* Normalize Spanish docs prose to UNE acoustic terminology
Sweep the Spanish pages for the non-UNE forms "presion sonora",
"potencia sonora" and "intensidad sonora" and normalize the prose to
the wording used by the official Spanish editions of the standards:
"presion acustica" (UNE-EN ISO 16283-2:2016, UNE-EN ISO 10052:2005,
UNE-EN ISO 3746:2011, UNE-ISO 226:2013), "potencia acustica"
(UNE-EN ISO 3746:2011, UNE-EN ISO 4871:2010, UNE-EN ISO 9614-1:2010),
"intensidad acustica" (UNE-EN 61043:1999, UNE-EN ISO 9614-1:2010) and
"nivel de presion acustica de impactos" (UNE-EN ISO 12354-2:2018,
UNE-EN ISO 717-2:2013). 92 replacements across 32 pages, prose only:
code snippets keep the labels they share with the generated figures.
Rename the intensity guide to "Intensidad acustica (p-p)" and the
structure-borne power guide to "Potencia acustica estructural de
equipos (EN 15657)", updating every cross-link text and the theory
anchor intensidad-sonora-iec-61043 accordingly. The API sidebar group
label becomes "Potencia acustica e intensidad" via api_taxonomy.py
with the generated sidebar regenerated.
* Use decimal commas in Spanish docs prose quantities
Sweep every Spanish page for decimal points leaking into prose numbers
and switch them to the comma the locale convention calls for: the
rotorcraft validation prose (fourteen tolerances such as 0,08 dB(A) and
0,5 s), the insulation-prediction worked example (53,6 dB / 53,8 dB),
the tone-audibility Annex E tone (137,3 Hz), the Mackenzie check value
(1550,744 m/s), the EPNL, loudness and absorption fiche descriptions
(98,3 EPNdB; 8,2 sonos and 70,4 fonios; 0,60 y 0,55), the junction
constants table and the third-octave band-edge table in the signal
analysis theory page. Code snippets, inline code, math, clause numbers
and quoted source text keep their points.
* Pass language="es" in Spanish figure-reproduction snippets
The <details> blocks under each localized figure in the Spanish guides
are meant to reproduce the _es variant shown on the page, but 82 of the
one-line result .plot() calls omitted the language="es" argument and
would render English axis labels instead. Add the argument across 33
guides, matching the pattern the intensity field-indicators snippet
already used. The three lab-insulation results whose plot() does not
take a language argument (lab airborne, lab impact and the
element-normalized intensity difference) are left as they were.
* Extend the UNE terminology sweep to sound absorption
Normalize "absorción sonora" to "absorción acústica" across the
Spanish pages (36 occurrences in 14 pages, including the EN 12354-6
guide title and the theory headings), the wording of UNE-EN ISO
3746:2011 and of the equivalent absorption area in UNE-EN ISO
10140-3:2011, UNE-EN ISO 12354-2:2018 and UNE-EN ISO 16283-2:2016, and
the same term the accredited report strings already use. Also restore
the accent in the theory index anchor to the renamed intensity heading,
since heading slugs keep accented characters.
* Theory pages: illustrate each section family with existing result figures
The seven theory pages carried no figures at all. Embed sixteen of the
existing generated figures (light and dark variants, Spanish variants on
the Spanish mirrors) where the concept matches the prose: weighting
curves, time integration and the p-p intensity estimator in the signal
analysis page; equal-loudness contours, the specific-loudness pattern
and the hearing-threshold panels in perception; NC/RC rating, the
Schroeder decay and the ISO 717-1 shifted reference in rooms and
buildings; the Lden profile, ISO 9613-1 absorption and the three sound
power routes in environment and transport; the scattering coefficient
and ISO 11654 rating in materials and surfaces; and the Wk weighting and
the resonator mobility triplet in vibration. Each embed reuses the alt
text of the guide that owns the figure and adds a one-line caption.
Also give the wave-simulation section hub the reading-order paragraph
its siblings already have, linking the closed-form pages the FDTD solver
cross-checks, in both languages.
* Address review findings from the Spanish locale sweeps
Second pass fixes flagged by review: five terminology occurrences the
line-based sweep missed because the noun and adjective were split by a
line wrap or emphasis markup (sound-power, rotorcraft-noise and two
section hubs); six Spanish pages that still embedded the English figure
variant although the _es variant exists (junction-transmission,
aircraft-noise airport contour, miso-coherence,
objective-intelligibility, psychoacoustic-annoyance twice); and 19 more
language="es" arguments on figure-reproduction plot calls the first
pass missed, chained-call receivers such as room.noise_criterion(spl)
.plot() and the axes-creating first call of the two-profile
atmospheric-refraction snippet among them. The lab-insulation plot
wrappers forward keyword arguments to the rating plot, so the three
calls excluded earlier now take the argument too. All Spanish python
fences re-checked to compile and EN/ES parity re-verified.
* Carry the UNE terminology into the generated-asset Spanish strings
Apply the same evidence-based decisions to every Spanish string source
that feeds generated assets: the translation tables of
generate_graphs.py and generate_diagrams.py, the _STRINGS tables of the
plot modules and the report i18n values. 35 strings change: "presión
acústica" (UNE-EN ISO 16283-2:2016, 10052:2005, 3746:2011),
"potencia acústica" (UNE-EN ISO 3746:2011, 4871:2010, 9614-1:2010),
"intensidad acústica" (UNE-EN 61043:1999, 9614-1:2010), "absorción
acústica" (UNE-EN ISO 3746:2011) and "nivel de presión acústica de
impactos" (UNE-EN ISO 12354-2:2018). Strings the UNE editions keep are
untouched: "emisión sonora", "exposición sonora", "campo sonoro",
"fuente sonora". Only Spanish values change; the English keys stay.
The two plot-i18n test assertions that pin the axis label follow, and
the eight Spanish doc fences that deliberately mirrored the old figure
labels now mirror the new ones.
* Regenerate the Spanish figure variants with the UNE terminology
make graphs after the string-source change: 48 files, all of them _es
or _es_dark variants (24 figure/diagram pairs), picking up "presión
acústica", "potencia acústica", "intensidad acústica", "absorción
acústica" and "nivel de presión acústica de impactos" in titles and
axis labels. The English variants are untouched. The committed example
fiches need no regeneration: every example in generate_reports.py
renders in English, so no changed Spanish report string reaches them.
* Align the intensity-fiche Spanish assertions with the UNE terminology
Spanish docs: UNE terminology, decimal commas, localized snippets and theory figures (#365)
* Normalize Spanish docs prose to UNE acoustic terminology
Sweep the Spanish pages for the non-UNE forms "presion sonora",
"potencia sonora" and "intensidad sonora" and normalize the prose to
the wording used by the official Spanish editions of the standards:
"presion acustica" (UNE-EN ISO 16283-2:2016, UNE-EN ISO 10052:2005,
UNE-EN ISO 3746:2011, UNE-ISO 226:2013), "potencia acustica"
(UNE-EN ISO 3746:2011, UNE-EN ISO 4871:2010, UNE-EN ISO 9614-1:2010),
"intensidad acustica" (UNE-EN 61043:1999, UNE-EN ISO 9614-1:2010) and
"nivel de presion acustica de impactos" (UNE-EN ISO 12354-2:2018,
UNE-EN ISO 717-2:2013). 92 replacements across 32 pages, prose only:
code snippets keep the labels they share with the generated figures.
Rename the intensity guide to "Intensidad acustica (p-p)" and the
structure-borne power guide to "Potencia acustica estructural de
equipos (EN 15657)", updating every cross-link text and the theory
anchor intensidad-sonora-iec-61043 accordingly. The API sidebar group
label becomes "Potencia acustica e intensidad" via api_taxonomy.py
with the generated sidebar regenerated.
* Use decimal commas in Spanish docs prose quantities
Sweep every Spanish page for decimal points leaking into prose numbers
and switch them to the comma the locale convention calls for: the
rotorcraft validation prose (fourteen tolerances such as 0,08 dB(A) and
0,5 s), the insulation-prediction worked example (53,6 dB / 53,8 dB),
the tone-audibility Annex E tone (137,3 Hz), the Mackenzie check value
(1550,744 m/s), the EPNL, loudness and absorption fiche descriptions
(98,3 EPNdB; 8,2 sonos and 70,4 fonios; 0,60 y 0,55), the junction
constants table and the third-octave band-edge table in the signal
analysis theory page. Code snippets, inline code, math, clause numbers
and quoted source text keep their points.
* Pass language="es" in Spanish figure-reproduction snippets
The <details> blocks under each localized figure in the Spanish guides
are meant to reproduce the _es variant shown on the page, but 82 of the
one-line result .plot() calls omitted the language="es" argument and
would render English axis labels instead. Add the argument across 33
guides, matching the pattern the intensity field-indicators snippet
already used. The three lab-insulation results whose plot() does not
take a language argument (lab airborne, lab impact and the
element-normalized intensity difference) are left as they were.
* Extend the UNE terminology sweep to sound absorption
Normalize "absorción sonora" to "absorción acústica" across the
Spanish pages (36 occurrences in 14 pages, including the EN 12354-6
guide title and the theory headings), the wording of UNE-EN ISO
3746:2011 and of the equivalent absorption area in UNE-EN ISO
10140-3:2011, UNE-EN ISO 12354-2:2018 and UNE-EN ISO 16283-2:2016, and
the same term the accredited report strings already use. Also restore
the accent in the theory index anchor to the renamed intensity heading,
since heading slugs keep accented characters.
* Theory pages: illustrate each section family with existing result figures
The seven theory pages carried no figures at all. Embed sixteen of the
existing generated figures (light and dark variants, Spanish variants on
the Spanish mirrors) where the concept matches the prose: weighting
curves, time integration and the p-p intensity estimator in the signal
analysis page; equal-loudness contours, the specific-loudness pattern
and the hearing-threshold panels in perception; NC/RC rating, the
Schroeder decay and the ISO 717-1 shifted reference in rooms and
buildings; the Lden profile, ISO 9613-1 absorption and the three sound
power routes in environment and transport; the scattering coefficient
and ISO 11654 rating in materials and surfaces; and the Wk weighting and
the resonator mobility triplet in vibration. Each embed reuses the alt
text of the guide that owns the figure and adds a one-line caption.
Also give the wave-simulation section hub the reading-order paragraph
its siblings already have, linking the closed-form pages the FDTD solver
cross-checks, in both languages.
* Address review findings from the Spanish locale sweeps
Second pass fixes flagged by review: five terminology occurrences the
line-based sweep missed because the noun and adjective were split by a
line wrap or emphasis markup (sound-power, rotorcraft-noise and two
section hubs); six Spanish pages that still embedded the English figure
variant although the _es variant exists (junction-transmission,
aircraft-noise airport contour, miso-coherence,
objective-intelligibility, psychoacoustic-annoyance twice); and 19 more
language="es" arguments on figure-reproduction plot calls the first
pass missed, chained-call receivers such as room.noise_criterion(spl)
.plot() and the axes-creating first call of the two-profile
atmospheric-refraction snippet among them. The lab-insulation plot
wrappers forward keyword arguments to the rating plot, so the three
calls excluded earlier now take the argument too. All Spanish python
fences re-checked to compile and EN/ES parity re-verified.
* Carry the UNE terminology into the generated-asset Spanish strings
Apply the same evidence-based decisions to every Spanish string source
that feeds generated assets: the translation tables of
generate_graphs.py and generate_diagrams.py, the _STRINGS tables of the
plot modules and the report i18n values. 35 strings change: "presión
acústica" (UNE-EN ISO 16283-2:2016, 10052:2005, 3746:2011),
"potencia acústica" (UNE-EN ISO 3746:2011, 4871:2010, 9614-1:2010),
"intensidad acústica" (UNE-EN 61043:1999, 9614-1:2010), "absorción
acústica" (UNE-EN ISO 3746:2011) and "nivel de presión acústica de
impactos" (UNE-EN ISO 12354-2:2018). Strings the UNE editions keep are
untouched: "emisión sonora", "exposición sonora", "campo sonoro",
"fuente sonora". Only Spanish values change; the English keys stay.
The two plot-i18n test assertions that pin the axis label follow, and
the eight Spanish doc fences that deliberately mirrored the old figure
labels now mirror the new ones.
* Regenerate the Spanish figure variants with the UNE terminology
make graphs after the string-source change: 48 files, all of them _es
or _es_dark variants (24 figure/diagram pairs), picking up "presión
acústica", "potencia acústica", "intensidad acústica", "absorción
acústica" and "nivel de presión acústica de impactos" in titles and
axis labels. The English variants are untouched. The committed example
fiches need no regeneration: every example in generate_reports.py
renders in English, so no changed Spanish report string reaches them.
* Align the intensity-fiche Spanish assertions with the UNE terminology
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.
Correct the slit-radiation sign in the slow-sound absorber and generalize the diffuser obliquity factor (#349)
* Fix slit-radiation sign, generalize Kirchhoff obliquity, record paper errata
The slit-to-free-air radiation correction of the slow-sound absorber was
transcribed with the papers' printed -iw sign, which in the e^{+jwt}
convention of the library acts as a negative mass and raises the slit-panel
resonance (378.6 to 386.8 Hz for the documented base geometry) where an
added radiation mass must lower it (370.8 Hz). The term is now conjugated
like the Stinson duct series of the same model, with the direction and the
shifted peak pinned by a regression test, and both source misprints (the
sign and the 4-vs-64 duct-series constant) are recorded in the errata
registry. The committed slow-sound figures are regenerated; the
critical-coupling design they show self-compensates to alpha = 1, but the
solved cavity length moves from 48.3 mm to 44.7 mm.
The diffuser far-field predictor now uses the symmetric Kirchhoff obliquity
factor (cos theta + cos psi)/2 for oblique sources, which reduces exactly
to the Cox and D'Antonio Eq. (9.32) normal-incidence form at psi = 0, so
all normal-incidence anchors are unchanged.
Also: the EN 29052-1 low-resistivity warning now echoes the clause 8.2
requirement to state the estimated error in the test report, and the
ISO 9053-2 volume-flow helper documents the normative 6.2 form against the
Annex A.2 rms variant.
* Update the porous-absorbers misprint count for the new errata entries
The page's sources now have five recorded misprints (Maa Eq. (5b), two in
Attenborough & Van Renterghem 2e, and the two Jimenez et al. entries); the
English and Spanish guides are kept in step.
Core metrology guides: result figures, ResampledSignalResult.plot() and real introductions (#364)
* Add ResampledSignalResult.plot(): the delivered anti-alias filter against its design spec
* Rewrite the multichannel guide intro: concrete use cases, per-channel guarantees, Bendat & Piersol citations
* Give the levels page a real introduction: descriptor families, when-to-use guidance and survey references
* Time weighting: framing intro with detector history, ANSI S1.4/Bies references and sibling links
* Calibration: proper introduction (SPL vs dBFS choice, workflow framing) with Bies reference and sibling links
* Add eleven core-metrology figures: resampling spec, cepstrum variants, liftering, correlation, IR alignment, Hilbert envelope, cross-spectra, Golay recovery, TSA noise law, runs test
* Test signals: anti-alias figure with dual snippet, res.plot() mention, See also, docs mirror sync
* Cepstrum guide: variants and liftering figures with dual snippets, complex-cepstrum plot mention
* Correlation guide: normalization, IR-alignment and Hilbert-envelope figures with dual snippets
* Spectral analysis: cross-spectral density and coherent-output-SNR figures with dual snippets
* System measurement: Golay exact-recovery figure and plot mentions for the inversion workflow
* Synchronous averaging: sqrt(N) figure, diagnostics-tool guidance, See also, recording naming
* Data qualification: runs-test figure with dual snippet and peak-statistics plot mention
* MISO coherence: conditioning-order snippet, practical pitfalls and See also across the three trees
* Time-frequency: Harris window reference and See also across the three trees
* GUM uncertainty: map the domain pages that consume the clause-5 machinery, expand See also
* Filter banks: compliance plot mentions and recording naming in the crossover snippet
* Changelog entry for the core-metrology documentation figures and prose
* Regenerate llms-full.txt for the core-metrology documentation
* Drop the duplicated phase-axis translation key
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.
Perception, hearing and speech guides: result figures and when-to-use prose (#361)
* Perception, hearing and speech guides: result figures and when-to-use prose
Add ToneAssessment.plot(), which draws an ECMA-418-1 tone against the
prominence criterion of its own method (recovered from criterion_db) over the
whole range of interest, with the deciding margin marked, and a
view="levels" option on ToneAudibilityResult.plot() that exposes the
level-versus-frequency view the ISO 1996-2 fiche already embedded.
Add eleven single-concept figures to the perception, hearing and speech
guides, each a realistic result drawn by its own .plot(): the tone prominence
verdict, the ISO/PAS 20065 tone levels, the ISO/PAS 1996-3 onset detection,
the ISO 532-2 specific loudness, the ECMA-418-2 specific tonality, the
specific fluctuation strength, the SII band audibility under a hearing loss,
the ISO 7029 fractile band, the ISO 1999 NIPTS audiogram, the STOI per-band
correlation and the IEC 60268-16 per-band MTI. Every figure is embedded in
the English guide, the Spanish mirror and the docs mirror with the dual
snippet block, and every plottable snippet on those pages now names its
.plot().
Prose: the tone-prominence guide gains a comparison of the four tonality
assessments the library implements and when each applies, the
hearing-threshold guide explains how the ISO 7029 fractiles feed the ISO 1999
age component and gains a See-also section, and the objective-intelligibility
guide gains the "which measure, and when" section that existed only in the
docs mirror, extended with the STOI/ESTOI, STI and SII choice.
Also label the STOI band axis at whole-hertz resolution and let the shaded
critical band of the tone-audibility levels plot follow the figure
background, so it reads on the white report page and on dark figures alike.
* Correct two figure alt texts against the plotted values
The ISO 7029 fractile figure describes the median and the 90 % fractile with
the values it actually draws (10 to 50 dB and 22 to 74 dB), and the
ECMA-418-2 specific tonality peak is at 9 Bark_HMS.
Follow the ANSI/ASA S12.2-2019 5.2.2 NC procedure and tighten the room fiches (#354)
Rate NC spectra by the standard's two-step procedure (5.2.2): the speech
interference level (clause 3.2, the average of the 500/1000/2000/4000 Hz
octave-band levels) selects the NC-(SIL) curve and the spectrum is
designated NC-(SIL) unless an octave band exceeds it, in which case the
tangency method (5.2.3) applies. NCResult gains sil, tangency_rating,
method, out_of_range and a label property; the tangency rating stays
available on every in-family result. Spectra outside the NC-15 to NC-70
family of Table 1 are flagged out of range instead of clamping the
interpolation to fabricated NC-71/NC-14 designations: above the family
the governing band is the maximum exceedance over the NC-70 curve and the
label reads ">NC-70 (band)"; below it, "<NC-15". Fiche, plot and
verdict render the flagged designation in English and Spanish.
On the RC Mark II side, warn when the clause D.4 minimum band set
(31.5 Hz through 4000 Hz) is incomplete, flag ratings outside the
tabulated RC-25 to RC-50 family (Table D.1) with an extrapolation note on
the fiche, and document the combined RH tag as a diagnostic extension
beyond the clause D.3.5 letters (N, R, H, RV; the RV vibration/rattle
classification needs the Table 6 test and is not implemented).
On the ISO 3382-1/-2 fiche, label the mid-frequency descriptor honestly
for one-third-octave data (only the 500 Hz and 1 kHz one-third-octave
bands are averaged, so the octave "500-1000 Hz" claim is dropped and the
bands are named), name the band of a fallback T30/EDT descriptor when the
range does not span both mid bands, and choose the EDT_mid vs EDT label
from EDT's own band coverage instead of T30's. Fiche verdicts (ISO 3382
and S12.2) now compare display-rounded values so the printed numbers can
never contradict the verdict at a tolerance boundary.
Correct two citations in the room-acoustics docstrings: the 5 %
reverberation-time JND is ISO 3382-1:2009 Table A.1 (ISO 3382-2:2008
Table A.1 tabulates the uncertainty constants G and H) and the -20 dB
onset trigger is ISO 3382-1:2009 A.3.4, with a note on the at-edge
sample choice. English ISO 3382-3 strings use point decimals
(STI = 0.50/0.20); the Spanish strings keep the comma.
Docs (EN/ES), API reference, conformance check, changelog and the
affected example fiches are updated accordingly.
Base the whole-body A(8) on the Directive 2002/44/EC Part B dominant axis and tighten the vibration fiches (#352)
Directive 2002/44/EC bases the whole-body daily exposure A(8) on the
highest of the frequency-weighted axis values 1,4*a_wx, 1,4*a_wy, a_wz
(Annex, Part B, point 1), not on the ISO 2631-1 Eq. (10) vector total
a_v; the hand-arm side keeps the Part A vector total a_hv.
- Relabel the whole-body magnitude of the daily-exposure fiche as
a_w,max with a printed note stating the Part B basis (EN/ES); add
vibration.wbv_exposure_basis() returning the dominant-axis value and
document in daily_vibration_exposure which magnitude each kind must
be fed with; state the same basis in the human-vibration guide
(EN/ES) and the measurement-chain diagram.
- Derive the boxed exposure zone of the fiche from the same
displayed-rounded comparisons as the assessment rows and the verdict,
so an A(8) printing exactly at a threshold cannot show a zone one
step below its own Exceeded row.
- ISO 10848 Kij fiche: distinguish an empty single-number mean caused
by every in-range band being bracketed (M < 0,25) from a spectrum
with no bands in the Annex A range (EN/ES).
- Spanish ISO 2631-5 outputs: translate the subject sex in the
injury-probability plot title and the word Formula in the fiche's
clause references.
- Correct the running_rms comment: the linear window divides by the
full window length (zero-padded front), it does not average over the
available samples.
Replace the ISO 17497-2 diffusion oracle with published-provenance data (#348)
The committed 37-receiver polar arc in tests/reference_data.py is now
generated by the library's own Fraunhofer far-field model for a published
geometry: the N = 7 QRD, 6 periods, 3.6 m wide, 0.2 m deep row of
Cox & D'Antonio, Acoustic Absorbers and Diffusers, 3rd ed., Appendix B
(the commercial N = 7 QRD of Hargreaves, Cox, Lam & D'Antonio, JASA 108(4),
1710-1720, 2000, Table I). The arc is an arithmetic oracle for ISO 17497-2
Formulas (5)/(7): d = 0.1099, flat reference d = 0.0049, d_n = 0.1055,
recomputed exactly from the committed rounded levels.
A new external third-party anchor pins the model's one-third-octave
band-averaged normalised diffusion to the published Appendix B 2D BEM values
for that row at normal incidence in the 200-400 Hz bands (agreement within
0.01, asserted at +/-0.015) in the tests and in four new conformance rows.
Across the full published 100-5000 Hz range the model-vs-BEM mean absolute
deviation is about 0.09, documented everywhere as a low-band anchor.
The previous in-house COMSOL simulation arc and its numbers are removed from
the tests, the conformance report, the surface-scattering guide (EN/ES), the
theory pages and the diffusion_polar figure, which now all use the same
published-geometry prediction.
fix: psychoacoustics standards-correctness fixes with corrected calibrations and printed oracles (#172)
* test: repair the ISO 532-1 Annex B fixtures path and extend the Annex B coverage
The relative data path was not adjusted when the test file moved into
tests/psychoacoustics/, so all 21 Annex B validation tests had been
silently skipping through the requires_iso_data guard. Point DATA at
tests/data/iso532_1 again and add a hard in-repo presence assertion so a
future move fails loudly instead of re-skipping.
Also enable the shipped Annex B.4 Test signal 13 (Nmax reproduces the
workbook header to 6e-6 relative), add a bounds-only Annex B.3 pink-noise
check against the workbook tolerance band, and note why the B.4 ramp
signals 6-9 cannot be regenerated.
* fix: ECMA-418-2 roughness front-end applies the Clause 5.1.2 fade-in and the calibration hits 1 asper
The roughness path skipped the mandatory 5 ms trigonometric fade-in of
Formula (1), which loudness and tonality already apply; recordings cut
mid-sound picked up an onset artefact that survived the transient discard
through the asymmetric smoother. The fade-in is now a shared helper used
by all three metrics.
With the fade-in in place and the Clause 7 reference signal synthesized at
its stated level (the overall RMS SPL of the modulated signal, 60 dB, not
the carrier-alone level), the chain reproduces the 1 asper calibration to
0.99990 asper using the tabulated c_R = 0.0180685. The former +7.35 %
'clean-room methodology variance' narrative was wrong and is removed from
the docstring, tests, reference data, conformance check and docs; the test
and conformance now pin the true 1.000 asper anchor.
A code note in the segmentation records why the printed Clause 5.1.5.2
l_last formula cannot be implemented literally (blocks would overrun the
padded signal): the flush-to-end reading is the only self-consistent one.
* fix: psychoacoustic annoyance combines the weightings per F&Z Eq. (16.2)
The combination formula had the '1 +' inside the radical,
N5*sqrt(1 + wS^2 + wFR^2), where Fastl & Zwicker (2006, p. 328) print
PA = N5*(1 + sqrt(wS^2 + wFR^2)). Every PA value with a nonzero
sharpness, fluctuation or roughness contribution was underestimated by
13 to 29 percent, and the worked reference value had been hand-computed
with the same wrong form, so tests and conformance confirmed the bug.
The worked tuple (N5,S,F,R) = (30, 2.0, 0.5, 0.3) now anchors
PA = 37.0478 (wS and wFR unchanged), and the docs, site guides and API
reference show the corrected formula. The SQAT claim is now accurate:
SQAT implements the (1 + sqrt) combination.
* fix: fluctuation-strength Bark constant 0.76e-3 per Zwicker-Terhardt (Osses 2016 Eq. 3 misprint)
Osses/Garcia/Kohlrausch (2016) Eq. (3) prints 0.76e-4 in the critical-band
rate formula, a 10x typo of the Zwicker-Terhardt 0.76e-3 disproved by the
paper's own anchors (0.5 Bark = 50 Hz, 15 Bark = 2.7 kHz, 23.5 Bark =
13.2 kHz). As transcribed, the 47 filter centres spanned 491 Hz-20 kHz with
12 bands pinned at the grid edge, distorting the specific pattern for any
signal away from 1 kHz (0.56 instead of 0.86 vacil at a 125 Hz carrier,
1.10 instead of 0.58 at 8 kHz).
The self-derived calibration constant lands at C_FS = 0.279, close to the
paper's 0.2490 and inside the sanity guard. The re-fitted H(fmod) corners
did not compensate the bug: the 1 kHz modulation sweep is essentially
unchanged (16 Hz point 0.19 -> 0.17 vacil), so the corners stand as fitted.
New oracles: the carrier-frequency sweep (125 Hz-8 kHz) against measured
reference values and the F&Z Fig. 10.5 trend (low-mid plateau, 8 kHz
roll-off), and the Osses Table 1 AM-broadband-noise row as a trend-only
cross-check (the excitation front-end overshoots the BBN pass-band by up
to ~3x, now documented; the FM row is deliberately not pinned). The module
docstring also records the front-end deviations from the paper and that
the normative ECMA-418-2 Clause 9 fluctuation strength is a different,
unimplemented method.
* fix: ECMA-418-2 loudness uses the full Clause 6.2.3 cross-block-size averaging
The loudness path averaged neighbouring-band ACFs only within a block-size
group, skipping the cross-group recomputation Clause 6.2.3 mandates for
bands adjacent to a block-size change. Since Clause 8.1.1 builds loudness
on the Clause 6.2 outputs, the requirement applies to loudness identically;
tonal content near the block-size boundaries was mis-stated by up to
-9.5 % (166.7 Hz tone at the z=2 edge).
The full averaging now lives in loudness_ecma as a single shared
_tonal_noise_split (front-end + Clause 6.2 tonal/noise decomposition) that
both the loudness and tonality metrics call, so the two report an
identical N'_tonal(l, z) for the same signal -- asserted by a new
cross-consistency test that spies on the shared intermediate through both
call sites.
The 1 kHz / 40 dB calibration moves from 0.9960 to 0.9845 sone_HMS. The
-1.55 % residual exceeds the +/-0.25 % adjustment allowed for c_N, which
is kept at the verbatim tabulated value; the investigation (documented in
the module docstring) attributes it to the mandated band averaging around
the block-size-boundary bands excited by the tone's lower flank: no band
averaging reads 0.955, same-group-only averaging 0.996, full cross-group
averaging 0.9845, while block-time smoothing and the fade-in/LP transient
contribute under 0.01 %.
Also in this pass: the common time grid is sized from the 1024-sample
stage's block count instead of the 8192 group's (whose 7 extra trailing
blocks only produced edge-held interpolations), the benign max(.,0) clamp
that Formula 48 does not print is documented in place, _band_range
validates the Formulae 56/57 preconditions (16 Hz < f_L, f_H < 20 kHz,
f_L < f_H) instead of silently swapping reversed edges, and the three
Sottek metrics document their mono-only scope (no Formulae 112/118
binaural combination).
* feat: ISO 532-1 stationary TimeSkip parameter and reference-program provenance notes
Annex B.1 requires the stationary calculation to start from 0.2 s when
validating against the official Annex B recordings (the reference
f_square_and_smooth takes a TimeSkip and the CLI requires it); the
stationary path previously always averaged the whole signal, so leading
silence or the filterbank transient diluted the mean square. The new
time_skip parameter (default 0.0, validated in both modes, applied by the
stationary method only) implements it.
Documentation nits from the same review: the sone-to-phon mapping records
that the exact 10/lg 2 factor and the 3 phon floor come from the
electronic attachment's reference program rather than the printed
Formula (2), the percentile note records the same provenance for the
(k-1, k) mean, and the filterbank tables are labelled correctly (Table A.1
is the reference section; the deviations and gains are Table A.2).
* fix: sharpness Annex B variants use the literal k = 0.11; ISO 532-2/-3 oracle expansion
DIN 45692 Formulas (B.1)/(B.2) print S = 0,11 * moment for the Aures and
von Bismarck variants; the implementation had derived per-variant
constants so the clause 6 reference sound read exactly 1.00 acum, shifting
every Aures result +4.2 % against other implementations of the published
formulas. With the literal 0.11 the reference lands near (not exactly at)
1 acum: 0.96 Aures / 1.02 von Bismarck, asserted non-circularly at
1.00 +/- 0.05. The normative clause 5.2 k remains derived, as the standard
prescribes.
Sharpness verification now covers all 21 Table A.2 rows and all 20
Table A.3 broadband rows (band edges per Tabelle A.1, every signal set to
the clause 6 loudness of 4 sone) at the normative 5 % / 0.05 acum
tolerance, and the conformance report gains a non-definitional Table A.2
row (2.5 kHz -> 1.78 acum) beside the definitional reference-signal check.
ISO 532-3: the module docstring now states the native-fs conformance mode
(clause 5 prescribes a 32 kHz conversion; this implementation processes at
the native rate with per-window FFT lengths) and the Annex C.1 anchor is
pinned at 32/44.1/48 kHz (0.9918/0.9926/0.9900 with a 0.5 s tone, 0.3 %
cross-rate spread). New oracles: the Annex C.3 multi-tone complexes, the
Annex C.1.2/C.1.3 sone columns, the ISO 532-2 Annex B phon columns, and a
note marking the byte-identical anchor pin as a regression pin rather than
a conformance oracle. The Table 5 phon mapping documents its silent
saturation at 120 phon.
* feat: ISO/PAS 20065 extended uncertainty of the audibility (Clauses 5.4 and 6)
Implements the Gaussian propagation of the uniform 3 dB narrow-band level
uncertainty through the audibility chain (Formulae (22)-(27)) to the
extended uncertainty U with 90 % bilateral coverage (k = 1.645,
Formula (29)), plus the Formula (28) combination for the mean audibility.
analyze_spectrum now reports the per-tone U on the result object, and the
docstrings carry the Clause 6 requirement that U shall be considered when
fewer than 12 spectra are averaged.
Oracles: the printed Table E.2 U column (the decisive 137.3 Hz tone
reproduces 2.79 dB to 0.006 dB from the E.1 lines; the flanking tones
within 0.1 dB, their critical bands extending beyond the truncated table),
the 2 FG row (3.21 dB, using the N summated tone levels as the K summands,
the reading that reproduces the print), the Table E.4 decisive chains of
all five spectra, the Annex E Step 4 mean uncertainty (1.38 dB) and the
Table E.2 LG/av columns and line-snapped band limits. Table E.3's full
tone record is pinned as printed data (its spectra lines are not
published). The E.1 NOTE 1 truncated-band LS values are recorded as not
reproducible under any tested iteration variant.
The distinctness test and module docstring record the DIN-vs-ISO print
difference (fT/sqrt(2) on both edges per the executable DIN 45681 Annex J
program, versus the asymmetric ISO print), the A-weighting requirement of
Clause 5.3.2, and the Formula (19) validity range 50 Hz < fT < 1000 Hz.
Two new conformance rows cover the per-tone and mean uncertainties.
* feat: ECMA-418 decision-threshold constants, degenerate-input warnings and range-note fixes
The verbatim decision criteria of ECMA-418-2 are now exposed and pinned as
constants-tests: audibility at 0.01 sone_HMS total basis loudness
(Clause 5.1.9), prominent tonality at 0.4 tu_HMS (Clause 6.3) and
prominent roughness at 0.2 asper (Clause 7.2) -- beyond the calibration
points these are the standard's only further numeric anchors (its annexes
are graphical).
ECMA-418-1 TNR/PR: the prominent verdict is documented as the numeric
criterion only (the aural-examination and lower-threshold-of-hearing
requirements of clauses 11.8/12.8 and 8/9 are the caller's
responsibility), and degenerate inputs now warn instead of silently
returning meaningless verdicts -- peaks within one bin of the 89.1 Hz /
11.2 kHz range edge (where bin snapping can flip the verdict) and
critical bands at numeric-noise power (silence, DC). A code note records
that the '11 220 Hz' upper range limit printed in clause 4.1.2
contradicts every formula and table of the standard, which use 11 200 Hz.
The binaural-scope notes now cite the correct clauses: Formula (118) /
Clause 8.1.5 for loudness, Formula (112) / Clause 7.1.11 for roughness,
and no binaural combination exists for tonality.
* docs: errata entries for the ECMA-418, ISO/PAS 20065 and Osses 2016 print defects; changelog for the psychoacoustics review
Records four defects surfaced by the standards-correctness review in the
errata registry: the ECMA-418-1 clause 4.1.2 '11 220 Hz' range misprint,
the internally inconsistent ECMA-418-2 clause 5.1.5.2 last-block formula,
the ISO/PAS 20065 clause 5.3.4 edge-steepness print that contradicts the
executable DIN 45681 Annex J reference program, and the Osses 2016 Eq. (3)
Bark-constant exponent typo (marked as a non-standard source). The
changelog gains the corresponding Added/Fixed entries.
* docs: regenerate the psychoacoustics figures and the conformance report
The five figures whose generators consume the corrected metrics
(Sottek loudness, tonality/roughness demo, loudness-model comparison,
fluctuation strength, psychoacoustic annoyance) are re-rendered in all
four language/theme variants. The conformance report picks up the
corrected anchors (roughness 0.9999 asper at the overall-60 dB reference,
ECMA loudness 0.9843 sone_HMS with the full Clause 6.2.3 averaging,
PA 37.0478) and the three new rows: the DIN 45692 Table A.2 sharpness
oracle and the ISO/PAS 20065 per-tone and mean extended uncertainties.
235/235 checks pass.
* docs: correct the PA formula box in the annoyance figure
The figure's info panel still printed the pre-fix combination
N5 sqrt(1 + wS^2 + wFR^2); it now shows the Fastl & Zwicker Eq. (16.2)
form N5 (1 + sqrt(wS^2 + wFR^2)) that the plotted curves already use.
* test: regenerate the ECMA loudness and roughness golden baselines
The goldens pin exact refactor-drift baselines; the ECMA-418-2 loudness
values legitimately changed with the full Clause 6.2.3 band averaging and
the roughness values with the Clause 5.1.2 fade-in, so the two cases are
recaptured with scripts/bench.py --golden (the tonality case is
byte-identical: it already used the full averaging).
* docs: uncertainty API in the tone-audibility guides and API reference
The Clause 5.4/6 extended uncertainty gets its section in the guide (EN and
the two site mirrors), the API reference gains the new function row and the
changed signatures (time_skip, extended_uncertainties), and a Spanish
output comment prints the dot the program actually emits.
* refactor: review follow-ups on the psychoacoustics changes
Range guards use direct comparisons, the API-reference row states the real
audibility_uncertainty signature, and the loudness calibration comment
matches the report's computed figure.
Correct DSP estimator edge cases: TSA time axis, echo detection, EQ validation (#356)
* Report the true 1/fs time axis from time_synchronous_average
The averaged samples sit on the 1/fs sampling grid (each block is aligned
back to it by the band-limited fractional delay before averaging), but the
result reported a time axis with T/M spacing. For a non-integer number of
samples per period the two grids drift apart across the period: with
100.37 samples per period the noiseless recovery error read against the
reported axis was 9.3e-2, versus 2.8e-6 on the true m/fs grid.
Return times = arange(M)/fs, document that the M samples cover the period
only approximately when fs*T is not an integer (McFadden 1987 revised
model: a block average on the sampling grid, no angular resampling), and
assert the non-integer-period tests on the true grid, with a regression
test pinning the 100.37-sample case on both grids. Also soften the comb
response [0, 1] claim (float cancellation beside a tooth can exceed 1 by
a few 1e-9) and mark the ~14 dB N = 32 figure as derived from Eq. 8
rather than printed in the paper.
* Detect inverting echoes and off-sample delays in echo_detection
echo_detection picked the maximum of the signed power cepstrum, so an
inverting reflection (a < 0), whose first rahmonic is a itself and hence
negative, was silently missed: with a = -0.4 at 313 samples the true
rahmonic c[313] = -0.42 was ignored and an unrelated 4739-sample peak
returned. Peak-pick on |cepstrum| within the searched band instead and
report the signed cepstrum value at the peak as the reflection
coefficient (the Mercator series ln(1 + a e^{-j theta}) holds for either
sign of a).
Also refine the reported delay by three-point parabolic interpolation of
|cepstrum| through the peak, so an off-sample delay (e.g. 313.5 samples)
is estimated to a fraction of a sample, and document the bin-splitting
caveat: at off-sample delays the peak-sample coefficient underestimates
|a| (about 65 % of it midway between bins). One-line convention note
distinguishing Milner's IDFT(ln|X|^2) power cepstrum from Bogert's
squared original. Tests cover a < 0 (exact impulse-echo closed form and
a noise source) and the interpolated half-sample delay.
* Validate parametric-EQ sections at the numerical extremes
Three validation gaps in the RBJ cookbook designer:
- A wide 'bw' at an f0 near Nyquist drives the w0/sin(w0) warping factor
into math.sinh overflow, escaping as a raw OverflowError; slightly less
extreme pairs produced a large but finite alpha whose rounded a2 landed
exactly at -1.0: poles on the unit circle designed silently. Wrap the
sinh with an informative ValueError and check the realized coefficients
against the stability triangle (|a2| < 1, |a1| < 1 + a2) after
normalization.
- A shelf slope beyond the gain-dependent bound (A + 1/A)/(A + 1/A - 2)
makes the cookbook's alpha complex and surfaced as a bare 'math domain
error'. Validate the bound at section construction with the limit in
the message (any slope is fine at 0 dB, where A = 1).
- Non-finite f0/gain_db/q/bw/slope passed validation silently (NaN
compares false against every bound) and produced NaN/inf coefficient
rows. Require finiteness explicitly.
Tests cover all three paths plus extreme-but-valid sections that must
keep designing stably.
* Scope the envelope-spectrum amplitude claim and add the band-pass front end
The docstring promised that a sinusoidal modulation 'reads out as a line
at its exact amplitude'; that holds only when the modulation frequency
falls on an analysis bin. Off-bin lines read low by the taper's
scalloping loss (about 1.42 dB, ~15 %, for the default Hann midway
between bins), like any single-record amplitude spectrum. State the
on-bin condition on the closed forms and the caveat in the module,
function and result docstrings, pinned by a midway-between-bins test.
Also add the optional band=(low, high) pre-filter to envelope_spectrum:
a zero-phase 4th-order Butterworth band-pass ahead of the detector, the
band-pass front end of Bendat & Piersol Figure 13.11 and the classical
bearing-envelope chain (isolate the excited resonance band, then
envelope it). The band travels with the result; a test shows it
recovering the exact modulation line under a strong out-of-band
interferer that swamps the raw envelope.
* Warn when a tone burst cannot close on the tone's zero crossing
IEC 60268-1 Clause A2.1 asks for an integral number of full periods, but
the gate closes after round(fs*cycles/frequency) samples: sample-exact
only when f and fs are commensurate. For 10 cycles of 997 Hz at 48 kHz
the burst spans 481.44 samples, gated at 481, and the waveform carries a
residual step at the gate edge. Document the condition and emit a
PhonometryWarning quantifying the realized residual (bounded by
amplitude*sin(pi*f/fs)) for incommensurate pairs; commensurate bursts
stay silent.
Also tighten the resampler's 'equiripple-bounded' wording: the Kaiser
window method bounds both bands by the same delta = 10^(-A/20), but its
ripple decays away from the band edges, unlike a true equiripple design.
* Document estimator conventions verified against their sources
Documentation and comment corrections, no behavior changes:
- random_data: keep the reverse-arrangement acceptance bounds on the
normal approximation deliberately and say why: B&P Table A.6 itself
follows it, and no percentage-point convention on the exact Mahonian
distribution reproduces the published entries (N = 40 and N = 100 at
a = 0.975 pin it down), so exact bounds would disagree with the table
users check against. The p-value stays exact; the one-count boundary
disagreement is now documented on TrendTestResult.
- time_frequency: the spectrogram energy-recovery statement was wrong as
written; integrating 'density' columns over frequency and summing over
time recovers the record energy only after multiplying by the hop
duration, up to the taper roll-off at the record edges (verified
0.988 vs 1 for Hann at 75 % overlap on white noise).
- spectra: note that the adaptive multitaper sigma^2 is mean(x^2) with no
mean removal (consistent with the documented no-detrending
calibration), and mark the 'P&W Eq. 369a' citation as pending
verification against the book.
- compliance + tests/reference_data: transcription note for the bare
'+3' at 20 Hz, Type 2, in ANSI S1.4-1983 Table V (read as upper-only;
+/-3 plausible; either way the verdict cannot change, the realized
deviation is -0.05 dB).
- room_ir: the Mueller & Massarani 'below 4 dB' crest-factor figure
assumes their slight magnitude smoothing, which shaped_sweep_signal
does not implement; measured sweeps here sit at 4.2-4.3 dB.
* Translate EQ section labels, complete the bibliography and changelog
- The per-section legend labels of the parametric-EQ plot printed the raw
English filter-type names in Spanish figures; route them through the
plot string table (peaking -> campana, shelves, band-pass, notch,
all-pass; lowpass/highpass were already translated for the lifter
plot). English output stays byte-identical.
- docs/references.md: add the Harris 1978 window paper and IEC
60268-1:1985, both cited by guide pages but missing from the collected
bibliography; link the IEC entry (and the one in test-signals.md) to
the IEC webstore page.
- Changelog entries for the DSP fixes; regenerate the API reference,
llms-full.txt and the cepstrum_echo figures (the detected-echo marker
moved by the sub-sample delay interpolation); the conformance report is
unchanged (419/419).
* Propagate the echo and envelope semantics to the site guides
Review follow-ups:
- The EN and ES site guides (cepstrum-echoes.mdx) still described the
old echo reading and the unqualified 'exact amplitude' claim; bring
them in step with docs/cepstrum-echoes.md: |cepstrum| peak-picking,
signed coefficient, interpolated delay, bin-splitting caveat, the
on-bin/scalloping condition and the new band= front end (the docs page
gains the same envelope-section text).
- State in the changelog and the result docstring that delay is no
longer exactly delay_samples/fs when the interpolated offset is
nonzero (delay_samples remains the whole-sample index the coefficient
is read at), and merge the duplicated Unreleased headings.
- Clearer stability message (poles 'do not lie strictly inside the unit
circle', full-precision coefficients) and an informative short-record
error for the envelope band-pass pre-filter instead of scipy's padlen
message.
- Sharper tone-burst warning test (asserts the quantified span and
residual) and a tighter interpolated-delay tolerance.
* Address review notes: cepstrum terminology, band-pass claims, edge validation
- Guides (docs and site, EN and ES): call the signed quantity what it is,
Milner's cepstrum of the log-power spectrum, and spell out that
Bogert's original 1963 power cepstrum squares once more and is
non-negative, so the signed rahmonics are the library's documented
convention; qualify the band-pass front end (out-of-band content is
strongly attenuated, not eliminated: finite Butterworth rejection and
small zero-phase edge transients).
- cepstrum: render reflection_coefficient as inline code in the
delay_samples docstring so the generated API page stops linking the
unrelated underwater seabed function.
- envelope: validate that 'band' is exactly two numeric edges before
indexing (a one-item tuple raised IndexError, extra entries were
ignored); the validated edges now travel to the result.
- equalizer: reject finite but unrepresentable gains through a shared
_gain_amplitude helper (10^(gain_db/40) overflowing, or underflowing
to zero and then dividing by zero in the designers); document the 0 dB
exception to the shelf-slope bound on the slope field.
- tone_burst: validate the repetition configuration before emitting the
incommensurate-frequency warning, so invalid setups raise even under
a warnings-as-errors filter.
- comb_filter_response: reject phase products that overflow to infinity
(sin(inf) returned NaN instead of the documented bounded response).
Tests for every new rejection path; API reference and llms regenerated.
* Test the representable gain amplitude as a positive finite bound
* Scope the first-rahmonic height claim to the source-free identity
Audit pass 4b: deprecation-cycle renames of published API (#112)
* refactor: deprecation-cycle renames of published API
Audit batch 4b — the published names that violate the naming convention
gain canonical replacements, with the old names working for one cycle
(NEP 23 DeprecationWarning: deprecated since 3.1, removal in 4.0):
- module loudness -> loudness_zwicker, with a PEP 562 __getattr__ shim
(plain 'import phonometry' emits no warning)
- renamed keywords via the sklearn sentinel, positional compatibility
preserved: road_absorption sample_rate -> fs; outdoor_propagation
humidity -> relative_humidity; sound_power room_volume -> volume
- legacy PyOctaveBand names normalized: octave_filter,
nominal_frequencies, normalized_frequencies and sensitivity are the
canonical implementations; octavefilter, getansifrequencies,
normalizedfreq and calculate_sensitivity delegate with a warning via
the shared _warn_renamed helper
- public string enums annotated with Literal (sex, field, presentation,
method) - annotation only, runtime validation untouched
Tests, scripts, docs and site sweep to the canonical names (~310
occurrences in 46 files); tests/test_deprecated_aliases.py pins every
alias with pytest.warns plus delegation equality and the both-given /
missing-required error paths.
* fix: address review feedback on the deprecation batch
- an explicit room_volume=None (the old default) no longer trips the
deprecation warning; only a real value through the alias warns, with
a regression test (Copilot)
- the calibration snippets stop shadowing the imported sensitivity()
with their result variable, in the repo doc and both site languages
(Copilot)
FutureWarning declined again with the rationale posted on the PR:
DeprecationWarning is the ecosystem norm for renames (NEP 23, scipy),
now codified in CONTRIBUTING.
* fix: second-pass review feedback on the deprecation batch
- loudness_zwicker validates calibration_factor through the shared
require_positive, closing the NaN/inf-permeable check (CodeRabbit)
- test names catch up with the octave_filter and sensitivity renames
(CodeRabbit)
- markdownlint MD022 blank line before the ES calibration heading
(CodeRabbit)
perf: parallel test suite with shared heavy fixtures (#203)
* test: share memoised heavy analysis results and drop a debug artifact write
The Annex C.1 Moore-Glasberg time-varying loudness suites analysed the
same calibrated tones independently (the phon table and the sone table
pin different columns of the identical result), and the anchor tone was
recomputed by three checks; a module-scoped memoising fixture now
computes each distinct (frequency, level, field, presentation) tone once
and shares the frozen result object. The ECMA-418-2 roughness peak and
modulation-depth checks likewise reuse the module calibration result
instead of recomputing the same 70 Hz AM chain. No oracle, tolerance or
assertion changes; the shared dataclasses are frozen and only read.
The pink-noise flatness test no longer renders and writes a debug PNG to
a fixed repo path: the plot asserted nothing, left an untracked file
behind and raced under parallel test workers. Its numerical flatness
assertion is unchanged.
* build: run the test suite in parallel with pytest-xdist
pytest -n auto in make test, make coverage and the CI tests job fans the
3241 tests out across every CPU core; pytest-xdist joins the dev
requirements. Each worker pins its numerical thread pools to one thread
(OMP/MKL/OpenBLAS/NumExpr/Accelerate) because with one worker per core
the nested BLAS pools only add contention: measured about 25% faster
wall-clock and about 40% less total CPU than unpinned workers on the
heavy DSP modules. Full suite on a 12-core host: 508 s to 138 s
(about 3.7x). pytest-cov combines the per-worker data files itself, so
the coverage.xml handed to Codecov and Sonar is unchanged (verified
identical 96% line coverage, same statement and miss counts). The
tests-perf job stays serial: it exists to exercise the numba-jitted
kernel, where per-worker JIT recompilation would dominate.
* docs: changelog entry for the parallel test suite
Audit pass 4b: deprecation-cycle renames of published API (#112)
* refactor: deprecation-cycle renames of published API
Audit batch 4b — the published names that violate the naming convention
gain canonical replacements, with the old names working for one cycle
(NEP 23 DeprecationWarning: deprecated since 3.1, removal in 4.0):
- module loudness -> loudness_zwicker, with a PEP 562 __getattr__ shim
(plain 'import phonometry' emits no warning)
- renamed keywords via the sklearn sentinel, positional compatibility
preserved: road_absorption sample_rate -> fs; outdoor_propagation
humidity -> relative_humidity; sound_power room_volume -> volume
- legacy PyOctaveBand names normalized: octave_filter,
nominal_frequencies, normalized_frequencies and sensitivity are the
canonical implementations; octavefilter, getansifrequencies,
normalizedfreq and calculate_sensitivity delegate with a warning via
the shared _warn_renamed helper
- public string enums annotated with Literal (sex, field, presentation,
method) - annotation only, runtime validation untouched
Tests, scripts, docs and site sweep to the canonical names (~310
occurrences in 46 files); tests/test_deprecated_aliases.py pins every
alias with pytest.warns plus delegation equality and the both-given /
missing-required error paths.
* fix: address review feedback on the deprecation batch
- an explicit room_volume=None (the old default) no longer trips the
deprecation warning; only a real value through the alias warns, with
a regression test (Copilot)
- the calibration snippets stop shadowing the imported sensitivity()
with their result variable, in the repo doc and both site languages
(Copilot)
FutureWarning declined again with the rationale posted on the PR:
DeprecationWarning is the ecosystem norm for renames (NEP 23, scipy),
now codified in CONTRIBUTING.
* fix: second-pass review feedback on the deprecation batch
- loudness_zwicker validates calibration_factor through the shared
require_positive, closing the NaN/inf-permeable check (CodeRabbit)
- test names catch up with the octave_filter and sensitivity renames
(CodeRabbit)
- markdownlint MD022 blank line before the ES calibration heading
(CodeRabbit)
feat: normative ISO 717 rating report (.report() PDF fiche) (#238)
* feat(building): ISO 717 Annex C rating report (.report() -> PDF)
Add a report(path) method that renders an ISO 717 Annex C sound-insulation
fiche to a one-page PDF. WeightedRatingResult.report() renders the airborne
ISO 717-1 layout (the Rw (C; Ctr) statement, the measured-versus-shifted
reference plot and the Table C.1 evaluation table with the sum of unfavourable
deviations), and ImpactRatingResult.report() renders the impact ISO 717-2
counterpart with the Ln,w (CI) statement and the opposite deviation sign.
SoundReductionResult.report() is a convenience that rates the predicted R(f)
and writes its fiche in one call.
Rendering uses reportlab, added as the optional phonometry[report] extra so it
stays out of the runtime dependencies; a missing reportlab raises a clear
ImportError with the install command, mirroring the matplotlib guard behind
.plot(). The rating results gain a quantity field ("airborne"/"impact") that
selects the report labels and standard reference.
* test(building): verify the ISO 717 fiche against the Annex C worked examples
Pins the report content to the printed worked examples: ISO 717-1 Annex C
Table C.1 (Rw(C;Ctr) = 30(-2;-3) dB, unfavourable sum 31,8 dB, reference shifted
by -22 dB and the per-band deviations) and ISO 717-2 Annex C Table C.1
(Ln,w = 79 dB, CI = -11 dB, sum 28,0 dB).
* feat(building): one-page ISO 717 fiche with a vector (SVG) plot
Embeds the ISO 717 plot as vector graphics via svglib (svg2rlg, text as paths)
so the curve stays crisp at any resolution, tightens the layout to a single
A4 page, and reduces the single-number statement line. Adds svglib to the
optional 'report' extra.
* feat(building): two-column ISO 717 fiche (plot beside condensed table)
Places the vector plot and the condensed ISO 717 Annex C evaluation table
side by side (datasheet layout) and drops the plot's redundant title, since
the header already states the single number and the unfavourable-deviation sum.
* feat(building): accredited-laboratory ISO 717 report fiche + ReportMetadata
Rework the ISO 717 `.report()` PDF into an accredited-laboratory test-report
layout modelled on ISO 10140-2 / ISO 16283 lab reports rated per ISO 717:
a standard-basis line, an optional metadata header block, the one-third-octave
table beside the measured-versus-shifted-reference plot, a boxed single-number
result, an optional PASS/FAIL verdict row and a footer with a fixed disclaimer.
Report metadata is now a shared frozen `ReportMetadata` dataclass (specimen,
client, room and climatic conditions, laboratory identity and an optional
requirement); all fields optional and only the supplied ones render, so the
same object drives a full accredited fiche or, with metadata=None, a prediction
fiche. `report()` gains `metadata` and `verbose` keywords; `verbose=True` keeps
the ISO 717 Annex C evaluation columns. The rating results keep their vector
(SVG) plot. `ReportMetadata` is exported at the top level and documented.
* test: add pypdf dev dependency for the report one-page assertion
* feat(building): enlarge the report plot and narrow the table to match the accredited reference
* feat(building): fixed report plot axis, legend above, per-room climate metadata
* feat: generate committed example ISO 717 report fiches (make reports)
Adds scripts/generate_reports.py and a 'reports' Make target that render the
example .report() fiches into .github/reports/ (airborne and impact), linked
from the documentation as normative-report examples, mirroring the figure
pipeline. Not byte-checked in CI (the vector plot differs by ~1 ULP across
CPUs); tests/test_generate_reports.py checks the generator still yields valid
one-page PDFs.
* test(architecture): treat _report as a rendering leaf like _plot
The report renderer mirrors _plot: domain modules import it lazily inside
.report() and it references the rating result types only under TYPE_CHECKING,
so the whitelist exempts the _report edges exactly as it does _plot, and the
TYPE_CHECKING-only guarantee now covers _report too.
* refactor(_report): address Sonar findings on the ISO 717 renderer
Imports reportlab names directly per function instead of via a dict alias
(clears the capitalized-local warnings), extracts the result box and verdict
into helpers to lower the render function's cognitive complexity, switches the
doc kwargs to a dict literal, and hoists the path construction out of the
report tests' pytest.raises blocks.
* fix(_report): address PR review (XML escaping, temperature/humidity ranges, extras)
- Escape user-supplied metadata strings before rendering them in reportlab
Paragraphs so a '&', '<' or '>' can no longer crash the XML parser.
- ReportMetadata now validates by physical range: dimensions/mass/volume/
pressure/requirement stay strictly positive, temperatures may be zero or
negative (cold test conditions), relative humidity must be 0..100 %.
- Add svglib to the 'full' extra (it is a real report dependency).
- Use a comma decimal separator consistently across the fiche, enclose the
verbose sum row in the table border, and assert equal-length band arrays.
- Rename the render-leaf architecture test for its _plot + _report scope.
* fix(_report): use a period decimal separator (English fiche)
The fiche text is English, so numbers use a period separator throughout
(metadata grid and one-third-octave table alike), matching accredited English
lab reports. A locale-aware separator will come with the planned EN/ES i18n.
* docs: regenerate API reference after the ReportMetadata docstring update
* refactor(_report): lower ReportMetadata validation complexity
Extract a _require helper so __post_init__ is three short field loops,
dropping the cognitive complexity flagged by Sonar.
* docs(_report): describe ReportMetadata validation accurately in the summary
The class summary now states the actual per-range validation (positive
dimensions/mass/volume/pressure/requirement, finite temperatures allowing 0 C
or below, humidity within 0..100 %) instead of a blanket strictly-positive
claim; the :raises: note and __post_init__ behaviour are unchanged.
docs: modular-first documentation, metrology filter exports and the always-expanded sidebar (#210)
* feat: export octave_filter and octavefilter from phonometry.metrology
Move the octave_filter() convenience wrapper, its deprecated
octavefilter() alias and the design cache from phonometry/__init__.py
into phonometry.metrology.core, next to the OctaveFilterBank they wrap.
Both names are now re-exported by the metrology subpackage (matching
how every other deprecated alias propagates, e.g. normalizedfreq) and
the top level keeps re-exporting them, so existing imports are
unaffected and the deprecation warning behavior is unchanged.
The generated API reference now documents both under metrology.core,
and the API index and llms.txt quickstart present the modular
subpackage import as the primary form.
* docs: modular API in every snippet, with self-contained continuation blocks
Convert every remaining flat-API code block in docs/ and in the English
and Spanish site guides and theory pages to the modular subpackage form
(from phonometry import <domain> plus <domain>.func(...)): 228 blocks
per tree, including the figure <details> blocks the earlier retrofit
had left flat and the legacy module-path imports
(phonometry.occupational_exposure, phonometry.room_noise,
phonometry.noise_induced_hearing_loss).
Continuation blocks that leaned on names defined in a neighbouring
snippet now carry their own minimal setup (imports plus the defining
statements), so every runnable example executes exactly as printed; the
stale 'from the snippet above' comments are gone. Blocks that
deliberately reference the reader's own data (recording, audio_blocks,
device captures) keep their placeholder form.
Every block in the three trees was executed before and after the
rewrite: all previously-running blocks produce byte-identical output,
123 continuation blocks now run standalone, and the intentional
placeholders fail at the same documented point as before.
llms.txt and llms-full.txt regenerated (make llms) to pick up the
converted guide content and the modular quickstart.
* docs: drop the old-anchor map from the theory index
The three theory index pages (docs/theory.md and the EN and ES site
reference pages) still carried the migration table mapping every anchor
of the old single-page theory reference to its new host page. All
internal links were retargeted when the reference was split, so the
table only added noise; the per-domain section lists stay.
* docs: present the modular namespaces as the primary API surface
The API quick-reference intro and the README quickstart still framed
the flat top-level API as the primary surface. Both now lead with the
domain-subpackage import (the form used across the documentation) and
note that every public name remains importable from the top level. The
ph.-prefixed usage snippets in the quick table drop the alias prefix,
and the deprecation note for the pre-3.2 module paths is unchanged.
* site: fully expanded sidebar with typographic hierarchy
Every sidebar group and subgroup now ships open: the collapsed flags are
gone from astro.config.mjs and from the generated api-sidebar.mjs (the
emitter in scripts/generate_api_docs.py no longer writes them), so the
whole map of the docs is visible at once, including in the mobile
drawer where the domain groups used to render as closed chevrons. The
groups stay toggleable.
With ~150 entries visible, hierarchy comes from a small stylesheet
(src/styles/sidebar.css) instead of folding: top-level groups become
small uppercase eyebrow headers with a hairline separator, nested group
labels step down one text size, and links two levels deep tighten up so
the API reference stays compact. Starlight color tokens only, so light
and dark themes are covered; summary toggles and focus styles are
untouched (pa11y 30/30).
* docs: runnable wind-turbine snippets on the site pages
The docs-tree wind turbine guide already synthesized its inputs (the
band levels for the apparent sound power example and the narrowband
tonality spectrum), but the EN and ES site pages still consumed
undefined placeholder names. Port the same setup lines so the site
blocks execute standalone like their docs counterpart.
* docs: review fixes for the snippet sweep
Drop the calibrator-recording step comment that the self-containment
pass copied into the dBFS and RMS-vs-peak calibration blocks (their
code synthesizes a plain capture, not a calibrator tone), and remove
the docs index's mention of the theory anchor map that no longer
exists. llms-full.txt regenerated.