Skip to content
This documentation describes version 4.0.0, which is not released yet. The current version on PyPI is 3.3.0 and does not carry everything described here.

Filter Class Verification (IEC 61260-1)

Standards: IEC 61260ANSI S1.11

A filter bank becomes a measuring instrument only once its bands have been proved against a specification. IEC 61260-1:2014 writes that specification as an acceptance mask: a corridor of relative attenuation around each mid frequency, narrow in the passband, opening into a minimum-attenuation requirement far outside the band, with one corridor per performance class. A bank “is class 1” when every band of it stays inside the class 1 corridor at every normalized frequency, and the margin in decibels says by how much.

This page is the verification half of the octave-filtering topic: the 2014 mask and the per-band verdict, the two requirements Part 2 of the series computes from the same response (the effective bandwidth and the summation of adjacent outputs) and the swept test of time-invariant operation, the stricter class 0 kept alive by the withdrawn IEC 61260:1995 and ANSI S1.11-2004 masks, a reading of what a class actually buys in a measurement (passband error, stopband leakage, uncertainty budget), the periodic tests a laboratory runs on an instrument and how their results are graded, and the one-page accredited fiche that turns the verdict into a document. The design half, the band mathematics and the parameter reference, is Filter Banks, and the five architectures with their compared responses are Filter Architecture Gallery; the same machinery applied to the frequency weightings is section 6 of Frequency Weighting.

1. Verifying the class against IEC 61260-1:2014

Section titled “1. Verifying the class against IEC 61260-1:2014”

verify_filter_class checks every band of a bank against the acceptance limits of IEC 61260-1:2014 (Table 1, with the fractional-octave breakpoint mapping and log-frequency interpolation from the standard), and, for the 2014 edition, against the effective bandwidth and the summation of outputs the way IEC 61260-2:2016 computes them (section 1b). It reports the performance class per band with its margins in dB:

from phonometry import filters
bank = filters.OctaveFilterBank(fs=48000, fraction=3, order=6)
result = filters.verify_filter_class(bank)
print(result.overall_class) # 1
print(result.range_limited) # True: its top bands reach Nyquist
print(result.requirements)
# ('relative_attenuation', 'effective_bandwidth', 'summation')
band = result.bands[0]
print(band["freq"], band["class"], band["checked_to_omega"])
# 12.589254117941678 1 17.984790220172403
print(band["margin_class1_db"], band["bandwidth_margin_class1_db"])
# 0.39999999999978114 0.3512657809681092
Calculation chain for grading one band filter against Table 1 of IEC 61260-1. Across the top, four boxes lead into each other: the designed band, a one-third-octave band at 1000 Hz of order 6; its relative attenuation, the attenuation at each normalized frequency less the attenuation at mid-band; the Table 1 mask, its octave breakpoints carried to one-third octave with the limits straight in lg Ω between them; and the margin and class. Below, two panels share a frequency axis stretched breakpoint by breakpoint. The pass-band panel runs from Ω = 1 to the band edge at 1.122 and holds the class 1 corridor, a floor at −0.4 dB under a ceiling rising from +0.4 dB to +5.3 dB, with the wider class 2 corridor dashed around it; the band stays flat at 0 dB and climbs to 3 dB at the edge. The stop-band panel runs from the edge to 5.392, where only a minimum applies, rising from 1.2 dB through 16.6, 40.5 and 60 dB to 70 dB for class 1, and the band climbs far above it. Under the columns each octave breakpoint, from G raised to one eighth up to G raised to the fourth, is paired with its one-third-octave value, and a row gives the class 1 margin at each: +0.40 dB at the first three, boxed as the band's margin, then +0.49, +2.29 and +1.81 dB, and 26 to 92 dB in the stop band. A note adds the class 0 of the 1995 edition, ±0.15 dB at mid-band and 75 dB from G raised to the fourth. A green box gives the verdict, class 1 with margins of +0.40 dB and +0.60 dB, a bank taking the class of its worst band, and range_limited flagging a band walked only to its own Nyquist. On the right, a solid box fed from the relative-attenuation box lists what the verifier also computes on the design, the IEC 61260-2 tests that need no specimen: at least 24 sines per bandwidth at Ω = G to the power i over bS (Formula 1), the effective bandwidth deviation of 5.12 by Formula (2) within ±0.4 dB or ±0.6 dB, the summation of outputs of 5.16 by Formula (3) within +0.8 dB and −1.8 dB for class 1, and the sweep of 5.14 through the bank itself. Below it a dashed column, marked outside the check, lists what a laboratory does to an instrument: IEC 61260-2 on a specimen, at least three submitted and at least one tested in full, 1 dB under the top of the linear range, 20 °C to 26 °C and 35 % to 65 % relative humidity; and IEC 61260-3 periodic tests, every filter at mid-band or one sweep, three filters, low, middle and high, at up to 15 frequencies, whose results verify_filter_periodic grades, with the uncertainty within Annex B, 0.20 to 0.50 dB. Formulas 9 and 11 are boxed at the footCalculation chain for grading one band filter against Table 1 of IEC 61260-1. Across the top, four boxes lead into each other: the designed band, a one-third-octave band at 1000 Hz of order 6; its relative attenuation, the attenuation at each normalized frequency less the attenuation at mid-band; the Table 1 mask, its octave breakpoints carried to one-third octave with the limits straight in lg Ω between them; and the margin and class. Below, two panels share a frequency axis stretched breakpoint by breakpoint. The pass-band panel runs from Ω = 1 to the band edge at 1.122 and holds the class 1 corridor, a floor at −0.4 dB under a ceiling rising from +0.4 dB to +5.3 dB, with the wider class 2 corridor dashed around it; the band stays flat at 0 dB and climbs to 3 dB at the edge. The stop-band panel runs from the edge to 5.392, where only a minimum applies, rising from 1.2 dB through 16.6, 40.5 and 60 dB to 70 dB for class 1, and the band climbs far above it. Under the columns each octave breakpoint, from G raised to one eighth up to G raised to the fourth, is paired with its one-third-octave value, and a row gives the class 1 margin at each: +0.40 dB at the first three, boxed as the band's margin, then +0.49, +2.29 and +1.81 dB, and 26 to 92 dB in the stop band. A note adds the class 0 of the 1995 edition, ±0.15 dB at mid-band and 75 dB from G raised to the fourth. A green box gives the verdict, class 1 with margins of +0.40 dB and +0.60 dB, a bank taking the class of its worst band, and range_limited flagging a band walked only to its own Nyquist. On the right, a solid box fed from the relative-attenuation box lists what the verifier also computes on the design, the IEC 61260-2 tests that need no specimen: at least 24 sines per bandwidth at Ω = G to the power i over bS (Formula 1), the effective bandwidth deviation of 5.12 by Formula (2) within ±0.4 dB or ±0.6 dB, the summation of outputs of 5.16 by Formula (3) within +0.8 dB and −1.8 dB for class 1, and the sweep of 5.14 through the bank itself. Below it a dashed column, marked outside the check, lists what a laboratory does to an instrument: IEC 61260-2 on a specimen, at least three submitted and at least one tested in full, 1 dB under the top of the linear range, 20 °C to 26 °C and 35 % to 65 % relative humidity; and IEC 61260-3 periodic tests, every filter at mid-band or one sweep, three filters, low, middle and high, at up to 15 frequencies, whose results verify_filter_periodic grades, with the uncertainty within Annex B, 0.20 to 0.50 dB. Formulas 9 and 11 are boxed at the foot

The 1 kHz band of a bank like the one above, walked through the check: its relative attenuation at every Table 1 breakpoint, carried to one-third octave, against the class 1 and class 2 limits, with the smallest margin deciding the class. The default bank walks it to the end of the mask: its Nyquist frequency at 48 kHz is 24 times its mid-band frequency, and a decimated band keeps its own at least sixteen times its upper edge. The box on the right is what section 1b computes on the design as well, the IEC 61260-2 tests that need no specimen; the dashed column under it is what a laboratory does to an instrument, and section 3b grades the periodic-test results it returns.

How far up the mask the verdict actually reaches. checked_to_omega is the highest normalized frequency at which that band was evaluated: the band’s own processing Nyquist frequency, which on a multirate bank is the Nyquist frequency of its decimated rate. The bank decimates a band only as far as that Nyquist frequency stays at least sixteen times its upper band edge (section 1b says why), so every decimated band of the bank above is evaluated to or more, past the end of the Table 1 mask (at least 70 dB for class 1 from up, the octave row carried to one-third octave). The bands that stop short are the ones filtered at the full rate near the top of the bank: from 5 kHz up the 24 kHz Nyquist frequency lies below , and the 20 kHz band is evaluated only to . There the far-stopband requirement is not demonstrated on the band filter at all. It is taken as satisfied because a sampled signal carries no energy above its Nyquist frequency: the anti-alias filter of the capture chain removed it before the band ever saw it. range_limited is the flag that this argument was used, and it is True here.

Say that plainly in a report: the verdict attests the mask up to checked_to_omega, and the rest is an argument about the capture chain. The flag clears only when every band’s Nyquist frequency lies past the end of the mask. The one-third-octave bank of section 1b, from 125 Hz to 4 kHz at 48 kHz, is not range-limited; the octave bank over the same range is, because its 2 kHz and 4 kHz bands, filtered at the full rate, are evaluated only to and 6, short of the where the octave mask ends. When a document requires the full mask on every band filter, lower the top of the bank or raise fs: designing the bank with design=filters.FilterDesign(resample=False) changes nothing here, because the bands that stop short already run at the full rate.

What the margin measures. It is the minimum, over every normalized frequency the band was evaluated at, of the distance to the nearest limit of that class: positive when the response stays inside the corridor everywhere, and negative by exactly the worst violation when it does not. The reported class is the strictest class for which that margin and the margins of section 1b are all non-negative.

Which constraint binds is worth knowing before you try to improve the number. A maximally-flat Butterworth is flat at mid-band, where the class 1 corridor is ±0.4 dB, so its margin saturates at +0.400 dB the moment the stopband stops being the limiting factor: measured on a 48 kHz one-third-octave bank, order 2 fails outright (class None, worst class 1 margin −27.03 dB) and orders 4, 6, 8 and 10 all hold exactly +0.400 dB on Table 1 (order 4 then misses class 1 on the summation of section 1b, and orders 6 to 10 are class 1 throughout). Raising the order therefore helps only while the margin is negative for a stopband reason; once a design passes, the margin is capped by the passband half-corridor and no order will move it. A margin of +0.400 dB is not a mediocre result to be improved; it is the best a compliant design can report against this mask.

The Table 1 acceptance mask itself is public too: class_limits(fraction, filter_class, omega) returns the minimum/maximum relative-attenuation limits at normalized frequencies , the same limits the verifier and the figure below use.

Butterworth band response threading between the forbidden regions of the IEC 61260-1 class 1 acceptance maskButterworth band response threading between the forbidden regions of the IEC 61260-1 class 1 acceptance mask

The order-6 Butterworth response (blue) threads between the forbidden regions: it must attenuate at least the red mask outside the band and no more than the purple mask inside it.

Show the code for this figure
import matplotlib.pyplot as plt
import numpy as np
from scipy.signal import sosfreqz
from phonometry import filters
fs = 48000
bank = filters.OctaveFilterBank(fs, fraction=1, order=6, limits=[800, 1200])
idx = int(np.argmin(np.abs(np.array(bank.freq) - 1000)))
fm, fsd = bank.freq[idx], fs / bank.factor[idx]
w, h = sosfreqz(bank.sos[idx], worN=2**15, fs=fsd)
att = -20 * np.log10(np.abs(h) + 1e-12)
delta_a = att - np.interp(fm, w, att) # relative attenuation
grid = np.logspace(np.log10(0.05), np.log10(8), 2000)
lo1, hi1 = filters.class_limits(1.0, 1, grid) # class 1 min/max attenuation
fig, ax = plt.subplots(figsize=(9, 5.5))
ax.fill_between(grid, -10, lo1, alpha=0.15, color="tab:red",
label="Forbidden: too little attenuation")
finite = np.isfinite(hi1)
ax.fill_between(grid[finite], hi1[finite], 90, alpha=0.15, color="tab:purple",
label="Forbidden: too much attenuation")
ax.plot(w / fm, delta_a, label="Butterworth order 6")
ax.set(xscale="log", xlim=(0.08, 8), ylim=(-6, 90),
xlabel="Normalized frequency f / fm",
ylabel="Relative attenuation [dB]")
ax.legend()
plt.show()

With default parameters (order 6), Butterworth meets the class 1 mask of Table 1, and so does Chebyshev II: its attenuation default is now 72 dB, clearing the 70 dB far-stopband class 1 limit (scipy pins the cheby2 equiripple floor at exactly attenuation, so any value qualifies; the 72 dB default keeps the same +0.400 dB passband margin as Butterworth). Chebyshev I, Elliptic and Bessel do not meet class limits at order 6: passband ripple (cheby1/ellip) and slow roll-off (bessel) violate the mask. Class 1 is the strictest verdict this edition can return; the stricter class 0 the default bank also clears belongs to the withdrawn 1995 mask and is section 2. Whether a whole bank is class 1 also depends on the two requirements of section 1b, and the default octave and one-third-octave banks meet class 1 there too.

1b. Effective bandwidth and the summation of outputs (IEC 61260-2)

Section titled “1b. Effective bandwidth and the summation of outputs (IEC 61260-2)”

Table 1 judges a band one frequency at a time. IEC 61260-1:2014 adds two requirements that judge a band, and a set of bands, as a whole, and IEC 61260-2:2016 says how a pattern-evaluation laboratory computes them from the same relative attenuation. Neither needs anything but the designed response, so for the 2014 edition verify_filter_class grades both, and a band’s class is the strictest class it meets on all three.

Effective bandwidth (5.12). For broadband sound a band analyser reports the power its band lets through, so its width in power is what matters. The normalized effective bandwidth is Formula (13) of IEC 61260-1,

and its deviation from the bandwidth of an ideal band, , is , within ±0.4 dB for class 1 and ±0.6 dB for class 2 (5.12.2). A band with dB reads pink noise 0.05 dB high. The verifier evaluates the integral as IEC 61260-2 7.2.3.2 recommends: the trapezoidal rule of its Formula (2) over the test frequencies of its Formula (1),

with at least 24 frequencies per bandwidth (points_per_bandwidth, 24 by default), carried out to the outermost breakpoint of Table 1, where a class 1 band is at least 70 dB down.

Summation of output signals (5.16). A tone between two mid-band frequencies has to come out of the set with its power, shared between the bands that see it, neither lost nor added. Formula (3) of IEC 61260-2 sums on an energy basis the relative attenuation of band and of its two neighbours, at the test frequencies inside band ():

and the result has to stay between −1.8 dB and +0.8 dB for class 1, and between −3.8 dB and +1.8 dB for class 2. The two end bands, with a neighbour on one side only, are left out (7.2.4.4), so the requirement is graded only on a bank of three bands or more, and result.requirements says whether it was. The printed Formula (3) and the words of 7.2.4.3 and of 5.16 take the difference in opposite directions, which with limits this lopsided is not the same test; the verifier applies the limits to the formula as printed, as 7.2.4.5 instructs, and the errata registry records the conflict.

from phonometry import filters
third = filters.verify_filter_class(
filters.OctaveFilterBank(48000, fraction=3, order=6, limits=[125, 4000]))
print(third.overall_class) # 1
octave = filters.verify_filter_class(
filters.OctaveFilterBank(48000, fraction=1, order=6, limits=[125, 4000]))
print(octave.requirement_class("relative_attenuation")) # 1
print(octave.requirement_class("effective_bandwidth")) # 1
print(octave.requirement_class("summation")) # 1
print(round(octave.binding_margin_db("summation", 1), 2)) # 0.64
print(octave.overall_class) # 1
Two panels of the summed output of adjacent bands against normalized frequency, from the lower to the upper band edge, with the class 1 limits dashed at +0.8 dB and −1.8 dB and the class 2 limits dotted at +1.8 dB and −3.8 dB. On the left, the decimated octave bank from 125 Hz to 4 kHz: every inner band sums to about 0 dB across the middle of the band and rises in a narrow bump just inside each band edge, to about +0.13 dB near the lower edge and to between +0.14 dB and +0.16 dB near the upper edge, before returning to 0 dB at the edges themselves; the 1995 Hz band, drawn heavier as the binding band, reaches +0.16 dB, far inside class 1. On the right, the one-third-octave bank over the same range traces the same shape from 0.00 dB to +0.16 dB, its 3162 Hz band the binding one, inside class 1Two panels of the summed output of adjacent bands against normalized frequency, from the lower to the upper band edge, with the class 1 limits dashed at +0.8 dB and −1.8 dB and the class 2 limits dotted at +1.8 dB and −3.8 dB. On the left, the decimated octave bank from 125 Hz to 4 kHz: every inner band sums to about 0 dB across the middle of the band and rises in a narrow bump just inside each band edge, to about +0.13 dB near the lower edge and to between +0.14 dB and +0.16 dB near the upper edge, before returning to 0 dB at the edges themselves; the 1995 Hz band, drawn heavier as the binding band, reaches +0.16 dB, far inside class 1. On the right, the one-third-octave bank over the same range traces the same shape from 0.00 dB to +0.16 dB, its 3162 Hz band the binding one, inside class 1

Formula (3) of IEC 61260-2 on every inner band of the two default banks, drawn by result.plot(requirement="summation"). Both banks return a tone’s power to within 0.16 dB: the sum stays within a few hundredths of a decibel of 0 dB across the middle of each band and rises by a little more than a tenth of a decibel just inside its edges, where two neighbouring bands share the tone.

Show the code for this figure
import matplotlib.pyplot as plt
from phonometry import filters
octave = filters.verify_filter_class(
filters.OctaveFilterBank(48000, fraction=1, order=6, limits=[125, 4000]))
third = filters.verify_filter_class(
filters.OctaveFilterBank(48000, fraction=3, order=6, limits=[125, 4000]))
fig, (ax_oct, ax_third) = plt.subplots(1, 2, figsize=(13, 6.2), sharey=True)
octave.plot(ax=ax_oct, requirement="summation")
third.plot(ax=ax_third, requirement="summation")
ax_oct.set_title("Octave bank, decimated: class 1 on §5.16")
ax_third.set_title("One-third-octave bank: class 1 on §5.16")
ax_third.set_ylabel("")
plt.show()

Why the bank decimates a band only to sixteen times its upper edge. The summation is the requirement a multirate design puts at risk. Close to its own Nyquist frequency the bilinear transform bends a band: just inside its upper band edge it attenuates less than the same band designed far from Nyquist, and above that edge it falls more steeply. Just below an upper band edge the band and its neighbour above then pass more than the tone’s power; just above a lower band edge the band below has already fallen away and power is lost. An octave bank (Butterworth, order 6, 48 kHz) decimated until each band’s processing Nyquist frequency was only 1.25 times its upper band edge sums its adjacent outputs from −1.16 dB to +0.94 dB about the input: past the +0.8 dB of class 1, a class 2 bank. The bend shrinks as the square of that ratio: it moves the summation of an octave band by 0.074 dB at a ratio of four, 0.018 dB at eight and 0.005 dB at sixteen, and that of a one-third-octave band by 0.002 dB at sixteen. So the bank stops decimating a band while its processing Nyquist frequency is still at least sixteen times its upper band edge, and its decimated banks sum as the same banks designed at the full rate (design=filters.FilterDesign(resample=False)): class 1 on every requirement at 44.1, 48 and 96 kHz. Over its full default range at 48 kHz the octave bank sums from −0.06 dB to +0.69 dB, decimated or not, the top set by the 8 kHz band next to the 16 kHz band, which runs at the full rate close to the 24 kHz Nyquist frequency: class 1 with 0.11 dB to spare. The one-third-octave bank sums from −0.25 dB to +0.76 dB, both at its 16 kHz band, a margin of 0.04 dB, and the decimated Chebyshev II octave bank from −0.08 dB to +0.64 dB. None of this is an artefact of grading transfer functions: tones run through the bank itself, its decimation filters included, read the Formula (3) sums to within 0.01 dB.

A multirate bank does not filter a tone the same way wherever it falls against the decimation, and a transfer function cannot show that. IEC 61260-1:2014 5.14 tests it on the running filter instead: a sinusoid of constant amplitude whose frequency rises one decade in 2 s to 5 s, and each band’s time-averaged output has to stay within ±0.4 dB (class 1) or ±0.6 dB (class 2) of the level of Formula (17),

the output of an ideal band of the same bandwidth. swept_band_level is that formula (107.97 dB for the example Annex B of IEC 61260-2 and -3 works through), and swept_level_uncertainty its standard uncertainty from the uncertainties of the sweep (Annex A). verify_time_invariance runs the test of IEC 61260-2 7.4 on a bank: the sweep of Formulas (A.3) and (A.4) goes through every band exactly as OctaveFilterBank.filter processes a signal, polyphase decimation included, at 2 s and at 5 s per decade.

from phonometry import filters
bank = filters.OctaveFilterBank(48000, fraction=3, order=6, limits=[25, 10000])
swept = filters.verify_time_invariance(bank)
print(swept.overall_class) # 1
print(round(swept.worst_deviation_db, 3)) # 0.056
print(swept.seconds_per_decade) # (2.0, 5.0)
print(round(filters.swept_band_level(
127.0, fraction=3, sweep_duration_s=30, averaging_time_s=30,
start_frequency_hz=0.01, end_frequency_hz=1e6), 2)) # 107.97
The deviation of each band's swept output from the level of Formula (17), for the one-third-octave bank from 25 Hz to 10 kHz, at 2 s and at 5 s per decade, between the class 1 limits dashed at ±0.4 dB and the class 2 limits dotted at ±0.6 dB. Every band at both rates sits at about +0.05 dB, the two rates on top of each other, so the bank is class 1The deviation of each band's swept output from the level of Formula (17), for the one-third-octave bank from 25 Hz to 10 kHz, at 2 s and at 5 s per decade, between the class 1 limits dashed at ±0.4 dB and the class 2 limits dotted at ±0.6 dB. Every band at both rates sits at about +0.05 dB, the two rates on top of each other, so the bank is class 1

Every band of the decimated one-third-octave bank reads about +0.05 dB at both sweep rates. That is its effective bandwidth deviation of section 1b, which is what Annex G of IEC 61260-1 says a band that behaves as its transfer function must read (G.2.8); decimation that folded energy back into a band would read more, and the two rates would part.

Show the code for this figure
import matplotlib.pyplot as plt
from phonometry import filters
bank = filters.OctaveFilterBank(48000, fraction=3, order=6, limits=[25, 10000])
filters.verify_time_invariance(bank).plot()
plt.show()

2. Class 0 (IEC 61260:1995 / ANSI S1.11-2004)

Section titled “2. Class 0 (IEC 61260:1995 / ANSI S1.11-2004)”

The tightest performance class, class 0, was defined by the earlier IEC 61260:1995 and its US twin ANSI S1.11-2004 (both withdrawn/superseded but still referenced for laboratory-grade instruments); IEC 61260-1:2014 dropped it. Its class 1/2 masks differ slightly from the 2014 edition, so it lives behind an edition switch rather than being mixed into the 2014 mask:

from phonometry import filters
fs = 48000
bank = filters.OctaveFilterBank(fs, fraction=1, order=6, limits=[800, 1200])
result = filters.verify_filter_class(bank, edition="1995") # classes 0, 1, 2
print(result.overall_class) # 0 (the default Butterworth clears it)
print(result.bands[0]["margin_class0_db"])
Nested pass-band acceptance corridors for class 0, 1 and 2 of IEC 61260:1995 with the order-6 Butterworth response sitting inside the tightest class 0 corridorNested pass-band acceptance corridors for class 0, 1 and 2 of IEC 61260:1995 with the order-6 Butterworth response sitting inside the tightest class 0 corridor

The class 0 corridor (±0.15 dB at mid-band) is the tightest; class 1 (±0.3 dB) and class 2 (±0.5 dB) are progressively wider. The order-6 Butterworth threads inside class 0 across the whole pass-band.

Show the code for this figure
import matplotlib.pyplot as plt
import numpy as np
from scipy.signal import sosfreqz
from phonometry import filters
fs = 48000
bank = filters.OctaveFilterBank(fs, fraction=1, order=6, limits=[800, 1200])
idx = int(np.argmin(np.abs(np.array(bank.freq) - 1000)))
fm, fsd = bank.freq[idx], fs / bank.factor[idx]
w, h = sosfreqz(bank.sos[idx], worN=2**15, fs=fsd)
att = -20 * np.log10(np.abs(h) + 1e-12)
delta_a = att - np.interp(fm, w, att)
# Pass-band only: outside the band edges the maximum limit is +inf.
g = 10 ** (3 / 10)
grid = np.linspace(g ** -0.5, g ** 0.5, 1500)
pb = (w / fm >= g ** -0.5) & (w / fm <= g ** 0.5)
fig, ax = plt.subplots(figsize=(9, 5.5))
for cls in (2, 1, 0): # nested corridors, class 0 tightest
lo, hi = filters.class_limits(1.0, cls, grid, edition="1995")
ax.plot(grid, hi, label=f"Class {cls} corridor")
ax.plot(grid, lo, color=ax.lines[-1].get_color())
ax.plot(w[pb] / fm, delta_a[pb], "k", lw=2, label="Butterworth order 6")
ax.set(xscale="log", xlim=(g ** -0.5, g ** 0.5), ylim=(-0.7, 6),
xlabel="Normalized frequency f / fm",
ylabel="Relative attenuation [dB]")
ax.legend()
plt.show()

The masks are worst-case error bounds on a measurement, not abstract grades:

  • In the passband the corridor bounds how much the band can mis-read in-band content: a class 1 bank reads a mid-band tone within ±0.4 dB of its true level and a class 2 bank within ±0.6 dB (IEC 61260-1:2014 Table 1; the stricter IEC 61260:1995 Table 1 masks allowed ±0.3 dB for class 1, ±0.5 dB for class 2 and ±0.15 dB for class 0). Toward the band edges the corridor widens, which is the honest admission that a tone sitting exactly on an edge is genuinely ambiguous between two bands (both read it about 3 dB down).

  • In the stopband the minimum-attenuation mask bounds leakage from the rest of the spectrum: far from the band, class 1 demands at least 70 dB of relative attenuation (the reason the cheby2 default is 72 dB). In energy terms, an out-of-band tone must be roughly 70 dB stronger than the band’s own content before it doubles the band’s energy reading (+3 dB). The practical consequence: measuring bands far below a dominant tone, the reading floors out at the leakage skirt about 70 dB down, and a steeper architecture (or higher order) is the only way to push that floor lower.

    One-third-octave band levels of a 1 kHz tone at 100 dB SPL sitting on a pink-noise floor whose own band levels are about 22 dB, analysed with order 6 and order 10 banks. The 1 kHz band reads 100 dB, the neighbouring bands fall away steeply, and below about 500 Hz the measured levels flatten onto a skirt some 65 to 70 dB below the tone, well above the true noise floor drawn dotted; above about 8 kHz the skirt has fallen below the noise and the measured levels join it. The order 10 bank pulls the skirt visibly lower on the high side of the toneOne-third-octave band levels of a 1 kHz tone at 100 dB SPL sitting on a pink-noise floor whose own band levels are about 22 dB, analysed with order 6 and order 10 banks. The 1 kHz band reads 100 dB, the neighbouring bands fall away steeply, and below about 500 Hz the measured levels flatten onto a skirt some 65 to 70 dB below the tone, well above the true noise floor drawn dotted; above about 8 kHz the skirt has fallen below the noise and the measured levels join it. The order 10 bank pulls the skirt visibly lower on the high side of the tone

    What the last sentence looks like as a measurement. Every band whose level sits on the skirt is reporting the filter’s rejection of the 1 kHz tone, not the sound present in that band: the dotted line is what is really there. The test is not subtle once you look for it: raise the order and the bands that are measuring the filter move, while the bands that are measuring the sound do not.

  • For the uncertainty budget, the class is the filter’s contribution to the measurement uncertainty: a class 1 bank adds up to a few tenths of a dB to a band level, comparable to a class 1 sound level meter’s other tolerance terms, which is why instrument-grade chains specify the class of every stage rather than a single overall figure.

Which architecture reaches which class? Under the 2014 edition, whose only classes are 1 and 2, the library’s default Butterworth order-6 octave and one-third-octave banks meet class 1 on every requirement, with a +0.400 dB margin on Table 1 (the ceiling, and section 1 explains why) and the summation of section 1b inside class 1 as well. Against the stricter 1995 / ANSI S1.11-2004 mask (edition="1995") the same default reaches class 0; the configuration the conformance suite verifies at that class is the octave-band bank at 48 kHz, so re-run verify_filter_class(bank, edition="1995") yourself before writing class 0 into a document for any other fraction or sample rate. Writing “class 0 per IEC 61260-1:2014” is a claim against a class that edition does not define.

The table reports the best class each architecture reaches at order 6, fs 48 kHz, under the 1995 mask; the architectures other than Butterworth fall short because they trade the IEC mask for a different property by construction:

ArchitectureBest class (order 6, fs 48 kHz, edition="1995")Why
butter (default)0Maximally-flat pass-band, monotone roll-off; fits the mask
cheby21Flat pass-band but the mask relationship binds at class 1
cheby1—Pass-band ripple violates the flatness limit
ellip—Pass- and stop-band ripple
bessel—Flat group delay bought with a slow roll-off

Under the 2014 edition the same column reads 1, 1 and no class for the other three: cheby2 joins Butterworth at the top because class 0 no longer exists to separate them. That is the one-band bank of the figure below; a bank of several bands adds the summation of section 1b, and the decimated Butterworth and Chebyshev II octave banks meet class 1 on it too.

Four panels, each the 1 kHz octave band of an order-6 bank drawn against the IEC 61260-1 acceptance corridor with its out-of-tolerance samples marked in red. Butterworth threads the class 1 corridor cleanly and reports overall_class 1. Chebyshev I leaves the corridor in two short bursts just inside each band edge, where its passband ripple sits, and reports None. Elliptic does the same at both edges, and reports None. Bessel drifts out of the corridor along the whole lower skirt because it rolls off too slowly, and reports NoneFour panels, each the 1 kHz octave band of an order-6 bank drawn against the IEC 61260-1 acceptance corridor with its out-of-tolerance samples marked in red. Butterworth threads the class 1 corridor cleanly and reports overall_class 1. Chebyshev I leaves the corridor in two short bursts just inside each band edge, where its passband ripple sits, and reports None. Elliptic does the same at both edges, and reports None. Bessel drifts out of the corridor along the whole lower skirt because it rolls off too slowly, and reports None

The verdicts of the table, drawn. Chebyshev I and Elliptic poke through the limit just inside the band edges, where their ripple lives; Bessel leaves it along the skirt, because it never falls fast enough. The red samples are the ones verify_filter_class counted against the design, and this is exactly what verify_filter_class(bank).plot() draws for a bank of your own; note that the plot shades the corridor of the class each design came closest to, so the three failing panels show the class 2 corridor.

Show the code for this figure
import matplotlib.pyplot as plt
# `filters` is the import of the snippets above.
fs = 48000
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
for ax, ftype in zip(axes.ravel(), ("butter", "cheby1", "ellip", "bessel")):
bank = filters.OctaveFilterBank(
fs, fraction=1, order=6, limits=[800, 1200],
design=filters.FilterDesign(filter_type=ftype))
result = filters.verify_filter_class(bank)
result.plot(ax=ax)
ax.set_title(f"{ftype}: overall_class = {result.overall_class}")
plt.tight_layout()
plt.show()

So the sensible default is the common one (Butterworth order 6), while the alternative architectures are deliberate opt-ins whose purpose (steeper roll-off, linear phase) works against the class mask. Away from these settings (very high fraction or near-Nyquist bands), always re-run verify_filter_class to confirm the class you need.

verify_filter_class answers a question about a design: does this transfer function fit the mask. A laboratory answers a different question about a device on a date, and the two verdicts are not interchangeable.

The full Table 1 walk belongs to pattern evaluation (IEC 61260-2:2016), which a filter-set model passes once, on specimens, under stated climate and after immunity tests. The parts of it that are arithmetic on a response, the effective bandwidth, the summation and the swept test, are sections 1b and 1c; the rest needs the device. What a working instrument actually receives is the periodic test of IEC 61260-3:2016, and it is narrower on purpose: the relative attenuation is measured at the exact midband frequency of every filter in the set, through the electrical input on the reference level range, or, for time-invariant filters, the effective-bandwidth deviation from one exponential sweep across the whole set instead; the relative attenuation of three selected filters, one low, one in the middle and one high, at the clause 13 normalized frequencies ( from to , as far as the frequency range of the set reaches); the linear operating range with its level-range control and overload behaviour; and the lower limit of that range (all under stated environmental conditions and with traceable test equipment). That is why a certificate carries a date, a temperature and a set of serial numbers, and why it says nothing about the parts of the mask it did not walk.

What carries over is worth stating in a report: a class verdict from this page is inherited by every measurement made with the library’s filters, and a hardware chain adds a verdict of its own. Name both. The equivalent regime for sound level meters (IEC 61672-3 periodic tests, and IEC 60942 for the calibrator) is in Calibration and dBFS.

Grading a periodic test. The tests are the laboratory’s to run; the verdict on what it measured is arithmetic, and verify_filter_periodic does it. Each result goes in with the laboratory’s expanded uncertainty, and each is judged by the conformance rule of IEC TC 29 (metrology.verify_conformance): the deviation within its acceptance limits and the uncertainty within the maximum IEC 61260-1:2014 Annex B permits for that test. Clause by clause:

ClauseWhat the laboratory measuredAcceptance limitsMaximum (Annex B)
10.2relative attenuation of every filter at its exact mid-band±0.4 dB class 1, ±0.6 dB class 20.20 dB, or 0.30 dB past a of 2 dB
10.3or, time-invariant filters, from one sweep±0.4 dB class 1, ±0.6 dB class 20.20 dB
11.7level linearity on the reference range±0.5 dB class 1, ±0.6 dB class 2 within 40 dB of the upper boundary; ±0.7 dB and ±0.9 dB beyond0.20 dB within 40 dB, 0.35 dB beyond
11.9level linearity on each other range, 30 dB below its upper boundaryas 11.7as 11.7
13relative attenuation of three filters at up to 15 IEC 61260-3 Table 10.20, 0.30 or 0.50 dB as is up to 2 dB, up to 40 dB, or more

The fifteen frequencies of clause 13 come from Formulas (1) and (2) of IEC 61260-3 for any bandwidth designator, periodic_test_frequencies(b), and its Table 1 is PERIODIC_TEST_ATTENUATION_LIMITS_DB, the stop-band rows with no maximum. A laboratory drops the frequencies that fall outside the range of the set (13.4) by writing NaN in their place.

from phonometry import filters
omega = filters.periodic_test_frequencies(3)
print(round(float(omega[8]), 5)) # 1.02667, Table C.1
print(filters.PERIODIC_TEST_ATTENUATION_LIMITS_DB[1][7]) # (70.0, inf)
row = [76.0, 63.0, 45.0, 20.0, 0.8, 0.3, 0.1, 0.0, 0.1, 0.2, 0.7, 19.0, 44.0, 63.0, 77.0]
row_u = [0.4, 0.4, 0.4, 0.25, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.25, 0.4, 0.4, 0.4]
record = filters.FilterPeriodicMeasurements(
midband_attenuations_db=[0.12, -0.05, 0.08, 0.02, -0.1, 0.15],
midband_uncertainties_db=[0.15] * 6,
linearity_deviations_db=[0.0, 0.1, 0.2, -0.3, 0.4],
linearity_levels_below_upper_db=[0.0, 10.0, 20.0, 45.0, 55.0],
linearity_uncertainties_db=[0.12, 0.12, 0.25, 0.2, 0.3],
relative_attenuations_db=[row, [x + 0.2 for x in row], row],
relative_attenuation_uncertainties_db=[row_u, row_u, row_u],
)
verdict = filters.verify_filter_periodic(1, record, fraction=3)
print(verdict.passes) # False
print(verdict.unusable) # (('11.7', '20 dB below the upper boundary'),)
print(verdict.failed) # ()

Every deviation of that record is inside its limits, and the verdict is still not a pass: one linearity reading was taken with 0.25 dB of expanded uncertainty where Annex B allows 0.20 dB, and 5.3 of IEC 61260-3 forbids using it. verdict.statement says so in the words of the standard, and once every result is usable it becomes the statement Clause 14 prescribes: 14 k) when the model’s pattern approval is public (pattern_approval_public=True), 14 l) otherwise, with the caveat of 1.5 that no general conclusion about IEC 61260-1 follows from the periodic tests alone.

A pass also needs the tests to cover what the standard asks of them: 11.3 and 13.1 measure three filters, and 13.4 measures each of them at every test frequency above 0.5 times the lowest mid-band frequency of the set and below 1.5 times the highest. Given the mid-band frequencies of the set (set_midband_frequencies_hz), of the three tested filters (tested_midband_frequencies_hz) and of the filter behind each linearity result (linearity_midband_frequencies_hz), the verdict counts them, and verdict.incomplete lists each shortfall, a NaN where 13.4 asks for a measurement included; it holds the pass back as a missing clause does. A record without them cannot be checked for coverage: verdict.coverage_checked is False, and a passing statement ends by saying the coverage was not checked. A mid-band attenuation of 10.2 takes its Annex B maximum from what was measured, as clause 13 does: a filter that has drifted past 2 dB at its mid-band is allowed 0.30 dB, and so fails rather than being unusable.

The margin of every result of a class 1 periodic test to its nearer acceptance limit, grouped by clause: six mid-band results of 10.2 between 0.25 dB and 0.38 dB, five level-linearity results of 11.7 and forty-five relative attenuations of clause 13, on a scale linear up to 1 dB and logarithmic above. Every result lies above the red acceptance line at zero with its expanded uncertainty drawn as a bar, and all are green diamonds except one 11.7 result drawn as a hollow orange circle, unusable under 5.3 because its uncertainty exceeds the Annex B maximum. The title reads not passedThe margin of every result of a class 1 periodic test to its nearer acceptance limit, grouped by clause: six mid-band results of 10.2 between 0.25 dB and 0.38 dB, five level-linearity results of 11.7 and forty-five relative attenuations of clause 13, on a scale linear up to 1 dB and logarithmic above. Every result lies above the red acceptance line at zero with its expanded uncertainty drawn as a bar, and all are green diamonds except one 11.7 result drawn as a hollow orange circle, unusable under 5.3 because its uncertainty exceeds the Annex B maximum. The title reads not passed

verdict.plot(): every result’s margin to its nearer limit with its expanded uncertainty, clause by clause. The hollow circle is the one result 5.3 makes unusable, and it alone holds the verdict back.

Show the code for this figure
import matplotlib.pyplot as plt
# `verdict` is the result of the snippet above.
verdict.plot()
plt.show()

What the periodic grader leaves to the laboratory report: the self-generated noise of Clause 12, for which Annex B sets no maximum uncertainty, the overload indication of 11.5 and 11.8, and the observations of Clauses 4 and 6 to 8 (preliminary inspection, environmental conditions, the manual) are not graded; record them beside the verdict.

verify_filter_class(bank) returns a result object that exposes .plot() and .report(), so a type-test verdict can be rendered as a one-page accredited fiche. The fiche lists every band’s achieved class and its binding margin over every requirement graded, tabulates the requirements of sections 1 and 1b with each one’s class, binding margin and range, overlays the worst-margin band’s measured relative attenuation on the class corridor, and boxes the overall class-compliance result. Pass a required_class on the ReportMetadata to add a PASS/FAIL verdict row (a bank “meets class N” when its achieved class is at least as strict, i.e. a class index of N or lower). The fiche renders in English by default; pass language="es" for a Spanish fiche (translated fixed strings and a comma decimal separator), e.g. result.report("iec61260_es.pdf", language="es").

The example is the decimated octave bank from 125 Hz to 4 kHz, the configuration section 1b shows to be class 1 on every requirement:

from phonometry import ReportMetadata, filters
bank = filters.OctaveFilterBank(fs=48000, fraction=1, order=6, limits=[125, 4000])
result = filters.verify_filter_class(bank) # overall_class == 1
result.plot() # the worst-margin band on its class corridor
result.report(
"iec61260.pdf",
metadata=ReportMetadata(
specimen="1/1-octave filter bank",
measurement_standard="IEC 61260-1:2014",
required_class=1, # class 1 (or stricter) required
),
) # -> Class 1 - COMPLIES, PASS

The example fiche is regenerated with make reports and kept rendered in the repository; click the preview to open the PDF.

IEC 61260-1 filter class compliance example report (PDF)

One-page filter-class-compliance fiche: a metadata header, a per-band classification table listing each octave band's achieved class and binding margin, the worst-margin band's measured relative attenuation overlaid on the green class-1 acceptance corridor with the three requirements graded beneath it, the boxed Class 1 - COMPLIES result and a PASS verdict against the required class 1.

Download the report (PDF)

Filter class compliance fiche (FilterComplianceResult.report), achieved class with the binding margin in dB, the measured relative attenuation over the Table 1 corridor, and the class of each requirement of IEC 61260-1:2014 graded.

Passing edition="1995" verifies against the older IEC 61260:1995 / ANSI S1.11-2004 mask, which keeps the stricter class 0 that the 2014 edition dropped, so a modest order-6 bank can be certified to class 0:

bank = filters.OctaveFilterBank(fs=48000, fraction=1, order=6, limits=[250, 4000])
result = filters.verify_filter_class(bank, edition="1995") # overall_class == 0
result.plot() # the class-0 corridor of the 1995 edition
result.report("iec61260_1995.pdf",
metadata=ReportMetadata(measurement_standard="IEC 61260:1995",
required_class=0)) # -> Class 0 - COMPLIES
IEC 61260:1995 class-0 filter compliance example report (PDF)

One-page filter-class-compliance fiche under the 1995 edition: a per-band classification table showing every octave band achieving class 0, the measured relative attenuation overlaid on the green class-0 acceptance corridor, the boxed Class 0 - COMPLIES (margin +0.15 dB) result and a PASS verdict against the required class 0.

Download the report (PDF)

Class 0 is retained by the IEC 61260:1995 / ANSI S1.11-2004 edition (edition='1995'); the 2014 edition keeps only classes 1 and 2.
  • Covered

    The IEC 61260-1:2014 Table 1 class 1 / class 2 acceptance limits (with the fractional-octave breakpoint mapping and the log-frequency interpolation of the standard), checked band by band by verify_filter_class and published as a mask by class_limits; the effective bandwidth deviation (5.12) and the summation of output signals (5.16), graded by the same call as IEC 61260-2:2016 computes them (Formulas (1) to (3)); the swept test of time-invariant operation (5.14, IEC 61260-2 7.4) run on the bank by verify_time_invariance, with Formula (17) and its Annex A uncertainty; the withdrawn IEC 61260:1995 / ANSI S1.11-2004 class 0 mask, reachable with edition="1995"; the grading of a laboratory’s IEC 61260-3:2016 periodic-test results clause by clause by verify_filter_periodic, with its test frequencies, its Table 1 and the statement of Clause 14; and the accredited one-page fiche of verify_filter_class().report(), with its optional required_class PASS/FAIL verdict, in English and Spanish.

  • Not covered

    The tests themselves on a physical filter: the specimens, climate, immunity, overload and linearity tests of IEC 61260-2 and the measurements of IEC 61260-3 are a laboratory’s to run, and verify_filter_periodic grades the numbers it returns without producing them. Of those, the self-generated noise of IEC 61260-3 Clause 12 and the overload indications of 11.5 and 11.8 are not graded. An edition="1995" verdict is its Table 1 mask alone: the effective bandwidth and other requirements of that edition are not graded. Near Nyquist, the bilinear transform warps the frequency axis and the bank has no correction for it, so the stopband mask beyond the processing Nyquist is reported as range_limited rather than verified: keep the top band edge comfortably below Nyquist or raise fs.

Which filter architecture meets IEC 61260-1 class 1 with default settings?

Section titled “Which filter architecture meets IEC 61260-1 class 1 with default settings?”

With the default order 6, Butterworth meets class 1 of the IEC 61260-1:2014 Table 1 acceptance limits, and so does Chebyshev II: its default attenuation of 72 dB clears the 70 dB far-stopband class 1 limit. Chebyshev I, Elliptic and Bessel do not: passband ripple (cheby1, ellip) and slow roll-off (bessel) violate the mask. verify_filter_class reports the achieved class per band, on Table 1 and on the effective bandwidth and summation requirements too.

Is the default octave bank class 1 under IEC 61260-1:2014?

Section titled “Is the default octave bank class 1 under IEC 61260-1:2014?”

Yes, on every requirement it is graded on: the Table 1 mask, the effective bandwidth (5.12) and the summation of output signals (5.16). The decimated Butterworth octave bank sums adjacent outputs within −0.06 dB and +0.69 dB of the input over its default range at 48 kHz, inside the −1.8 dB and +0.8 dB of class 1 with 0.11 dB to spare, exactly as the same bank designed at the full rate with FilterDesign(resample=False): the bank decimates a band only as far as its processing Nyquist frequency stays at least sixteen times its upper band edge, where the decimation moves the summation by at most 0.005 dB. The one-third-octave bank is class 1 on every requirement too, with 0.04 dB to spare on the summation.

What is class 0 and which standard defines it?

Section titled “What is class 0 and which standard defines it?”

Class 0 is the tightest filter performance class, defined by IEC 61260:1995 and its US twin ANSI S1.11-2004 and dropped by IEC 61260-1:2014. Its passband corridor allows only ±0.15 dB at mid-band, against ±0.3 dB for class 1 in the 1995 masks. It stays available through edition="1995", and the default order-6 Butterworth bank meets class 0 in the configuration the conformance report verifies, the octave-band bank at 48 kHz. Under IEC 61260-1:2014 that same bank is class 1, on the mask of Table 1 and on the effective bandwidth and summation of 5.12 and 5.16: the 2014 edition defines no class 0, so a class 0 claim must cite the 1995 / ANSI S1.11-2004 mask it was measured against.