<!-- canonical: https://jmrplens.github.io/phonometry/es/signals/spectra/cepstrum-echoes/ -->
Source: https://jmrplens.github.io/phonometry/es/signals/spectra/cepstrum-echoes/

Los [estimadores
espectrales](/phonometry/es/signals/spectra/spectral-analysis/) describen
*qué frecuencias* contiene una señal; esta página cubre lo que se esconde en
la *forma* de ese espectro. El **cepstrum** - la transformada de Fourier
inversa del espectro logarítmico - vive en `phonometry.signals` y convierte
dos problemas espectrales difíciles en simple localización de picos: el
rizado espectral periódico (un eco, una familia de armónicos) colapsa en un
único pico en la **quefrencia** de su periodo, y la envolvente espectral
suave se separa de la estructura fina mediante un simple enventanado -
**liftering** - en el dominio de la quefrencia. La misma maquinaria extiende
la [envolvente de Hilbert](/phonometry/es/signals/spectra/correlation-delay/) con un
**espectro de la envolvente** en el que las modulaciones de amplitud se
vuelven líneas discretas.

Todo el truco es una cadena de cuatro pasos: el logaritmo convierte un eco
multiplicativo en ondulación espectral aditiva, y la FFT inversa colapsa esa
ondulación en un único pico de quefrencia.

El vocabulario es el juego de palabras deliberado de Bogert sobre el dominio
que refleja, que es el único dato que hace los nombres memorables en lugar de
arbitrarios:

| Término | Refleja | Qué es | Unidades |
| :--- | :--- | :--- | :--- |
| cepstrum | espectro | el espectro de un espectro logarítmico | unidades de la señal |
| quefrencia | frecuencia | su variable independiente | segundos |
| rahmónicos | armónicos | los picos que se repiten a lo largo de ella en $n t_0$ | segundos |
| liftering | filtrado | seleccionar un tramo de ella | — |

## 1. El cepstrum y sus tres variantes

Como el logaritmo convierte la convolución $x = h \ast u$ en la suma
$\ln X = \ln H + \ln U$, componentes que se solapan en el espectro se suman - y
se separan - en el dominio cepstral (Havelock cap. 27, Ecs. (22)-(23)).
`cepstrum` calcula las tres variantes estándar sobre el eje de quefrencia:

- `'power'` (por defecto): la DFT inversa de $\ln|X|^2$ (Fig. 21 de Milner).
  Real, par y ciega a la fase - el caballo de batalla para detectar ecos y
  familias de armónicos. Es el cepstrum *con signo* del espectro logarítmico
  de potencia según Milner; el «cepstrum de potencia» original de Bogert
  (1963) eleva al cuadrado una vez más y es no negativo - la biblioteca
  sigue a Milner en todo, así que los rahmónicos negativos conservan su
  signo;
- `'real'`: la DFT inversa de $\ln|X|$ - exactamente la mitad del cepstrum de
  potencia, y la cantidad cuyo plegado causal es la reconstrucción de fase
  mínima (más abajo);
- `'complex'`: la DFT inversa de $\ln|X| + j\arg X$ con la fase desenrollada
  y su componente lineal eliminada (Havelock cap. 87, Ec. (14)). Conserva la
  fase, así que es **invertible**: la puerta de entrada a la deconvolución
  homomórfica.

```python

from phonometry import cepstrum

fs = 48000.0
rng = np.random.default_rng(1)
x = rng.standard_normal(4096)

res = cepstrum(x, fs, kind="power")
print(res.quefrencies[:3], res.cepstrum.shape)   # eje de quefrencia, en s
res.plot(language="es")
```

*Las tres variantes de un mismo registro con eco (una ondícula de banda
limitada más una reflexión en 8 ms, $a = 0{,}5$). Las tres llevan los
rahmónicos en 8 y 16 ms con alturas $a$ y $-a^2/2$ (convención con signo de
Milner); el recuadro muestra el cepstrum real exactamente a la mitad del de
potencia, y la ondícula fuente se concentra por debajo de 2 ms.*

<details>
<summary>Mostrar el código de esta figura</summary>

```python

from scipy import signal as sp_signal
from phonometry import cepstrum

fs = 48000.0
# Una ondícula fuente de banda limitada más una reflexión en 8 ms (a = 0,5)
b, a = sp_signal.butter(2, 0.3)
s = np.zeros(4096)
s[37:37 + 256] = sp_signal.lfilter(b, a, np.r_[1.0, np.zeros(255)])
x = s + 0.5 * np.roll(s, 384)

# Una línea por variante: cada CepstrumResult se dibuja solo:
cepstrum(x, fs, kind="power").plot(language="es")
plt.show()

# Las tres variantes superpuestas a mano en un mismo eje de quefrencia:
fig, ax = plt.subplots()
for kind, style in (("power", "-"), ("real", "--"), ("complex", ":")):
    res = cepstrum(x, fs, kind=kind)
    q_ms = 1e3 * res.quefrencies
    mask = (q_ms > 0.5) & (q_ms <= 20.0)
    ax.plot(q_ms[mask], res.cepstrum[mask], style, label=f"cepstrum {kind}")
ax.set(xlabel="Quefrencia [ms]", ylabel="Cepstrum")
ax.legend()
plt.show()
```

</details>

El resultado lleva el eje de quefrencia periódico completo
(`0 .. (nfft-1)/fs`); las quefrencias por encima de `nfft/(2·fs)` son las
quefrencias negativas especulares, donde los cepstra de potencia y real (que
son pares) se repiten y el cepstrum complejo guarda su contenido anticausal
(de fase no mínima). El relleno de ceros vía `nfft` reduce el aliasing
temporal cepstral cuando el espectro logarítmico tiene rasgos abruptos,
exactamente igual que el relleno `oversample` de
[`minimum_phase`](/phonometry/es/devices/electroacoustics/swept-sine-distortion/).

## 2. Detección de ecos: el tren de picos rahmónicos

Una reflexión única $x(t) = s(t) + a\,s(t-t_0)$ multiplica el espectro por
$1 + a e^{-j 2\pi f t_0}$ - un rizado de periodo $1/t_0$ en toda la banda.
Su logaritmo se expande, para $|a| < 1$, en la serie exactamente sumable

$$
\ln\!\left(1 + a e^{-j\theta}\right)
= \sum_{n \ge 1} (-1)^{n+1} \frac{a^n}{n}\, e^{-jn\theta},
$$

así que el cepstrum lleva un tren de picos en los **rahmónicos** $n t_0$ con
amplitudes $a, -a^2/2, a^3/3, \dots$ (su suma es $\ln(1+a)$),
independientemente del espectro de la propia $s$, que se concentra en las
quefrencias bajas. En
el cepstrum con signo del espectro logarítmico de potencia (`kind='power'`,
la convención de Milner) la altura del primer pico es el coeficiente de
reflexión $a$ - con su signo - más lo que el cepstrum de la fuente aporte en
esa quefrencia (despreciable para fuentes de banda ancha, cuyo cepstrum se
concentra en las quefrencias bajas); sobre un impulso ideal con eco la
identidad es una forma cerrada que los tests y la batería de conformidad
fijan a 1e-10. `echo_detection`
automatiza la lectura: busca el mayor pico de `|cepstrum|` en la banda de
búsqueda (así una reflexión inversora, $a < 0$, se encuentra en su retardo
verdadero en vez de perderse), refina el retardo por interpolación
cuadrática a través del pico y sus vecinos, y devuelve como coeficiente de
reflexión el valor del pico con su signo. Cuando el retardo verdadero cae
entre muestras, el rahmónico se reparte entre bins de quefrencia vecinos:
el retardo interpolado sigue acertando, pero el coeficiente devuelto
subestima $|a|$ (hasta cerca del 65 % de su valor a mitad de camino entre
muestras).

```python

from phonometry import echo_detection

fs = 48000.0
rng = np.random.default_rng(2)
s = rng.standard_normal(12000)               # fuente de banda ancha
x = s + 0.5 * np.roll(s, 384)                # eco: 8 ms, a = 0.5

res = echo_detection(x, fs, min_quefrency=0.002)
print(res.delay, res.reflection_coefficient)  # 0.008 s, ~0.5
res.plot(language="es")
```

*El cepstrum lee el eco en el eje y no en la forma de onda: el pico detectado
cae en 8,000 ms y se levanta 0,488 frente al $a = 0{,}5$ verdadero. La
confirmación es el pequeño pico **negativo** de 16,0 ms, −0,126 contra el
$-a^2/2 = -0{,}125$ de la serie: una periodicidad espectral ajena no tiene por
qué producir un tren de rahmónicos alternantes, así que el segundo pico es lo
que separa un eco de una casualidad. Todo lo que queda a la izquierda de la
banda sombreada es la envolvente de la propia fuente, que es por lo que la
búsqueda empieza por encima de ella.*

<details>
<summary>Mostrar el código de esta figura</summary>

```python

from scipy import signal as sp_signal
from phonometry import echo_detection, noise_signal

fs = 48000.0
n = 12000
impulse = np.zeros(n)
impulse[0] = 1.0
b, a = sp_signal.butter(2, [0.004, 0.9], btype="bandpass")
direct = sp_signal.lfilter(b, a, impulse)              # clic de banda ancha
ir = direct + 0.5 * np.roll(direct, int(0.008 * fs))   # eco a 8 ms
ir += noise_signal(fs, n / fs, color="white", rms=1e-4, seed=13)

res = echo_detection(ir, fs, min_quefrency=0.002)

fig, ax = plt.subplots(figsize=(10, 6))
half = res.nfft // 2 + 1
ax.plot(1e3 * res.quefrencies[:half], res.cepstrum[:half], lw=1.1)
ax.axvline(8.0, ls="--", color="k", label="Retardo verdadero del eco")
ax.plot([1e3 * res.delay], [res.reflection_coefficient], "v", ms=10,
        label="Pico detectado (altura = reflexión a)")
ax.set_xlim(0.0, 30.0)
ax.set_xlabel("Quefrencia [ms]")
ax.set_ylabel("Cepstrum")
ax.legend()
plt.show()
```

</details>

La banda de búsqueda empieza por encima de la región de quefrencias bajas
que ocupa la envolvente espectral de la propia fuente (`min_quefrency`, por
defecto 16 muestras) y termina en la mitad no ambigua del eje
(`max_quefrency`). Los trenes de picos por reverberación de la sismología
(Havelock cap. 87) son la misma firma a escala geofísica. Fíjate en el
segundo rahmónico negativo en $2 t_0$ de la figura: el término $-a^2/2$ de la
serie, una confirmación útil de que un pico es de verdad un eco y no una
periodicidad espectral sin relación.

### A partir de una respuesta al impulso medida

Todo lo anterior corre sobre un clic sintético, y lo que el método consume de
verdad es una respuesta al impulso. Mídela con cualquiera de las excitaciones de
la biblioteca (un barrido exponencial, una MLS o la pareja de Golay de
[medición de sistemas](/phonometry/es/signals/spectra/system-measurement/)),
mantén el sonido directo y la reflexión dentro de un mismo registro y pásale la
respuesta al impulso directamente a `echo_detection`.

**El retardo es una diferencia de longitud de camino.** $\Delta d = c\,t_0$, así
que los 8 ms de la página a 343 m/s significan que el camino reflejado es 2,74 m
más largo que el directo, lo que fija el reflector una vez conocidas las
posiciones de fuente y receptor. Haz la conversión con la temperatura que
mediste, no con 343: la velocidad del sonido se mueve unos 0,6 m/s por kelvin,
que es un 0,2 % sobre la distancia por cada grado.

**El coeficiente es una razón de amplitudes en el micrófono, no una propiedad de
la superficie.** $a = 0{,}5$ significa que la reflexión llega con la mitad de
amplitud que el sonido directo (una cuarta parte de la energía), y las dos juntas
ondulan el espectro de magnitud entre $+3{,}5$ y $-6{,}0$ dB, que es la
ondulación que aísla el §3. Pero $a$ ya lleva dentro la divergencia esférica de
más del camino largo, así que el factor de reflexión propio de la superficie es
$R = a\,(r_\text{reflejado}/r_\text{directo})$. Para un retardo de 8 ms sobre un
camino directo de 1 m esa razón es 3,74, así que $R = 3{,}74\,a$, un número que
no puede pasar de uno, lo que significa que una $a$ aparente por encima de
$1/(1 + c t_0/r_\text{directo})$ no es en absoluto una única reflexión especular.
Donde $R$ es válido, el coeficiente de absorción a ese ángulo de incidencia es
$\alpha = 1 - R^2$; las
[páginas de absorción](/phonometry/es/materials/absorbers/absorption-measurement/)
son las dueñas de las vías normalizadas hacia la misma magnitud.

**Un pico negativo es una inversión de fase.** Una frontera de presión nula, o un
camino eléctrico invertido, cambia el signo de la reflexión, y con él el de toda
la serie de rahmónicos.

Cuatro condiciones deciden si el número significa algo:

1. `min_quefrency` por encima de la duración de la llegada directa, para que la
   envolvente espectral de la propia fuente quede fuera de la banda de búsqueda.
2. La reflexión al menos 10 dB por encima del fondo de ruido de la respuesta.
3. El segundo rahmónico presente en $2t_0$ con el signo opuesto, la comprobación
   que separa un eco real de una periodicidad espectral sin relación.
4. Un tramo analizado varias veces mayor que el retardo buscado. La ondulación
   tiene periodo $1/t_0$ en frecuencia y un registro de duración $T$ solo
   resuelve el espectro hasta $1/T$; enventanar la respuesta alrededor de las
   llegadas que interesan también evita que reflexiones posteriores añadan
   rahmónicos propios.

## 3. Liftering: envolvente frente a estructura fina

Filtrar en el dominio de la quefrencia se llama **liftering** (Havelock
cap. 27, sec. 4.3). Un lifter *paso bajo* conserva las quefrencias por
debajo del corte y devuelve la envolvente log-espectral suave con el rizado
eliminado; un lifter *paso alto* conserva el complemento - solo el rizado.
Los dos modos son exactamente complementarios en dB, porque la partición es
lineal en el dominio logarítmico:

```python

from phonometry import lifter

fs = 48000.0
rng = np.random.default_rng(3)
s = rng.standard_normal(12000)
x = s + 0.5 * np.roll(s, 384)                # el mismo eco de 8 ms

low = lifter(x, fs, cutoff=0.004, mode="lowpass")    # envolvente de ln|X|
high = lifter(x, fs, cutoff=0.004, mode="highpass")  # el rizado del eco
print(np.allclose(low.liftered_db + high.liftered_db, low.spectrum_db))
low.plot(language="es")
```

*El reparto del lifter de 4 ms sobre un registro de eco puro: el lado paso
bajo devuelve la envolvente espectral suave, el lado paso alto aísla el
rizado de 125 Hz del eco, oscilando exactamente entre las formas cerradas
$20\log_{10}(1 \pm a) = +3{,}5$ y $-6{,}0$ dB.*

<details>
<summary>Mostrar el código de esta figura</summary>

```python

from scipy import signal as sp_signal
from phonometry import lifter

fs = 48000.0
# Una ondícula de banda limitada con un eco puro de 8 ms (a = 0,5), de modo
# que el rizado del paso alto tiene las cotas exactas de forma cerrada.
b, a = sp_signal.butter(2, 0.3)
s = np.zeros(4096)
s[37:37 + 256] = sp_signal.lfilter(b, a, np.r_[1.0, np.zeros(255)])
x = s + 0.5 * np.roll(s, 384)

low = lifter(x, fs, cutoff=0.004, mode="lowpass")
high = lifter(x, fs, cutoff=0.004, mode="highpass")

# Una línea: el cepstrum con el corte y los dos espectros logarítmicos:
low.plot(language="es")
plt.show()

# El reparto envolvente/rizado a mano, ampliado a 500-2000 Hz:
band = (low.frequencies >= 500) & (low.frequencies <= 2000)
fig, axes = plt.subplots(2, 1, sharex=True)
axes[0].semilogx(low.frequencies[band], low.spectrum_db[band], "0.6",
                 lw=0.7, label="Espectro logarítmico")
axes[0].semilogx(low.frequencies[band], low.liftered_db[band], lw=2,
                 label="Lifter paso bajo: envolvente")
axes[1].semilogx(high.frequencies[band], high.liftered_db[band], "r",
                 label="Lifter paso alto: rizado")
for bound in (20 * np.log10(1.5), 20 * np.log10(0.5)):
    axes[1].axhline(bound, color="g", linestyle="--")
axes[1].set_xlabel("Frecuencia [Hz]")
for ax in axes:
    ax.set_ylabel("Magnitud [dB]")
    ax.legend()
plt.show()
```

</details>

Para la señal de eco puro el rizado del paso alto oscila entre las formas
cerradas $20\log_{10}(1+a)$ y $20\log_{10}(1-a)$ dB, otro oráculo que fijan los
tests. En análisis de voz la misma operación separa la envolvente del tracto
vocal (formantes) de los armónicos de la excitación; aquí es la herramienta
general para separar «lo suave de lo periódico» en cualquier respuesta en
magnitud medida.

## 4. El cepstrum complejo y la conexión con la fase mínima

El cepstrum complejo conserva la fase desenrollada, así que la transformada
es de ida y vuelta: `CepstrumResult.invert()` restaura el registro con
precisión de máquina, incluida la componente de fase lineal (retardo puro)
que la transformada directa elimina y guarda en `linear_phase_samples`:

```python

from scipy import signal as sp_signal
from phonometry import cepstrum

fs = 48000.0
x = np.zeros(2048)
b, a = sp_signal.butter(2, 0.3)
x[37:293] = sp_signal.lfilter(b, a, np.r_[1.0, np.zeros(255)])

res = cepstrum(x, fs, kind="complex")
print(res.linear_phase_samples)              # negativo: retardo eliminado
print(np.max(np.abs(res.invert() - x)))      # ~1e-14
res.plot(language="es")   # el cepstrum complejo frente a la quefrencia
```

Entre el logaritmo y la transformada inversa se puede editar lo que se
quiera - eso es la deconvolución homomórfica (Havelock cap. 87, sec. 3.3):
anular los rahmónicos elimina un eco, conservar solo las quefrencias bajas
extrae la ondícula de la fuente. Una señal de fase mínima tiene un cepstrum
complejo causal, y por eso plegar el cepstrum real sobre las quefrencias
positivas reconstruye la fase mínima a partir de $|H|$ únicamente:
[`minimum_phase`](/phonometry/es/devices/electroacoustics/swept-sine-distortion/) y
`phase_decomposition` corren sobre ese mismo núcleo de plegado (Bendat y
Piersol sec. 13.1.4; Tohyama, en Havelock cap. 75, edita la reverberación
manipulando exactamente estas partes causal y anticausal).

La ida y vuelta de arriba sale exacta con precisión de máquina porque el registro
es suave, sin ruido y casi de fase mínima. El cepstrum complejo no es robusto en
general, y la razón es la fase. Necesita una fase desenrollada que se pueda
seguir de forma continua por toda la banda, y allí donde la magnitud se acerca a
cero (un cero profundo, un cero cerca de la circunferencia unidad) la fase gira
$\pi$ en el espacio de uno o dos bins y el desenrollador puede saltar en el
sentido equivocado. Todas las quefrencias de ahí en adelante heredan el error.
Los cepstra de potencia y real son inmunes, porque ni siquiera miran la fase.

Tres salvaguardas. Rellena con ceros mediante `nfft` para que la fase quede
muestreada lo bastante fina como para que el desenrollador la siga; mantén alta
la relación señal-ruido del registro en las bandas que importan, porque el ruido
aditivo aleatoriza la fase justo donde la magnitud es pequeña; y **comprueba la
ida y vuelta con `invert()` antes de fiarte de ningún cepstrum editado**: un
desenrollado fallido se manifiesta al instante como un error de reconstrucción
muy por encima de 1e-10. Cuando el registro no da para tanto, usa el cepstrum de
potencia para la detección y el plegado del cepstrum real de `minimum_phase`
cuando solo hace falta una fase coherente con la magnitud.

## 5. El espectro de la envolvente: modulaciones como líneas

Donde el cepstrum encuentra periodicidades *del espectro*, el **espectro de
la envolvente** encuentra periodicidades *de la amplitud*. La sección 13.3
de Bendat y Piersol (Fig. 13.11) formaliza la estructura: un detector de
envolvente, un eliminador de continua y una vista espectral de lo que queda.
`envelope_spectrum` pasa la
[envolvente de Hilbert](/phonometry/es/signals/spectra/correlation-delay/)
(`kind="magnitude"`, el valor práctico por defecto) o el detector cuadrático
del libro (`kind="squared"`) por exactamente esa cadena, escalada por la
ganancia coherente de la ventana para que una modulación sinusoidal cuya
frecuencia cae en un bin de análisis se lea como una línea con su amplitud
exacta (las líneas fuera de bin leen de menos por la pérdida de scalloping
de la ventana, hasta cerca de 1,4 dB con la Hann por defecto). El argumento
opcional `band=(low, high)` reproduce el filtro paso banda de entrada de la
figura 13.11 del libro - la cadena clásica de envolvente para rodamientos: aislar con un
paso banda de fase cero la banda de resonancia estructural que excitan los
impactos del defecto y después obtener su envolvente - de modo que un
interferente fuera de banda llega muy atenuado al detector (la caída de un
Butterworth de cuarto orden aplicado hacia delante y hacia atrás; el
rechazo es finito, y el paso de fase cero deja pequeños transitorios en
los bordes del registro).

Para un tono AM $A_0 (1 + m \cos(2\pi f_\mathrm{m} t)) \cos(2\pi f_\mathrm{c} t)$ con $f_\mathrm{m}$
en un bin de análisis las formas cerradas son:

| `kind` | nivel medio | línea en $f_\mathrm{m}$ | línea en $2f_m$ |
|---|---|---|---|
| `'magnitude'` | $A_0$ | $A_0 m$ | - |
| `'squared'` | $A_0^2 (1 + m^2/2)$ | $2 A_0^2 m$ | $A_0^2 m^2/2$ |

```python

from phonometry import envelope_spectrum

fs = 8192.0
t = np.arange(int(4 * fs)) / fs
x = (1.0 + 0.4 * np.cos(2 * np.pi * 25.0 * t)) * np.cos(2 * np.pi * 1000.0 * t)

res = envelope_spectrum(x, fs)
k = int(round(25.0 * res.nfft / fs))
print(res.mean_level, res.amplitude[k])      # ~1.0 y ~0.4
res.plot(language="es")
```

*La línea no es proporcional a la modulación: **es** la modulación. Con $f_\mathrm{m}$ en
un bin de análisis el detector de magnitud lee 0,3996 frente a la forma cerrada
$A_0 m = 0{,}4$, y `mean_level` lee 1,001 frente a $A_0 = 1$. Ese es el sentido
de escalar por la ganancia coherente de la ventana: la ordenada es una
profundidad de modulación, no un nivel cualquiera. A su alrededor el suelo se queda cerca de
5×10⁻⁴, así que la línea se despega unos 58 dB incluso con ruido blanco al 3 %
de la portadora, que es por lo que la cadena encuentra defectos de rodamiento
que el espectro de la propia portadora esconde.*

<details>
<summary>Mostrar el código de esta figura</summary>

```python

from phonometry import envelope_spectrum, noise_signal

fs = 8192.0
seconds = 4.0
t = np.arange(int(seconds * fs)) / fs
x = (1.0 + 0.4 * np.cos(2 * np.pi * 25.0 * t)) * np.cos(2 * np.pi * 1000.0 * t)
x += noise_signal(fs, seconds, color="white", rms=0.03, seed=8)

res = envelope_spectrum(x, fs)

fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(res.frequencies, res.amplitude, lw=1.4,
        label="Espectro de la envolvente")
ax.axvline(25.0, ls="--", color="k", label="Frecuencia de modulación")
ax.axhline(0.4, ls=":", color="r",
           label=r"Amplitud exacta de la línea $A_0 m$")
ax.set_xlim(0.0, 100.0)
ax.set_xlabel("Frecuencia [Hz]")
ax.set_ylabel("Amplitud de modulación")
ax.legend()
plt.show()
```

</details>

Los defectos de rodamientos y engranajes, el zumbido de la red eléctrica y
la modulación de amplitud de los aerogeneradores aparecen así: líneas en la
frecuencia de modulación y sus armónicos, limpiamente separadas del espectro
de la propia portadora. La media de la envolvente que elimina la etapa de
continua se conserva en `mean_level` (la amplitud de la portadora para el
detector de magnitud), y `remove_dc=False` omite el eliminador cuando
importa la línea de continua absoluta.

### Elegir la banda, y montar el sensor

El argumento de la banda es donde se gana o se pierde la cadena de rodamientos, y
ni la banda ni el transductor son una elección libre.

**La banda.** Pasa primero un espectro simple del registro bruto y busca la
joroba ancha que excitan los impactos del defecto, una resonancia estructural del
soporte, normalmente en algún punto entre 1 y 20 kHz para rodamientos de
elementos rodantes. Coloca la banda alrededor de esa joroba. Hazla varias veces
más ancha que la mayor tasa de repetición que necesites ver (de tres a cinco
veces la mayor frecuencia de defecto, para que sus armónicos sobrevivan al
detector), y elige $f_\mathrm{s}$ al menos 2,56 veces el extremo superior de la banda.
Cuando haya dos jorobas candidatas, quédate con aquella cuyo espectro de la
envolvente dé mayor relación línea-fondo: la banda es una elección de relación señal-ruido,
no una constante física. Una banda demasiado ancha vuelve a admitir las
componentes fuertes de baja frecuencia que el filtro de entrada existe para
eliminar; una demasiado estrecha deja a la envolvente sin energía de impacto; y
el paso de fase cero deja transitorios en los dos bordes del registro, así que
recórtalos antes de leer amplitudes.

**El montaje.** La frecuencia superior utilizable la fija la resonancia del
acelerómetro *montado*, no el límite de la hoja de características del sensor, y
esa es justamente la banda que necesita la cadena de rodamientos. El montaje
atornillado o con una capa fina de adhesivo mantiene utilizable un acelerómetro
de uso general hasta unos 10 kHz; una base magnética suele hundir eso a un par de
kilohercios, y una sonda de mano a cerca de 1 kHz, es decir, el montaje por sí
solo puede destruir la banda de la que depende el método. Pon el sensor sobre el
soporte del rodamiento, en la zona de carga, tan cerca de la pista exterior como
permita la máquina, con su eje en la dirección de la carga y el cable con
descarga de tracción. ISO 5348 es la referencia de montaje.

```python
# Una resonancia estructural de 3 kHz excitada a una frecuencia de defecto de
# 120 Hz, bajo una línea de 50 Hz tres veces más fuerte: el paso banda es lo
# que las separa.
t_s = np.arange(int(4 * fs)) / fs
ring = np.sin(2 * np.pi * 3000.0 * t_s) * np.exp(-(t_s % (1 / 120.0)) / 0.001)
raw = ring + 3.0 * np.sin(2 * np.pi * 50.0 * t_s)

wide = envelope_spectrum(raw, fs)
narrow = envelope_spectrum(raw, fs, band=(2000.0, 4000.0))
k120 = int(round(120.0 * wide.nfft / fs))
print(round(wide.amplitude[k120], 4),        # 0,0093: enterrada
      round(narrow.amplitude[k120], 4))      # 0,1964: 26 dB mejor
```

Las líneas que produce la cadena se identifican por su *frecuencia*, no por su
amplitud, y las familias cinemáticas que les ponen nombre (paso de bola por la
pista exterior y por la interior, giro de la bola, jaula, engrane y sus bandas
laterales a la frecuencia de eje) se calculan a partir de la geometría y la
velocidad de eje en
[Frecuencias de fallo de máquinas](/phonometry/es/vibration/machinery/machine-diagnostics/),
que las dibuja directamente sobre un espectro de la envolvente como este. Pasar
la envolvente sobre el `residual` de un
[promedio síncrono](/phonometry/es/signals/spectra/synchronous-averaging/) elimina
antes las componentes deterministas del engranaje y deja limpias las líneas del
rodamiento.

## Relación con los demás estimadores

El cepstrum parte de las mismas convenciones FFT de registro único que los
[estimadores espectrales calibrados](/phonometry/es/signals/spectra/spectral-analysis/),
y su núcleo de plegado es literalmente el que hay dentro de
[`minimum_phase`](/phonometry/es/devices/electroacoustics/swept-sine-distortion/) - la
refactorización queda fijada bit a bit en los tests. El espectro de la
envolvente es la vista en frecuencia de la misma señal analítica que la
[envolvente de Hilbert](/phonometry/es/signals/spectra/correlation-delay/) devuelve en
el tiempo, y un preanálisis natural antes de las métricas dedicadas de
[modulación de amplitud de aerogeneradores](/phonometry/es/environment/sources/wind-turbine-noise/):
el espectro de la envolvente dice *si hay modulación y a qué ritmo*, las
métricas del dominio la cuantifican normativamente.

## Qué cubre esta guía

Las tres variantes del cepstrum y el liftering del *Handbook of Signal
Processing in Acoustics* de Havelock, Kuwano y Vorländer (`cepstrum`,
`lifter`, capítulos 27 y 87), el retardo de un único eco y el coeficiente de
reflexión leídos en el cepstrum de potencia (`echo_detection`), el cepstrum
complejo invertible y su ida y vuelta homomórfica (`CepstrumResult.invert`), y
el espectro de la envolvente del capítulo 13 de Bendat y Piersol
(`envelope_spectrum`) con sus líneas de forma cerrada para un tono AM.

`echo_detection` solo elige el mayor pico cepstral de la banda de búsqueda,
así que una respuesta con varios ecos solapados necesita localización manual
de picos o llamadas repetidas sobre bandas más estrechas: no es un separador
multi-eco. El módulo tampoco tiene un cepstrum en escala mel ni de tipo MFCC
para rasgos de audio perceptual: `lifter` y `cepstrum` trabajan solo sobre el
espectro logarítmico en frecuencia lineal.

## Véase también

- [Correlación, retardo temporal y envolvente](/phonometry/es/signals/spectra/correlation-delay/): la envolvente de Hilbert que esta página convierte en espectro.
- [Análisis espectral calibrado](/phonometry/es/signals/spectra/spectral-analysis/): el espectro logarítmico del que parte el cepstrum.
- [Promediado síncrono en el tiempo](/phonometry/es/signals/spectra/synchronous-averaging/): eliminar la parte síncrona antes de pasar un espectro de la envolvente sobre el residuo.
- [Frecuencias de fallo de máquinas](/phonometry/es/vibration/machinery/machine-diagnostics/): las familias de rodamientos y engranajes que se leen exactamente sobre este espectro de la envolvente.
- [Distorsión con barrido sinusoidal](/phonometry/es/devices/electroacoustics/swept-sine-distortion/): el plegado de fase mínima que comparte el núcleo de esta página.
- Referencia de la API: [`signals.cepstrum`](/phonometry/es/reference/api/signals/cepstrum/).
