Skip to content

Airport Noise (ECAC Doc 29)

Standards: ECAC.CEAC Doc 29SAE AIR 5662

Certification measures one aeroplane at one reference point on one flight; an airport study asks a different question: how loud is it there, on that street, for that departure. The ECAC Doc 29 method answers it without re-measuring anything. The aeroplane arrives as a noise-power-distance (NPD) table, a measured event level against slant distance for a handful of engine power settings; the flight arrives as a path of segments; and the method corrects the NPD baseline segment by segment for everything the tables could not know: the atmosphere on the day, the finite length of each segment, how far off to the side the receiver sits, where the engines are mounted, and the rearward lobe of a jet still on the runway.

This page covers that chain end to end, from the NPD interpolation to the ground-grid contour, and it is validated against the reference workbook of Doc 29 5th ed. Vol. 3. The certification metric these tables ultimately come from, the EPNL of ICAO Annex 16, is Aircraft noise; the physical propagation ingredients (ground effect, atmospheric absorption, barriers) are Outdoor sound propagation.

The ECAC Doc 29 airport-noise method describes an aircraft with noise-power- distance (NPD) tables. npd_level reads the event level (/SEL) for an arbitrary power and distance, interpolating linearly in power (Eq. 4-3) and log-linearly in slant distance (Eq. 4-4).

Noise-power-distance curves for two engine power settings, the event level falling log-linearly with slant distance between the tabulated nodesNoise-power-distance curves for two engine power settings, the event level falling log-linearly with slant distance between the tabulated nodes

The markers are the only distances the table asserts; everything between them is the interpolation. The two curves are close to parallel — 8.7 to 9.2 dB apart over the whole range — so thrust mostly shifts the level while distance sets the shape, and the fall is 21.7 dB per decade of slant distance rather than the 20 dB of pure divergence, the excess being the reference atmosphere’s absorption. Power is interpolated linearly, not in decibels: 16 000 N reads 88.15 dB at 1 km, exactly halfway between the two tabulated rows.

Show the code for this figure
import matplotlib.pyplot as plt
from phonometry import aircraft
# A schematic NPD table: SEL vs slant distance for two thrust settings.
powers = [12000.0, 20000.0]
distances = [200.0, 400.0, 630.0, 1000.0, 2000.0, 4000.0, 6300.0, 10000.0]
levels = [[98.5, 92.0, 88.2, 83.6, 76.8, 69.4, 63.9, 56.8],
[107.2, 100.9, 97.2, 92.7, 86.0, 78.5, 72.9, 65.6]]
fig, ax = plt.subplots()
for p in (20000.0, 12000.0):
curve = aircraft.npd_curve(powers, distances, levels, power=p)
line, = ax.semilogx(curve.distance, curve.level, label=f"P = {p:.0f} N")
ax.semilogx(curve.table_distances, curve.table_levels, "o", markersize=4,
color=line.get_color())
ax.set(xlabel="Slant distance [m]", ylabel="Event level [dB]",
title="Noise-power-distance curves (ECAC Doc 29)")
ax.grid(True, which="both", alpha=0.3)
ax.legend()
plt.show()
from phonometry import aircraft
powers = [12000.0, 20000.0]
distances = [200.0, 400.0, 1000.0, 2000.0, 6300.0, 10000.0]
levels = [[98.5, 92.0, 83.6, 76.8, 63.9, 56.8],
[107.2, 100.9, 92.7, 86.0, 72.9, 65.6]]
aircraft.npd_curve(powers, distances, levels, power=20000.0).plot()

This is the NPD engine underneath the method.

A tabulated NPD level is not a free-standing measurement. It is the event level of that aeroplane in steady straight flight along an infinite path, at a fixed reference condition (§2.5): sea-level pressure, the average atmospheric attenuation rates of Appendix D Table D-1, no precipitation, wind below 8 m/s, a 160 kn groundspeed for the exposure levels, and flat acoustically soft ground with the microphone 1.2 m above it. Those attenuation rates are arithmetic averages over European and American certification campaigns, so the reference day is a notional atmosphere — Doc 29 calls it the AIR-1845 atmosphere — rather than a temperature and a humidity you could quote back. Every correction in section 2 exists to undo one of those assumptions.

The tables may be used as tabulated while the site average stays inside the §2.5 envelope: air temperature below 30 °C, the product of temperature in degrees Celsius and relative humidity in percent above 500, and wind below 8 m/s. Outside it the tables themselves have to be converted by the Appendix D procedure, which this chain does not do — impedance_adjustment only rescales the levels for the specific acoustic impedance of the air, and there is no humidity argument anywhere in the chain.

Read the curves accordingly. Between the markers the level falls close to linearly against the logarithm of distance and steepens at long range as absorption accumulates; the markers are the only distances at which the table asserts anything, everything between them is the Eq. 4-4 interpolation, and everything beyond the last node is a straight-line extrapolation of the terminal slope. Do not query below the recommended 30 m floor, where a whole aeroplane stops behaving like a point source. One restriction runs under all of it: the SAE AIR 5662 lateral attenuation, and therefore the single-event chain that uses it, is derived for acoustically soft, grassy ground, so a contour drawn over water, an apron or dense urban surfaces is outside the method as published.

The flight arrives as a path of points, and the method cuts it into segments. path is an array:

ColumnQuantityUnits
0, along the runway centre linem
1, across it, positive to starboardm
2, height above the aerodromem
3power, the NPD power parameterwhatever the table is indexed by
4true airspeedm/s

points make segments, so every per-segment mask carries entries — which is why the ground-roll masks below read xs[:-1] < 1500.0 and not xs < 1500.0. Two defaults decide more than they look like they do. The speed column enters the duration correction relative to reference_speed, whose default is 82.31 m/s, the Doc 29 160 kn reference, so a path flown at exactly that speed carries no duration correction at all — which is the case in every example on this page, and varying the column is how you see the term work. And mounting= defaults to "wing": that is a property of the aeroplane rather than a modelling preference, so pass "fuselage" or "propeller" explicitly, because a propeller takes no installation correction at all and leaving the default in place gives it a directivity it does not have. Where a real type’s mounting and power units come from is The ANP fleet database.

Each segment starts from a baseline level read from the NPD table at that segment’s power and distance, and is corrected term by term:

FunctionSymbolDoc 29What it accounts for
impedance_adjustment§4.2.1the air at the aerodrome against the 409.81 N·s/m³ reference impedance of the tables
duration_correction§4.5.1the segment speed against the NPD reference speed (exposure levels only)
engine_installation_correction§4.5.3where the engines are mounted, through the depression angle
lateral_attenuation§4.5.4ground effect and refraction for a receiver off to the side
noise_fraction§4.5.6the share of the infinite-path energy a finite segment contributes (exposure levels only)
start_of_roll_directivity§4.5.7the rearward jet lobe behind a take-off ground-roll segment

is the elevation angle of the propagation path above the ground line at the receiver and the perpendicular distance from the receiver to the ground track; is the bank angle, positive with the starboard wing up, and the depression angle in the aircraft frame, plus for observers to starboard and minus to port (§4.5.2). Along the track, is the segment length, the distance from the segment start to the foot of the perpendicular (negative behind the segment), the perpendicular distance to the extended segment and the shortest distance to the segment itself (§4.4.1). is the azimuth from the nose that the start-of-roll lobe is a function of.

Four panels of ECAC Doc 29 flight-path segment geometry drawn to scale. Panel a, observer alongside a 464 m climbing segment: the infinite flight path dashed with the finite segment bold between S1 and S2, the perpendicular foot Sp, q = 214 m from S1, the minimum slant range dp = 526 m down to the observer O and the distances d1 = 568 m and d2 = 582 m to the segment ends; a note says that with q between 0 and the segment length the NPD lookup for an exposure level uses dp. Panel b, observer behind the segment with q = -300 m: the perpendicular foot falls on the extension ahead of the observer, dp = 520 m is dashed and the shortest distance ds equals d1 = 600 m, with a note that the lookup uses ds behind a take-off ground roll and dp elsewhere. Panel c, the plane normal to the flight path: the aircraft with its wing plane banked 15 degrees, the propagation path down to a receiver 1.2 m above the ground line, the elevation angle beta of 18 degrees at the receiver and the depression angle phi equal to beta plus epsilon, 33 degrees, at the aircraft. Panel d, plan view behind the take-off roll: the runway with a jet at the start of roll, dashed rearward arcs, the azimuth psi of 120 degrees measured from the nose and the observer at the distance dSOR. Footer states that phi is beta plus epsilon to starboard and beta minus epsilon to port, that psi runs from 90 degrees abeam to 180 degrees astern with the jet lobe peaking near 120 degrees, and that the NPD lookup uses dp for exposure levels and ds for maximum levels, floored at 30 m, with the start-of-roll term scaled by 762 m over dSOR beyond 762 mFour panels of ECAC Doc 29 flight-path segment geometry drawn to scale. Panel a, observer alongside a 464 m climbing segment: the infinite flight path dashed with the finite segment bold between S1 and S2, the perpendicular foot Sp, q = 214 m from S1, the minimum slant range dp = 526 m down to the observer O and the distances d1 = 568 m and d2 = 582 m to the segment ends; a note says that with q between 0 and the segment length the NPD lookup for an exposure level uses dp. Panel b, observer behind the segment with q = -300 m: the perpendicular foot falls on the extension ahead of the observer, dp = 520 m is dashed and the shortest distance ds equals d1 = 600 m, with a note that the lookup uses ds behind a take-off ground roll and dp elsewhere. Panel c, the plane normal to the flight path: the aircraft with its wing plane banked 15 degrees, the propagation path down to a receiver 1.2 m above the ground line, the elevation angle beta of 18 degrees at the receiver and the depression angle phi equal to beta plus epsilon, 33 degrees, at the aircraft. Panel d, plan view behind the take-off roll: the runway with a jet at the start of roll, dashed rearward arcs, the azimuth psi of 120 degrees measured from the nose and the observer at the distance dSOR. Footer states that phi is beta plus epsilon to starboard and beta minus epsilon to port, that psi runs from 90 degrees abeam to 180 degrees astern with the jet lobe peaking near 120 degrees, and that the NPD lookup uses dp for exposure levels and ds for maximum levels, floored at 30 m, with the start-of-roll term scaled by 762 m over dSOR beyond 762 m

They assemble into a segment exposure level as

where the bracket is the impedance-adjusted NPD baseline of §4.2.1 and the rest is Eq. 4-8b (Eq. 4-9b behind a ground roll), and the event level is the energy sum over the segments, (Eq. 4-11). A maximum level takes the largest segment value instead (Eq. 4-10) and drops the two terms that exist only for exposure, and .

Their magnitudes are worth carrying, because they decide which term matters for a given receiver.

Four panels of the ECAC Doc 29 per-segment corrections. Panel a, the engine-installation correction against depression angle from 0 to 180 degrees: the wing-mounted curve runs from -1.5 dB at 0 degrees up to +0.4 dB near 50 degrees and back, the fuselage-mounted curve from -3.0 dB at 0 degrees to 0 dB at 90 degrees, and the propeller curve is flat at zero. Panel b, the lateral attenuation subtracted, against elevation angle for lateral offsets of 100, 300 and 914 metres: the 914 m curve starts at 10.9 dB at grazing incidence and all three fall to exactly zero above 50 degrees. Panel c, the noise fraction in decibels against q over lambda for a 464 m and a 2000 m segment at a scaled distance of 132 m, with the region where the observer is alongside the segment shaded and the correction always negative. Panel d, the duration correction against segment speed, crossing zero at the 82.3 metres per second reference and reaching about plus 1.4 dB at 60 and minus 0.9 dB at 100 metres per second, with a box giving the impedance adjustment at 15 degrees Celsius and 101.3 kilopascals as plus 0.07 dB, at 30 degrees as minus 0.04 dB and at 95 kilopascals as minus 0.21 dBFour panels of the ECAC Doc 29 per-segment corrections. Panel a, the engine-installation correction against depression angle from 0 to 180 degrees: the wing-mounted curve runs from -1.5 dB at 0 degrees up to +0.4 dB near 50 degrees and back, the fuselage-mounted curve from -3.0 dB at 0 degrees to 0 dB at 90 degrees, and the propeller curve is flat at zero. Panel b, the lateral attenuation subtracted, against elevation angle for lateral offsets of 100, 300 and 914 metres: the 914 m curve starts at 10.9 dB at grazing incidence and all three fall to exactly zero above 50 degrees. Panel c, the noise fraction in decibels against q over lambda for a 464 m and a 2000 m segment at a scaled distance of 132 m, with the region where the observer is alongside the segment shaded and the correction always negative. Panel d, the duration correction against segment speed, crossing zero at the 82.3 metres per second reference and reaching about plus 1.4 dB at 60 and minus 0.9 dB at 100 metres per second, with a box giving the impedance adjustment at 15 degrees Celsius and 101.3 kilopascals as plus 0.07 dB, at 30 degrees as minus 0.04 dB and at 95 kilopascals as minus 0.21 dB

The lateral attenuation is the big term and the only one that can reach double figures: is 10.86 dB at grazing incidence and identically zero above an elevation angle of 50°, while the distance factor ramps from zero on the ground track to unity at 914 m — so a receiver under the track gets none of it and one 900 m to the side at low elevation gets all of it. The installation term is worth 1.5 dB between a wing and a fuselage mounting at and nothing at all for a propeller. The duration correction is , so a departure flown at 100 m/s loses 0.85 dB of exposure per segment. The noise fraction is always negative and vanishes only for an infinite segment. The impedance adjustment is +0.07 dB at the standard atmosphere and rarely leaves a few tenths.

Show the code for this figure
import matplotlib.pyplot as plt
import numpy as np
from phonometry import aircraft
fig, ((ax_i, ax_l), (ax_f, ax_v)) = plt.subplots(2, 2, figsize=(11, 8))
phi = np.linspace(0.0, 180.0, 361)
for mounting in ("wing", "fuselage", "propeller"):
ax_i.plot(phi, [aircraft.engine_installation_correction(p, mounting)
for p in phi], label=mounting)
beta = np.linspace(0.0, 90.0, 361)
for ell in (100.0, 300.0, 914.0):
ax_l.plot(beta, [aircraft.lateral_attenuation(b, ell) for b in beta],
label=f"l = {ell:.0f} m")
# The scaled distance of Eq. 4-20 for this NPD table at dp = 526 m.
powers, distances = [8000.0, 12000.0], [60.0, 240.0, 960.0, 3840.0]
sel = [[98.0, 86.0, 74.0, 62.0], [104.0, 92.0, 80.0, 68.0]]
lmax = [[94.0, 82.0, 70.0, 58.0], [100.0, 88.0, 76.0, 64.0]]
base = float(aircraft.npd_level(powers, distances, sel, 12000.0, 526.0)[0])
peak = float(aircraft.npd_level(powers, distances, lmax, 12000.0, 526.0)[0])
d_lambda = (2 / np.pi) * 82.3 * 10 ** ((base - peak) / 10)
frac = np.linspace(-1.0, 2.0, 601)
for length in (464.0, 2000.0):
ax_f.plot(frac, [aircraft.noise_fraction(f * length, length, d_lambda)
for f in frac], label=f"segment {length:.0f} m")
speeds = np.linspace(50.0, 130.0, 401)
ax_v.plot(speeds, [aircraft.duration_correction(82.31104, s) for s in speeds])
for ax in (ax_i, ax_l, ax_f, ax_v):
ax.grid(True, alpha=0.3)
for ax in (ax_i, ax_l, ax_f):
ax.legend(fontsize=8)
plt.show()

event_level does the assembly. Put one receiver 3 km down the track, 500 m to the side of a departure and 1.2 m above the ground, and ask for its sound exposure level:

import numpy as np
from phonometry import aircraft
powers = [8000.0, 12000.0]
distances = [60.0, 120.0, 240.0, 480.0, 960.0, 1920.0, 3840.0, 7680.0]
sel = [[98.0, 92.0, 86.0, 80.0, 74.0, 68.0, 62.0, 56.0],
[104.0, 98.0, 92.0, 86.0, 80.0, 74.0, 68.0, 62.0]]
lmax = [[94.0, 88.0, 82.0, 76.0, 70.0, 64.0, 58.0, 52.0],
[100.0, 94.0, 88.0, 82.0, 76.0, 70.0, 64.0, 58.0]]
xs = np.linspace(0.0, 18000.0, 40) # ground roll, then a climb
z = np.clip((xs - 1500.0) * 0.11, 0.0, 2500.0)
power = np.where(xs < 3000.0, 12000.0, 10000.0)
path = np.column_stack([xs, np.zeros_like(xs), z, power, np.full_like(xs, 82.3)])
ground_roll = xs[:-1] < 1500.0 # one entry per segment
flyover = aircraft.event_level(
path, [3000.0, 500.0, 1.2], powers, distances, sel, lmax,
segments=aircraft.FlightSegmentState(ground_roll=ground_roll))
seg = flyover.segment_levels
top = int(np.argmax(seg))
print(round(float(flyover.level), 1)) # 82.3 dB: the event SEL
print(top, round(float(seg[top]), 1)) # 6 82.1: the nearest segment
flyover.plot() # per-segment contributions (needs matplotlib)
Bar chart of the sound exposure level each flight-path segment contributes at one receiver, against segment index. The first four bars, hatched, are the take-off ground roll and contribute between 26 and 43 dB; the bars then rise steeply to 82.1 dB at segment 6, the segment whose closest point of approach is nearest the receiver, and fall away to a few decibels by segment 38. A dashed line marks the energy-summed total of 82.3 dB, barely above the tallest bar, and a note records that the closest segment alone carries 94 per cent of the energyBar chart of the sound exposure level each flight-path segment contributes at one receiver, against segment index. The first four bars, hatched, are the take-off ground roll and contribute between 26 and 43 dB; the bars then rise steeply to 82.1 dB at segment 6, the segment whose closest point of approach is nearest the receiver, and fall away to a few decibels by segment 38. A dashed line marks the energy-summed total of 82.3 dB, barely above the tallest bar, and a note records that the closest segment alone carries 94 per cent of the energy

Thirty-nine segments, and one of them is the answer: segment 6 contributes 82.1 dB against a total of 82.3 dB, which is 94 % of the energy. That is what the noise fraction does — the contribution collapses within a couple of segments of the closest point of approach — and it is why a contour is only ever as good as the flight path near each receiver. The ground-roll segments are hatched: they are 40 dB down here, but move the receiver behind the runway and the start-of-roll term brings them back.

Show the code for this figure
import matplotlib.pyplot as plt
ax = flyover.plot()
for i, patch in enumerate(ax.patches[:len(seg)]):
if ground_roll[i]:
patch.set_hatch("///")
ax.set_ylim(0.0, float(max(seg)) + 12.0)
plt.show()

The 82.1 dB of that segment is the table plus the five corrections, and nothing else:

s1, s2 = path[top, :3], path[top + 1, :3]
observer = np.array([3000.0, 500.0, 1.2])
u = (s2 - s1) / np.linalg.norm(s2 - s1)
length = float(np.linalg.norm(s2 - s1)) # 464 m of segment
q = float((observer - s1) @ u) # 214 m along it
dp = float(np.linalg.norm(observer - (s1 + q * u))) # 526 m, minimum slant range
beta = float(np.degrees(np.arccos(500.0 / dp))) # 18.0 deg (§4.5.5)
p_seg = float(np.sqrt(power[top] ** 2
+ (q / length) * (power[top + 1] ** 2 - power[top] ** 2)))
base = float(aircraft.npd_level(powers, distances, sel, p_seg, dp)[0])
peak = float(aircraft.npd_level(powers, distances, lmax, p_seg, dp)[0])
d_lambda = (2 / np.pi) * 82.3 * 10 ** ((base - peak) / 10) # Eq. 4-20
terms = {
"impedance": aircraft.impedance_adjustment(),
"duration": aircraft.duration_correction(82.3, 82.3),
"installation": aircraft.engine_installation_correction(beta),
"lateral": -aircraft.lateral_attenuation(beta, 500.0),
"noise fraction": aircraft.noise_fraction(q, length, d_lambda),
}
print(round(base, 2), {k: round(v, 2) for k, v in terms.items()})
# 83.89 {'impedance': 0.07, 'duration': 0.0, 'installation': -0.43,
# 'lateral': -1.2, 'noise fraction': -0.25}
print(round(base + sum(terms.values()), 2)) # 82.08, the bar above

The duration correction is exactly zero because the path was flown at the reference speed; the lateral attenuation is only 1.2 dB because the segment is still 18° above the horizon at this receiver; at the same 500 m offset but with the aeroplane on the runway, at grazing incidence, it would be 8.8 dB. noise_contour repeats this calculation at every point of a ground grid, so a contour is worth exactly what this single number is worth.

Single-event SEL contour of a departure: an elongated footprint along the flight track, loudest near the ground rollSingle-event SEL contour of a departure: an elongated footprint along the flight track, loudest near the ground roll

The footprint stretches along the track because every segment contributes to every receiver, and it is widest where the aeroplane is lowest and its slant distances shortest. The sideline levels fall faster than the inverse-square law alone, because lateral attenuation switches on as the elevation angle drops below 50°, and the lobe behind the runway threshold is the start-of-roll directivity of the jet exhaust, which is why the contour does not close symmetrically at the runway end. The outermost contours are the least trustworthy: they are set by the segments at the greatest slant distance, where the NPD table is being extrapolated. This is one movement, not a study — a planning contour accumulates many such events into an -style index, which is this page’s “Not covered”.

Show the code for this figure
import matplotlib.pyplot as plt
import numpy as np
from phonometry import aircraft
# NPD tables (SEL and LAmax) for one aircraft, two power settings.
powers = [8000.0, 12000.0]
distances = [60.0, 120.0, 240.0, 480.0, 960.0, 1920.0, 3840.0, 7680.0]
sel = [[98.0, 92.0, 86.0, 80.0, 74.0, 68.0, 62.0, 56.0],
[104.0, 98.0, 92.0, 86.0, 80.0, 74.0, 68.0, 62.0]]
lmax = [[94.0, 88.0, 82.0, 76.0, 70.0, 64.0, 58.0, 52.0],
[100.0, 94.0, 88.0, 82.0, 76.0, 70.0, 64.0, 58.0]]
# Departure: ground roll along +x, then a steady climb.
xs = np.linspace(0.0, 18000.0, 40)
z = np.clip((xs - 1500.0) * 0.11, 0.0, 2500.0)
power = np.where(xs < 3000.0, 12000.0, 10000.0)
path = np.column_stack([xs, np.zeros_like(xs), z, power, np.full_like(xs, 82.3)])
ground_roll = xs[:-1] < 1500.0 # takeoff roll: segments still on the runway
segments = aircraft.FlightSegmentState(ground_roll=ground_roll)
contour = aircraft.noise_contour(path, powers, distances, sel, lmax,
segments=segments,
x=np.linspace(-2500.0, 20000.0, 56),
y=np.linspace(-6000.0, 6000.0, 44))
contour.plot() # single-event SEL footprint (needs matplotlib)
plt.show()

The mechanism behind these ground corrections is two-path interference: the direct wave and its ground reflection. Below, a 400 Hz source 1.5 m above a rigid plane forms the lobe pattern, with the image source ghosted below the ground and a receiver sitting in an interference dip.

A 2D FDTD simulation of a 400 Hz point source 1.5 metres above rigid ground. The direct and ground-reflected wavefronts interfere and a lobe pattern forms, the ghosted image source below the ground explains the geometry, and the level sampled on an 8 metre arc converges to the two-path image-source model with its predicted nulls.

Download the animation (WebM)

A 2D FDTD simulation of a 400 Hz point source 1.5 metres above rigid ground. The direct and ground-reflected wavefronts interfere and a lobe pattern forms, the ghosted image source below the ground explains the geometry, and the level sampled on an 8 metre arc converges to the two-path image-source model with its predicted nulls.

Download the animation (WebM)

The start-of-roll directivity is the lobed rearward radiation of jet-exhaust noise: strongest near an azimuth from the nose, falling off abeam () and directly behind ().

Polar diagram of the start-of-roll directivity ΔSOR over the rearward semicircle for turbofan-jet and turboprop aircraft, both showing a lobe near 120° from the nosePolar diagram of the start-of-roll directivity ΔSOR over the rearward semicircle for turbofan-jet and turboprop aircraft, both showing a lobe near 120° from the nose

The lobe is narrow and the penalty behind it is large. At 300 m the jet peaks at +0.99 dB at = 123° and the turboprop at +1.96 dB at 121°, both within 0.2 dB of abeam at 90°; by 180°, straight behind the aircraft, they are down to −13.5 and −10.1 dB. A receiver placed a few degrees off the lobe therefore sees a completely different start-of-roll contribution, which is why is applied per segment and per receiver rather than as a single correction to the event.

Show the code for this figure
import matplotlib.pyplot as plt
import numpy as np
from phonometry import aircraft
az = np.linspace(90.0, 270.0, 361) # rearward semicircle
psi = np.where(az <= 180.0, az, 360.0 - az) # ΔSOR is left/right symmetric
jet = [aircraft.start_of_roll_directivity(p, 300.0, "jet") for p in psi]
prop = [aircraft.start_of_roll_directivity(p, 300.0, "turboprop") for p in psi]
ax = plt.subplot(projection="polar")
ax.set_theta_zero_location("N") # nose up, azimuth clockwise
ax.set_theta_direction(-1)
ax.plot(np.radians(az), jet, label="Turbofan jet")
ax.plot(np.radians(az), prop, label="Turboprop")
ax.set_rlim(-16.0, 0.0) # radial axis: dB re abeam
ax.legend(loc="lower center")
plt.show()
import numpy as np
from phonometry import aircraft
powers = [8000.0, 12000.0]; distances = [60.0, 240.0, 960.0, 3840.0]
sel = [[98.0, 86.0, 74.0, 62.0], [104.0, 92.0, 80.0, 68.0]]
lmax = [[94.0, 82.0, 70.0, 58.0], [100.0, 88.0, 76.0, 64.0]]
xs = np.linspace(0.0, 18000.0, 40)
path = np.column_stack([xs, np.zeros_like(xs), np.clip((xs-1500)*0.11, 0, 2500),
np.where(xs < 3000, 12000.0, 10000.0), np.full_like(xs, 82.3)])
ground_roll = xs[:-1] < 1500.0 # takeoff roll: segments still on the runway
aircraft.noise_contour(path, powers, distances, sel, lmax,
segments=aircraft.FlightSegmentState(ground_roll=ground_roll),
x=np.linspace(-2500, 20000, 60), y=np.linspace(-6000, 6000, 48)).plot()

Validated against the ECAC Doc 29 5th ed. Vol 3 Part 1 reference workbook: the segment geometry, lateral attenuation, engine installation, noise fraction and the start-of-roll directivity (turbofan and turboprop) reproduce the reference values to , and the segment energy sum matches the reference SEL.

Four more pieces of the method are in place. The landing rollout is the landing_roll mask of the FlightSegmentState: ahead of it the noise fraction takes its reduced form (Eq. 4-21b) and the nearest-end geometry applies, with no directivity term, because Doc 29 assumes a semicircular horizontal directivity there. The per-segment bank angle is its bank, positive with the starboard wing up, and it selects the branch of by which side of the track the receiver is on (§4.5.2). Behind a take-off ground roll the §4.5.5 nearest-end lateral geometry replaces the equivalent-level-path one, which is the same switch that makes the NPD lookup use there. And a runway segment takes the Eq. 4-13b average of its end speeds rather than the quadratic interpolation an airborne segment uses, while every NPD lookup is floored at the recommended 30 m. Seven branch-covering receptor events of the reference workbook are reproduced end-to-end in the test suite.

  • Covered

    The ECAC Doc 29 single-event airport-noise chain: the NPD interpolation of npd_level/npd_curve (Eqs. 4-3, 4-4), the per-segment corrections impedance_adjustment, lateral_attenuation (SAE AIR 5662 soft ground), engine_installation_correction, duration_correction, noise_fraction and start_of_roll_directivity, their assembly into SEL/ by event_level and the ground-grid noise_contour, including the landing rollout, per-segment bank angle and the nearest-end lateral geometry. Validated to under 0.01 dB against the Doc 29 5th ed. Vol. 3 Part 1 reference workbook.

  • Not covered

    The chain builds single-event contours only: it does not assemble the cumulative multi-event indices (an -style sum over a full flight schedule) that a complete Doc 29 noise-contour study needs on top of these single-event levels. NPD tables themselves are an input, not a prediction: the library interpolates the tables supplied for an aircraft type, it does not synthesise them from engine data. The EASA/EUROCONTROL ANP database of measured tables for real types does ship with phonometry and is read by The ANP fleet database, which wires it straight into the functions of this page. Converting NPD data to a site outside the §2.5 envelope (the Appendix D recalculation) is not implemented, and neither is the acoustically hard ground that SAE AIR 5662 excludes: the lateral attenuation, and with it the whole single-event chain, assumes soft grassy ground.

  • Aircraft noise: Effective Perceived Noise Level: the ICAO Annex 16 certification metric behind the aircraft data.
  • The ANP fleet database: the shipped NPD tables and default trajectories for real aircraft types, which run this chain without a hand-written table.
  • Rotorcraft noise: the hemisphere method: the ECAC Doc 32 contour method, where a noise hemisphere plays the role the NPD table plays here. Mind the symbols on the way across: Doc 32 uses for the hemisphere azimuth and writes the bank angle , where Doc 29 uses for the depression angle and for the bank. Each is its own standard’s notation.
  • Outdoor sound propagation: the ISO 9613-2 attenuation terms and the ground effect the lateral attenuation condenses into one curve.
  • Environmental Levels: the -style long-term indices that a full airport study accumulates from single events.
  • API reference: aircraft.airport_noise.