A Spanish figure writes its decimal comma on every panel it draws, and two guards keep it that way (#777)
Every helper that writes text takes a language and defaults to English, so a call that forgets to pass it on raises nothing and puts an English decimal point into a Spanish figure. The plotting modules, the figure generators and the fiches now hand the caller's language to every helper that takes one, and localize_axes is called once per axes a plot builds rather than once per figure: a twin axis, a colorbar and the z of a 3-D panel each carry a formatter of their own, and each was shipping in English beside a panel already in Spanish. What decides whether an axis is localised is its formatter and not its scale, so a log axis of distances still writing its own numbers is reached and a pinned frequency axis is left to format_frequency_axis. A zoom inset and a contour colorbar, which the save-time pass of the figure generators cannot reach, are localised where they are built. The band column of the room criterion sheet and of the whole sound-power fiche family is written in the sheet's language too.
scripts/check_language_forwarding.py fails on any call to a helper that takes a language, made from anywhere a language is in scope, that does not pass it on. It reads module and class bodies as well as functions, follows a helper through a local alias and a functools.partial, names the shapes it cannot reach, keys an exemption by its line so one approved call cannot cover another, and reports a method name only some of whose namesakes take the language, which silently switched the untyped-receiver check off.
scripts/check_figure_decimal_point.py reads the committed Spanish figures and fails on a tick label that is a number with a point in it, whichever pass was meant to write the comma. It found six figures, regenerated here. Both run from the Makefile and in CI.
Judge a hearing loop by IEC 60118-4 and its amplifier, loop and neck loop by IEC 62489-1 (#908)
An installed hearing loop can now be judged by IEC 60118-4 with its Amendment 1: the field reaches its level at one point, stays within 3 dB over the useful volume, keeps its frequency response and its noise, a refuge or a counter is judged at the points of Figures 2 and 3, and the amplifier passes the overload test at the frequency of Table 4. Its components are measured by IEC 62489-1: the Biot-Savart field of a rectangular loop, the current and size a loop needs, its resistance, inductance and impedance, the amplifier's maximum current, compliance voltage, noise, response, AGC range and quadrature error, and a neck loop against the two types of the draft Amendment 2. Every result draws its own figure, a new guide in both languages walks a place of worship, a counter, an amplifier and a neck loop through both standards, the conformance report gains 67 rows and thirteen printed defects are in the errata register.
Judge a hearing loop by IEC 60118-4 and its amplifier, loop and neck loop by IEC 62489-1 (#908)
An installed hearing loop can now be judged by IEC 60118-4 with its Amendment 1: the field reaches its level at one point, stays within 3 dB over the useful volume, keeps its frequency response and its noise, a refuge or a counter is judged at the points of Figures 2 and 3, and the amplifier passes the overload test at the frequency of Table 4. Its components are measured by IEC 62489-1: the Biot-Savart field of a rectangular loop, the current and size a loop needs, its resistance, inductance and impedance, the amplifier's maximum current, compliance voltage, noise, response, AGC range and quadrature error, and a neck loop against the two types of the draft Amendment 2. Every result draws its own figure, a new guide in both languages walks a place of worship, a counter, an amplifier and a neck loop through both standards, the conformance report gains 67 rows and thirteen printed defects are in the errata register.
phonometry installs on Python 3.12 and on the numpy, scipy and matplotlib it really needs, and a CI job keeps those floors honest (#956)
phonometry now installs on Python 3.12, 3.13 and 3.14, and the version floors in the package metadata are the oldest releases the library's tests pass with on Python 3.12, each measured with every other floor installed beside it: NumPy 2.0.2, SciPy 1.14.0, matplotlib 3.10.7, reportlab 4.0.4, svglib 0.9.0, numba 0.60.0 and python-soundfile 0.12.0. A new minimum-versions job installs exactly those floors and runs the library's tests, Dependabot moves the pins and leaves the floors, and a one-element array passed where one number is asked for is now refused with a ValueError that names the parameter on every NumPy.
The same argument for the geometry that had two units on one name (#755)
* The same argument for the geometry that had two units on one name
#752 put the unit in the name where a pressure, a temperature or a
humidity could be read two ways. Measured over the imported API, four
geometric quantities were in the same state, and one of them was two
different dimensions.
diameter was metres in twenty public parameters and millimetres in one,
cnossos_rail.wheel_transfer, where CNOSSOS-EU Table G-3b tabulates 920,
840 and 680. angles was degrees in ten and radians in four while angle
was radians in eleven, so the plural and the singular of one word
disagreed. period was a lattice pitch in metres in the slit and
metadiffuser panels and a repetition time in seconds in the synchronous
average. gradient was a road slope in per cent in CNOSSOS-EU and a
vertical sound-speed gradient in s^-1 in refraction and sound speed.
They become diameter_m and diameter_mm, angle_rad, angles_rad and
angles_deg, period_m and period_s, road_slope_percent and gradient_per_s.
periods, a count of repetitions sitting beside a period that is a length,
becomes repetitions; the two places where it really means evaluation
periods keep the word and are in the guard's exemptions with the reason.
GaussianPulse.width is a half-width in seconds and the perfectly matched
layer of the same module measures its own width in cells. It says
half_width_s now.
No value changes: the conformance report is the same 995 checks with the
same numbers. The guard grows a second tuple, matched against the whole
name, and says in its own comment that the seventy-five compound names
are a second pass rather than an exemption: WestonRegimeBoundaries has
critical_angle in radians one screen above a function that takes it in
degrees. Against main the guard reports 71; here none.
* Re-render the three clips the renamed scenes drew
Close the three refusals that could not say what they refused (#643)
* Close the three refusals that could not say what they refused
A comparison helper walked results field by field and named the field it
reached, except at the top: the path started empty, so a mismatch there
raised an AssertionError carrying no message. The two tests that assert a
difference IS expected could then only write a bare pytest.raises, which
accepts any failure of the helper, including a shape mismatch that has
nothing to do with the contract they are named for. The path starts at
"result" now and a leaf says what the two sides hold.
A loop gain guarded three parameters with one sentence: levels and the
directivity index must be finite. True of all three and a help with none,
since the gain is a difference of two separately measured levels plus a
directivity index, so which one arrived bad decides where the caller looks.
Each is named, and the test that pinned the old sentence is parametrised
over the three, which the old sentence made impossible.
And the samples were checked for finiteness before the calibration factor
scaled them, so two finite numbers could produce an infinity that nothing
had refused. Both operands are finite by contract, so the value was created
between them. The multiplication now refuses it and names both, because
neither is wrong alone.
That last one was left open earlier for costing a second pass over every
calibrated recording. It does not: numpy detects the overflow in the
multiplication itself, and errstate only decides what to do about it, which
measures at parity while the second pass costs half as much again. The
headroom is also not close. A double reaches 1.8e308 and the loudest
quantity acoustics measures is a blast overpressure of a few bar, so a
product that overflows is a calibration chain built wrong rather than a
scale that will not fit, and a wider type would not have helped: longdouble
is the same 64 bits as double on the Windows runners this repository tests
on.
* Say in resolve_samples what it now refuses