← [Documentation index](README.md)

# Why phonometry

phonometry is a standards-based acoustic measurement toolkit. Its
differentiator is not the list of features but how they are built: every
metric is implemented from the governing standard's text, and the standard's
own reference values and acceptance limits are transcribed into the test
suite and enforced in CI. This page explains that approach with a concrete
case study — time weighting under **IEC 61672-1:2013** — and summarizes what
is conformance-tested today. The time-weighting analysis was originally
published in [issue #38](https://github.com/jmrplens/phonometry/issues/38).

## Design philosophy: a case study in time weighting

Standard time weighting is defined as a **continuous function of time** via the
differential equation

$$
\tau \frac{dy(t)}{dt} + y(t) = x^2(t)
$$

which corresponds to a stable first-order low-pass filter with a pole in the
left half-plane ($s = -1/\tau$).

| | phonometry | python-acoustics |
| :--- | :--- | :--- |
| Output | Continuous time-weighted envelope (one value per sample) | Stepped output (one value every τ seconds) |
| Input units | Raw sound pressure (Pa); squared internally | Energetic quantity (Pa²) expected as input |
| Filter | Stable exponential averaging (pole at $s=-1/\tau$) | Theoretically unstable design (pole in the right half-plane) stabilized by resetting the filter state every τ seconds |
| Behavior | True IEC time weighting | Closer to a block integrator ($L_{eq,\tau}$) |

A pole on the negative real axis corresponds to a decaying exponential impulse
response ($h(t) \propto e^{-t/\tau}$) — exactly what "exponential time
weighting" means: past events are forgotten exponentially. A pole on the
positive real axis grows without bound; block-resetting hides this but changes
the measurement's nature.

## Verification against IEC 61672-1 (tone bursts)

The rigorous test for time weighting is the **Tone Burst Response**
(IEC 61672-1, Table 4), using a 4 kHz sine burst referenced to the steady-state
level.

**phonometry results (FAST):**

| Burst duration | IEC target (dB) | phonometry (dB) | Error (dB) | Status |
| :--- | :--- | :--- | :--- | :--- |
| 200 ms | −1.0 | −0.98 | +0.02 | ✅ PASS |
| 50 ms | −4.8 | −4.82 | −0.02 | ✅ PASS |
| 10 ms | −11.1 | −11.14 | −0.04 | ✅ PASS |
| 1 ms | −20.9 | −20.99 | −0.09 | ✅ PASS |

**python-acoustics results (FAST, squared signal passed as required by that
library):**

| Burst duration | IEC target (dB) | python-acoustics (dB) | Error (dB) | Status |
| :--- | :--- | :--- | :--- | :--- |
| 200 ms | −1.0 | −0.97 | +0.03 | ✅ PASS |
| 50 ms | −4.8 | −3.93 | **+0.87** | ⚠️ FAIL |
| 10 ms | −11.1 | −10.90 | +0.20 | ✅ PASS |
| 1 ms | −20.9 | −20.90 | +0.00 | ✅ PASS |

phonometry maintains high precision across all test cases. The block-based
approach deviates significantly (> 0.8 dB) for the 50 ms burst because 125 ms
blocks cannot resolve short transient events accurately — the result depends on
how the burst aligns with the block boundaries.

<picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/jmrplens/phonometry/main/.github/images/tone_burst_iec_dark.svg"><img src="https://raw.githubusercontent.com/jmrplens/phonometry/main/.github/images/tone_burst_iec.svg" alt="Fast envelope responses to 200, 50 and 10 ms tone bursts peaking exactly at the IEC 61672-1 Table 4 reference values" width="80%"></picture>

*Measured Fast envelopes (blue) matching the Table 4 reference values
(dashed) within 0.1 dB for 200/50/10 ms bursts.*

## What this means in practice

- If you need **standard-compliant Fast/Slow/Impulse envelopes** (sound level
  meter behavior, one level per sample), use phonometry's
  [`time_weighting`](time-weighting.md).
- If you need **block-averaged Leq per interval**, that is a different, equally
  valid metric — you can compute it with [`leq`](levels.md) over consecutive
  slices.
- Both approaches are useful; they just answer different questions. The
  discrepancy reported in issue #38 comes from comparing a continuous envelope
  against a block integrator, not from an implementation error.

## Conformance testing across the library

The tone-burst case above is not an isolated check. For each standard the
library implements, the reference values and acceptance limits are transcribed
from the official text into the test suite, so any regression fails CI:

| Standard | What is verified | Test file |
| :--- | :--- | :--- |
| IEC 61672-1:2013 Table 3 | A/C/Z weighting at all 34 nominal frequencies, class 1 limits, at 48 and 96 kHz | `tests/test_iec_weighting_table3.py` |
| IEC 61672-1:2013 Table 4 | F/S tone-burst responses (1 s to 1 ms) and the LAE column for `sel()` | `tests/test_iec_compliance.py` |
| IEC 61672-1:2013 Table 5 | `lc_peak()` one-cycle/half-cycle peak responses, class 1 limits | `tests/test_levels.py` |
| IEC 61260-1:2014 Table 1 | Filter-bank class 1/2 acceptance limits via `verify_filter_class()` | `tests/test_compliance.py` |
| ISO 7196:1995 Table 2 | G weighting (infrasound) at every nominal response value, 0.25–315 Hz | `tests/test_g_weighting.py` |
| ISO 226:2023 Annex B | Equal-loudness contours, loudness levels and hearing threshold against the Annex B tables | `tests/test_loudness_contours.py` |
| ECMA-418-1:2024 | TNR/PR tone prominence: critical bandwidths, proximity spacing and prominence criteria against the worked examples in clauses 10–12 | `tests/test_tonality.py` |
| ISO 1996-1:2016 | `lden()`, `ldn()` and `composite_rating_level()` against hand-computed formula values | `tests/test_environmental.py` |
| IEC 60942:2017 Table 2 | Calibrator short-term stability limits (frequency-dependent, class 1) in `sensitivity()` | `tests/test_calibration_validation.py` |

The full numerical report — the expected value and the value the library
computes for every check, regenerated on every pull request — is published
as [CONFORMANCE.md](CONFORMANCE.md).

Beyond IEC 61252-style noise dose (`sound_exposure()`, `lex_8h()`), the same
standards-first mindset shows up in the numerics: filter banks place their
−3 dB points on the **ANSI S1.11 / IEC 61260-1** band edges for every
architecture (including Chebyshev II and Bessel, where scipy's raw
parametrization would not), and A/C weighting stays within class 1 tolerances
up to 16 kHz at common audio rates via internal oversampling (see
[Frequency Weighting](weighting.md)).

## Where phonometry fits in the Python ecosystem

- **python-acoustics** was archived in February 2024 and is no longer
  maintained. Its comparison above reflects the last released code.
- **acoustic-toolbox**, the community successor to python-acoustics, depends
  on phonometry for its weighted level computations rather than reimplementing
  them.
- **MoSQITo** focuses on psychoacoustic sound-quality metrics (loudness,
  sharpness, roughness). It complements rather than overlaps phonometry:
  it does not cover sound level metrology (weighting filters, ballistics,
  Leq/SEL/Lden, calibration) and does not claim conformance testing against
  the standards' tolerance tables.

If your work needs numbers you can defend against a standard's tolerance
table — measurement reports, environmental assessments, instrument
cross-checks — that verification layer is what phonometry is for.
