<!-- canonical: https://jmrplens.github.io/phonometry/es/aircraft/rotorcraft-noise/ -->
Source: https://jmrplens.github.io/phonometry/es/aircraft/rotorcraft-noise/

El ruido de los helicópteros es muy directivo, así que el método de **ECAC
Doc 32** describe la fuente con un **hemisferio de ruido**: niveles de presión
acústica en bandas de tercio de octava sobre una malla esférica de acimut
$\varphi$ y ángulo polar $\theta$, definidos a una distancia de referencia
fija de **60 m** en condiciones atmosféricas de referencia de la OACI. Situar
esa fuente en un receptor añade el ajuste de propagación
$\Delta L_p = \Delta L_\mathrm{s} + \Delta L_\mathrm{a} + \Delta L_\mathrm{g}$.

El EPNL que calcula esta página es la misma magnitud que pide la certificación de
ala fija; lo único que cambia es la descripción de la fuente. El Capítulo 8 del
Anexo 16 de la OACI hace volar el helicóptero nivelado a 150 m sobre un micrófono
central, con otros dos a 150 m a cada lado.

## De dónde sale un hemisferio

La biblioteca implementa el método, no los datos: **con phonometry no se
distribuye ninguna base de datos de hemisferios**, y no hay lector de ficheros
NORAH, así que los arrays de más abajo los tiene que montar quien lee. Salen de
dos sitios. Uno es una campaña de ensayos en vuelo: se hace volar el helicóptero
sobre un array de micrófonos en tierra, el espectro de cada dirección de emisión
se lleva de vuelta, deshaciendo la propagación, hasta una esfera de referencia de
60 m en condiciones de referencia de certificación de ruido de la OACI
(101 325 Pa, 298,15 K, 70 % de humedad relativa), y la medida sigue la práctica
del Vol. I del Anexo 16 a la que apunta el §4.2 de la Doc 32. El otro es la base
de datos de referencia NORAH2, que cubre once tipos de helicóptero.

Un hemisferio utilizable lleva algo más que niveles. El formato de fichero NORAH
(Apéndice A de la Doc 32) registra la distancia de referencia `POLDIST`, si
incluye o no la absorción atmosférica, el indicador de ausencia de valor, la
atmósfera del propio hemisferio (`TAMB`, `RELHUM`, `PAMB`), y después las
*condiciones en las que se adquirieron los datos*: la temperatura, la humedad y
la presión de medida a 10 m, el régimen de rotor, la velocidad indicada, el
ángulo de trayectoria, las actitudes de cabeceo y alabeo y las componentes de
viento. La velocidad y el ángulo de trayectoria no son metadatos: son la clave
por la que la interpolación entre condiciones de vuelo de más abajo busca el
hemisferio, así que un hemisferio sin ellos no puede colocarse en una base de
datos.

Lo que puede ver un array en tierra es limitado, y esa es la razón por la que los
hemisferios reales llegan con agujeros. La Doc 32 exige cubrir al menos
$-60° \le \varphi \le 60°$ y los ángulos polares comprendidos entre los dos
instantes de 10 dB por debajo; medir ángulos laterales más abiertos, o ángulos
polares que se acerquen a 0° y a 180°, pide un montaje más elaborado. El relleno
de huecos de la Ec. 6/7 es la consecuencia directa de ese límite, no un detalle
de implementación: fuera del parche medido el nivel es el del bin lleno más
cercano, y la media energética de ellos cuando hay varios igual de cerca.

La matriz de condiciones que debería abarcar una base de datos la fija el §4.1 de
la Doc 32: hemisferios a ángulos de descenso con intervalos de al menos 3° y
cuatro velocidades distintas, para cubrir la región de descenso donde vive la
interacción pala-vórtice; ángulos de ascenso a la velocidad de mejor régimen de
subida $V_y$ o a una velocidad de despegue típica, incluido el ángulo de ascenso
máximo que declare el manual de vuelo; y vuelo nivelado a 0,9 $V_H$ con
incrementos de $+10$ kt (o $V_H$, el que sea menor), $-15$ kt y $-30$ kt. Fuera
de la envolvente que abarcan esas condiciones, la interpolación degenera a vecino
más próximo, y en silencio.

Cuando una clase no tiene ningún hemisferio, el §3.2 de la Doc 32 da la regla de
sustitución: agrupar el tipo temporalmente con una clase que sí lo tenga,
eligiendo una clase cuyos niveles de certificación de ruido sean inferiores a los
del objetivo o queden dentro de su horquilla; cuando no hay niveles de
certificación disponibles o encajan varias clases, el peso pasa a ser el
parámetro que manda y se toma la clase que mejor case *por debajo* del peso del
tipo, con el desplazamiento de nivel $\Delta\mathrm{EPNL}$ puesto a cero para que
la estimación siga siendo conservadora. Dentro de una clase, el desplazamiento es
la diferencia de niveles de certificación registrados, los de despegue,
sobrevuelo y aproximación corrigiendo respectivamente las condiciones de ascenso,
nivelada y descenso, y entra aquí como `level_offset`.

## El hemisferio de ruido

Un `RotorcraftHemisphere` contiene los niveles de banda sobre la malla
acimut/polar.

| Campo | Forma | Unidades y convenio |
|---|---|---|
| `frequencies` | `(F,)` | centros de banda de tercio de octava en Hz; el formato NORAH lleva 31 bandas de 10 Hz a 10 kHz |
| `azimuth` $\varphi$ | `(A,)` | grados alrededor del eje del morro: $0$ recto hacia abajo, $+90$ a estribor, $-90$ a babor; 19 valores cada 10° |
| `polar` $\theta$ | `(P,)` | grados desde el morro: $0$ hacia delante, $90$ al través de la aeronave, $180$ hacia atrás; 19 valores cada 10° |
| `levels` | `(A, P, F)` | niveles de presión acústica de banda en dB a la distancia de referencia; `NaN` en los bins que la medida no cubrió |
| `distance` | escalar | distancia de referencia en metros, 60 por defecto |

Los dos ángulos son las coordenadas esféricas de la Ec. 3 de la Doc 32,
$x = r\cos\theta$, $y = r\sin\theta\sin\varphi$, $z = r\sin\theta\cos\varphi$
en ejes cuerpo con $z$ hacia abajo, de modo que $\theta$ se mide desde el morro y
$\varphi$ gira alrededor del eje del morro empezando por el **recto hacia
abajo**. `hemisphere_source_level(h, azimuth_deg, polar_deg)` toma los ángulos en
ese orden y devuelve un nivel por banda: `hemisphere_source_level(h, 0.0, 90.0)`
es el nivel radiado verticalmente hacia abajo, que es lo que oye un micrófono
justo debajo de un sobrevuelo nivelado, mientras que un receptor a 500 m del
costado de ese mismo sobrevuelo a 150 m se direcciona aproximadamente en
$(\varphi, \theta) = (73°, 90°)$. Equivocar el signo de $\varphi$ refleja la
aeronave; tratar $\theta$ como un ángulo desde la vertical traspone el array y da
una directividad verosímil pero falsa. `NaN` y 0 dB no son intercambiables: los
bins sin medir tienen que ser `NaN`, porque se rellenan desde el bin lleno
angularmente más cercano antes de la consulta bilineal, y un 0 dB guardado se
tomaría como dato. `h.mirrored()` implementa la relación de sentido de rotor de
la Ec. 2 invirtiendo el signo del acimut.

`hemisphere_source_level` lee el nivel en una dirección de emisión arbitraria:
primero rellena los huecos de la malla desde los bins llenos angularmente más
cercanos (Ec. 14/15, con caché) y después interpola de forma bilineal en el
dominio de energía sobre los cuatro bins vecinos (Ec. 13), de modo que las celdas
parcialmente medidas se mantienen continuas con sus esquinas medidas.

```python
from phonometry import aircraft

h = aircraft.RotorcraftHemisphere(frequencies=freqs, azimuth=phi, polar=theta, levels=levels)
lv = aircraft.hemisphere_source_level(h, 0.0, 90.0)   # recto hacia abajo, por banda, a 60 m
h.plot()                                          # directividad proa-popa
```

*Por qué el método necesita un hemisferio: en la banda más directiva, 9,4 dB
separan la dirección medida más ruidosa de la más silenciosa, y el lóbulo se
sitúa en $\theta \approx 140°$, bien por detrás de la vertical. Las tres secciones de la izquierda muestran que la directividad
depende de la frecuencia: la banda baja es casi plana, la banda media lleva el
lóbulo y la banda alta está callada en todas partes, así que un único nivel de
fuente no puede sustituirla. Fíjate además en lo que hacen las curvas fuera de la
banda sombreada: se vuelven planas. Eso es el relleno de huecos, no medida.*

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

```python

from phonometry import aircraft

# Un hemisferio sintético sobre la malla de la Doc 32: 31 bandas, 19 x 19
# ángulos, con un lóbulo de frecuencias medias por detrás del través y las
# celdas que un array en tierra no puede ver dejadas a NaN.
freqs = 1000.0 * 10.0 ** (np.arange(-20, 11) / 10.0)
az, po = np.arange(-90.0, 91.0, 10.0), np.arange(0.0, 181.0, 10.0)
spectrum = 88.0 - 12.0 * np.log10(freqs / 100.0) ** 2
band = np.exp(-((np.log10(freqs) - np.log10(630.0)) ** 2) / 0.08)
levels = (spectrum[None, None, :]
          - 0.030 * np.abs(po - 110.0)[None, :, None]
          - 0.020 * np.abs(az)[:, None, None]
          + 7.0 * np.exp(-((po - 140.0) ** 2) / (2 * 16.0 ** 2))[None, :, None]
          * np.exp(-(az ** 2) / (2 * 34.0 ** 2))[:, None, None]
          * band[None, None, :])
unseen = (np.abs(az)[:, None] > 60.0) | (po[None, :] < 40.0) | (po[None, :] > 140.0)
h = aircraft.RotorcraftHemisphere(freqs, az, po, np.where(unseen[:, :, None],
                                                          np.nan, levels))

fig, (ax, ax2) = plt.subplots(1, 2, figsize=(12, 5.4))
for f in (100.0, 630.0, 4000.0):
    h.plot(ax=ax, band=f, language="es")
ax.axvspan(40.0, 140.0, color="0.85", zorder=0)
idx = int(np.nanargmax(np.nanmax(levels, axis=(0, 1)) - np.nanmin(levels, axis=(0, 1))))
filled = np.array([[aircraft.hemisphere_source_level(h, a, p)[idx] for p in po]
                   for a in az])
cs = ax2.contourf(po, az, filled, levels=12)
ax2.plot([40, 140, 140, 40, 40], [-60, -60, 60, 60, -60], "k--")
fig.colorbar(cs, ax=ax2, label="Nivel de fuente a 60 m [dB]")
plt.show()
```

</details>

## Ajustes de propagación

El nivel del hemisferio a 60 m se lleva al receptor con tres ajustes (§A.4):
`spherical_spreading_adjustment` ($\Delta L_\mathrm{s} = -20\log_{10}(r/60)$, Ec. 24),
`atmospheric_adjustment` ($\Delta L_\mathrm{a} = -\alpha(f)\,(r - 60)$ con el
coeficiente de
ISO 9613-1, Ec. 26/27) y `ground_effect_adjustment` (interferencia directo/
reflejado sobre un plano de impedancia, Chien-Soroka Ec. 28-35, con la impedancia
de Delany-Bazley y las clases de resistividad de flujo CNOSSOS `"A"`-`"H"`).

*Una geometría, dos clases de suelo, y la superficie dura es la más extrema en
los dos sentidos: refuerza más a baja frecuencia (+5,3 dB frente a +4,7 dB en
50 Hz), cancela mucho más a fondo en el primer mínimo (−13,7 dB frente a
−7,8 dB) y sigue oscilando en torno a +2,4 dB en 5 kHz, donde la curva blanda ya
se ha asentado hacia una suma incoherente cercana a +0,6 dB. Las tres regiones
se leen debajo del código.*

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

```python

from phonometry import aircraft

freqs = 1000.0 * 10.0 ** (np.arange(-13, 11) / 10.0)   # tercios 50 Hz-10 kHz
hs, hr, dp = 150.0, 1.5, 500.0                          # geometría de sobrevuelo
grass = aircraft.ground_effect_adjustment(freqs, hs, hr, dp, flow_resistivity="D")
asphalt = aircraft.ground_effect_adjustment(freqs, hs, hr, dp, flow_resistivity="G")

fig, ax = plt.subplots()
ax.axhline(0.0, color="0.5", linewidth=1.0)
ax.semilogx(freqs, asphalt, marker="o", markersize=3,
            label="Duro (asfalto/hormigón, clase G)")
ax.semilogx(freqs, grass, marker="s", markersize=3,
            label="Blando (hierba/pasto, clase D)")
ax.set(xlabel="Frecuencia central de banda de tercio de octava [Hz]",
       ylabel="Ajuste por efecto de suelo ΔLg [dB]",
       title="Efecto de suelo de helicópteros (ECAC Doc 32, Chien-Soroka)")
ax.grid(True, which="both", alpha=0.3)
ax.legend()
plt.show()
```

</details>

```python

from phonometry import aircraft

freqs = 1000.0 * 10.0 ** (np.arange(-13, 11) / 10.0)   # tercios 50 Hz-10 kHz
r = 500.0
recibido = (lv
            + aircraft.spherical_spreading_adjustment(r)
            + aircraft.atmospheric_adjustment(freqs, r)
            + aircraft.ground_effect_adjustment(freqs, 150.0, 1.5, 500.0, flow_resistivity="D"))
```

La base de datos estándar está registrada a 60 m, el valor por defecto. Si un
hemisferio usa otra distancia polar (`h.distance`, p. ej. anillos en vuelo
estacionario a 70 m), pásala a los dos ajustes dependientes de la distancia
como `reference_distance=h.distance`.

La curva de suelo se lee en tres regiones. A baja frecuencia el rayo directo y su
reflexión en el suelo llegan casi en fase, así que el nivel se acerca a 6 dB por
encima del campo libre: 5,3 dB en 50 Hz sobre suelo duro en la figura, que suben
hasta 5,8 dB. La primera cancelación se sitúa donde la diferencia de caminos es
media longitud de onda, que para esta geometría, con la fuente a 150 m, el
micrófono a 1,5 m y 500 m de separación horizontal, cae en 200 Hz y tiene 13,7 dB
de profundidad; y va subiendo a medida que la aeronave pasa por la vertical, así
que el ajuste es un peine móvil y no una forma espectral fija. Sobre hierba la
impedancia finita gira la fase de la onda reflejada y absorbe parte de ella, así
que el refuerzo es algo menor (4,7 dB en 50 Hz) y, lo que más importa, la
cancelación es mucho menos completa: el primer mínimo es de 7,8 dB en lugar de
13,7 dB. A alta frecuencia los dos caminos se descorrelacionan hacia una suma
incoherente, que es donde la curva blanda se asienta cerca de 0,6 dB mientras la
dura sigue oscilando en 2,4 dB a 5 kHz. Todo esto es coherente por construcción,
así que un nivel promediado sobre un evento entero muestra mucha menos estructura
que cualquiera de estos espectros congelados.

Tres parámetros del emplazamiento deciden el término de suelo, y el valor por
defecto no es el que quiere un emplazamiento con hierba:

| Campo | Por defecto | A qué ponerlo |
|---|---|---|
| `receiver_height` | 1,2 m | la altura de micrófono del Anexo 16; la figura de arriba usa 1,5 m para que el mínimo se vea |
| `ground_elevation` | 0,0 m | por receptor en emplazamientos desiguales, o un valor por punto de malla |
| `flow_resistivity` | `"G"` | **suelo duro.** Un emplazamiento de hierba o pasto tiene que pasar `"C"` o `"D"` |

Las letras son las clases CNOSSOS-EU de la Tabla 3 de la Doc 32, en Pa·s/m²:
`"A"` muy blando (nieve, musgo) a 12,5·10³, `"B"` suelo forestal blando a
31,5·10³, `"C"` suelo suelto sin compactar, césped, hierba o tierra suelta, a
80·10³, `"D"` suelo normal sin compactar como el pasto a 200·10³, `"E"` campo
compactado y grava a 500·10³, `"F"` suelo denso compactado como una pista de
grava o un aparcamiento a 2·10⁶, `"G"` superficies duras, la mayoría del asfalto
y el hormigón normales, a 20·10⁶, y `"H"` superficies muy duras y densas,
incluida el agua, a 200·10⁶. En lugar de una letra se admite una σ numérica en
Pa·s/m², y también un array con un valor por punto de malla.

La atmósfera calla del mismo modo. El hemisferio está definido en las condiciones
de referencia de la OACI y ya contiene la absorción de los primeros 60 m, que es
la razón por la que solo se corrige el trayecto en exceso; el coeficiente para
ese trayecto en exceso vale por defecto esas mismas condiciones de referencia
(25 °C, 70 % de HR, 101,325 kPa) salvo que se pase un `RotorcraftAtmosphere`. La
diferencia es mayor justo donde un espectro de helicóptero todavía lleva energía,
las bandas de tercio de octava altas sobre trayectos oblicuos largos, así que un
emplazamiento frío, seco o de gran altitud necesita el argumento.
`atmospheric_method` elige `"iso9613"` (el valor por defecto) o `"sae"`; los dos
coinciden en unos 0,05 dB por debajo de 3,15 kHz.

## Condiciones de vuelo: interpolar entre hemisferios

Una base de datos registra un hemisferio por condición de vuelo (velocidad
aerodinámica $V$, ángulo de trayectoria $\gamma$). Las condiciones reales rara
vez coinciden con una medida, así que la guía NORAH2 interpola (Ec. 3-10):
ambos ejes se normalizan por sus rangos en la base de datos (con el factor
empírico $F_\text{fc} = 2$ sobre el ángulo de trayectoria), una triangulación
de Delaunay cubre
las condiciones normalizadas, y una consulta dentro de la envolvente convexa
mezcla su triángulo envolvente con pesos inversos a la distancia en el dominio
de energía. Fuera de la envolvente se adopta la condición más cercana sin
mezclar, que es también el comportamiento que ECAC Doc 32, 1.ª ed. prescribe
para toda su envolvente (aún no define interpolación).

```python
from phonometry import aircraft

speeds = [50.0, 70.0, 60.0]                # un hemisferio por condición
angles = [0.0, 0.0, 10.0]                  # ángulos de trayectoria, grados
weights = aircraft.flight_condition_weights(speeds, angles, 60.0, 2.5)
lv = aircraft.interpolated_source_level(
    [h_50_nivel, h_70_nivel, h_60_ascenso], speeds, angles,
    60.0, 2.5, 0.0, 90.0)                  # nivel mezclado por banda
```

La velocidad aerodinámica, no la de suelo, selecciona el hemisferio; los pesos
son invariantes a las unidades mientras la consulta use las de la base de
datos. Los miembros de clase con rotor especular sustituyen `h.mirrored()`
(Ec. 2, $\varphi \to -\varphi$) y los desplazamientos por nivel de certificación
entran como `level_offset`.

**Hay dos triangulaciones en juego, y no coinciden.** Por defecto la biblioteca
triangula el plano normalizado
$(V/\Delta V,\ F_\text{fc}\gamma/\Delta\gamma)$, que mantiene los dos ejes
comparables para una base de datos cualquiera. Las tablas NORAH, en cambio,
incluyen una triangulación del plano $(V, \gamma)$ sin normalizar, y hay que
pasarla como `triangles` siempre que estés reproduciendo un resultado NORAH,
porque las dos discrepan sobre qué símplice encierra una consulta y, por tanto,
sobre los pesos de la mezcla.

*La misma consulta, dos triangulaciones, dos ternas distintas de hemisferios
mezcladas. La diagonal del cuadrilátero que la rodea cambia de lado entre el
plano sin normalizar y el normalizado, así que la triangulación sin normalizar
mezcla las condiciones 5, 6 y 9 y la normalizada mezcla la 5, la 9 y la 10. Las
dos son defendibles; solo una reproduce NORAH. Fuera de la envolvente ninguna
mezcla nada, y se adopta entera la condición más cercana, cosa que cambia la
respuesta sin avisar de nada, y es la trampa que conviene conocer.*

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

```python

from scipy.spatial import Delaunay
from phonometry import aircraft

# Una matriz de condiciones del 4.1 de la Doc 32: descenso a pasos de 3 grados y
# cuatro velocidades, ascenso a Vy y vuelo nivelado a 0,9 VH con los incrementos
# recomendados.
speeds = list(np.repeat([30.9, 36.0, 41.2, 46.3], 4)) + [33.4, 33.4, 64.8, 70.0, 57.1, 49.4]
angles = list(np.tile([-3.0, -6.0, -9.0, -12.0], 4)) + [6.0, 9.0, 0.0, 0.0, 0.0, 0.0]
v, g = np.asarray(speeds), np.asarray(angles)
raw_tri = Delaunay(np.column_stack([v, g])).simplices

print(aircraft.flight_condition_weights(v, g, 38.0, -7.0))
print(aircraft.flight_condition_weights(v, g, 38.0, -7.0, triangles=raw_tri))
# [(5, 0.42), (9, 0.36), (10, 0.22)]  y  [(5, 0.42), (6, 0.23), (9, 0.36)]

fig, ax = plt.subplots(figsize=(6, 5))
ax.triplot(v, g, raw_tri, color="0.6", lw=0.9)
ax.plot(v, g, "o", ms=5)
ax.plot([38.0], [-7.0], "*", ms=15)
ax.set(xlabel="Velocidad aerodinámica V [m/s]", ylabel="Ángulo de trayectoria γ [°]")
plt.show()
```

</details>

## Estacionario, ralentí y rodaje (Tabla 3)

Un helicóptero en estacionario o al ralentí no se mide como un sobrevuelo: se
mide sobre un **anillo de micrófonos en tierra** alrededor de la aeronave
inmóvil (la práctica CAEP de estacionario con efecto suelo a la que remite la
guía), un espectro de bandas por rumbo del anillo (0° en el morro, positivo a
estribor), reducido a la distancia polar del anillo, normalmente 70 m frente
a los 60 m de la base de datos de sobrevuelo. El §A.3.5 de la guía define
cuatro condiciones (estacionario con efecto suelo, HIGE; estacionario sin
efecto suelo, HOGE; ralentí a régimen reducido; y ralentí a régimen pleno) y
tres enfoques en prioridad descendente: medir los cuatro anillos; medir el
anillo HIGE y las otras tres condiciones solo en la dirección de 0°; o medir
el anillo HIGE a secas y aplicar desplazamientos fijos.

`hover_ring_hemisphere` extiende el anillo a un `RotorcraftHemisphere`
completo asumiendo directividad constante en φ: cada bin
$(\varphi, \theta)$ lee el anillo en el rumbo $\pm\theta$ de su propia mitad
(los bins de babor desde los rumbos negativos, los de estribor desde los
positivos, y la columna $\varphi = 0$ bajo la aeronave toma la media
energética de los valores del anillo en $\pm\theta$), interpolando
periódicamente en el dominio de energía. La implementación de referencia
NORAH2 lee el mismo anillo por el rumbo horizontal de la dirección de emisión
(directividad constante en elevación): `mapping="bearing"` la reproduce. Las
dos lecturas coinciden en el borde del hemisferio y se separan varios dB en
ángulos de emisión abruptos: justo bajo el estacionario la lectura de φ
constante promedia babor y estribor donde el prototipo lee el rumbo del
morro.

`hover_derived_hemisphere` deriva después HOGE y los ralentís del hemisferio
HIGE con un desplazamiento de nivel uniforme: una diferencia medida en la
dirección de 0° (Enfoque 2, pasada como `offset_db`) o las constantes
publicadas del Enfoque 3: +12 dB para HOGE, −12 dB para el ralentí a régimen
reducido y −2,5 dB para el ralentí a régimen pleno, todas desde el disco
HIGE. Un desplazamiento espectral constante mueve el nivel ponderado A
exactamente ese valor, que es como la Tabla 3 (definida sobre niveles $L_\mathrm{A}$)
se extiende a espectros de bandas.

```python

from phonometry import aircraft

# Un anillo de estacionario con la forma de una campaña real: un espectro de
# 31 tercios de octava por cada 30° de rumbo, reducido a los 70 m polares.
freqs = 1000.0 * 10.0 ** (np.arange(-20, 11) / 10.0)    # tercios de 10 Hz a 10 kHz
bearings = np.arange(-180.0, 151.0, 30.0)               # el anillo se cierra en ±180°
niveles_anillo = (86.0 - 10.0 * np.log10(freqs / 160.0) ** 2
                  + 3.5 * np.cos(np.radians(bearings))[:, None])
hige = aircraft.hover_ring_hemisphere(freqs, bearings, niveles_anillo, distance=70.0)
hoge = aircraft.hover_derived_hemisphere(hige, "out_of_ground_hover")   # +12 dB
ralenti_pleno = aircraft.hover_derived_hemisphere(hige, "full_rpm_idle")  # -2,5 dB
```

*El anillo de la izquierda es la única medida; todo lo de la derecha se deriva
de él. La sección HIGE arranca en el valor del morro del anillo y termina en
el de la cola (esa es la extensión de φ constante en acción), y las otras
tres fuentes son la misma directividad desplazada entera: +12 dB hasta HOGE,
−2,5 dB hasta el ralentí a régimen pleno, −12 dB hasta el reducido.*

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

```python

from phonometry import aircraft

freqs = 1000.0 * 10.0 ** (np.arange(-20, 11) / 10.0)   # tercios de 10 Hz a 10 kHz
bearings = np.arange(-180.0, 151.0, 30.0)
spectrum = 86.0 - 10.0 * np.log10(freqs / 160.0) ** 2
directivity = (3.5 * np.cos(np.radians(bearings))
               + 3.0 * np.exp(-((bearings - 120.0) ** 2) / (2.0 * 40.0**2)))
ring = spectrum[None, :] + directivity[:, None]
hige = aircraft.hover_ring_hemisphere(freqs, bearings, ring, distance=70.0)

k = int(np.argmin(np.abs(freqs - 315.0)))
theta = np.linspace(0.0, 180.0, 361)
fig, (ax, ax2) = plt.subplots(1, 2, figsize=(12, 5.2))
ax.plot(np.append(bearings, 180.0), np.append(ring[:, k], ring[0, k]),
        marker="o")
ax.set(xlabel="Rumbo del anillo [°]  (0° morro, +90° estribor)",
       ylabel="Nivel de banda a 70 m [dB]")
for condition, label in ((None, "HIGE (del anillo)"),
                         ("out_of_ground_hover", "HOGE (+12 dB)"),
                         ("full_rpm_idle", "Ralentí a régimen pleno (-2,5 dB)"),
                         ("reduced_rpm_idle", "Ralentí a régimen reducido (-12 dB)")):
    h = hige if condition is None else aircraft.hover_derived_hemisphere(hige, condition)
    ax2.plot(theta, [aircraft.hemisphere_source_level(h, 0.0, t)[k] for t in theta],
             label=label)
ax2.set(xlabel="Ángulo polar θ [°]  (0° hacia proa → 180° hacia popa)",
        ylabel="Nivel de fuente a 70 m [dB]")
ax2.legend()
plt.show()
```

</details>

:::note[Los desplazamientos publicados y los distribuidos]
La guía advierte de que sus constantes salen de medidas con micrófonos
invertidos sobre placas en el suelo y pueden no valer para otros montajes; y
la base de datos pública de NORAH2 distribuye en realidad **+8/−10/−2 dB** en
el archivo de interpolación de cada tipo. Pásalos como `offset_db` para
reproducir el prototipo; las constantes publicadas de la Tabla 3 son los
valores por defecto. La prosa del Enfoque 3 deriva además el ralentí a
régimen pleno desde HOGE donde su propia tabla lo deriva desde HIGE, con
12 dB de separación; lo implementado es la tabla, respaldada por la base de
datos. Ambas divergencias están registradas en las
[erratas encontradas en fuentes publicadas](/phonometry/es/reference/errata/).
:::

El **rodaje** es una selección entre dos de estas fuentes, no una fuente
nueva: un helicóptero sin ruedas rueda en estacionario con efecto suelo (el
hemisferio HIGE), y uno con ruedas rueda sobre ellas con el rotor al ralentí
(el hemisferio de ralentí a régimen pleno). La frase de la guía los empareja
al revés («… in-ground hover and full-rpm idle respectively»); el
emparejamiento de aquí sigue la física de las dos operaciones (véase el
registro de erratas).

```python
con_ruedas = False                            # patines: el rodaje es un estacionario
taxi = ralenti_pleno if con_ruedas else hige  # guía p. 19 (véase la salvedad)
```

Un **evento** de estacionario o ralentí es el evento único estándar con una
trayectoria inmóvil: un hemisferio, una posición fija, el receptor de
interés. El hemisferio derivado lleva la `distance` del anillo, que toda la
cadena respeta como distancia de referencia; los hemisferios de estacionario
y de sobrevuelo registrados a distancias distintas se ejecutan como eventos
separados, que es también como la implementación de referencia cambia de
fuente por modo de operación.

```python
t = np.arange(0.0, 30.5, 0.5)                 # 30 s de estacionario a 3 m
track = np.column_stack([np.zeros_like(t), np.zeros_like(t),
                         np.full_like(t, 3.0)])
res = aircraft.rotorcraft_event_level([hige], [0.0], [0.0], t, track, (100.0, 0.0))
```

Lo que queda fuera de la implementación es el último recurso del §A.3.5
cuando no existe ningún dato de estacionario: corregir dos micrófonos
laterales de vuelo nivelado al círculo de estacionario de 150 m por el método
integrado del Anexo 16 de la OACI, una cadena de corrección de medida que
este módulo (que consume hemisferios ya reducidos) no modela.

## Cinemática de la trayectoria

`flight_path_kinematics` deriva de una trayectoria con marcas de tiempo, por
diferencias finitas centradas, todo lo que necesita el evento (Ec. 16-21 /
Doc 32 Ec. 8-10): velocidad respecto al suelo, velocidad aerodinámica (sin
viento), rumbo, curvatura, ángulo de alabeo $\Phi = \arctan(K V_\mathrm{g}^2 / g)$ y
ángulo de trayectoria $\gamma = \arctan(\Delta Z / \Delta S)$. La guía
recomienda suavizar las trazas radar
(p. ej. remuestreo con splines a cadencia de 0,5 s) antes de derivar.

```python
kin = aircraft.flight_path_kinematics(times, positions)   # posiciones (N, 3), m
kin.airspeed, kin.path_angle          # seleccionan el hemisferio por punto
kin.bank_angle                        # inclina el hemisferio en virajes
kin.plot()                            # perfiles de velocidad y ángulos
```

*La misma traza, derivada dos veces. El ángulo de alabeo se deriva de la
curvatura, que es una segunda derivada de la posición, así que unos pocos metros
de ruido radar con cadencia de 1 s convierten un viraje de 9,2° en 41° de
disparate, y el marco de emisión se inclina con ese ángulo, de modo que el error
se va derecho al nivel de fuente. Después de remuestrear con splines a 0,5 s la
traza muestra un único valle limpio a lo largo del viraje, con un máximo de 10°
frente a los 9,2° que pide la geometría. Suaviza antes de derivar.*

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

```python

from scipy.interpolate import UnivariateSpline
from phonometry import aircraft

rng = np.random.default_rng(20260808)
t_true = np.arange(0.0, 120.001, 0.1)
speed, radius, sigma = 30.87, 600.0, 4.0          # 60 kt, viraje de 600 m, ruido de 4 m
turn = np.clip((t_true - 40.0) / (radius / speed), 0.0, np.pi / 2)
xy = np.column_stack([np.cumsum(np.cos(turn)), np.cumsum(np.sin(turn))]) * speed * 0.1
t_radar = np.arange(0.0, 120.001, 1.0)
raw = np.column_stack([np.interp(t_radar, t_true, xy[:, 0]),
                       np.interp(t_radar, t_true, xy[:, 1]),
                       300.0 - 1.2 * t_radar]) + rng.normal(0.0, sigma, (121, 3))
t_fine = np.arange(0.0, 120.001, 0.5)
smooth = np.column_stack([
    UnivariateSpline(t_radar, raw[:, k], s=t_radar.size * sigma ** 2)(t_fine)
    for k in range(3)])

fig, axes = plt.subplots(1, 2, figsize=(12, 5.4))
for ax, times, pos in ((axes[0], t_radar, raw), (axes[1], t_fine, smooth)):
    aircraft.flight_path_kinematics(times, pos).plot(ax=ax, language="es")
plt.show()
```

</details>

## El evento único: SEL, LASmax y EPNL

`rotorcraft_event_level` ejecuta toda la cadena para un sobrevuelo en un
receptor: por punto de trayectoria la condición de vuelo selecciona (o mezcla)
los hemisferios, los ángulos de emisión direccionan el nivel de fuente (el
marco se orienta con el rumbo y se inclina con el alabeo en virajes; la actitud
de cabeceo está implícita en los hemisferios), y el historial recibido en
tercios de octava se expresa en tiempo registrado $t_\mathrm{r} = t_\mathrm{e} + r/c$ (Ec. 22,
$c = 346{,}1\ \text{m/s}$) y se integra: `LASmax`, `SEL` sobre el historial
completo y
sobre la ventana de certificación de 10 dB por debajo (Doc 32 Ec. 27), y `EPNL`
según Anexo 16 de la OACI (Doc 32 Ec. 28).

```python
res = aircraft.rotorcraft_event_level(
    hemispheres, speeds, angles,      # la base de datos
    times, positions,                 # la trayectoria (m, z hacia arriba)
    receiver=(120.0, 0.0),            # posición en tierra del micrófono
    ground=aircraft.RotorcraftGround(flow_resistivity="D"))   # hierba
res.la_max, res.sel, res.epnl         # LASmax, SEL, EPNL
res.plot()                            # el historial temporal LA(t)
```

Tres números del mismo evento, y no son intercambiables. El **LASmax** es un
instante: el mayor nivel ponderado A del historial, y nada sobre cuánto duró el
evento. El **SEL** integra el historial completo y lo normaliza a un segundo, así
que supera al LASmax en una cantidad que crece con la distancia oblicua y decrece
con la velocidad: para el sobrevuelo nivelado de más abajo, 60 kt a 150 m sobre
una separación lateral de 120 m, la diferencia es de 10,3 dB; a la mitad de
velocidad crece hasta 12,2 dB, y al doble de altura hasta 12,5 dB. El **EPNL** es
otro cantar: se construye con espectros ponderados en noys y una corrección tonal
sobre la ventana de 10 dB por debajo (Doc 32 Ec. 28, ejecutando la
[cadena del Apéndice 2 del Anexo
16](/phonometry/es/aircraft/aircraft-noise/)), así que no es comparable con
ninguno de los otros dos: para ese mismo evento marca 91,2 EPNdB frente a un SEL
de 88,8 dB(A). El LASmax y el SEL se usan para contornos y para estudios de
molestia, y el EPNL solo contra niveles de certificación.

La forma del historial también merece leerse. Cada emisión se estampa en el
tiempo registrado $t_\mathrm{r} = t_\mathrm{e} + r/c$, y $r$ es mayor lejos del punto de máxima
aproximación y menor en él, así que el historial recibido se comprime a la ida y
se estira a la vuelta: la curva es asimétrica antes de que intervenga
directividad ninguna. Dónde cae entonces el LASmax depende del hemisferio: con el
de más abajo, ligeramente lobulado hacia delante, llega aproximadamente un
segundo *antes* que el sonido emitido en el punto de máxima aproximación, porque
la dirección de emisión más ruidosa se alcanza un poco antes del través. Esa es
la razón por la que el método lleva un hemisferio y no un nivel de fuente.

*Dos asimetrías en una misma curva, y ninguna es un artefacto del modelo. El
$L_\mathrm{AS,max}$ = 78,5 dB(A) llega a los 64,56 s, un segundo entero **antes** que
el sonido emitido en el punto de máxima aproximación (65,56 s), porque la
dirección más ruidosa del hemisferio se alcanza un poco antes del través. Y la
ventana de 10 dB por debajo ocupa 9,4 s por delante del máximo y 11,5 s por
detrás: el tiempo retardado comprime la aproximación y estira el alejamiento.
Integrar sus 20,9 s enteros y normalizar a un segundo da SEL = 88,8 dB(A),
10,3 dB por encima del máximo que marcaría el sonómetro.*

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

```python

from phonometry import aircraft

# Hemisferio sintético tipo helicóptero en la malla estándar de 31 bandas y 10°.
freqs = 1000.0 * 10.0 ** (np.arange(-20, 11) / 10.0)   # tercios 10 Hz-10 kHz
az = np.arange(-90.0, 91.0, 10.0)
po = np.arange(0.0, 181.0, 10.0)
spectrum = 88.0 - 12.0 * np.log10(freqs / 100.0) ** 2   # joroba en baja-media
levels = (spectrum[None, None, :] - 0.045 * np.abs(po - 80.0)[None, :, None]
          - 0.02 * np.abs(az)[:, None, None])
h = aircraft.RotorcraftHemisphere(freqs, az, po, levels)

speed = 30.87                                           # 60 kt, en m/s
t = np.arange(0.0, 130.01, 0.5)
track = np.column_stack([np.zeros_like(t), speed * (t - 65.0),
                         np.full_like(t, 150.0)])
event = aircraft.rotorcraft_event_level(
    [h], [speed], [0.0], t, track, (120.0, 0.0),
    ground=aircraft.RotorcraftGround(flow_resistivity="D"))
event.plot(language="es")
plt.show()
```

</details>

Los flujos de trabajo con trazas radar pueden pasar directamente los valores
suavizados por punto de `airspeed`, `path_angle`, `heading` y `bank_angle` de un
`RotorcraftTrackState` en lugar de derivarlos de las posiciones; cuando se
derivan, la trayectoria está en metros y segundos, así que la base de datos de
velocidades debe estar entonces en m/s.

## Contornos en malla de tierra

`rotorcraft_noise_contour` evalúa el mismo evento sobre una malla completa en
una pasada vectorizada por paso de emisión y reduce el historial de cada
receptor a la huella `SEL` (`metric="exposure"`) o `LASmax`
(`metric="maximum"`):

```python

from phonometry import aircraft

res = aircraft.rotorcraft_noise_contour(
    hemispheres, speeds, angles, times, positions,
    x=np.linspace(-2000.0, 2000.0, 81),
    y=np.linspace(-3000.0, 3000.0, 121),
    metric="exposure",
    ground=aircraft.RotorcraftGround(flow_resistivity="D"))
res.plot()                            # contornos SEL rellenos
```

El suelo puede variar entre receptores sin un modelo de elevación completo:
`flow_resistivity` y `ground_elevation` del `RotorcraftGround` aceptan un valor
por punto de malla (forma `(len(y), len(x))`), y el modelo de dos rayos de cada
receptor usa entonces sus valores locales.

*El entregable de todo el método, y lo que le hace el suelo. Sobre pasto uniforme
la huella es una banda a lo largo de la traza, con la anchura que le fijan el
hemisferio y la distancia oblicua. Dale a esa misma pasada una franja dura de
600 m cruzando la traza, una plataforma, una pista, un lago, y todos los
contornos se escalonan hacia fuera dentro de ella, porque la reflexión deja de
absorberse. Ese escalón es el mapa de `flow_resistivity` por receptor haciendo su
trabajo; un estudio que deje puesto el `"G"` por defecto está dibujando la imagen
de la derecha en todas partes.*

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

```python

from phonometry import aircraft

x = np.linspace(-1500.0, 1500.0, 61)
y = np.linspace(-2000.0, 2000.0, 81)
sigma = np.where(np.abs(y)[:, None] < 300.0, 20.0e6,        # la franja dura
                 np.full((y.size, x.size), 200.0e3))        # pasto en el resto

fig, axes = plt.subplots(1, 2, figsize=(11, 6.4))
for ax, ground in ((axes[0], aircraft.RotorcraftGround(flow_resistivity="D")),
                   (axes[1], aircraft.RotorcraftGround(flow_resistivity=sigma))):
    res = aircraft.rotorcraft_noise_contour(
        [h], [speed], [0.0], times, positions, x=x, y=y,
        metric="exposure", ground=ground)
    res.plot(ax=ax, language="es")        # la gráfica va en kilómetros
    ax.plot(positions[:, 0] / 1000.0, positions[:, 1] / 1000.0, "k--", lw=1.4)
plt.show()
```

</details>

## Terreno: el plano medio del suelo y el apantallamiento

La Doc 32, 1.ª ed., supone terreno plano. La guía de modelado NORAH2 de EASA
(Olsen et al. 2024), que el resto de esta página sigue a nivel de ecuación, añade
la maquinaria para emplazamientos reales. Una sección vertical variable se
representa por su **plano medio del suelo** (Ec. 36-40), la recta de mínimos
cuadrados a través de la polilínea del terreno calculada en forma cerrada; la
fuente y el receptor entran en las ecuaciones de suelo plano con sus **alturas
equivalentes**, medidas ortogonalmente a ese plano y con una cota inferior de
0,1 m. El suelo que cambia de tipo a lo largo del camino promedia su
resistividad de flujo por el logaritmo, ponderada por la longitud de cada
segmento (Ec. 41).

*Por qué existe la construcción. El receptor está a 1,2 m sobre el terreno, pero
el terreno bajo él está 4 m por encima del plano que ajusta la sección, así que
su altura equivalente es de 5,1 m, y el efecto de suelo es función de las alturas
equivalentes, no de las verdaderas. Cuatro metros de relieve local mueven el
primer mínimo de interferencia una octava. La cota inferior de 0,1 m existe para
el caso contrario, un receptor en una hondonada por encima de la cual pasa el
plano ajustado.*

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

```python

from phonometry import aircraft

d = np.linspace(0.0, 800.0, 33)
z = 0.035 * d + 6.0 * np.sin(d / 90.0) + 2.5 * np.sin(d / 31.0)
plane = aircraft.mean_ground_plane(d, z)
m, c = float(plane.slope), float(plane.intercept)

def equivalent(point):
    """Altura medida ortogonalmente al plano ajustado, con mínimo de 0,1 m."""
    return max(abs(point[1] - m * point[0] - c) / np.hypot(1.0, m), 0.1)

src, rcv = (0.0, 60.0), (800.0, float(z[-1]) + 1.2)
freqs = 1000.0 * 10.0 ** (np.arange(-13, 11) / 10.0)
fig, (ax, ax2) = plt.subplots(2, 1, figsize=(10, 8))
plane.plot(ax=ax, language="es")
for heights, style in (((src[1] - z[0], 1.2), "--"),
                       ((equivalent(src), equivalent(rcv)), "-")):
    ax2.semilogx(freqs, aircraft.ground_effect_adjustment(
        freqs, heights[0], heights[1], rcv[0], flow_resistivity="D"), style)
plt.show()
```

</details>

Cuando el terreno bloquea la línea de visión, el sonido sigue el camino
convexo más corto sobre él (la guía NORAH2 lo llama la goma elástica) y cada
vértice tocado es una **arista de difracción**. La atenuación combina la
difracción pura de la diferencia de camino $\delta$ (Ec. 42-44,
$10 C_h \log_{10}\bigl(3 + (40/\lambda)\,C''\,\delta\bigr)$, con tope de 25 dB) con
los efectos de suelo
del lado fuente y del lado receptor, cada uno sobre su propio plano medio y
ponderado por la difracción de su camino imagen (Ec. 45-47, el esquema
CNOSSOS-EU que adopta la guía NORAH2). El efecto de suelo no se evalúa por
separado en ese régimen.

Si todo eso merece o no la pena es una cuestión de longitudes de onda. El
apantallamiento lo gobierna la diferencia de camino sobre el obstáculo medida en
longitudes de onda, la atenuación crece con $\delta/\lambda$ y tiene un tope de
25 dB, así que un metro de diferencia de camino vale poco en 100 Hz y muchísimo
en 4 kHz. Para un helicóptero la geometría rara vez aparece: la fuente está
normalmente a cientos de metros de altura, así que la línea de visión solo se
rompe para receptores muy apartados a un lado o detrás de crestas, que es justo
donde los niveles ya son bajos. Cerca de la traza, el terreno actúa a través del
plano medio y de las alturas equivalentes de más arriba, no por apantallamiento.
La regla práctica es ejecutar primero una sección con
`terrain_screening_adjustment` en el receptor de peor pinta, y pagar el modelo de
elevaciones completo solo si esa sección resulta estar apantallada.

```python
from phonometry import diffraction_attenuation

bandas = [63.0, 250.0, 1000.0, 4000.0]
diffraction_attenuation(bandas, 0.0, edge_height=2.5)   # incidencia rasante
diffraction_attenuation(bandas, 1.0, edge_height=2.5)   # 1 m dentro de la sombra
```

*Un único número cuenta toda la historia solo cuando la longitud de onda y
$C_h$ están fijos a la vez: la curva de 4 kHz ya está en el tope de 25 dB en
$\delta$ = 0,68 m, la de 1 kHz todavía sigue subiendo en $\delta$ = 1,5 m
(22,5 dB, por debajo del tope), y la de 63 Hz solo llega a 7,2 dB ahí, frenada
por su propio $C_h$ = 0,63 (Ec. 43, $h_0$ = 2,5 m) además de la longitud de
onda mayor. En incidencia rasante las tres bandas con $C_h$ = 1 cruzan
4,8 dB, pero la de 63 Hz cruza en 3,0 dB, porque ese valor también depende
de $C_h$, no solo de la longitud de onda.*

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

```python

from phonometry import diffraction_attenuation

bandas = np.array([63.0, 250.0, 1000.0, 4000.0])
delta = np.linspace(-0.4, 1.5, 441)
ld = np.array([diffraction_attenuation(bandas, float(d), edge_height=2.5)
              for d in delta])

fig, ax = plt.subplots(figsize=(9, 6))
for i, etiqueta in enumerate(["63 Hz", "250 Hz", "1 kHz", "4 kHz"]):
    ax.plot(delta, ld[:, i], label=etiqueta)
ax.axvline(0.0, color="0.5", linestyle="--")
ax.axhline(25.0, color="0.5", linestyle=":", label="tope de 25 dB")
ax.set(xlabel="Diferencia de camino δ [m]",
       ylabel="Atenuación por difracción ΔLd [dB]")
ax.legend()
plt.show()
```

</details>

`mean_ground_plane`, `mean_flow_resistivity` y `diffraction_attenuation`
exponen las piezas; `terrain_screening_adjustment` ejecuta la sección
completa:

```python

from phonometry import aircraft

d = [0.0, 150.0, 260.0, 300.0, 340.0, 420.0, 600.0]     # distancias de sección
z = [0.0, 4.0, 48.0, 62.0, 40.0, 8.0, 2.0]              # alturas del terreno
freqs = 1000.0 * 10.0 ** (np.arange(-13, 11) / 10.0)
res = aircraft.terrain_screening_adjustment(
    freqs, source=(0.0, 90.0), receiver=(600.0, 3.2), distances=d, heights=z,
    flow_resistivity="D")
res.screened, res.path_difference      # True, la delta de la goma elástica
res.adjustment                         # por banda, sustituye al ΔLg plano
res.plot()                             # la geometría de la sección
```

*La colina vale 0,766 m de diferencia de camino, y ese único número es todo el
espectro. Es una novena parte de la longitud de onda en 50 Hz y casi nueve
longitudes de onda en 4 kHz, así que la curva apantallada marca −7,3 dB en la
banda más baja y satura cerca del tope de 25 dB a partir de unos 2,5 kHz:
23,9 dB en 4 kHz. Leída frente al peine de suelo plano, la colina gana 12,3 dB
en 50 Hz y unos 25 dB en lo alto del intervalo. El apantallamiento por terreno
es un instrumento de alta frecuencia, y no arregla una queja de baja
frecuencia.*

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

```python

from phonometry import aircraft

freqs = 1000.0 * 10.0 ** (np.arange(-13, 11) / 10.0)   # tercios 50 Hz-10 kHz
d = np.array([0.0, 150.0, 260.0, 300.0, 340.0, 420.0, 600.0])
z = np.array([0.0, 4.0, 48.0, 62.0, 40.0, 8.0, 2.0])
res = aircraft.terrain_screening_adjustment(
    freqs, (0.0, 90.0), (600.0, 3.2), d, z, flow_resistivity="D")
flat = aircraft.ground_effect_adjustment(freqs, 90.0, 1.2, 600.0,
                                         flow_resistivity="D")

fig, (ax, ax2) = plt.subplots(2, 1, figsize=(9, 7))
res.plot(ax=ax, language="es")
ax2.axhline(0.0, color="0.5", linewidth=1.0)
ax2.semilogx(freqs, flat, ls="--", marker="s", markersize=3,
             label="Suelo plano (sin colina)")
ax2.semilogx(freqs, res.adjustment, marker="o", markersize=3,
             label="Apantallado por la colina (Ec. 45-47)")
ax2.set(xlabel="Frecuencia central de banda de tercio de octava [Hz]",
        ylabel="Ajuste de suelo y apantallamiento [dB]")
ax2.grid(True, which="both", alpha=0.3)
ax2.legend()
plt.show()
```

</details>

El evento y el contorno funcionan sobre emplazamientos reales pasando un
modelo digital de elevaciones en el `RotorcraftGround`: `terrain=(x, y, z)` en
el marco de la trayectoria. Cada par emisión-receptor muestrea entonces su propia sección
vertical con paso `terrain_resolution` (por defecto, el tamaño de celda del
modelo) y la evalúa con la maquinaria anterior; el suelo del receptor sale
del modelo. El coste crece con puntos de trayectoria por puntos de malla, así
que con terreno conviene mantener mallas de contorno moderadas.

```python
res = aircraft.rotorcraft_event_level(
    hemispheres, speeds, angles, times, positions, receiver=(1200.0, 300.0),
    ground=aircraft.RotorcraftGround(terrain=(tx, ty, tz),
                                     flow_resistivity="D"))
```

## Validación

La implementación está anclada en la Tabla 4 de la guía NORAH2 (las 31 bandas),
la divergencia inversa al cuadrado de forma cerrada, los límites analíticos de
suelo rígido y rasante del efecto de suelo, consultas bilineales fuera de nodo
sobre los hemisferios de referencia de los once tipos de helicóptero, símplices
de interpolación comprobados a mano, cinemática de forma cerrada y la integral
lorentziana $\mathrm{SEL} - L_\mathrm{ASmax}$ del sobrevuelo. De extremo a extremo
reproduce los casos ARP de verificación del prototipo NORAH2:

| Magnitud | Oráculo | Coincidencia |
|---|---|---|
| Niveles de banda, 31 bandas | Tabla 4 de la guía NORAH2 | exacta |
| Ángulos de emisión | prototipo NORAH2, casos ARP | 0,01° |
| Tiempo retardado | el mismo | 0,02 s |
| Nivel por paso, suelo duro, hasta 18 km | el mismo | 0,08 dB(A) |
| $L_\mathrm{ASmax}$ | el mismo | 0,03 dB |
| `SEL` | el mismo | 0,05 dB en suelo duro, 0,4 dB en blando |
| Malla de contorno, 187 micrófonos | caso 3 ARP | 0,7 dB en el peor caso |
| $\mathrm{PNLTM}$ | el mismo | 0,1 dB |
| `EPNL` | el mismo | ≈ 1,3 dB (véase más abajo) |
| `LASmax`/`SEL` de estacionario, 8 micrófonos | caso 4 ARP (estacionario sin efecto suelo) | 0,45 dB en el peor caso |

:::note[Dos salvedades, no redondeo]
La coincidencia del **EPNL** es un orden de magnitud más floja que todo lo demás
porque el prototipo aplica una política de ruidosidad percibida por debajo de
la cota inferior de noys que difiere de la ley publicada del Anexo 16; la
divergencia está en la referencia, no en la cadena.

A gran distancia sobre **suelo blando** el prototipo amortigua la interferencia
coherente de dos rayos de la Ec. 30 de la guía hacia la suma incoherente, cosa
que llega a 4,9 dB en pasos individuales de bajo nivel más allá de 7 km. Ni la
Doc 32 ni la guía NORAH2 contienen tal término, así que esta implementación sigue
las ecuaciones publicadas y no lo reproduce.
:::

La derivación de la Tabla 3 está anclada además en forma cerrada (la
identidad anillo-borde, bins de φ constante calculados a mano, los
desplazamientos exactos del Enfoque 3), y la fila de estacionario de la tabla
es el caso de estacionario sin efecto suelo del prototipo reproducido con las
convenciones del propio prototipo pasadas explícitamente: `mapping="bearing"`
y su corrección de base de datos de +8 dB como `offset_db`. Sus ocho
micrófonos abarcan rumbos, elevaciones, alturas de receptor de 0,01 m a
8,2 m y cuatro resistividades de flujo; el residuo culmina en los micrófonos del nadir,
donde la interferencia amortiguada del prototipo más se aparta de la Ec. 30
coherente publicada.

La maquinaria de terreno está anclada en forma cerrada: el plano medio del
suelo es exacto en perfiles lineales y simétricos, una sección plana
reproduce el modelo de suelo plano a precisión de máquina y un plano
inclinado su rotación analítica, la resistividad media logarítmica recupera
la media geométrica, la difracción rasante da el clásico $10\log_{10} 3$, y
una colina comprobada a mano fija la diferencia de camino de la goma
elástica. El tratamiento del suelo por receptor valida de extremo a extremo
contra el caso 3 ARP del prototipo (187 micrófonos, cada uno sobre su propia
elevación: cada nivel por paso a 0,08 dB(A), `SEL`/`LASmax` a 0,05 dB, la
malla de contorno a 0,15 dB) y la malla de suelo mixto del caso 2 se
reproduce en una sola llamada con resistividad por receptor. La versión
pública del prototipo no incluye un caso de apantallamiento reconstruible
(el marco de su modelo de terreno no pudo fijarse a las salidas publicadas),
así que la cadena de difracción se ancla en las formas cerradas y en su
filiación CNOSSOS-EU.

## Qué cubre esta guía

El método del hemisferio de ECAC Doc 32 y la guía a nivel de ecuación NORAH2
de EASA (§A.3-A.5): el hemisferio de ruido y su consulta bilineal
(`RotorcraftHemisphere`, `hemisphere_source_level`), los tres ajustes de
propagación (`spherical_spreading_adjustment`, `atmospheric_adjustment`,
`ground_effect_adjustment`, el modelo de Chien-Soroka con la impedancia de
Delany-Bazley y las clases de resistividad de flujo CNOSSOS), la interpolación
entre condiciones de vuelo (`flight_condition_weights`,
`interpolated_source_level`), la derivación de fuente de estacionario, ralentí
y rodaje de la Tabla 3 (`hover_ring_hemisphere`, `hover_derived_hemisphere`),
la cinemática de la trayectoria (`flight_path_kinematics`), el SEL, LASmax y
EPNL de evento único (`rotorcraft_event_level`), los contornos en malla de
tierra (`rotorcraft_noise_contour`) y la cadena de plano medio del suelo y
apantallamiento por terreno (`mean_ground_plane`, `mean_flow_resistivity`,
`diffraction_attenuation`, `terrain_screening_adjustment`). Validado contra la
Tabla 4 de la guía NORAH2, límites geométricos y rasantes en forma cerrada, y
de extremo a extremo contra los casos de verificación ARP del prototipo
NORAH2, incluida su operación de estacionario sin efecto suelo.

El recurso del §A.3.5 para un tipo sin ningún dato de estacionario (dos
micrófonos laterales de vuelo nivelado corregidos al círculo de estacionario
de 150 m por el método integrado del Anexo 16 de la OACI) es una cadena de
corrección de medida que este módulo, que consume hemisferios ya reducidos, no
modela. **Con phonometry no se distribuye ninguna base de datos de
hemisferios, y no hay lector de ficheros NORAH**: el método está implementado,
los datos no, así que los arrays salen de una campaña de medida o de la base
de datos de referencia NORAH2 y los monta quien lee. La refracción por
gradientes atmosféricos queda fuera de la propia Doc 32, igual que la
interpolación entre condiciones de vuelo, que sigue en su lugar la guía
NORAH2.

## Véase también

- [Ruido de aeronaves: nivel efectivo de ruido percibido](/phonometry/es/aircraft/aircraft-noise/):
  la cadena del Apéndice 2 del Anexo 16 de la OACI a la que llama el término de la
  Ec. 28 de la Doc 32 de esta página, y la práctica de medida que sigue una
  campaña de hemisferios.
- [Ruido de aeropuertos (ECAC Doc 29)](/phonometry/es/aircraft/airport-noise/): el
  método de contornos de ala fija, donde una tabla nivel-potencia-distancia hace
  el papel que aquí hace el hemisferio de ruido.
- [Propagación exterior](/phonometry/es/environment/propagation/outdoor-propagation/):
  el efecto de suelo de ISO 9613-2 y las clases de suelo CNOSSOS que comparte este
  ajuste de dos rayos.
- Referencia de la API: [`aircraft.rotorcraft_noise`](/phonometry/es/reference/api/aeroacoustics/rotorcraft-noise/).
