Bancos de filtros
Normas aplicables: IEC 61260ANSI S1.11ISO 266Referencias: Oppenheim y Schafer 2010
phonometry soporta varios tipos de filtro, cada uno con su propia función de transferencia característica. Todos los bancos sitúan sus puntos de −3 dB en los bordes de banda de ANSI S1.11, de modo que los niveles por banda son comparables entre arquitecturas.
1. Bandas de octava fraccionaria: las matemáticas
Sección titulada «1. Bandas de octava fraccionaria: las matemáticas»IEC 61260-1:2014 construye cada banda a partir de la razón de octava en base 10 (es decir, “una octava” no es exactamente 2). Para la fracción de banda , las frecuencias centrales y los bordes de banda siguen (5.2-5.5):
de modo que cada banda de tercio de octava abarca : diez bandas por década, y por eso las frecuencias nominales (25, 31,5, 40 …) se repiten escaladas por 10. phonometry diseña cada banda como una cascada SOS cuyos puntos de −3 dB caen exactamente en y en todas las arquitecturas; para Chebyshev II, Elíptico y Bessel eso exige pre-deformar (pre-warping) el mapeo analítico de los bordes de banda en lugar de confiar en la parametrización por defecto de SciPy.
Polos, ceros y estabilidad
Sección titulada «Polos, ceros y estabilidad»Un paso-banda digital es una constelación de polos y ceros en el plano z: los ceros en DC y Nyquist, o cerca de ellos, sujetan la respuesta lejos de la banda (hasta el suelo de la banda atenuada, en los diseños equirizados), y los polos se agrupan justo dentro del círculo unidad en los ángulos que abarca la banda de paso. De ahí salen dos intuiciones. Primera, la selectividad es proximidad: cuanto más cerca del círculo unidad están los polos, más abrupta es la banda y más tiempo resuena el filtro (los picos de retardo de grupo de la sección 8 son esa resonancia, medida). Segunda, la estabilidad es un margen, no una propiedad de la arquitectura: un filtro IIR es estable solo mientras todos sus polos permanecen estrictamente dentro del círculo unidad, y una banda estrecha a una frecuencia de muestreo alta los empuja hacia fuera (radio del polo para un ancho de banda ) y los aprieta entre sí, hasta que los coeficientes en doble precisión ya no pueden representar sus posiciones con exactitud. Las secciones de segundo orden (SOS) desactivan la mitad del problema: cada par de polos conserva sus propios coeficientes, de modo que los errores de redondeo quedan localizados en lugar de acumularse en un único polinomio de orden alto. La otra mitad, la minúscula razón en sí, es lo que arregla el diezmado.
Diezmado multitasa
Sección titulada «Diezmado multitasa»Una banda de tercio de octava de 25 Hz a 48 kHz abarca unos 5,8 Hz, el 0,024 % de Nyquist, con coeficientes tan rígidos que se vuelven numéricamente inestables. El banco lo evita filtrando las bandas bajas a una frecuencia diezmada:
Diezmar por reescala el problema: el mismo ancho de banda de 5,8 Hz se vuelve veces mayor respecto al nuevo Nyquist, el radio de los polos se separa del círculo unidad y los coeficientes SOS regresan a un rango bien condicionado. El precio es una contabilidad que el banco paga internamente: antes de cada etapa de diezmado debe ejecutarse un paso-bajo antialiasing, porque una componente por encima del nuevo Nyquist que se pliegue hacia abajo cae dentro de las bandas bajas que se están midiendo, y ningún filtro posterior puede eliminarla.
Trampas de aliasing
Sección titulada «Trampas de aliasing»El banco protege sus propias etapas de diezmado, pero solo puede analizar lo que la cadena de captura le entrega:
- Plegado en el ADC. La energía por encima de que llega al conversor sin un filtro antialiasing analógico se pliega dentro del rango de análisis y es indistinguible del sonido real dentro de banda. Las tarjetas de sonido lo filtran internamente; las cadenas de instrumentación a medida pueden no hacerlo.
- Remuestreo barato. Convertir una grabación de 44,1 kHz a 48 kHz con un
remuestreador de baja calidad deja imágenes que sesgan las bandas más
altas. Usa un remuestreador polifásico (
scipy.signal.resample_poly) o, más sencillo, analiza a la frecuencia nativa: todas las funciones de phonometry aceptanfsdirectamente. - Bandas cerca de Nyquist. Una banda cuyo borde superior se acerca a
no puede realizar su respuesta de diseño: la transformada bilineal
comprime allí el eje de frecuencias (el mismo efecto que los filtros de
ponderación contrarrestan con
high_accuracy). Mantén el borde de la banda superior holgadamente por debajo de Nyquist o subefs, y deja queverify_filter_classinforme del margen restante.
2. Comparación de filtros y zoom
Sección titulada «2. Comparación de filtros y zoom»Usamos secciones de segundo orden (SOS) en todos los filtros para garantizar la estabilidad numérica. La siguiente gráfica compara las arquitecturas centrándose en el punto de cruce a −3 dB.
Mostrar el código de esta figura
import matplotlib.pyplot as pltimport numpy as npfrom scipy.signal import sosfreqzfrom phonometry import metrology
fs = 48000fig, ax = plt.subplots(figsize=(9, 5))for ftype in ("butter", "cheby1", "cheby2", "ellip", "bessel"): # limits selecciona únicamente la banda de octava de 1 kHz bank = metrology.OctaveFilterBank(fs, fraction=1, order=6, limits=[800, 1200], filter_type=ftype) idx = int(np.argmin(np.abs(np.array(bank.freq) - 1000))) fsd = fs / bank.factor[idx] # frecuencia real de la banda w, h = sosfreqz(bank.sos[idx], worN=16384, fs=fsd) ax.semilogx(w, 20 * np.log10(np.abs(h) + 1e-9), label=ftype)ax.axhline(-3, color="gray", linestyle=":", label="-3 dB")ax.set(xlim=(100, 8000), ylim=(-80, 5), xlabel="Frecuencia [Hz]", ylabel="Magnitud [dB]")ax.grid(True, which="both", alpha=0.3)ax.legend()plt.show()| Tipo | Nombre | Ejemplo de uso | Ideal para |
|---|---|---|---|
butter | Butterworth | octave_filter(x, fs, filter_type='butter') | Medición acústica general. |
cheby1 | Chebyshev I | octave_filter(x, fs, filter_type='cheby1', ripple=0.1) | Caída más abrupta a costa de rizado. |
cheby2 | Chebyshev II | octave_filter(x, fs, filter_type='cheby2') | Banda de paso plana con ceros en la banda atenuada. |
ellip | Elíptico | octave_filter(x, fs, filter_type='ellip', ripple=0.1) | Máxima selectividad. |
bessel | Bessel | octave_filter(x, fs, filter_type='bessel') | Preservar la forma de los transitorios. |
3. Parámetros de octave_filter() / OctaveFilterBank
Sección titulada «3. Parámetros de octave_filter() / OctaveFilterBank»| Parámetro | Tipo | Unidades | Rango / por defecto | Notas |
|---|---|---|---|---|
x | array 1D o 2D | unidades digitales | no vacío | 2D es [channels, samples] |
fs | int | Hz | > 0 | |
fraction | int | — | por defecto 1; habitual 3; cualquier b ≥ 1 | Bandas por octava = b |
order | int | — | por defecto 6 | Orden SOS por banda |
limits | lista [lo, hi] | Hz | por defecto [12, 20000] | Rango de análisis |
filter_type | str | — | 'butter' (por defecto), 'cheby1', 'cheby2', 'ellip', 'bessel' | Ver la comparación más arriba |
ripple / attenuation | float | dB | ripple defecto 0.1; attenuation defecto 72.0 | Rizado de banda de paso / atenuación en banda atenuada (cheby/ellip); cheby2 necesita attenuation ≥ 70 para clase 1, ya que scipy fija su suelo equirizado en exactamente este valor |
show | bool | — | por defecto False | Dibuja la respuesta del banco (requiere matplotlib) |
sigbands | bool | — | por defecto False | Devuelve también las señales temporales por banda |
mode | str | — | 'rms' (por defecto), 'peak', 'sum' | Estadístico por banda devuelto |
nominal | bool | — | por defecto False | Devuelve etiquetas nominales (p. ej. 1000) en vez de las frecuencias centrales exactas |
detrend | bool | — | por defecto True | Elimina el offset DC de cada banda antes del nivel (mejora la precisión en graves) |
calibration_factor | float | — | por defecto 1.0 | Escala la entrada a pascales (consulta la guía de Calibración) |
dbfs | bool | — | por defecto False | Referencia los niveles a fondo de escala digital en vez de 20 µPa |
plot_file | str o None | — | por defecto None | Guarda la gráfica de respuesta del banco en esta ruta |
zero_phase | bool | — | por defecto False | Filtrado adelante-atrás (offline) |
stateful / steady_ic (clase) | bool | — | por defecto False | Estado en streaming; consulta Procesado por bloques |
verify_filter_class(bank) comprueba el banco diseñado contra los límites de
aceptación de la Tabla 1 de IEC 61260-1 e informa de la clase (1, 2 o None si queda fuera de ambas) con los
márgenes por banda.
4. Galería de respuestas del banco
Sección titulada «4. Galería de respuestas del banco»Vista espectral completa de los bancos para octava (1/1) y tercio de octava (1/3).
| Arquitectura | Octava 1/1 (fraction=1) | Octava 1/3 (fraction=3) |
|---|---|---|
| Butterworth | ||
| Chebyshev I | ||
| Chebyshev II | ||
| Elíptico | ||
| Bessel |
Mostrar el código de esta figura
from phonometry import metrology
# Una figura por arquitectura y fracción: la galería completa de respuestasfs = 48000for ftype in ("butter", "cheby1", "cheby2", "ellip", "bessel"): for fraction in (1, 3): # show=True dibuja la respuesta en frecuencia del banco metrology.OctaveFilterBank(fs=fs, fraction=fraction, order=6, limits=[12, 20000], filter_type=ftype, show=True)5. Uso y ejemplos por tipo de filtro
Sección titulada «5. Uso y ejemplos por tipo de filtro»1. Butterworth (butter)
Sección titulada «1. Butterworth (butter)»El filtro Butterworth es conocido por su banda de paso máximamente plana. Es la elección estándar para mediciones acústicas donde no se admite rizado dentro de las bandas de frecuencia.
import numpy as npfrom phonometry import metrology
# Una señal calibrada en Pa para que la guía funcione por sí solafs = 48000x = 0.2 * np.sin(2 * np.pi * 1000 * np.arange(fs) / fs)
# Medición estándar por defectospl, freq = metrology.octave_filter(x, fs, filter_type='butter')Mostrar el código de esta figura
from phonometry import metrology
# Dibuja la respuesta de este banco (1/3 de octava, orden 6, Butterworth)metrology.OctaveFilterBank(fs=48000, fraction=3, order=6, limits=[12, 20000], filter_type='butter', show=True)2. Chebyshev I (cheby1)
Sección titulada «2. Chebyshev I (cheby1)»Los filtros Chebyshev tipo I ofrecen una caída más abrupta que Butterworth a cambio de rizado en la banda de paso. Útiles cuando se necesita alta selectividad cerca de las frecuencias de corte.
import numpy as npfrom phonometry import metrology
# Una señal calibrada en Pa para que la guía funcione por sí solafs = 48000x = 0.2 * np.sin(2 * np.pi * 1000 * np.arange(fs) / fs)
# Selectividad con 0.1 dB de rizado en la banda de pasospl, freq = metrology.octave_filter(x, fs, filter_type='cheby1', ripple=0.1)Mostrar el código de esta figura
from phonometry import metrology
# Dibuja la respuesta de este banco (1/3 de octava, orden 6, Chebyshev I)metrology.OctaveFilterBank(fs=48000, fraction=3, order=6, limits=[12, 20000], filter_type='cheby1', ripple=0.1, show=True)3. Chebyshev II (cheby2)
Sección titulada «3. Chebyshev II (cheby2)»También llamado Chebyshev inverso, tiene banda de paso plana y rizado en la
banda atenuada. Ofrece una caída más rápida que Butterworth sin afectar a la
señal en la banda de paso. Los bordes de la banda atenuada se colocan
automáticamente para que los puntos de −3 dB caigan en los bordes de banda
(attenuation debe ser > 3,01 dB).
import numpy as npfrom phonometry import metrology
# Una señal calibrada en Pa para que la guía funcione por sí solafs = 48000x = 0.2 * np.sin(2 * np.pi * 1000 * np.arange(fs) / fs)
# Banda de paso plana, 72 dB de atenuación por defecto (clase 1)spl, freq = metrology.octave_filter(x, fs, filter_type='cheby2')Mostrar el código de esta figura
from phonometry import metrology
# Dibuja la respuesta de este banco (1/3 de octava, orden 6, Chebyshev II)metrology.OctaveFilterBank(fs=48000, fraction=3, order=6, limits=[12, 20000], filter_type='cheby2', show=True)4. Elíptico (ellip)
Sección titulada «4. Elíptico (ellip)»Los filtros elípticos (Cauer) tienen la transición más corta (caída más abrupta) para un orden dado. Presentan rizado tanto en la banda de paso como en la atenuada.
import numpy as npfrom phonometry import metrology
# Una señal calibrada en Pa para que la guía funcione por sí solafs = 48000x = 0.2 * np.sin(2 * np.pi * 1000 * np.arange(fs) / fs)
# Máxima selectividad para aislamiento extremo entre bandasspl, freq = metrology.octave_filter(x, fs, filter_type='ellip', ripple=0.1)Mostrar el código de esta figura
from phonometry import metrology
# Dibuja la respuesta de este banco (1/3 de octava, orden 6, elíptico)metrology.OctaveFilterBank(fs=48000, fraction=3, order=6, limits=[12, 20000], filter_type='ellip', ripple=0.1, show=True)5. Bessel (bessel)
Sección titulada «5. Bessel (bessel)»Los filtros Bessel están optimizados para una respuesta de fase lineal y un retardo de grupo mínimo. Preservan la forma de las ondas filtradas (transitorios) mejor que ningún otro tipo, pero tienen la caída más lenta.
import numpy as npfrom phonometry import metrology
# Una señal calibrada en Pa para que la guía funcione por sí solafs = 48000x = 0.2 * np.sin(2 * np.pi * 1000 * np.arange(fs) / fs)
# Ideal para análisis de pulsos y preservación de transitoriosspl, freq = metrology.octave_filter(x, fs, filter_type='bessel')Mostrar el código de esta figura
from phonometry import metrology
# Dibuja la respuesta de este banco (1/3 de octava, orden 6, Bessel)metrology.OctaveFilterBank(fs=48000, fraction=3, order=6, limits=[12, 20000], filter_type='bessel', show=True)6. Linkwitz-Riley (linkwitz_riley)
Sección titulada «6. Linkwitz-Riley (linkwitz_riley)»Diseñado específicamente para crossovers de audio. Los filtros Linkwitz-Riley (típicamente de 4.º orden, aunque se admite cualquier orden par) permiten dividir una señal en bandas que, al sumarse, producen una respuesta en magnitud perfectamente plana y sin diferencia de fase entre bandas en el cruce.
import numpy as npfrom phonometry import metrology
# recording: una captura calibrada en Pa para que la guía funcione por sí solafs = 48000recording = 0.2 * np.sin(2 * np.pi * 1000 * np.arange(fs) / fs)
# Dividir la grabación en bandas grave y aguda a 1000 Hzlow, high = metrology.linkwitz_riley(recording, fs, freq=1000, order=4)# Reconstrucción: low + high == recording (respuesta plana)Mostrar el código de esta figura
import matplotlib.pyplot as pltimport numpy as npfrom scipy.signal import freqzfrom phonometry import metrology
# Medimos ambas ramas: dividimos un impulso unitario y tomamos los espectros.fs = 48000impulse = np.zeros(fs)impulse[0] = 1.0low, high = metrology.linkwitz_riley(impulse, fs, freq=1000, order=4)
w, h_lp = freqz(low, worN=8192, fs=fs)_, h_hp = freqz(high, worN=8192, fs=fs)
fig, ax = plt.subplots(figsize=(9, 5))ax.semilogx(w, 20 * np.log10(np.abs(h_lp) + 1e-9), label="Paso-bajo (LR4)")ax.semilogx(w, 20 * np.log10(np.abs(h_hp) + 1e-9), label="Paso-alto (LR4)")ax.semilogx(w, 20 * np.log10(np.abs(h_lp + h_hp) + 1e-9), "--", label="Suma (plana)")ax.set(xlim=(20, 20000), ylim=(-60, 5), xlabel="Frecuencia [Hz]", ylabel="Magnitud [dB]")ax.grid(True, which="both", alpha=0.3)ax.legend()plt.show()6. EQ paramétrico (ParametricEQ)
Sección titulada «6. EQ paramétrico (ParametricEQ)»Secciones biquad de ecualización según el RBJ Audio EQ Cookbook
(Bristow-Johnson): campana (peaking), shelving grave/agudo, paso bajo/alto,
paso banda (pico a 0 dB constante o faldas constantes), rechazo de banda
(notch) y pasa-todo, cada una parametrizada por fs, f0, gain_db y uno
de q, bw (ancho de banda en octavas) o slope, exactamente como los
define el cookbook. Las secciones se encadenan en una cascada SOS
numéricamente robusta, y el diseño es exacto en forma cerrada: una campana
pasa exactamente gain_db en f0 y exactamente 0 dB en DC y Nyquist, los
shelving caen exactamente en gain_db en su extremo, y el pasa-todo tiene
magnitud unidad en todas las frecuencias (solo gira la fase).
import numpy as npfrom phonometry import EQSection, ParametricEQ
fs = 48000rng = np.random.default_rng(1)x = rng.standard_normal(fs) # un segundo de ruido
eq = ParametricEQ(fs, [ EQSection("lowshelf", 100.0, gain_db=4.0), EQSection("peaking", 1000.0, gain_db=-6.0, bw=1.0), # recorte de 1 octava EQSection("highshelf", 8000.0, gain_db=3.0),])y = eq.filter(x) # aplicar la cascadares = eq.response() # resultado congelado con la cascada SOSaxes = res.plot(language="es") # magnitud + fase de la cascadaPara procesado por bloques pasa stateful=True (la misma convención que
WeightingFilter); el atajo de un solo uso es
parametric_eq(x, fs, sections).
Mostrar el código de esta figura
import matplotlib.pyplot as pltfrom phonometry import EQSection, ParametricEQ
fs = 48000familia = [ EQSection("peaking", 1000.0, gain_db=6.0, q=1.4), EQSection("lowshelf", 125.0, gain_db=6.0), EQSection("highshelf", 4000.0, gain_db=-6.0), EQSection("lowpass", 10000.0), EQSection("highpass", 50.0), EQSection("bandpass", 500.0, q=2.0), EQSection("notch", 2000.0, q=6.0),]fig, ax = plt.subplots(figsize=(10, 6))for section in familia: res = ParametricEQ(fs, [section]).response(f_min=20.0, f_max=20000.0) ax.semilogx(res.frequencies, res.magnitude_db, label=f"{section.filter_type} @ {section.f0:g} Hz")ax.set(xlim=(20, 20000), ylim=(-27, 9), xlabel="Frecuencia [Hz]", ylabel="Magnitud [dB]")ax.grid(True, which="both", alpha=0.3)ax.legend(loc="lower center", ncols=2, fontsize=9)plt.show()7. Verificar la clase IEC 61260-1
Sección titulada «7. Verificar la clase IEC 61260-1»verify_filter_class comprueba cada banda de un banco contra los límites de
aceptación de IEC 61260-1:2014 (Tabla 1, con el mapeo de breakpoints a
fraccionales y la interpolación logarítmica de la norma) e informa de la clase
por banda con su margen en dB:
from phonometry import metrology
bank = metrology.OctaveFilterBank(fs=48000, fraction=3, order=6)result = metrology.verify_filter_class(bank)print(result["overall_class"]) # 1print(result["bands"][0])# {'freq': 12.589254117941678, 'class': 1, 'margin_class1_db': 0.39999999999997266, 'margin_class2_db': 0.5999999999999727}La propia máscara de aceptación de la Tabla 1 también es pública:
class_limits(fraction, filter_class, omega) devuelve los límites
mínimo/máximo de atenuación relativa en frecuencias normalizadas Ω = f/fm,
los mismos límites que usan el verificador y la figura de abajo.
La respuesta del Butterworth de orden 6 (azul) serpentea entre las regiones prohibidas: debe atenuar al menos la máscara roja fuera de la banda y no más que la morada dentro de ella.
Mostrar el código de esta figura
import matplotlib.pyplot as pltimport numpy as npfrom scipy.signal import sosfreqzfrom phonometry import metrology
fs = 48000bank = metrology.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) # atenuación relativa
grid = np.logspace(np.log10(0.05), np.log10(8), 2000)lo1, hi1 = metrology.class_limits(1.0, 1, grid) # atenuación mín/máx de clase 1
fig, ax = plt.subplots(figsize=(9, 5.5))ax.fill_between(grid, -10, lo1, alpha=0.15, color="tab:red", label="Prohibido: atenuación insuficiente")finite = np.isfinite(hi1)ax.fill_between(grid[finite], hi1[finite], 90, alpha=0.15, color="tab:purple", label="Prohibido: atenuación excesiva")ax.plot(w / fm, delta_a, label="Butterworth de orden 6")ax.set(xscale="log", xlim=(0.08, 8), ylim=(-6, 90), xlabel="Frecuencia normalizada f / fm", ylabel="Atenuación relativa [dB]")ax.legend()plt.show()Con parámetros por defecto (orden 6), Butterworth cumple clase 1, y también
lo hace Chebyshev II: su valor por defecto de attenuation es ahora 72 dB,
superando el límite de clase 1 de 70 dB en el stopband lejano (scipy fija el
suelo equirizado de cheby2 en exactamente attenuation, así que cualquier valor
≥ 70 dB cumple; el valor por defecto de 72 dB mantiene el mismo margen de banda
de paso de +0,400 dB que Butterworth). Chebyshev I, Elíptico y Bessel no cumplen
los límites de clase con orden 6: el rizado de banda de paso (cheby1/ellip) y la
caída lenta (bessel) violan la máscara.
Clase 0 (IEC 61260:1995 / ANSI S1.11-2004)
Sección titulada «Clase 0 (IEC 61260:1995 / ANSI S1.11-2004)»La clase de prestaciones más estricta, la clase 0, la definió la edición
anterior IEC 61260:1995 y su gemela estadounidense ANSI S1.11-2004
(ambas retiradas/sustituidas pero aún referenciadas para instrumentos de
laboratorio); IEC 61260-1:2014 la eliminó. Sus máscaras de clase 1/2 difieren
ligeramente de la edición de 2014, así que se selecciona con un conmutador edition en
lugar de mezclarse con la máscara de 2014:
from phonometry import metrology
fs = 48000bank = metrology.OctaveFilterBank(fs, fraction=1, order=6, limits=[800, 1200])
result = metrology.verify_filter_class(bank, edition="1995") # clases 0, 1, 2print(result["overall_class"]) # 0 (el Butterworth por defecto la supera)print(result["bands"][0]["margin_class0_db"])El corredor de clase 0 (±0,15 dB en el centro de banda) es el más estrecho; la clase 1 (±0,3 dB) y la clase 2 (±0,5 dB) son progresivamente más anchas. El Butterworth de orden 6 serpentea dentro de la clase 0 en toda la banda de paso.
Mostrar el código de esta figura
import matplotlib.pyplot as pltimport numpy as npfrom scipy.signal import sosfreqzfrom phonometry import metrology
fs = 48000bank = metrology.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)
# Solo banda de paso: fuera de los bordes el límite máximo es +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): # corredores anidados, clase 0 el más estrecho lo, hi = metrology.class_limits(1.0, cls, grid, edition="1995") ax.plot(grid, hi, label=f"Corredor de clase {cls}") ax.plot(grid, lo, color=ax.lines[-1].get_color())ax.plot(w[pb] / fm, delta_a[pb], "k", lw=2, label="Butterworth de orden 6")ax.set(xscale="log", xlim=(g ** -0.5, g ** 0.5), ylim=(-0.7, 6), xlabel="Frecuencia normalizada f / fm", ylabel="Atenuación relativa [dB]")ax.legend()plt.show()Qué significa físicamente una clase
Sección titulada «Qué significa físicamente una clase»Las máscaras son cotas de error en el peor caso de una medición, no calificaciones abstractas:
- En la banda de paso el corredor acota cuánto puede desviarse la lectura del contenido dentro de banda: un banco de clase 1 lee un tono de centro de banda a ±0,4 dB de su nivel verdadero y uno de clase 2 a ±0,6 dB (Tabla 1 de 2014; las máscaras de 1995, más estrictas, permitían ±0,3 dB para la clase 1 y ±0,15 dB para la clase 0). Hacia los bordes el corredor se ensancha, lo que es la admisión honesta de que un tono situado exactamente en un borde es genuinamente ambiguo entre dos bandas (ambas lo leen unos 3 dB por debajo).
- En la banda atenuada la máscara de atenuación mínima acota la fuga del
resto del espectro: lejos de la banda, la clase 1 exige al menos 70 dB de
atenuación relativa (la razón por la que el valor por defecto de
cheby2es 72 dB). En términos de energía, un tono fuera de banda tiene que ser unos 70 dB más fuerte que el contenido propio de la banda para duplicar su lectura de energía (+3 dB). La consecuencia práctica: al medir bandas muy por debajo de un tono dominante, la lectura toca fondo en la falda de fuga unos 70 dB por debajo, y una arquitectura más abrupta (o mayor orden) es la única forma de empujar ese suelo más abajo. - Para el presupuesto de incertidumbre, la clase es la contribución del filtro a la incertidumbre de la medición: un banco de clase 1 añade hasta unas décimas de dB a un nivel de banda, comparable a los demás términos de tolerancia de un sonómetro de clase 1, y por eso las cadenas con calidad de instrumento especifican la clase de cada etapa en lugar de una única cifra global.
¿Qué arquitectura alcanza qué clase? El valor por defecto de la biblioteca, Butterworth de orden 6, cumple clase 0 en las configuraciones que verifica el conjunto de conformidad (bancos de octava y tercio de octava a 48 kHz), así que no hace falta ninguna configuración especial para bancos de laboratorio en ese rango. La tabla siguiente indica la mejor clase que alcanza cada arquitectura con esa misma configuración de orden 6 / 48 kHz; las demás arquitecturas se quedan por debajo de clase 0 porque cambian la máscara IEC por otra propiedad por construcción:
| Arquitectura | Mejor clase (orden 6, fs 48 kHz) | Por qué |
|---|---|---|
butter (por defecto) | 0 | Banda de paso máximamente plana, caída monótona; encaja en la máscara |
cheby2 | 1 | Banda de paso plana pero la relación de la máscara limita en clase 1 |
cheby1 | — | El rizado de banda de paso viola el límite de planitud |
ellip | — | Rizado en banda de paso y atenuada |
bessel | — | Retardo de grupo plano a costa de una caída lenta |
Así que el valor por defecto sensato es el habitual (Butterworth de orden 6):
supera la clase 0 en las configuraciones verificadas, mientras que las
arquitecturas alternativas son opciones deliberadas cuyo propósito (caída más
abrupta, fase lineal) va en contra de la máscara de clase. Fuera de estos
ajustes (fraction muy alto o bandas cercanas a Nyquist), vuelve a ejecutar
verify_filter_class para confirmar la clase que necesitas, y sube el orden si
una banda necesita más margen.
Informe de conformidad de filtro IEC 61260-1 (.report())
Sección titulada «Informe de conformidad de filtro IEC 61260-1 (.report())»filter_class_compliance(bank) envuelve la misma verificación en un objeto de
resultado que expone .plot() y .report(), de modo que el veredicto del
ensayo de tipo puede renderizarse como una ficha acreditada de una página. La
ficha lista la clase alcanzada por cada banda y su margen vinculante, superpone
la atenuación relativa medida de la banda de peor margen sobre el corredor de la
clase y encuadra el resultado global de conformidad. Pasa un required_class en
el ReportMetadata para añadir una fila de veredicto PASS/FAIL (un banco
“cumple la clase N” cuando su clase alcanzada es al menos igual de estricta, es
decir, un índice de clase N o inferior). La ficha se genera en inglés por
defecto; pasa language="es" para una ficha en español (cadenas fijas
traducidas y coma como separador decimal), p. ej.
result.report("iec61260_es.pdf", language="es").
from phonometry import ( OctaveFilterBank, ReportMetadata, filter_class_compliance,)
bank = OctaveFilterBank(fs=48000, fraction=1, order=6, limits=[125, 4000])result = filter_class_compliance(bank) # overall_class == 1result.plot(language="es") # la banda de peor margen sobre su corredor de clase
result.report( "iec61260.pdf", metadata=ReportMetadata( specimen="Banco de filtros de octava (1/1)", measurement_standard="IEC 61260-1:2014", required_class=1, # se exige clase 1 (o más estricta) ),) # -> Class 1 - COMPLIES, PASSLa ficha de ejemplo se regenera con make reports y se mantiene renderizada en
el repositorio; haz clic en la vista previa para abrir el PDF.

Ficha de conformidad de clase de filtro de una página: una cabecera de metadatos, una tabla de clasificación por bandas con la clase alcanzada y el margen vinculante de cada banda de octava, la atenuación relativa medida de la banda de peor margen superpuesta sobre el corredor de aceptación verde de clase 1, el resultado encuadrado Class 1 - COMPLIES (margin +0.40 dB) y un veredicto PASS frente a la clase 1 exigida.
Pasar edition="1995" verifica contra la máscara más antigua de IEC 61260:1995 /
ANSI S1.11-2004, que conserva la clase 0 más estricta que la edición de 2014
eliminó, de modo que un banco de orden 6 puede certificarse a clase 0:
bank = OctaveFilterBank(fs=48000, fraction=1, order=6, limits=[250, 4000])result = filter_class_compliance(bank, edition="1995") # overall_class == 0result.plot(language="es") # el corredor de clase 0 de la edición de 1995result.report("iec61260_1995.pdf", metadata=ReportMetadata(measurement_standard="IEC 61260:1995", required_class=0)) # -> Class 0 - COMPLIES
Ficha de conformidad de clase de filtro de una página bajo la edición de 1995: una tabla de clasificación por bandas con cada banda de octava alcanzando la clase 0, la atenuación relativa medida superpuesta sobre el corredor de aceptación verde de clase 0, el resultado encuadrado Class 0 - COMPLIES (margin +0.15 dB) y un veredicto PASS frente a la clase 0 exigida.
8. Descomposición de la señal y estabilidad
Sección titulada «8. Descomposición de la señal y estabilidad»Con sigbands=True puedes recuperar las componentes en el dominio del tiempo de
cada banda. Esto permite análisis avanzados o comparar cómo afectan distintas
arquitecturas (p. ej. Butterworth vs Chebyshev) a la fase y al transitorio.
import numpy as npfrom phonometry import metrology
# 1. Generar una señal (suma de 250 Hz y 1000 Hz)fs = 48000t = np.linspace(0, 0.5, int(fs * 0.5), endpoint=False)y = np.sin(2 * np.pi * 250 * t) + np.sin(2 * np.pi * 1000 * t)
# 2. Comparar arquitecturas (Butterworth vs Chebyshev II)spl_b, freq, xb_butter = metrology.octave_filter(y, fs=fs, fraction=1, sigbands=True, filter_type='butter')spl_c2, _, xb_cheby2 = metrology.octave_filter(y, fs=fs, fraction=1, sigbands=True, filter_type='cheby2')
# 'xb_butter' y 'xb_cheby2' contienen las señales por banda en el dominio del tiempoLa gráfica compara las respuestas de Butterworth (azul, línea continua) y Chebyshev II (rojo, discontinua). El panel inferior muestra la respuesta al impulso, destacando las diferencias de estabilidad y decaimiento transitorio.
Mostrar el código de esta figura
import matplotlib.pyplot as pltimport numpy as npfrom phonometry import metrology
fs = 48000t = np.linspace(0, 0.5, int(fs * 0.5), endpoint=False)y = np.sin(2 * np.pi * 250 * t) + np.sin(2 * np.pi * 1000 * t)
bank_b = metrology.OctaveFilterBank(fs=fs, fraction=1, order=6, limits=[100.0, 2000.0])bank_c = metrology.OctaveFilterBank(fs=fs, fraction=1, order=6, limits=[100.0, 2000.0], filter_type="cheby2")_, freq, xb_butter = bank_b.filter(y, sigbands=True)_, _, xb_cheby2 = bank_c.filter(y, sigbands=True)
fig, axes = plt.subplots(len(freq), 1, figsize=(9, 2 * len(freq)), sharex=True)for ax, fc, xb, xc in zip(axes, freq, xb_butter, xb_cheby2): ax.plot(t, xb, label="Butterworth") ax.plot(t, xc, "--", label="Chebyshev II") ax.set_title(f"Banda de {fc:.0f} Hz") ax.set_xlim(0, 0.04)axes[0].legend()axes[-1].set_xlabel("Tiempo [s]")plt.tight_layout()plt.show()El retardo de grupo, cuantificado
Sección titulada «El retardo de grupo, cuantificado»El retardo de grupo de la banda de octava de 1 kHz muestra el compromiso directamente: Bessel se mantiene casi plano en la banda de paso (los transitorios sobreviven), mientras que Chebyshev I y el Elíptico pagan su caída abrupta con fuertes picos de retardo en los bordes de banda.
Mostrar el código de esta figura
import matplotlib.pyplot as pltimport numpy as npfrom scipy.signal import group_delayfrom phonometry import metrology
fs = 48000w = np.logspace(np.log10(500), np.log10(2000), 1024)fig, ax = plt.subplots(figsize=(9, 5))for ftype in ("butter", "cheby1", "cheby2", "ellip", "bessel"): bank = metrology.OctaveFilterBank(fs, fraction=1, order=6, limits=[800, 1200], filter_type=ftype) idx = int(np.argmin(np.abs(np.array(bank.freq) - 1000))) fsd = fs / bank.factor[idx] # Retardo de grupo de una cascada SOS = suma del de sus secciones gd = sum(group_delay((sec[:3], sec[3:]), w=w, fs=fsd)[1] for sec in bank.sos[idx]) ax.semilogx(w, gd / fsd * 1000, label=ftype)ax.set(xlim=(500, 2000), xlabel="Frecuencia [Hz]", ylabel="Retardo de grupo [ms]")ax.grid(True, which="both", alpha=0.3)ax.legend()plt.show()9. Filtrado de fase cero
Sección titulada «9. Filtrado de fase cero»Para análisis offline puedes eliminar por completo el retardo de grupo:
zero_phase=True filtra cada banda hacia delante y hacia atrás
(scipy.signal.sosfiltfilt), manteniendo las señales por banda alineadas con la
entrada. La atenuación efectiva se duplica y la banda de paso efectiva se
estrecha, reduciendo el nivel de banda ancha medido en ~0,2 a 0,3 dB por banda
(un tono puro dentro de banda no se ve afectado); prefiere el filtrado hacia
delante cuando el SPL absoluto de banda debe coincidir con el convenio de un
solo paso, y reserva la fase cero para cuando importa la envolvente temporal
(p. ej. decaimiento de reverberación). La opción es incompatible con el
procesado por bloques (stateful).
import numpy as npfrom phonometry import metrology
fs = 48000t = np.linspace(0, 0.5, int(fs * 0.5), endpoint=False)y = np.sin(2 * np.pi * 250 * t) + np.sin(2 * np.pi * 1000 * t)
bank = metrology.OctaveFilterBank(fs=48000, fraction=3)spl, freq, xb = bank.filter(y, sigbands=True, zero_phase=True)El filtrado causal retrasa la ráfaga según el retardo de grupo del filtro; el filtrado de fase cero la mantiene alineada con la entrada.
Mostrar el código de esta figura
import matplotlib.pyplot as pltimport numpy as npfrom phonometry import metrology
fs = 48000t = np.linspace(0, 0.15, int(fs * 0.15), endpoint=False)x = np.zeros_like(t) # ráfaga de 250 Hz a mitad de tramastart, end = int(0.05 * fs), int(0.10 * fs)x[start:end] = np.sin(2 * np.pi * 250 * t[start:end]) * np.hanning(end - start)
bank = metrology.OctaveFilterBank(fs=fs, fraction=1, order=6, limits=[200.0, 300.0])_, _, fwd = bank.filter(x, sigbands=True, calculate_level=False)_, _, zp = bank.filter(x, sigbands=True, calculate_level=False, zero_phase=True)
fig, ax = plt.subplots(figsize=(9, 4.5))ax.plot(t, x, color="gray", alpha=0.5, label="Ráfaga de entrada (250 Hz)")ax.plot(t, fwd[0], label="Causal (retardo de grupo)")ax.plot(t, zp[0], "--", label="zero_phase=True (alineado)")ax.set(xlabel="Tiempo [s]", ylabel="Amplitud")ax.legend()plt.show()Qué cubre esta guía
Sección titulada «Qué cubre esta guía»Cubierto. La matemática de bordes de banda de la IEC 61260-1:2014
(apartados 5.2-5.5) y los límites de aceptación de clase 1/2 de la Tabla 1,
verificados por verify_filter_class y reportados por
filter_class_compliance().report(); la máscara de clase 0 de la IEC
61260:1995 / ANSI S1.11-2004, retirada pero accesible con edition="1995";
y la serie de frecuencias preferentes de la ISO 266:1997 tras
nominal_frequencies. La biblioteca diseña cada banda como una cascada SOS
en cinco arquitecturas (Butterworth, Chebyshev I/II, elíptico, Bessel), más
el crossover Linkwitz-Riley y el ParametricEQ del RBJ Audio EQ Cookbook.
No cubierto. Los ensayos de conformidad de la IEC 61260-1 sobre el
propio filtro físico (recuperación tras sobrecarga, linealidad del filtro,
influencias ambientales) se aplican a filtros analógicos y digitales de
hardware y no están implementados: verify_filter_class comprueba la
respuesta digital diseñada frente a la Tabla 1, no un instrumento. Cerca de
Nyquist, la transformada bilineal deforma el eje de frecuencia y el banco
no tiene corrección para ello (a diferencia de la opción high_accuracy de
WeightingFilter): mantén el borde superior de banda con margen cómodo
respecto a Nyquist, o sube fs, y confirma el margen con
verify_filter_class.
Véase también
Sección titulada «Véase también»- Referencia de la API:
phonometry,metrology.coreymetrology.parametric_filters.
Respuestas rápidas
Sección titulada «Respuestas rápidas»¿Cómo se definen las frecuencias centrales y los bordes de banda de octava fraccionaria?
Sección titulada «¿Cómo se definen las frecuencias centrales y los bordes de banda de octava fraccionaria?»IEC 61260-1:2014 (apartados 5.2-5.5) construye cada banda a partir de la razón de octava en base 10 , de modo que una octava no es exactamente 2. Las frecuencias centrales siguen (para impar) y los bordes son y ; cada banda de tercio de octava abarca , diez bandas por década.
¿Qué arquitectura de filtro cumple la clase 1 de IEC 61260-1 con los ajustes por defecto?
Sección titulada «¿Qué arquitectura de filtro cumple la clase 1 de IEC 61260-1 con los ajustes por defecto?»Con el orden 6 por defecto, Butterworth cumple la clase 1 de los límites de
aceptación de la Tabla 1 de IEC 61260-1:2014, y también Chebyshev II: su
attenuation por defecto de 72 dB supera el límite de clase 1 de 70 dB en
el stopband lejano. Chebyshev I, elíptico y Bessel no la cumplen: el rizado
de banda de paso (cheby1, ellip) y la caída lenta (bessel) violan la
máscara. verify_filter_class informa de la clase alcanzada por banda.
¿Qué es la clase 0 y qué norma la define?
Sección titulada «¿Qué es la clase 0 y qué norma la define?»La clase 0 es la clase de prestaciones más estricta, definida por IEC
61260:1995 y su gemela estadounidense ANSI S1.11-2004 y eliminada por IEC
61260-1:2014. Su corredor de banda de paso admite solo ±0,15 dB en el
centro de banda, frente a ±0,3 dB para la clase 1 en las máscaras de 1995.
Sigue accesible con edition="1995", y el banco Butterworth de orden 6 por
defecto cumple la clase 0 en las configuraciones verificadas a 48 kHz.
Referencias
Sección titulada «Referencias»- American National Standards Institute. (2004). Specification for octave-band and fractional-octave-band analog and digital filters (ANSI S1.11-2004). Acoustical Society of America. Sus límites de clase de la Tabla 1 son idénticos a los de IEC 61260:1995 y respaldan la misma máscara de clase 0.
- Bristow-Johnson, R. (2021). Audio EQ Cookbook. Nota de grupo de trabajo del W3C (ed. R. Toy), 8 de junio de 2021. Las recetas de coeficientes biquad y la parametrización por Q / ancho de banda / pendiente de shelving detrás de ParametricEQ (sección 6).
- International Electrotechnical Commission. (1995). Electroacoustics — Octave-band and fractional-octave-band filters (IEC 61260:1995). La primera edición retirada cuya Tabla 1 aporta la máscara de clase 0 más estricta que ofrece edition='1995', y el convenio de bordes de banda sobre el que cada banco sitúa sus puntos de −3 dB.
- International Electrotechnical Commission. (2014). Electroacoustics — Octave-band and fractional-octave-band filters — Part 1: Specifications (IEC 61260-1:2014). Las matemáticas de bordes de banda de la sección 1 (frecuencias centrales y bordes de banda en base 10, apartados 5.2-5.5), las etiquetas nominales de banda y los límites de aceptación de clase 1 / clase 2 de la Tabla 1 (con el mapeo de breakpoints a fraccionales y la interpolación en frecuencia logarítmica) verificados en la sección 6.
- International Organization for Standardization. (1997). Acoustics — Preferred frequencies (ISO 266:1997). La serie de frecuencias preferentes en la que se basan las etiquetas nominales de banda que devuelve nominal_frequencies.
- Oppenheim, A. V. y Schafer, R. W. (2010). Discrete-time signal processing (3.ª ed.). Pearson. La teoría de polos y ceros, estabilidad y multitasa condensada en la sección 1: cascadas SOS, transformada bilineal y diezmado (ISBN 978-0-13-198842-2).
- Smith, J. O. (s. f.). Introduction to digital filters with audio applications. Center for Computer Research in Music and Acoustics (CCRMA), Stanford University. Libro en línea gratuito y complementario sobre diseño y análisis de filtros digitales, de la geometría de polos y ceros a la estabilidad.