Skip to content

Impedance Tube

Standards: ISO 10534ASTM E2611Key references: Allard & Atalla 2009

The impedance tube is the materials laboratory shrunk to a bench: a rigid duct a few centimetres across in which a loudspeaker drives a single plane wave against a small sample, and everything the sample does to that wave is read back from microphones in the tube wall. Because there is one wave and one angle, the tube recovers what a reverberation room cannot: the complex reflection factor and surface impedance at normal incidence, magnitude and phase, from a specimen the size of a coffee-cup lid. Three standards share the hardware and differ in method, and the library keeps their helpers separate and never mixes them: the standing-wave-ratio method of ISO 10534-1, the two-microphone transfer-function method of ISO 10534-2, and the four-microphone transfer-matrix method of ASTM E2611 that adds the transmission loss. The closing section runs the whole instrument, sample included, inside the FDTD solver and recovers the analytic answer through the same reduction chains.

ISO 10534-2 two-microphone impedance tube: a loudspeaker radiating a plane wave down the tube, two microphones flush in the wall at spacing s and distance x1 from the specimen face, the test specimen against a rigid backing, and the incident and reflected wavesISO 10534-2 two-microphone impedance tube: a loudspeaker radiating a plane wave down the tube, two microphones flush in the wall at spacing s and distance x1 from the specimen face, the test specimen against a rigid backing, and the incident and reflected waves

Everything the tube reports (ISO 10534-2, Clause 4) assumes the field inside is a single plane wave, and the geometry sets both ends of the usable band. Above the cut-on of the first cross-sectional mode the field stops being planar: a circular tube of diameter needs (Eq. (2); for a rectangular tube, Eq. (3)). The microphone spacing must also stay clear of the half-wavelength singularity of the transfer-function method, (Eq. (4)). At the low end the opposite problem appears: a spacing much shorter than the wavelength leaves almost no phase difference between the microphones to measure, so the Clause 4.2 guideline keeps the spacing above 5 % of the wavelength:

No single tube covers the building-acoustics range. A 100 mm tube with a 100 mm spacing works from roughly 170 Hz to 1.5 kHz; reaching the 5 kHz bands takes a small tube (29 mm) with a close spacing, which in turn cannot see the low bands. Laboratories therefore pair a large and a small tube (or one tube with two spacings) and splice the spectra; the two must agree in the overlap bands, and a mismatch there points at the sample cut or mounting, not at the physics.

from phonometry import materials
# A 100 mm tube with 100 mm spacing, and a 29 mm tube with 20 mm spacing.
f_l, f_u = materials.plane_wave_frequency_range(0.100, 343.2, diameter=0.100)
print(round(f_l, 1), round(f_u, 1)) # 171.6 1544.4
f_l, f_u = materials.plane_wave_frequency_range(0.020, 343.2, diameter=0.029)
print(round(f_l, 1), round(f_u, 1)) # 858.0 6864.0
Horizontal bars on a shared logarithmic frequency axis from 100 Hz to 8 kHz with the one-third-octave grid marked: a 100 mm bore with 100 mm and 50 mm microphone spacings, a 29 mm bore with 20 mm spacing, and the same 100 mm bore under the ASTM E2611 limits, each bar labelled with the limit that binds it at either end, and the overlap between the large and the small tube hatchedHorizontal bars on a shared logarithmic frequency axis from 100 Hz to 8 kHz with the one-third-octave grid marked: a 100 mm bore with 100 mm and 50 mm microphone spacings, a 29 mm bore with 20 mm spacing, and the same 100 mm bore under the ASTM E2611 limits, each bar labelled with the limit that binds it at either end, and the overlap between the large and the small tube hatched

Which tube sees which bands. Each bar is a geometry, and each end is labelled with the constraint that binds it: the low end is always the phase-resolution floor, while the top end is the cut-on for a wide-spaced tube and the spacing singularity once the microphones are far apart. The 100 mm tube with 100 mm spacing covers the 200 Hz to 1250 Hz one-third-octave bands and the 29 mm tube with 20 mm spacing the 1 kHz to 5 kHz bands, so the pair splices in the 1 kHz to 1.25 kHz overlap — and even together they do not reach the 100 Hz, 125 Hz and 160 Hz bands, which is why low-frequency absorption data come from the reverberation room and not from a tube. The ASTM bar is the same hardware under E2611’s constants: wider at the bottom (a 1 % of wavelength floor instead of 5 %) and narrower at the top (a spacing bound instead of ).

Show the code for this figure
for spacing, diameter in ((0.100, 0.100), (0.050, 0.100), (0.020, 0.029)):
iso = materials.plane_wave_frequency_range(spacing, 343.2, diameter=diameter)
astm = materials.plane_wave_frequency_range_astm(spacing, 343.2, diameter=diameter)
print(f"{diameter * 1e3:.0f} mm bore, s = {spacing * 1e3:.0f} mm:",
f"ISO {iso[0]:.0f}-{iso[1]:.0f} Hz, ASTM {astm[0]:.0f}-{astm[1]:.0f} Hz")

Those limits are easier to see on the hardware itself. plot_impedance_tube_geometry draws a tube to scale with its plane-wave band worked out, and a measured two_microphone_impedance result that was given its geometry (spacing, x1, diameter) redraws its own setup with result.plot_geometry().

To-scale side view of a 100 mm ISO 10534-2 impedance tube: the loudspeaker at the left end, microphones 1 and 2 flush in the wall at s = 50 mm spacing with microphone 1 at x1 = 150 mm from the specimen face, the specimen against the rigid backing at the right, the circular cross-section beside the tube and the plane-wave working range of 343 to 1991 Hz that this geometry setsTo-scale side view of a 100 mm ISO 10534-2 impedance tube: the loudspeaker at the left end, microphones 1 and 2 flush in the wall at s = 50 mm spacing with microphone 1 at x1 = 150 mm from the specimen face, the specimen against the rigid backing at the right, the circular cross-section beside the tube and the plane-wave working range of 343 to 1991 Hz that this geometry sets

Everything the working-range inequalities talk about, in one to-scale side view: the 100 mm bore sets the 1991 Hz top end, the 50 mm spacing sets the 343 Hz bottom end, and the microphones sit at = 50 mm with the farther one = 150 mm from the specimen face.

Show the code for this figure
import matplotlib.pyplot as plt
from phonometry import materials
materials.plot_impedance_tube_geometry(spacing=0.05, x1=0.15, diameter=0.10)
plt.show()
# A measured result that retains its geometry draws its own tube:
# res = materials.two_microphone_impedance(
# h12, frequency=f, spacing=0.05, x1=0.15, diameter=0.10, ...)
# res.plot_geometry()

The working range is set by and ; where along the tube the pair goes is a separate set of constraints, and Clause 4.3 fixes both ends of it.

Away from the source. The loudspeaker radiates cross-modes as well as the plane wave. Below the first cut-on they die out within about three tube diameters, so the nearest microphone is recommended to be at least three diameters from the source and shall in any case be no closer than one — 300 mm and 100 mm respectively on the 100 mm tube of the figure above.

Away from the specimen. The specimen distorts the field near its face too, and the minimum depends on what the specimen looks like: half a diameter for a non-structured sample, one diameter for a semi-laterally structured one, and two diameters for a strongly asymmetrical one. On a 100 mm tube that is 50 mm, 100 mm and 200 mm — so the = 150 mm of the figure above is comfortable for a plain porous sample, adequate for a perforated facing and too close for a strongly structured specimen such as a resonator array. Nothing enforces this: reflection_factor only rejects , so an chosen by eye can sit inside the evanescent near field of a perforated sample and still return a perfectly plausible-looking spectrum.

How well the distances must be known. Fixed microphone positions to ±0.2 mm or better and traversing ones to ±0.5 mm (Clause 4.5); the reference plane’s position relative to microphone 1 reported to ±0.5 mm (Clause 7.1); and the microphone diameter under 20 % of the spacing, and small against , so the capsule averages a negligible piece of the wave (Clause 4.4). The reference plane, , is normally the specimen surface, but a profiled or laterally structured specimen has its reference plane placed some distance in front of it — and is measured to that plane, not to the sample holder.

The same helper covers square and rectangular tubes: pass shape="square" or shape="rectangular" and the Eq. (3) factor replaces the circular one, with the maximum side length. The four-microphone branch keeps its own limits in plane_wave_frequency_range_astm: ASTM E2611 retains the unrounded circular constant (, 6.2.4.1), the same rectangular (6.2.5), a slightly stricter spacing bound (6.5.4) and a laxer low end at 1 % of the wavelength (6.2.3). Passing diameter= (and shape=) to two_microphone_impedance, wave_decomposition or the transfer_matrix_* solvers turns the matching check into an advisory warning, and for the Annex A attenuation estimate of a rectangular tube hydraulic_diameter(width, height) supplies the diameter that tube_attenuation_constant expects.

The whole tube, sample included, can also be simulated: section 5 runs this exact measurement virtually inside the FDTD solver and recovers the analytic absorption of the modelled sample through the same reduction chain.

2. Standing-wave-ratio method (ISO 10534-1)

Section titled “2. Standing-wave-ratio method (ISO 10534-1)”
ISO 10534-1 standing-wave apparatus in side view: a loudspeaker sealed to the left end driven with one pure tone at a time through a narrowband analyser, a probe microphone on a graduated carriage running along the tube axis, the test specimen against the rigid backing at the right with its face marked x = 0, the standing-wave envelope drawn inside the tube with its maxima decaying away from the specimen, the level difference between the first maximum and the first minimum, and the first minimum dimensioned from the specimen faceISO 10534-1 standing-wave apparatus in side view: a loudspeaker sealed to the left end driven with one pure tone at a time through a narrowband analyser, a probe microphone on a graduated carriage running along the tube axis, the test specimen against the rigid backing at the right with its face marked x = 0, the standing-wave envelope drawn inside the tube with its maxima decaying away from the specimen, the level difference between the first maximum and the first minimum, and the first minimum dimensioned from the specimen face

A probe traverses the standing wave and reads the level difference between a pressure maximum and the adjacent minimum. The standing-wave ratio — written in ISO 10534-1, the same letter Part 2 uses for the microphone spacing of section 1; the two never appear in the same equation, and the code keeps them apart as swr and spacing — follows in closed form together with the reflection magnitude and the absorption:

ISO 10534-1 standing-wave-ratio method: the absorption coefficient and the reflection factor magnitude as functions of the standing-wave level difference, showing that a 9.54 dB difference corresponds to a standing-wave ratio of 3, a reflection magnitude of 0.5 and an absorption of 0.75ISO 10534-1 standing-wave-ratio method: the absorption coefficient and the reflection factor magnitude as functions of the standing-wave level difference, showing that a 9.54 dB difference corresponds to a standing-wave ratio of 3, a reflection magnitude of 0.5 and an absorption of 0.75

A small level difference means a near-perfect absorber; a level difference of 9.54 dB gives , and .

Show the code for this figure
import matplotlib.pyplot as plt
import numpy as np
from phonometry import materials
level_diff = np.linspace(0.5, 40.0, 300) # L_max - L_min [dB]
swr = materials.standing_wave_ratio_from_level(level_diff)
fig, ax = plt.subplots()
ax.plot(level_diff, materials.standing_wave_absorption(swr),
label="Absorption coefficient alpha")
ax.plot(level_diff, materials.standing_wave_reflection_magnitude(swr), "--",
label="Reflection factor magnitude |r|")
ax.set_xlabel("Standing-wave level difference L_max - L_min [dB]")
ax.set_ylabel("alpha, |r|")
ax.legend()
plt.show()
from phonometry import materials
s = float(materials.standing_wave_ratio_from_level(9.542)) # level difference [dB] -> SWR
print(round(s, 2)) # 3.0
print(round(float(materials.standing_wave_absorption(s)), 2)) # 0.75

The ratio method predates the FFT analyser and survives because it is self-contained. The apparatus is a probe microphone on a graduated carriage: a thin tube sliding along the axis, sampling the interior field point by point while the loudspeaker holds a single pure tone. At each frequency the operator (or the stepper motor) locates one pressure maximum and the adjacent minimum, reads the level difference, and moves on to the next tone. Slow, but there is nothing to calibrate against anything else: one microphone measures both levels, so its sensitivity cancels exactly, and there is no inter-channel phase mismatch because there is only one channel. That is why the standing-wave method remains the arbitration method when two transfer-function tubes disagree.

The closed forms above use only the level difference; the positions of the minima carry the rest of the information. With measured from the sample face towards the source, the interior field of a wave with reflection factor is

so pressure minima sit where the cosine is , at

with the minimum nearest the sample. A minimum a quarter wavelength from the face means (a hard-backed sample at resonance); a minimum right at the face means (a rigid wall). Magnitude from the ratio, phase from the position: together they give the same complex , and hence the same normalised impedance , that the transfer-function method of section 3 computes from in one shot.

The standing-wave envelope level in decibels along the first metre of a 100 mm tube at 500 Hz for three terminations: a rigid wall whose minima plunge off the axis with the first at the specimen face, the worked sample of reflection magnitude 0.5 whose first minimum sits 12 centimetres out at minus 6 decibels, and a near-anechoic termination that is almost flat. A dotted copy of the 0.5 case carries the ISO 10534-2 tube attenuation, and the levels of its first three minima are annotatedThe standing-wave envelope level in decibels along the first metre of a 100 mm tube at 500 Hz for three terminations: a rigid wall whose minima plunge off the axis with the first at the specimen face, the worked sample of reflection magnitude 0.5 whose first minimum sits 12 centimetres out at minus 6 decibels, and a near-anechoic termination that is almost flat. A dotted copy of the 0.5 case carries the ISO 10534-2 tube attenuation, and the levels of its first three minima are annotated

What the carriage actually traverses, drawn as the level the analyser shows at 500 Hz in a 100 mm tube. The rigid wall () has its minima on zero pressure and the first of them at the face, ; the worked sample () reads = 9.54 dB between the maximum and the adjacent minimum, the first of them at = 12 cm, and the quarter-wavelength ruler converts that distance into ; the near-anechoic case () modulates by only 1.74 dB, which is why the method loses precision exactly where an absorber is good. The dotted curve is the same sample with the Eq. (A.18) tube attenuation: the further from the face, the more the reflected wave has decayed, so the notches fill in — the first three minima read −5.99, −5.92 and −5.85 dB. That drift is small here because Eq. (A.18) is a lower bound; ISO 10534-1’s approximate extrapolation (Annex A.2.1.4) is offered while the first-to-second minimum difference stays under 2 dB, and above that the attenuation has to be measured on the empty tube. Either way it is why the operator reads the minimum nearest the sample.

Show the code for this figure
# The interior field of section 2, for three reflection factors.
c0 = 343.2
x = np.linspace(0.0, 1.0, 800)
k = 2 * np.pi * 500.0 / c0
for magnitude, phi in ((1.0, -np.pi), (0.5, np.radians(-54.1)), (0.1, 0.0)):
envelope = np.sqrt(1 + magnitude**2 + 2 * magnitude * np.cos(2 * k * x - phi))
print(round(float(envelope.min()), 3), round(float(envelope.max()), 3))
# The attenuated variant uses the Eq. (A.18) lower-bound constant.
print(round(float(materials.tube_attenuation_constant(500.0, c0, 0.10)), 4))

Magnitude and position are the two halves of the reading, and the library takes them together: standing_wave_reflection(swr, x_min1, wavelength) returns the complex (Eqs. (17)-(23)) and standing_wave_normalized_impedance the ratio (Eqs. (24)-(26)). Both accept arrays, so a stepper-motor traverse that logs per tone reduces to two calls:

import numpy as np
from phonometry import materials
# A three-tone traverse: the level difference and the first-minimum distance
# read at each frequency, which is all the method produces.
f = np.array([500.0, 1000.0, 2000.0])
c0 = 343.2
level_difference = np.array([9.542, 6.021, 3.522]) # L_max - L_min [dB]
x_min1 = np.array([0.120, 0.061, 0.030]) # first minimum [m]
swr = materials.standing_wave_ratio_from_level(level_difference)
lam = c0 / f
r = materials.standing_wave_reflection(swr, x_min1, lam)
z = materials.standing_wave_normalized_impedance(swr, x_min1, lam)
print(np.round(np.abs(r), 2)) # [0.5 0.33 0.2 ]
print(np.round(np.degrees(np.angle(r)), 1)) # [-54.1 -52. -54.1]
print(np.round(z, 2)) # Z / rho c0

Two practical cautions from the standard’s own text. The minima far from the sample are shallower than the theory above says, because the travelling waves decay along the tube (viscous and thermal losses at the wall); ISO 10534-1 has the operator read the minimum nearest the sample and, for precision work, extrapolate the minimum levels to the sample face. And the method leans on the purity of the tone: any harmonic distortion from a hard-driven loudspeaker puts energy at frequencies whose minima sit elsewhere, partially filling the notch being measured, so the analyser must be narrowband around the drive frequency rather than a broadband level meter.

Two fixed microphones measure the complex transfer function ; from it the reflection factor at the sample face, the absorption and the normalised surface impedance follow (Eqs. (17)–(19)):

with , , microphone spacing (the Part 2 meaning of the letter again) and the distance from the reference plane to the farther microphone.

What an error in costs, and what it does not. enters only through , a pure phase rotation. It therefore leaves — and hence the absorption spectrum — completely untouched, while rotating the real and imaginary parts of the surface impedance. At 1.5 kHz in air a 2 mm error in rotates the reflection phase by about 6°, enough to move a flow resistivity fitted from that impedance noticeably. The practical consequences are two: measure to the reference plane — the specimen face, or the plane in front of a profiled specimen — rather than to the sample holder, since a specimen that does not sit where it was meant to moves with it; and read a healthy-looking beside an implausible impedance (a negative real part, say) as an or a mismatch fault, not as a material result.

The incident and reflected waves travel inside a drawn impedance tube and their sum forms the standing-wave envelope; a rigid termination with deep envelope nodes is compared with a porous sample with shallow ones, sampled by the two flush wall microphones.

Download the animation (WebM)

The incident and reflected waves travel inside a drawn impedance tube and their sum forms the standing-wave envelope; a rigid termination with deep envelope nodes is compared with a porous sample with shallow ones, sampled by the two flush wall microphones.

Download the animation (WebM)

import numpy as np
from phonometry import materials
f = np.array([500.0, 1000.0, 1800.0])
x1, spacing, c0 = 0.12, 0.03, 343.2
k0 = materials.tube_wavenumber(f, c0)
# A measured transfer function H12 (here synthesised from r = 0.3 - 0.4j)
target = 0.3 - 0.4j
x2 = x1 - spacing
h12 = (np.exp(1j*k0*x2) + target*np.exp(-1j*k0*x2)) / \
(np.exp(1j*k0*x1) + target*np.exp(-1j*k0*x1))
r = materials.reflection_factor(h12, spacing=spacing, x1=x1, wavenumber=k0)
print(np.round(materials.absorption_from_reflection(r), 3)) # [0.75 0.75 0.75]
print(np.round(materials.normalized_surface_impedance(r), 2)) # Z / rho c0
# [1.15-1.23j 1.15-1.23j 1.15-1.23j]
print(np.round(materials.normalized_surface_admittance(r), 2)) # its reciprocal,
# [0.41+0.43j 0.41+0.43j 0.41+0.43j] what an impedance boundary condition wants

The high-level two_microphone_impedance wraps this chain and returns an ImpedanceTubeResult with absorption, reflection factor, surface impedance and normalised impedance, applying the plane-wave frequency-range check and optional tube attenuation. Its .plot() draws the absorption spectrum with the reflection-factor magnitude overlaid, and normalized_surface_admittance(r) gives the reciprocal quantity that a locally-reacting boundary condition — an FDTD or BEM wall, the library’s own solver — consumes.

3.1 Correcting the channel mismatch (Clause 7.5)

Section titled “3.1 Correcting the channel mismatch (Clause 7.5)”

A channel is a microphone plus its preamplifier plus its analyser input, and no two are identical. ISO 10534-2 Clause 7.5 does not recommend correcting for that, it requires it: “one of the following procedures … shall be used”. Annex E.1.2 is blunter still — with two microphones the phase mismatch “is unavoidable and shall be compensated for” — and E.1.3 adds that the amplitudes should be within 0.3 dB of each other to begin with. The correction is a complex division applied exactly where is small and the phase of carries the whole answer, which is why the symptom of skipping it is frequency-dependent wander rather than an offset: the snippet below leaves a 0.5 dB / 1° mismatch uncorrected on a specimen whose true absorption is 0.75 and gets 0.73, 0.78 and 0.72 in three neighbouring bands. On a near-perfect absorber the same mismatch pushes the spectrum against the ceiling, which is where the classic uncorrected-tube signature — an absorption just above 1 or just below 0 — comes from.

Both permitted procedures use the same measurement, made twice:

  1. Place an absorptive specimen in the tube and measure in the standard configuration.
  2. Physically interchange the two microphone cartridges, so that A takes the exact position B occupied and vice versa, without moving the cables on the preamplifier or the analyser. Measure .
  3. Form (Eq. (10)) and divide the working transfer function by it.

The difference between the two procedures is only how often step 2 is done. Clause 7.5.1 repeats the swap for every specimen, which suits a small number of samples; Clause 7.5.2 determines once against a calibration specimen and keeps it for the whole series, which suits a production run — after which the microphones must stay where they are. The one-microphone technique of Annex B needs no correction at all, because there is only one channel: it buys that with measurement time, and it is the same argument that makes the standing-wave method of section 2 the arbitration method.

# A channel pair mismatched by 0.5 dB and 1 degree, and its removal. The two
# configurations differ by exactly that factor, which is what Eq. (10) recovers.
mismatch = 10 ** (0.5 / 20) * np.exp(1j * np.radians(1.0))
h12_i = h12 * mismatch # standard configuration
h12_ii = h12 / mismatch # microphones physically interchanged
hc = materials.mic_calibration_factor(h12_i, h12_ii)
print(round(float(np.abs(hc[0])), 4), round(float(np.degrees(np.angle(hc[0]))), 2))
# 1.0593 1.0 -- the mismatch itself, recovered
h12_corrected = materials.apply_mic_calibration(h12_i, hc)
print(float(np.abs(h12_corrected - h12).max()) < 1e-12) # True
# What the uncorrected data would have claimed, on a sample of alpha = 0.75:
bad = materials.reflection_factor(h12_i, spacing=spacing, x1=x1, wavenumber=k0)
print(np.round(materials.absorption_from_reflection(bad), 3)) # [0.732 0.775 0.718]

The reduction chain above is a few lines; the conditions that make its inputs trustworthy are most of ISO 10534-2. In the order they matter:

  • The air, measured rather than assumed (Clause 7.2). and are not constants of the method, they are readings. The tube air temperature must be measured and held to ±1 K with a transducer accurate to ±0.5 K, and the atmospheric pressure to ±0.5 kPa (Clause 4.12); Eq. (5) turns the first into and Eq. (7) the pair into . The library exposes both: speed_of_sound_iso(temperature) takes kelvin, and air_density_iso(temperature, atmospheric_pressure) takes kelvin and kilopascals — the fiche snippet below feeds them straight into two_microphone_impedance instead of literals. Note that the ASTM E2611 helpers of section 4 take degrees Celsius, and the two are not interchangeable.
  • Excitation (Clause 4.10, 7.3). A stationary signal with a flat spectral density over the range of interest: random, pseudo-random, periodic pseudo-random or chirp. The level must sit at least 10 dB above the background at every frequency of interest, and any frequency whose response is 60 dB below the maximum is rejected outright. The number of spectral averages follows from the accuracy wanted on ; a bandwidth-time product of 50 to 100 is the usual target (Annex E.2), and the record must be much longer than or time aliasing corrupts it (Eq. (E.1)).
  • Warm-up (Clause 5). At least 10 minutes of loudspeaker operation before the first measurement, so the tube air temperature has stopped drifting.
  • The source end (Clauses 4.9, 4.11). The loudspeaker membrane covers at least two thirds of the tube cross-section, sits in an insulating box against airborne flanking to the microphones, and is elastically isolated from the tube — structure-borne excitation of the tube wall is a classic mimic of absorption. At least 200 mm of effective absorbent lining near the loudspeaker damps the air-column resonances.
  • The specimen end (Clause 4.7). The back plate is rigid and tightly fixed, a metal plate of at least 20 mm being recommended, because it is the rigid termination most measurements rest on. A separate sample holder must match the tube bore to ±0.2 % and be sealed with vaseline rather than an elastic gasket.
  • The analyser (Clause 4.8). More than 65 dB of dynamic range, and transfer-function errors from non-linearity, resolution, instability and temperature under 0.2 dB.

Everything the ISO 10534-2 report needs about the geometry is already in the fiche’s metadata block — tube diameter and shape, microphone spacing, mounting, temperature and pressure — so that block reads as a report checklist rather than decoration.

Tube attenuation, and when to switch it on. The plane wave in the tube is not lossless: viscous and thermal boundary layers at the wall attenuate the incident and the reflected wave over the distance , and attenuation that is not accounted for is read as sample absorption. tube_attenuation_constant is the Eq. (A.18) estimate, , and its scaling says where it matters: it grows as and falls as , so a 29 mm tube suffers roughly three and a half times the loss of a 100 mm one at the same frequency, and moving the microphone pair closer to the specimen reduces it directly. It matters most for a near-perfect absorber, where the reflected wave is already weak. Two cautions come with it. Clause A.2.1.5 says plainly that this estimate ignores porous or dirty walls and any object in the tube, so it is a lower limit: a large discrepancy is a sign to clean or re-qualify the tube, not a number to trust. And the standard prefers the measured route — determine the attenuation from the empty tube at mid and upper frequencies (Eqs. (A.13)/(A.14), after every modification of the tube) and extrapolate downwards.

ISO 10534-2 two-microphone tube result for a 50 mm porous absorber: the normal-incidence absorption coefficient rising from about 0.2 at 200 Hz towards 0.97 above 1 kHz, with the reflection-factor magnitude falling as its mirror imageISO 10534-2 two-microphone tube result for a 50 mm porous absorber: the normal-incidence absorption coefficient rising from about 0.2 at 200 Hz towards 0.97 above 1 kHz, with the reflection-factor magnitude falling as its mirror image

A 50 mm porous absorber measured over the working band of a 100 mm tube: the absorption climbs as the layer thickness grows against the wavelength, and falls as its mirror image ().

Show the code for this figure
import matplotlib.pyplot as plt
import numpy as np
from phonometry import materials
# A 50 mm porous absorber (Miki, sigma = 20 kPa s/m^2) in a 100 mm tube with
# 100 mm spacing (working band ~170 Hz to 1.5 kHz): the layer model supplies
# the true reflection factor, from which the transfer function H12 follows.
f = np.linspace(200.0, 1500.0, 260)
med = materials.miki(f, 20000.0)
layer = materials.layered_absorber(f, [materials.PorousLayer(0.05, med)])
spacing, x1, c0 = 0.10, 0.20, 343.2
k0 = materials.tube_wavenumber(f, c0)
x2 = x1 - spacing
r_true = layer.reflection
h12 = (np.exp(1j*k0*x2) + r_true*np.exp(-1j*k0*x2)) / \
(np.exp(1j*k0*x1) + r_true*np.exp(-1j*k0*x1))
result = materials.two_microphone_impedance(
h12, frequency=f, spacing=spacing, x1=x1, speed_of_sound=c0,
characteristic_impedance=407.0, diameter=0.10,
)
# One line: alpha(f) with |r| overlaid.
result.plot()
plt.show()
# By hand, from the result's fields:
fig, ax = plt.subplots()
ax.plot(f, result.absorption, label="Absorption alpha")
ax.plot(f, np.abs(result.reflection), "--", label="Reflection factor |r|")
ax.set_xlabel("Frequency [Hz]")
ax.set_ylabel("Coefficient")
ax.legend()
plt.show()

ImpedanceTubeResult.report(path) renders a one-page PDF fiche laid out like an accredited normal-incidence impedance-tube test report (ISO 10534-2:2001): a standard-basis line, a metadata header block, the per-frequency table (the absorption coefficient and the real and imaginary parts of the normalised surface impedance ) beside the curve (the result’s own .plot(), on a continuous logarithmic frequency axis), a boxed characterisation headline and a footer with the fixed disclaimer. ISO 10534-2 is a characterisation, so the fiche carries no pass/fail verdict and no single-number rating; the normal-incidence coefficient is not comparable to the random-incidence / of ISO 354 / ISO 11654. Setting verbose=True inserts the reflection-factor magnitude column.

It uses the same ReportMetadata container and rendering engine as the other fiches. The measured frequency range is taken from the result; the applicable descriptive and geometric ReportMetadata fields are client, manufacturer, specimen, tube_diameter, tube_shape, mic_spacing, mounting, test_room, test_date, temperature, pressure, measurement_standard, laboratory, operator, report_id and notes (tube_diameter and mic_spacing are given in metres and printed in millimetres). The requirement field is ignored (ISO 10534-2 has no verdict). Rendering needs reportlab and, for the figure the fiche embeds, matplotlib (pip install "phonometry[report,plot]"); only engine="reportlab" is supported. The fiche renders in English by default; pass language="es" for a Spanish fiche (translated fixed strings and a comma decimal separator).

The block below is the specimen of the fiche underneath it: a resistive facing of normalised flow resistance over an 86 mm rigidly-backed air cavity, whose surface impedance is known in closed form, so the printed can be checked by hand: 1.00 at the 1 kHz quarter-wave resonance, where the matched screen gives and ; 0.80 at 500 Hz, where and .

import numpy as np
from phonometry import materials, ReportMetadata
# 100 mm tube, s = 50 mm, far microphone at x1 = 100 mm, 20 degC / 101 kPa.
c0 = float(materials.speed_of_sound_iso(293.15)) # 343.29 m/s
rho_c = materials.characteristic_impedance(
float(materials.air_density_iso(293.15, 101.0)), c0) # 405.6 Pa.s/m
diameter, spacing, x1 = 0.100, 0.050, 0.100
theta, cavity = 1.0, c0 / (4.0 * 1000.0) # quarter-wave at 1 kHz: 86 mm
freqs = np.array([400, 500, 630, 800, 1000, 1250, 1600], dtype=float)
# The transfer function the tube would measure, synthesised from the known r.
k0 = materials.tube_wavenumber(freqs, c0)
z = theta - 1j / np.tan(2 * np.pi * freqs / c0 * cavity)
r_known = (z - 1.0) / (z + 1.0)
x2 = x1 - spacing
h12_fiche = (np.exp(1j*k0*x2) + r_known*np.exp(-1j*k0*x2)) / \
(np.exp(1j*k0*x1) + r_known*np.exp(-1j*k0*x1))
fiche = materials.two_microphone_impedance(
h12_fiche, frequency=freqs, spacing=spacing, x1=x1,
speed_of_sound=c0, characteristic_impedance=rho_c, diameter=diameter,
)
print(np.round(fiche.absorption, 2)) # [0.68 0.8 0.9 0.97 1. 0.96 0.68]
fiche.report(
"alpha_fiche.pdf",
metadata=ReportMetadata(
specimen="Resistive facing over an 86 mm rigidly-backed air cavity",
tube_diameter=diameter, # m (printed as 100 mm)
mic_spacing=spacing, # m (printed as 50 mm)
measurement_standard="ISO 10534-2",
laboratory="Phonometry Reference Laboratory",
),
) # normal-incidence alpha and impedance over the tube band
ISO 10534-2 impedance-tube example report (PDF)

One-page normal-incidence impedance-tube fiche: a metadata header (client, specimen, tube diameter d, microphone spacing s, the measured frequency range, mounting, test facility, temperature and pressure), the per-frequency table of the absorption coefficient alpha and the real and imaginary parts of the normalised surface impedance z beside the alpha curve on a logarithmic frequency axis, and the boxed characterisation headline over the tested frequency range.

Download the report (PDF)

Normal-incidence impedance-tube fiche (ImpedanceTubeResult.report), the alpha spectrum with the surface impedance.

With four microphones (two upstream, two downstream of the sample) a two-load (or one-load) measurement recovers the sample’s transfer matrix, whose entries give the normal-incidence transmission loss, reflection and wavenumber:

What a “load” is, and why there are two of them. A load is a downstream termination. The matrix has four unknowns, and one measurement of the four microphone transfer functions supplies only two independent equations, so the general method changes the termination and repeats the whole measurement: E2611 Clause 8.5.4.1 solves the two sets together (Eq. (22)). In practice the tube ends in a weakly anechoic termination — a wedge or pyramid of glass fibre about 30 cm long — and Clause 6.2.8 asks for it to be easily removable precisely so that an open or a blocked end can provide the second load. All four transfer functions of each load are referenced to one common channel, and each of them carries the same switched-configuration mismatch correction as section 3 (Clause 8.5.3): the four-microphone method does not escape that requirement, it multiplies it. The source end is sealed and isolated from the tube (Clause 6.4.2), as in Part 2.

The one-load method is not the general method with fewer steps. Clause 8.5.4.2 closes the system instead by assuming the specimen is geometrically symmetric — “presenting the same physical properties to the sound field on either side” — so that reciprocity gives and (Eq. (23)), and one termination, preferably the anechoic one, is enough. That holds for a homogeneous layer and fails for any asymmetric build-up: a facing on one side, a gradient density, a mounted membrane, a stack of unequal layers — which is most of what people actually put in a transmission tube. transfer_matrix_one_load does not and cannot detect the asymmetry: it warns about the plane-wave range and about an ill-conditioned denominator, and otherwise returns a plausible, wrong matrix, and with it a wrong transmission loss. The rule is simple: if the specimen has a front and a back, use two loads. If you believe it is symmetric, run both solvers and compare. None of this touches the hard-backed absorption route of section 3, which needs only the surface impedance.

ASTM E2611 four-microphone transmission-loss tube: a sound source, two microphones upstream and two downstream of the test specimen at spacings s1 and s2 and offsets l1 and l2, an adjustable termination for the two-load method, the upstream A and B and downstream C and D travelling waves, and the transfer matrix and transmission-loss relationsASTM E2611 four-microphone transmission-loss tube: a sound source, two microphones upstream and two downstream of the test specimen at spacings s1 and s2 and offsets l1 and l2, an adjustable termination for the two-load method, the upstream A and B and downstream C and D travelling waves, and the transfer matrix and transmission-loss relations

To scale it looks like this. A TransferMatrix recovered by the two-load or one-load solvers retains l1, s1, l2, s2 and the specimen thickness, so tm.plot_geometry() redraws the tube a measurement was taken in.

To-scale side view of a 100 mm ASTM E2611 transmission tube: the loudspeaker at the left, microphones 1 and 2 upstream at s1 = 50 mm spacing and l1 = 100 mm from the specimen, the 50 mm specimen in the middle of the tube, microphones 3 and 4 downstream at s2 = 50 mm spacing and l2 = 200 mm, the changeable termination of the two-load method at the right, the circular cross-section beside the tube and the plane-wave working range of 69 to 2011 HzTo-scale side view of a 100 mm ASTM E2611 transmission tube: the loudspeaker at the left, microphones 1 and 2 upstream at s1 = 50 mm spacing and l1 = 100 mm from the specimen, the 50 mm specimen in the middle of the tube, microphones 3 and 4 downstream at s2 = 50 mm spacing and l2 = 200 mm, the changeable termination of the two-load method at the right, the circular cross-section beside the tube and the plane-wave working range of 69 to 2011 Hz

Where the four microphones of the transfer-matrix method actually sit around a 50 mm specimen in a 100 mm tube: = 50 mm, = 100 mm, = 200 mm, and the changeable termination that provides the second load. The ASTM working range for this geometry is 69 to 2011 Hz.

Show the code for this figure
import matplotlib.pyplot as plt
from phonometry import materials
materials.plot_transmission_tube_geometry(
l1=0.10, s1=0.05, l2=0.20, s2=0.05, thickness=0.05, diameter=0.10,
)
plt.show()
# A TransferMatrix from transfer_matrix_two_load / _one_load retains its
# geometry, so tm.plot_geometry() redraws the tube it was measured in.
import numpy as np
from phonometry import materials
# An air layer is a known transfer matrix; TL = 0 dB (nothing is lost)
f = np.array([500.0, 1000.0, 2000.0])
k0 = 2*np.pi*f / 343.2
rho_c = 1.186 * 343.2
tm = materials.air_layer_transfer_matrix(thickness=0.05, wavenumber=k0,
characteristic_impedance=rho_c)
print(np.round(tm.transmission_loss(rho_c), 6)) # [0. 0. 0.]

transfer_matrix_two_load / transfer_matrix_one_load build the TransferMatrix from the four measured microphone transfer functions (H1, H2, H3, H4) of each load; its methods (transmission_loss, reflection_hard_backed, absorption_hard_backed, characteristic_impedance_material, material_wavenumber) then read off the ASTM E2611 quantities, and its .plot() draws the transmission loss with the hard-backed absorption overlaid (a matrix built by the solvers retains and, when supplied, the frequency vector, so only a hand-built matrix needs them as arguments). The matrix does not have to come from a measurement: the multilayer solver exposes the chain matrix of any modelled stack in the same convention, so a predicted specimen can be read out exactly like a measured one.

import numpy as np
from phonometry import materials
# The chain matrix of a modelled 50 mm porous layer, read back through the
# ASTM E2611 machinery: TL and hard-backed absorption of the same specimen.
f = np.linspace(200.0, 1600.0, 300)
med = materials.miki(f, 20000.0)
layer = materials.layered_absorber(f, [materials.PorousLayer(0.05, med)])
chain = layer.transfer_matrix # shape (2, 2, len(f))
tm = materials.TransferMatrix(t11=chain[0, 0], t12=chain[0, 1],
t21=chain[1, 0], t22=chain[1, 1])
print(np.round(float(tm.transmission_loss(407.0)[-1]), 1)) # 9.7 dB at 1.6 kHz
tm.plot(f, 407.0) # TL(f) with the hard-backed absorption overlaid
ASTM E2611 transfer-matrix quantities of a 50 mm porous layer: the normal-incidence transmission loss rising from about 6.6 dB at 200 Hz to over 9 dB at 1.6 kHz on the left axis, and the hard-backed absorption coefficient rising from 0.19 to about 0.97 on the right axisASTM E2611 transfer-matrix quantities of a 50 mm porous layer: the normal-incidence transmission loss rising from about 6.6 dB at 200 Hz to over 9 dB at 1.6 kHz on the left axis, and the hard-backed absorption coefficient rising from 0.19 to about 0.97 on the right axis

The same four-pole entries answer two different questions: how much sound the free-standing layer lets through (the transmission loss, Eq. (26)) and how much the rigidly-backed layer absorbs (Eq. (28)).

A tube TL is not a sound reduction index. The fiche of section 3 warns that the normal-incidence is not the random-incidence ; the same warning applies on the transmission side and is missed more often. What the tube measures is the transmission loss of a specimen a few centimetres across, for one plane wave at one angle, with the specimen edges clamped by the tube wall. What the sound reduction index is, is a diffuse-field quantity measured on a full-size element with its real mounting, and it carries the element’s own bending waves, its edge conditions and its coincidence dip — none of which exist inside a tube. Expect the normal-incidence value to sit above a field-incidence estimate for a mass-controlled layer, and expect the tube to miss the resonance and coincidence dips of a real panel entirely. Use the tube TL to compare materials and to fit and verify a transfer-matrix model; then predict the construction with the model, or with EN 12354, and never by carrying the tube number into a partition calculation.

Show the code for this figure
import matplotlib.pyplot as plt
import numpy as np
from phonometry import materials
f = np.linspace(200.0, 1600.0, 300)
med = materials.miki(f, 20000.0)
layer = materials.layered_absorber(f, [materials.PorousLayer(0.05, med)])
chain = layer.transfer_matrix # shape (2, 2, len(f))
tm = materials.TransferMatrix(t11=chain[0, 0], t12=chain[0, 1],
t21=chain[1, 0], t22=chain[1, 1])
# One line: TL(f) on the left axis, hard-backed absorption on the right.
tm.plot(f, 407.0)
plt.show()
# By hand, from the matrix methods:
fig, ax = plt.subplots()
ax.plot(f, tm.transmission_loss(407.0), label="Transmission loss TL_n")
twin = ax.twinx()
twin.plot(f, tm.absorption_hard_backed(407.0), "--", color="gray",
label="Hard-backed absorption alpha")
ax.set_xlabel("Frequency [Hz]")
ax.set_ylabel("Transmission loss TL_n [dB]")
twin.set_ylabel("Hard-backed absorption alpha")
plt.show()

Most bad tube data are made at the sample holder, long before the signal processing. The recurring failures:

  • Perimeter gaps. A specimen cut slightly undersize leaves an air sliver along the tube wall. Sound short-circuits around and behind the sample, and the gap itself resonates, so the measured absorption grows a spurious low-to-mid-frequency hump. Cut for a snug slide fit and seal the rim (a thin film of petroleum jelly is the classic remedy) without loading the front face.
  • Compression. A specimen cut oversize and forced in is denser than the product it is supposed to represent: the flow resistivity rises, a limp frame is stiffened, and the absorption curve shifts and flattens. The result is repeatable and wrong.
  • Hidden back cavity. If the specimen does not sit flush on the rigid backing, the unintended air layer acts as a quarter-wavelength cavity and moves the absorption peak down in frequency, flattering the material. Backing air gaps are perfectly legitimate mountings, but only when they are deliberate, dimensioned and reported with the result.
  • Face not plane. A bulging, tilted or torn front face scatters into cross-sectional modes below the nominal cut-on and breaks the normal-incidence assumption the equations rest on. Cut with a sharp tool; do not tear fibrous materials to size.
  • One specimen is not the material. Porous products are inhomogeneous at the scale of a tube sample. Measure several cuts and average; a spread between specimens is product variance worth reporting, not measurement noise to hide.

5. The virtual tube: the solver measured by the standards

Section titled “5. The virtual tube: the solver measured by the standards”

A three-row rigid-walled domain of the 2D FDTD solver is a plane-wave tube, and with the per-cell damping map a porous sample becomes an equivalent fluid (density, speed and loss maps). That closes a remarkable loop: the FDTD probe histories can be reduced through the library’s own ISO 10534-2 and ASTM E2611 chains as if they were measurements, and the recovered spectra agree with the exact analytic answer for the same lossy layer to within 0.035 in absorption and 0.1 dB in transmission loss (the tests/simulation cross-checks run this on every commit).

import numpy as np
from phonometry.materials import two_microphone_impedance
from phonometry.simulation import FDTD2D
c0, rho0, dx = 343.0, 1.2, 0.005
nx, d_cells = 280, 20 # 1.4 m tube, 10 cm sample
c = np.full((3, nx), c0); rho = np.full((3, nx), rho0)
sigma = np.zeros((3, nx))
c[:, -d_cells:] = 0.6 * c0 # the sample: slower,
rho[:, -d_cells:] = 3.0 * rho0 # denser and lossy
sigma[:, -d_cells:] = 600.0
sim = FDTD2D(c, dx, rho=rho, damping=sigma,
edge_impedance={"left": rho0 * c0}) # anechoic source end
sim.add_plane_wave("right", center=0.35, width=0.05)
mics = (219, 229) # the ISO microphone pair
records = np.zeros((2, 9000))
for n in range(records.shape[1]):
sim.step()
records[:, n] = sim.p[1, mics]
spec = np.fft.rfft(records, axis=1)
freqs = np.fft.rfftfreq(records.shape[1], sim.dt)
band = (freqs > 300.0) & (freqs < 1200.0)
result = two_microphone_impedance(
spec[1, band] / spec[0, band], frequency=freqs[band],
spacing=0.05, x1=(nx - d_cells) * dx - (mics[0] + 0.5) * dx,
speed_of_sound=c0, characteristic_impedance=rho0 * c0)
result.plot() # the absorption the virtual tube "measured"

The two clips below run exactly this experiment. In the impedance tube a sustained plane tone builds the standing wave the two microphones read: against the rigid end the minima are deep, in front of the sample they stay shallow. In the transmission tube a carrier packet crosses an anechoic duct: the empty tube passes it unchanged, the lossy layer splits it into a reflection and an attenuated transmission that the four ASTM microphones resolve.

A loudspeaker drives a sustained 850 Hz plane tone into a 1.2 metre rigid-walled virtual impedance tube, drawn as the real instrument with its two flush microphones and rigid end plug. Against the rigid plug the standing-wave envelope grows deep minima, while a 10 cm lossy sample in front of the same plug leaves the minima shallow; the recovered absorption of 0.54 is annotated.

Download the animation (WebM)

A loudspeaker drives a sustained 850 Hz plane tone into a 1.2 metre rigid-walled virtual impedance tube, drawn as the real instrument with its two flush microphones and rigid end plug. Against the rigid plug the standing-wave envelope grows deep minima, while a 10 cm lossy sample in front of the same plug leaves the minima shallow; the recovered absorption of 0.54 is annotated.

Download the animation (WebM)

The loudspeaker end fires a carrier packet down a 1.6 metre rigid-walled virtual transmission tube drawn as the real instrument, with its four flush microphones and hatched anechoic termination. The empty tube passes the packet unchanged; a 10 cm lossy layer mid-tube splits it into a reflected train and an attenuated transmission, with the 3.1 dB transmission loss annotated.

Download the animation (WebM)

The loudspeaker end fires a carrier packet down a 1.6 metre rigid-walled virtual transmission tube drawn as the real instrument, with its four flush microphones and hatched anechoic termination. The empty tube passes the packet unchanged; a 10 cm lossy layer mid-tube splits it into a reflected train and an attenuated transmission, with the 3.1 dB transmission loss annotated.

Download the animation (WebM)

  • Covered

    The impedance-tube trio. The ISO 10534-1 standing-wave-ratio method (standing_wave_ratio_from_level, standing_wave_reflection_magnitude, standing_wave_absorption, and the position half of the reading in standing_wave_reflection and standing_wave_normalized_impedance). The ISO 10534-2 transfer-function method with its Clause 4 frequency-range check (two_microphone_impedance, plane_wave_frequency_range, reflection_factor, absorption_from_reflection, normalized_surface_impedance, normalized_surface_admittance, the Clause 7.2 air properties speed_of_sound_iso and air_density_iso, the Clause 7.5 mismatch pair mic_calibration_factor and apply_mic_calibration, and tube_attenuation_constant). The ASTM E2611 four-microphone transfer-matrix method (transfer_matrix_two_load, transfer_matrix_one_load, TransferMatrix, plane_wave_frequency_range_astm, hydraulic_diameter), with the to-scale geometry drawings of both tubes. The virtual FDTD tubes of section 5, whose recovered spectra are cross-checked against the analytic answer on every commit.

  • Not covered

    Two editions cited only as later revisions are not implemented: ISO 10534-2:2023 (the code follows the 1998/2001 transfer-function method) and ASTM E2611-24 (the code follows E2611-19). ISO 10534-1:1996 is implemented as its European adoption BS EN ISO 10534-1:2001, not read directly from the 1996 ISO text; its probe-traverse refinements (extrapolating the minimum levels to the sample face, probe-body corrections) are described in section 2 but not automated. Neither are the equipment and procedure clauses of sections 1.1 and 3.2: the near-field distances of Clause 4.3, the position tolerances of Clause 4.5, the signal-to-noise floor of Clause 7.3 and the warm-up of Clause 5 are stated for the operator, and nothing in the library checks that any of them was met. tube_attenuation_constant implements only the Eq. (A.18) estimate, which the standard itself calls a lower limit; the measured determination of Eqs. (A.13)/(A.14) from the empty tube is not implemented. The random-incidence of the reverberation room is a different quantity with its own guide: Sound Absorption Measurement and Rating.