This document is the public version of the JPXWatch methodology audit maintained in the project repository. Last updated April 12, 2026.

JPXWatch Noise Estimation Pipeline — Methodology Audit

Audit date: 2026-04-12 Codebase: main branch at commit 5ca7c6c Auditor: Claude (automated code audit) Status: Complete


Purpose. This document is a factual, read-only accounting of what the JPXWatch noise estimation code does today. It is not a methodology document for the public, not a defense of the methodology, and not a list of recommended improvements. Every claim cites a file path and line number so it can be independently verified.


Executive Summary

Discrepancies between code locations computing the same thing

  1. Two source-level lookup tables with different coverage. getNoiseDb() (lib/noise/getNoiseDb.ts) falls back to getAircraftNoiseProfile() (28 types in data/noise/aircraftNoiseProfiles.ts), while trackNoiseCalculator.ts and related modules call getEASANoiseProfile() (47 types in data/noise/easa/icaoToEasaMap.ts). An aircraft type present in the EASA map but absent from the client-side table receives different dB values depending on which code path evaluates it. See §1b.

  2. Inconsistent ground elevation for MSL→AGL conversion. Trail noise uses GROUND_ELEVATION_FT = 30 ft (trailNoise.ts:22); footprint calculator uses KJPX_ELEVATION_FT = 55 ft (footprintCalculator.ts:36). The same aircraft at the same MSL altitude produces different AGL values — and thus different noise estimates — in trails vs. footprints. See §3c.

  3. Duplicate aircraft classification logic. lib/flightaware/classify.ts and lib/webhooks/processor.ts each define their own HELICOPTER_TYPES, JET_TYPES, and FIXED_WING_TYPES sets (copy-pasted, not shared). The webhook processor omits seaplane detection. See §2f.

  4. Noise category boundaries differ between classification systems. getNoiseCategory() uses 75/82/88 dB boundaries; getDbSeverityLevel() uses 55/70/85; getDbColor() uses 55/65/75/85; getNoiseColorFromDb() uses 65/75/85. These serve different purposes but this is not documented. See §1f.

  5. SEL formula omits atmospheric absorption. The DNL calculator's calculateSEL() uses only geometric spreading, while calculateGroundNoise() in the track calculator includes both geometric spreading and atmospheric absorption (0.5 dB/1000 ft). See §4b vs §3b.

  6. Two different "nighttime" definitions. DNL nighttime penalty: 10 PM – 7 AM (FAA standard). Town curfew / Noise Impact Score: 9 PM – 7 AM. This is correctly documented in the code (dnlCalculator.ts:21–24). See §4c.

Unattributed magic numbers requiring sourcing

ConstantValueLocationIssue
FAA approach dB offset+2 dBicaoToEasaMap.ts:682,690Applied to FAA-measured lamax1000ft to estimate approach dB. No citation.
Default flyover exposure15 secondsdnlCalculator.ts:39Used in SEL calculation. About page calls it "standard" but cites no source.
Atmospheric absorption0.5 dB/1000 ftMultiple filesComment references ISO 9613-1 but 0.5 is a simplified single-number A-weighted average, not the full frequency-dependent model.
Trail ground elevation30 fttrailNoise.ts:22Comment says "East End average" but no DEM or survey source cited.
Lateral attenuation table0–10 dB over 0–90°trackNoiseCalculator.ts:185–196Labeled "SAE-AIR-5662 simplified" — the simplification method is not described.
Category average dB valuesheli=84, jet=88, fw=76, unk=80icaoToEasaMap.ts:37–42No citation for how these averages were computed.
Weather adjustment valueswind ±2–6 dB, inversion 0–8 dBweatherAdjustments.ts:65–82Comment references ISO 9613-2 but specific values are not directly from the standard.
Noise Impact Score weights40/30/30%noiseImpactScore.ts:111Custom metric. No external basis cited.
Noise Impact Score scale factors500, 2, 1000noiseImpactScore.ts:40,55,75Chosen so that 20% loud ops, 50 ops/day, or 10% curfew ops each produce a score of 100. No cited basis for these anchoring points.

Inconsistencies with the About page

  1. Elevation source. The About page (about/page.tsx:1172–1174) claims "For properties where verified elevation data is available… we use the actual ground elevation from the USGS National Elevation Dataset" and "for all other locations, we use the airport field elevation of 55 feet MSL." The trail noise code uses a hardcoded 30 ft (trailNoise.ts:22). Neither 30 nor 55 corresponds to USGS DEM integration — no USGS data fetch was found in the codebase.

  2. Weather effects. The About page (about/page.tsx:1381) states "the model uses standard atmospheric conditions." The code includes a full weather adjustment module (weatherAdjustments.ts) that is plumbed into calculateGroundNoise() as an optional parameter but is not connected to any live weather feed. The weather model exists but is dormant.

  3. "47 aircraft types." The About page and docs/NOISE-METHODOLOGY.md reference 47 aircraft types with EASA certification data. This matches the EASA map (icaoToEasaMap.ts:45–657). However, the trail visualization and getNoiseDb() fallback use the 28-type client-side table. The number "47" is accurate for the EASA map but not for all code paths.

Open TODOs in the noise pipeline

  • lib/calculations/noiseImpactScore.ts:149: "Add a loud_jets column to monthly aggregates to improve accuracy." Currently all jets are treated as loud (≥85 dB) in aggregate calculations, overestimating the fleet mix score.

Table of Contents

  1. Aircraft Source Levels
  2. Aircraft Classification
  3. Propagation Model
  4. DNL Computation
  5. Noise Rank
  6. Noise Trails
  7. High-Noise Events

1. Aircraft Source Levels

1a. What the code does

JPXWatch assigns every flight a point-source noise level in dB (LAmax at 1,000 ft reference distance) for both takeoff and approach phases. These values are the starting point for all downstream noise calculations (propagation, DNL, trails, high-noise events).

There are three independent data stores for source levels, queried through a priority cascade:

  1. aircraftNoiseProfiles — a hand-curated lookup table of 28 aircraft types, used as the client-side fallback. data/noise/aircraftNoiseProfiles.ts:5–216

  2. icaoToEasaMap — a larger auto-generated map of 47 ICAO types with EASA certification EPNdB values converted to estimated LAmax. data/noise/easa/icaoToEasaMap.ts:45–657

  3. faaHelicopterMeasurements — FAA ROSAP field-measurement database with 20 helicopter entries (multiple variants per type), providing measured LAmax at 1,000 ft. data/noise/faa/helicopterMeasurements.ts:48–348

1b. Lookup cascade

The primary lookup function is getEASANoiseProfile(icaoType) (data/noise/easa/icaoToEasaMap.ts:664–716):

  1. Normalize type code to uppercase (line 665).
  2. Check hasFAAMeasurement() — if FAA ROSAP data exists for this type, use its lamax1000ft as takeoffDb and lamax1000ft + 2 as approachDb (lines 669–695). Data source set to 'FAA_MEASURED', confidence 'high'.
  3. If no FAA data, check icaoToEasaMap for an EASA-derived profile (line 698).
  4. If neither exists, return an unknown fallback: takeoffDb = 80, approachDb = 76, data source 'UNVERIFIED', confidence 'low' (lines 703–715). The 80 comes from CATEGORY_AVERAGES.unknown.default (line 711); the 76 is computed as 80 - 4 (line 712).

A separate function getNoiseDb(flight) (lib/noise/getNoiseDb.ts:34–42) selects the direction-appropriate value:

  1. Prefer flight.noise_profile.effective_db if present (line 36–37).
  2. Else call getAircraftNoiseProfile(flight.aircraft_type) — which queries the client-side aircraftNoiseProfiles map, not the EASA map — and returns approachDb for arrivals, takeoffDb for departures (lines 40–41).

FLAG — Discrepancy. getNoiseDb() falls back to getAircraftNoiseProfile() (the 28-type client-side table), while trackNoiseCalculator.ts and footprintCalculator.ts use getEASANoiseProfile() (the 47-type EASA/FAA table) or getAircraftNoiseProfile() respectively. A flight whose type is in the EASA map but not in the client-side aircraftNoiseProfiles table will get different source levels depending on which code path evaluates it. See Section 3 for details.

1c. Client-side profile table (aircraftNoiseProfiles)

All values in dB LAmax at 1,000 ft. File: data/noise/aircraftNoiseProfiles.ts.

ICAOCategorytakeoffDbapproachDbnoiseCategoryLines
R22helicopter7876moderate7–13
R44helicopter8280loud14–20
R66helicopter8381loud21–27
S76helicopter8885very_loud28–34
EC35helicopter8482loud35–41
A109helicopter8583loud42–48
B06helicopter8381loud49–55
B407helicopter8482loud56–62
AS50helicopter8280loud63–69
GLF5jet9288very_loud72–78
GLF4jet9086very_loud79–85
GLEXjet9187very_loud86–92
C56Xjet8682loud93–99
C680jet8581loud100–106
C525jet8076moderate107–113
E55Pjet8278moderate114–120
PC12fixed_wing7875moderate121–127
LJ45jet8480loud128–134
FA50jet8581loud135–141
C172fixed_wing7572moderate144–150
C182fixed_wing7673moderate151–157
C206fixed_wing7774moderate158–164
PA28fixed_wing7471moderate165–171
PA32fixed_wing7673moderate172–178
BE36fixed_wing7774moderate179–185
SR22fixed_wing7673moderate186–192
P28Afixed_wing7269quiet193–199
C150fixed_wing7067quiet200–206
UNKNunknown8076moderate209–215

Provenance: The file comment (line 3) says "Based on FAA noise certification data and typical aircraft noise levels at 1000ft altitude during takeoff/approach." No individual per-type citation is given. The values are round numbers, not raw certification values, suggesting they are rounded estimates derived from certification data rather than direct copies.

Fallback: getAircraftNoiseProfile(type) returns the UNKN profile (takeoffDb=80, approachDb=76) for any type not in the table (line 219–220).

1d. EASA map (icaoToEasaMap)

File: data/noise/easa/icaoToEasaMap.ts. Auto-generated 2026-02-12 (line 2). Source: EASA Certification Noise Levels database (line 3). Updated with FAA ROSAP measurements (line 4).

The file header (lines 7–13) documents the EPNdB-to-LAmax conversion: "LAmax ≈ EPNdB - (7 to 13), varying by aircraft spectrum shape. For helicopters, the offset is typically 7–10 dB; for jets, 10–13 dB."

47 aircraft types are mapped. Each entry includes: lateralEpnl, flyoverEpnl, approachEpnl (nullable; raw EASA EPNdB values), takeoffDb, approachDb (converted LAmax), dataSource, and confidence.

Category averages for unknown types (data/noise/easa/icaoToEasaMap.ts:37–42):

CategoryDefaultLightMediumHeavy
helicopter84788490
jet88828894
fixed_wing76727682
unknown80

Provenance of category averages: Not individually cited in code. The header states they are "representative LAmax values derived from the same methodology" (line 13) as the per-type EPNdB-to-LAmax conversion.

1e. FAA ROSAP helicopter measurements

File: data/noise/faa/helicopterMeasurements.ts. Contains 20 measurement entries for helicopter types, each with fields: takeoffEpndb, approachEpndb, flyoverEpndb, sel1000ft, lamax1000ft, faaReport (citation), measurementYear.

Selected entries (complete set in the file):

ICAOModellamax1000ftfaaReportYearLines
B06Bell 206B-382.0DOT/FAA/CT-84-2198451–63
B06Bell 206L-180.5DOT/FAA/CT-84-2198465–75
B407Bell 40783.0FAA-AEE-01-04200177–87
S76Sikorsky S-76A84.0DOT/FAA/CT-84-21984130–141
S76S-76C++82.5FAA-AEE-09-012009156–167
R22Robinson R2275.5FAA-AEE-01-042001294–305
R44Robinson R4477.5FAA-AEE-01-042001307–318
R66Robinson R6678.8FAA-AEE-15-012015320–331
EC35EC13580.5FAA-AEE-01-042001253–264
AS50AS35081.5FAA-AEE-01-042001240–251
A109AW109SP82.0FAA-AEE-15-012015211–222
H60UH-60A87.5DOT/FAA/CT-84-21984336–347

getFAAMeasurement() (line 356–372) returns the most recent measurement for a type (sorted by year descending).

Approach dB approximation: When FAA data is used, approachDb is set to lamax1000ft + 2 (lines 682, 690). The +2 dB offset is hardcoded with no comment explaining its provenance.

FLAG — Unattributed constant. The +2 dB approach-vs-takeoff offset applied to FAA-measured lamax1000ft values (line 682, 690) has no citation or comment. The client-side aircraftNoiseProfiles table uses per-type approach/takeoff differentials that range from 2–4 dB, suggesting this is a simplified approximation.

1f. Noise category thresholds

getNoiseCategory(db) in lib/noise/getNoiseDb.ts:47–52:

ThresholdCategory
≥ 88 dBvery_loud
≥ 82 dBloud
≥ 75 dBmoderate
< 75 dBquiet

These thresholds are used for display classification only; they do not affect noise calculations.

A separate severity scale exists in types/noise.ts:207–220:

ThresholdSeverityColor
< 55 dBlow#22c55e (green)
< 65 dB#84cc16 (lime)
< 70 dBmoderate
< 75 dB#eab308 (yellow)
< 85 dBhigh#f97316 (orange)
≥ 85 dBsevere#ef4444 (red)

FLAG — Discrepancy. The getNoiseCategory() boundaries (75/82/88) do not align with the getDbSeverityLevel() boundaries (55/70/85) or the getDbColor() boundaries (55/65/75/85). These appear to serve different purposes (aircraft-type classification vs. received-ground-level severity) but this is not documented in the code.

1g. TODOs and known issues

  • lib/calculations/noiseImpactScore.ts:145–149: TODO to add a loud_jets column to monthly aggregates. Currently all jets are treated as ≥ 85 dB in aggregate calculations, overestimating the fleet mix score.
  • No TODOs or FIXMEs in the source-level data files themselves.

2. Aircraft Classification

2a. What the code does

JPXWatch classifies each flight into one of five categories: helicopter, jet, fixed_wing, seaplane, or unknown. Classification determines which source-level dB value is used and how the flight is counted in noise metrics (e.g., all helicopters are counted as "loud" in the Noise Index regardless of their dB value).

The primary input is the ICAO aircraft type code from FlightAware (e.g., S76, GLF5, C172). The system does not directly use ADS-B hex codes for classification — FlightAware resolves hex codes to type codes upstream.

2b. Primary classification function

classifyAircraft(icaoType) in lib/flightaware/classify.ts:199–217:

  1. If input is null/undefined → 'unknown' (line 201).
  2. Trim and uppercase the code (line 204).
  3. Check against HELICOPTER_TYPES Set (48 types, lines 22–48) → 'helicopter'.
  4. Check against JET_TYPES Set (60 types, lines 53–81) → 'jet'.
  5. Check against FIXED_WING_TYPES Set (68 types, lines 87–110) → 'fixed_wing'.
  6. No match → 'unknown' (line 216).

All type sets are hardcoded. There is no dynamic discovery of new aircraft types.

2c. Type set contents

Helicopters (48 types, classify.ts:22–48): Robinson (R22, R44, R66), Airbus/Eurocopter (EC20, EC30, EC35, EC45, EC55, EC75, EC25, AS50, AS55, AS65, AS32, AS33, AS35, H125, H130, H135, H145, H155, H160, H175, H215, H225), Bell (B06, B06T, B204, B205, B206, B209, B212, B214, B222, B230, B407, B412, B427, B429, B430, B505, B525), Sikorsky (S76, S61, S70, S92, S58, S64, S76B, S76C, S76D, H60, S300), Leonardo (A109, A119, A139, A149, A169, A189, AW09, AW39, AW69, AW89), MD (MD52, MD60, EXPL, NOTR, H369, H500), Enstrom (EN28, EN48), Schweizer (S269, S333, H269), generic (HELI).

Jets (60 types, classify.ts:53–81): Gulfstream (GLF2–GLF6, GLEX, G150–G800), Bombardier (CL30, CL35, CL60, BD70, GL5T–GL7T), Learjet (LJ23–LJ75), Cessna Citation (C500–C750), Dassault (FA10–FA8X, F900, F2TH), Embraer (E135–E550), plus PC24, HDJT, EA50, SF50, PRM1, H25A–H25C, AJET.

Fixed Wing (68 types, classify.ts:87–110): Cessna piston/turboprop (C150–C441), Piper (P28A–PA60, PC12), Beechcraft (BE33–B300), Mooney (M20J–M20U), Cirrus (SR20, SR22), Diamond (DA20–DA62), TBM (TBM7–TBM9), de Havilland (DHC2, DHC3, DHC6).

2d. Seaplane detection

A multi-layer seaplane override exists in classify.ts:160–192 via isSeaplaneFlight(). It is not called from the cron or webhook ingestion pipelines — see section 2f below.

Layer 1 — Always-seaplane types (classify.ts:118–122): DHC2 (de Havilland Beaver) and DHC3 (Otter) are always classified as seaplanes.

Layer 2 — Seaplane-candidate types (classify.ts:119): C208, KODK, KOD1, DHC2, DHC3, DHC6 — these may be seaplanes depending on operator/route.

Layer 3 — Known seaplane operators (classify.ts:125–137):

  • ICAO codes: FTO (Tropic Ocean Airways), PGN (Tailwind Air).
  • Display names: TROPIC OCEAN AIRWAYS, TAILWIND AIR, ACADIAN SEAPLANES, FLYTHEWHALE, FLY THE WHALE.
  • Special case: MONTAUK SKY / MONTAUK SKY LLC (classify.ts:140–141) — classified as seaplane only if the route includes NYC Skyports; otherwise fixed_wing.

Layer 4 — Skyports detection (classify.ts:143–150): Detects NYC seaplane base by coordinate prefix (L 40.73* / -73.97*) or FAA identifier (6N5, 6N7, KJRA, KJRB).

2e. Data sources feeding classification

SourceInput fieldUsed forFile
FlightAware AeroAPIaircraft_type (ICAO code)Primary classification inputapp/api/cron/daily-pull/route.ts
FlightAware AeroAPIoperator (ICAO code)Seaplane operator check (unused in pipeline)lib/flightaware/classify.ts
FlightAware AeroAPIregistration (N-number)Not used for classification; used for FAA registry owner lookup onlylib/faa/registryDisplay.ts
FAA Aircraft Registryn_number, aircraft_mfr, aircraft_modelOwner identity display; not used for category classificationapp/api/cron/refresh-faa-registry/route.ts

2f. Duplicate classification logic

FLAG — Code duplication. Aircraft classification is implemented independently in two files:

  1. lib/flightaware/classify.ts:199–217 — the canonical implementation, with seaplane detection.
  2. lib/webhooks/processor.ts:25–82 — a duplicate, noted as "mirrors Python classify.py" (line 22).

Both contain identical HELICOPTER_TYPES, JET_TYPES, FIXED_WING_TYPES sets as of the audit date, but they are not shared — they are copy-pasted. The webhook processor's classifyAircraft() (line 75–82) does not call isSeaplaneFlight(), so webhook-ingested flights with seaplane-candidate types (e.g., C208 operated by Tropic Ocean Airways) will be classified as fixed_wing rather than seaplane.

2g. How unknowns are handled

  • Null/undefined aircraft_type'unknown' category.
  • Unrecognized ICAO code → 'unknown' category.
  • In noise calculations: unknown aircraft get the UNKN profile (takeoffDb=80, approachDb=76) from aircraftNoiseProfiles.ts:209–215, or CATEGORY_AVERAGES.unknown.default (80) from the EASA map fallback.
  • In the Noise Index: unknown-category flights are not counted as loud operations — only helicopter and jet categories contribute (lib/noise/getNoiseDb.ts:60–73).
  • In fleet filtering: unknown flights are grouped with fixed-wing under the "other" filter (lib/filterFlightsByFleet.ts:10).

2h. TODOs and known issues

  • No TODOs or FIXMEs in the classification files.
  • The seaplane detection system (isSeaplaneFlight) exists but is not integrated into either ingestion pathway (cron or webhook). Its export from lib/flightaware/index.ts makes it available, but no caller was found in the ingestion code.

3. Propagation Model

3a. What the code does

The propagation model computes estimated ground-level noise in dB given a source level, aircraft position, and receiver position. There are four independent implementations of this calculation at different levels of fidelity:

  1. Full modelcalculateGroundNoise() in lib/noise/trackNoiseCalculator.ts:338–434. Includes geometric spreading, atmospheric absorption, lateral attenuation (SAE-AIR-5662), and optional weather adjustment.
  2. Simplified footprint modelestimateGroundDb() in lib/noise/footprintCalculator.ts:117–130. Geometric spreading + atmospheric absorption only. Omits lateral attenuation (noted in comment, lines 111–115).
  3. Trail modelestimateGroundNoise() in lib/noise/trailNoise.ts:60–71. Altitude-only (assumes listener directly below aircraft). Geometric spreading + atmospheric absorption.
  4. Legacy simple estimategetSimpleNoiseEstimate() in lib/noise/trackNoiseCalculator.ts:634–661. Altitude-only, same physics as trail model.

3b. Full propagation formula

calculateGroundNoise() (trackNoiseCalculator.ts:338–434):

groundDb = sourceDb
         − geometricAttenuation
         − atmosphericAttenuation
         − lateralAttenuation
         + weatherAdjustment

Step 1: Horizontal distance (line 361–363) Haversine formula between observer and aircraft lat/lon, result in feet.

Step 2: Slant distance (line 366) slantDistanceFt = √(altitude_ft² + horizontalDistanceFt²) Clamped to minimum 100 ft (line 369) to prevent extreme values.

Step 3: Geometric spreading (lines 372–375) geometricAttenuation = 20 × log₁₀(effectiveSlantDistance / 1000) Reference distance is CERTIFICATION_REFERENCE_DISTANCE_FT = 1000 ft (line 98). This is the inverse square law: −6 dB per doubling of distance.

Step 4: Atmospheric absorption (lines 377–379) atmosphericAttenuation = (effectiveSlantDistance / 1000) × 0.5 Coefficient is ATMOSPHERIC_ABSORPTION_COEFFICIENT = 0.5 dB per 1,000 ft (line 101).

Step 5: Lateral attenuation (lines 382–388) Applied only if aircraft heading is known. Uses SAE-AIR-5662 simplified model. Angle computed as bearing difference between aircraft heading and observer direction (calculateLateralAngle(), lines 286–306), clamped to 0–90°. Attenuation looked up via linear interpolation from a 10-point table (LATERAL_ATTENUATION_TABLE, lines 185–196):

Angle (°)Attenuation (dB)Line
00.0186
100.5187
201.2188
302.5189
404.0190
505.5191
607.0192
708.5193
809.5194
9010.0195

Maximum lateral attenuation: 10 dB at 90° (perpendicular to flight path).

Step 6: Weather adjustment (lines 390–412) Applied only if weather conditions are passed. See section 3d below. Positive values = louder.

Step 7: Floor (line 422) Result clamped to minimum 0 dB (Math.max(0, roundedDb)).

All intermediate and final values rounded to 1 decimal place (0.1 dB precision).

3c. Propagation constants

ConstantValueUnitFileLineProvenance
CERTIFICATION_REFERENCE_DISTANCE_FT1000fttrackNoiseCalculator.ts98EASA certification standard (304.8 m)
ATMOSPHERIC_ABSORPTION_COEFFICIENT0.5dB/1000 fttrackNoiseCalculator.ts101Comment: "A-weighted average." Referenced to ISO 9613-1 in file header (line 9).
EARTH_RADIUS_FT20,902,230.97fttrackNoiseCalculator.ts1046371 km × 3280.84 ft/km
FT_PER_NM6076.12ft/nmtrackNoiseCalculator.ts107Standard conversion
REFERENCE_DISTANCE_FT1000ftfootprintCalculator.ts33Same constant, separately defined
ATMOSPHERIC_ABSORPTION_PER_1000FT0.5dBfootprintCalculator.ts34Same constant, separately defined
EARTH_RADIUS_FT20,902,231ftfootprintCalculator.ts35Rounded differently (integer vs. .97)
KJPX_ELEVATION_FT55ft MSLfootprintCalculator.ts36Airport field elevation
GROUND_ELEVATION_FT30ft ASLtrailNoise.ts22Comment: "East End average ground elevation"
REFERENCE_DISTANCE_FT1000fttrailNoise.ts25Same constant, third definition
REFERENCE_DISTANCE_FT1000ftdnlCalculator.ts42Same constant, fourth definition

FLAG — Inconsistent ground elevation. The footprint calculator uses KJPX_ELEVATION_FT = 55 ft (field elevation) for MSL-to-AGL conversion (footprintCalculator.ts:36). The trail noise calculator uses GROUND_ELEVATION_FT = 30 ft (trailNoise.ts:22). The About page claims USGS National Elevation Dataset is used "where verified elevation data is available" and "55 feet MSL" as baseline — but the trail model uses 30 ft. This means the same aircraft at the same MSL altitude will produce different AGL values (and thus different noise estimates) depending on whether the footprint or trail code path is used.

FLAG — Repeated constants. REFERENCE_DISTANCE_FT (1000), ATMOSPHERIC_ABSORPTION (0.5), and EARTH_RADIUS_FT are defined independently in four files with no shared constant module. The Earth radius values differ slightly (20,902,230.97 vs 20,902,231). This is cosmetic but indicates the constants were not derived from a single source.

3d. Weather adjustment model

lib/noise/weatherAdjustments.ts. Referenced standards: ISO 9613-2, ANSI S12.18, SAE-AIR-1845 (file header, lines 10–12).

Wind adjustment (calculateWindAdjustment(), lines 98–147):

ConditionSpeedAngle to observerAdjustmentLine
Calm< 3 ktsany0 dB104
Downwind, light3–10 kts< 45°+2 dB132
Downwind, moderate10–20 kts< 45°+4 dB130
Downwind, strong> 20 kts< 45°+6 dB128
Upwind, baseany> 135°−2 dB136
Upwind, strong> 10 kts> 135°−3 dB136+139
Crosswindany45°–135°0 dB143

Wind constants: WIND_CALM_THRESHOLD = 3 kts (line 57), WIND_MODERATE_THRESHOLD = 10 kts (line 58), WIND_STRONG_THRESHOLD = 20 kts (line 59).

Temperature inversion adjustment (calculateInversionAdjustment(), lines 187–210):

StrengthAdjustment (within/above inversion)Adjustment (below inversion)Line
none0 dB0 dB78
weak+2 dB+1 dB79, 209
moderate+5 dB+2.5 dB80, 209
strong+8 dB+4 dB81, 209

Below-inversion adjustment is full_adjustment × 0.5 (line 209).

Integration status: Weather adjustment is an optional parameter in calculateGroundNoise() (line 346). The trail noise model (trailNoise.ts) and footprint model (footprintCalculator.ts) do not pass weather data. No live weather feed is connected. The About page states: "The model uses standard atmospheric conditions" (line 1381 of about/page.tsx).

3e. Altitude-only models (trail and simple estimate)

The trail model (trailNoise.ts:60–71) and legacy simple estimate (trackNoiseCalculator.ts:634–661) both assume the listener is directly below the aircraft, using AGL altitude as the sole distance:

distanceAttenuation = 20 × log₁₀(effectiveAgl / 1000)
atmosphericAbsorption = 0.5 × (effectiveAgl / 1000)
groundDb = sourceDb − distanceAttenuation − atmosphericAbsorption

Differences:

  • Trail model clamps AGL to minimum 50 ft (trailNoise.ts:62).
  • Simple estimate clamps altitude to minimum 100 ft (trackNoiseCalculator.ts:643).
  • Trail model uses GROUND_ELEVATION_FT = 30 for MSL→AGL.
  • Footprint model uses KJPX_ELEVATION_FT = 55 for MSL→AGL.
  • Neither applies lateral attenuation or weather adjustment.

3f. Multiple simultaneous aircraft

There is no explicit combination of simultaneous aircraft in the propagation model. Each aircraft's noise is computed independently. The DNL calculation (Section 4) sums acoustic energy across events over a time period, but there is no code that computes a combined instantaneous noise level from overlapping flights at a single point and time.

3g. Lateral attenuation — additional implementation

A second lateral attenuation module exists in components/noise/LateralAttenuation.ts (lines 35–122). It contains the same SAE-AIR-5662 table but adds:

  • Aircraft category factors (lines 54–63): helicopter=0.7, jet=1.0, fixed_wing=0.85, unknown=1.0. These scale the base attenuation.
  • Ground surface factors (lines 70–79): hard=0 dB, mixed=+1.5 dB, soft=+3.0 dB, absorptive=+4.5 dB. Applied for angles > 30°.

FLAG — Discrepancy. The LateralAttenuation.ts component applies category-specific scaling factors (e.g., helicopter attenuation is only 70% of the table value) and ground surface corrections that trackNoiseCalculator.ts does not. It is unclear which, if either, is currently used in a production code path. The category factors and ground surface adjustments have no cited provenance.

3h. TODOs and known issues

  • No TODOs or FIXMEs in the propagation model files.
  • The About page (about/page.tsx:1376) discloses: "Terrain reflection and shielding. The model assumes unobstructed sound propagation."
  • The About page (about/page.tsx:1381) discloses: "Weather effects. Wind speed, direction, temperature inversions, and humidity affect sound propagation. The model uses standard atmospheric conditions."

4. DNL Computation

4a. What the code does

DNL (Day-Night Average Sound Level, also written Ldn) is the FAA's primary metric for assessing aircraft noise impact. JPXWatch implements DNL computation in lib/noise/dnlCalculator.ts (349 lines).

The calculation has two stages: (1) compute SEL for each flight event, (2) aggregate SEL values with nighttime weighting into a single DNL number.

4b. SEL (Sound Exposure Level) calculation

calculateSEL() (dnlCalculator.ts:133–159):

SEL = L_ref − 20 × log₁₀(d_slant / d_ref) + 10 × log₁₀(t_exposure)

Where:

  • L_ref = reference noise level from EASA/FAA certification data (dB at d_ref)
  • d_slant = slant distance from observer to closest approach (feet), clamped to min 1 ft (line 147)
  • d_ref = 1,000 ft (line 143, REFERENCE_DISTANCE_FT)
  • t_exposure = flyover duration in seconds, default 15 (line 142, DEFAULT_EXPOSURE_DURATION_SEC), clamped to min 0.1 s (line 148)

The first two terms compute the received maximum level (same inverse square law as the propagation model). The third term converts from instantaneous level to energy-integrated metric.

Result rounded to 1 decimal place (line 158).

Note. The SEL formula does not include atmospheric absorption or lateral attenuation. It uses only geometric spreading (inverse square law). This is a simpler model than calculateGroundNoise() in trackNoiseCalculator.ts, which includes both atmospheric absorption and lateral attenuation.

4c. Nighttime classification

isNighttimeET() (dnlCalculator.ts:176–186):

Uses Intl.DateTimeFormat with IANA timezone 'America/New_York' (line 180) to convert UTC timestamps to Eastern Time. Returns true if hour ≥ 22 (10 PM) or hour < 7 (7 AM) (line 185).

FAA nighttime window: 10 PM – 7 AM ET (DNL_NIGHTTIME_START_HOUR = 22, line 51; DNL_NIGHTTIME_END_HOUR = 7, line 57).

FLAG — Two different "night" definitions.

  • DNL nighttime: 10 PM – 7 AM ET (FAA standard, dnlCalculator.ts:51,57)
  • Town curfew: 9 PM – 7 AM ET (lib/constants/curfew.ts:10,13)

This is correctly documented in the code: "The nighttime period for DNL (10 PM - 7 AM) differs from the East Hampton Town voluntary curfew (9 PM - 7 AM)" (dnlCalculator.ts:21–24). The Noise Impact Score (Section 5) uses the curfew window (9 PM), not the DNL window.

4d. DNL aggregation formula

calculateDNL() (dnlCalculator.ts:212–230):

DNL = 10 × log₁₀( (1/T) × Σ( 10^(SELᵢ/10) × wᵢ ) )

Where:

  • T = reference time period in seconds = 86400 × periodDays (line 217)
  • SELᵢ = Sound Exposure Level for event i
  • wᵢ = weighting: 1.0 for daytime, 10.0 for nighttime (line 222, NIGHTTIME_PENALTY_FACTOR = 10)

The 10× nighttime multiplier is the energy equivalent of the FAA's 10 dB penalty.

Returns 0 for empty input (line 214). Default periodDays = 1.

Result rounded to 1 decimal place (line 229).

4e. DNL constants

ConstantValueUnitFile:LineProvenance
FAA_DNL_THRESHOLD65dB DNLdnlCalculator.ts:3014 CFR Part 150
NIGHTTIME_PENALTY_FACTOR10multiplier (= 10 dB)dnlCalculator.ts:36FAA standard
DEFAULT_EXPOSURE_DURATION_SEC15secondsdnlCalculator.ts:39Comment: "when track data is unavailable"
REFERENCE_DISTANCE_FT1000ftdnlCalculator.ts:42EASA/FAA certification standard
SECONDS_PER_DAY86400secondsdnlCalculator.ts:45Definition
DNL_NIGHTTIME_START_HOUR22hour (ET)dnlCalculator.ts:51FAA standard
DNL_NIGHTTIME_END_HOUR7hour (ET)dnlCalculator.ts:57FAA standard

FLAG — Unattributed constant. DEFAULT_EXPOSURE_DURATION_SEC = 15 has no citation. The About page states "a standard 15-second flyover duration" (about/page.tsx:1361) but does not cite a source for this value. This constant significantly affects SEL and therefore DNL — a 30-second exposure produces 3 dB more SEL than a 15-second exposure.

4f. DNL severity categories

getDNLCategory() (dnlCalculator.ts:314–348):

DNL rangeLevelColorExceeds FAA thresholdFAA guidanceLines
≥ 75 dBvery_high#DC2626 (red)yesUnacceptable for residential use315–321
≥ 65 dBhigh#EA580C (orange)yesNormally unacceptable for residential324–330
≥ 55 dBmoderate#D97706 (amber)noConditionally acceptable333–339
< 55 dBlow#16A34A (green)noGenerally acceptable342–347

Referenced to FAA guidance in docstring (lines 298–309): 14 CFR Part 150.

4g. Time window

The periodDays parameter controls the averaging window:

  • Default: 1 day (single 24-hour period)
  • For seasonal DNL: the full season duration is passed (e.g., Memorial Day through Labor Day ≈ 100 days)
  • The About page states DNL is "energy-averaged over the full season (Memorial Day through Labor Day)" (about/page.tsx:1365)

DNL is not computed on a rolling basis — it is calculated on demand from stored flight events for the requested period.

4h. Convenience wrapper

calculateDNLFromFlights() (dnlCalculator.ts:253–294) combines SEL computation and DNL aggregation. For each flight, it:

  1. Calls calculateSEL() with the flight's referenceDb and slantDistanceFt
  2. Classifies as nighttime/daytime via isNighttimeET()
  3. Aggregates all events via calculateDNL()

Returns a DNLResult with: dnl, events[], nighttimeCount, daytimeCount.

4i. Referenced standards

Listed in file header (dnlCalculator.ts:16–19):

  • FAA Order 1050.1F, Appendix B: Noise and Noise-Compatible Land Use
  • 14 CFR Part 150: Airport Noise Compatibility Planning
  • FICON (Federal Interagency Committee on Noise), 1992
  • EASA Certification Noise Levels database (source noise data)

4j. TODOs and known issues

  • No TODOs or FIXMEs in dnlCalculator.ts.
  • The SEL formula omits atmospheric absorption and lateral attenuation, making it a simpler model than the track-based propagation. This is not flagged in the code as intentional or unintentional.
  • Empty input returns 0 (line 214), not null or undefined. A location with zero flights during a period shows DNL=0 rather than "no data."

5. Noise Rank

5a. What the code does

"Noise Rank" in JPXWatch encompasses two distinct metrics:

  1. Noise Impact Score — a 0–100 composite metric combining fleet mix, traffic density, and time-of-day. Computed client-side. File: lib/calculations/noiseImpactScore.ts.
  2. NoiseRank Percentile — a location-specific percentile ranking based on estimated DNL at the user's address compared to a grid of points. Computed server-side via PostGIS. Displayed on the "My Home" page.

5b. Noise Impact Score formula

calculateNoiseImpactScore() (noiseImpactScore.ts:101–121):

score = round( fleetMixScore × 0.4 + concentrationScore × 0.3 + timeOfDayScore × 0.3 )

Component 1: Fleet Mix (40%)calculateFleetMixScore() (lines 23–41):

  • Counts "loud" operations: all helicopters + jets where getNoiseDb(f) >= LOUD_THRESHOLD_DB (85 dB).
  • Formula: min(100, (loudCount / totalOps) × 500).
  • Scale: 20% loud operations = score of 100.
  • Per flight, not per operator.

Component 2: Concentration (30%)calculateConcentrationScore() (lines 50–56):

  • Measures operations density.
  • Formula: min(100, avgOpsPerDay × 2).
  • Scale: 50 ops/day = score of 100.

Component 3: Time-of-Day (30%)calculateTimeOfDayScore() (lines 65–76):

  • Counts curfew-period operations: operation_hour_et >= 21 || operation_hour_et < 7 (line 70).
  • This uses the town curfew window (9 PM – 7 AM), not the FAA nighttime window (10 PM – 7 AM).
  • Formula: min(100, (curfewOps / totalOps) × 1000).
  • Scale: 10% curfew operations = score of 100.

5c. Severity tiers

getSeverity() (noiseImpactScore.ts:87–92):

ScoreSeverityDescription (from docstring)Line
80–100SEVEREExtreme noise burden requiring immediate attention88
65–79HIGHSignificant community impact89
40–64MODERATENoticeable noise burden90
0–39LOWMinimal community impact91

5d. Aggregate score variant

calculateNoiseImpactScoreFromAggregates() (noiseImpactScore.ts:128–170) is used when per-flight dB data is unavailable (e.g., monthly aggregates). It takes pre-aggregated counts: totalOps, helicopters, jets, curfewOps, daysWithData.

Critical difference: This variant treats all jets as loud (line 150: loudCount = helicopters + jets) because per-flight dB data is not available in the aggregate. This overestimates the fleet mix score.

FLAG — Known overestimation documented in code. Lines 145–149: "For monthly aggregates we don't have per-flight dB data, so we treat ALL jets as loud (≥85 dB). This overestimates fleetMixScore because many jets at KJPX are light jets below 85 dB." TODO on line 149: "Add a loud_jets column to monthly aggregates to improve accuracy."

5e. NoiseRank Percentile

Implemented in app/my-home/page.tsx (lines 449–546, approximate — this is a large React page).

Season determination:

  • If current month ≥ 5 (May): season year = current year.
  • If current month < 5: season year = previous year (continues prior summer season).
  • Season label: "Memorial Day YYYY - Today" (during season) or "Summer YYYY (Final)" (after season).

Percentile computation:

  1. Calls PostGIS RPC nearest_noise_grid with user's lat/lon to find the nearest noise grid point and its estimated DNL.
  2. Calls PostGIS RPC noise_rank_percentile with that DNL value and season year to get the percentile rank (0–100).
  3. Percentile represents: "X% of grid points have lower noise than this location."

Year-over-year comparison:

  • Fetches previous season's (year − 1) DNL and percentile for the same location.
  • Displayed as previousPercentile alongside current percentile.
  • Fails silently if prior year data unavailable.

Demo fallback values (when user not authenticated or no data):

  • dnl: 62.4, flightCount: 847, percentile: 90, totalPoints: 30000, seasonLabel: 'Summer 2025 (Demo)', previousPercentile: 85.

5f. Percentile display bands

components/noiserank/PercentileCard.tsx (lines 25–66):

RangeLabelColor
0–20Lowemerald
20–40Below averageteal
40–60Averageamber
60–80Above averageorange
80–100Highred

5g. DNL display on "My Home"

components/noiserank/DNLCard.tsx (lines 31–187):

  • DNL bar visualization with four segments: 0–55 (green), 55–65 (amber), 65–75 (orange), 75–85 (red).
  • Dashed marker at 65 dB (FAA threshold).
  • Triangle indicator at user's DNL value.
  • Shows flight count and season label.

5h. How ties are broken / sparse data handled

  • Ties: No explicit tie-breaking. The PostGIS noise_rank_percentile function returns a percentile; behavior on ties depends on the server-side RPC implementation (not in the client codebase).
  • Sparse data: If zero flights exist for a period, calculateNoiseImpactScore returns score=0, severity='LOW', all components=0. The percentile system relies on pre-computed noise grid data; if the grid is empty, the RPC returns null and the UI falls back to demo values.

5i. TODOs and known issues

  • noiseImpactScore.ts:149: TODO to add loud_jets column to monthly aggregates.
  • The Noise Impact Score is per-period (day/week/month), not per-aircraft or per-operator. It describes the aggregate noise environment, not individual actor responsibility.
  • The percentile grid computation (PostGIS RPCs) is not in the client codebase; the audit can only document the client's interaction with those RPCs.

6. Noise Trails

6a. What is being visualized

Noise Trails are animated flight paths on the map where each track point is colored by its estimated ground-level noise. The visualization shows:

  • A colored polyline tracing the aircraft's GPS path
  • Color at each point reflects the estimated dB a listener directly below would hear
  • Line width at each point reflects altitude (lower = wider = more impact)
  • An aircraft dot at the current position

File: lib/noise/trailNoise.ts (159 lines).

6b. Underlying computation

computeNoiseTrail() (trailNoise.ts:90–159) processes raw FlightAware track positions into NoiseTrailPoint[]:

  1. Profile lookup (line 102): getAircraftNoiseProfile(aircraftType) — uses the 28-type client-side table, not the 47-type EASA map.
  2. Source dB selection (line 103): arrivals → approachDb, departures → takeoffDb.
  3. Point ordering (lines 108–126): Sorts by distance to KJPX (40.9594, −72.2518) so that arrivals animate origin→KJPX and departures animate KJPX→destination. Uses Euclidean distance on lat/lon (not Haversine).
  4. Altitude conversion (lines 139–141):
    • FlightAware altitude is in hundreds of feet (flight levels) — multiplied by 100 (line 140).
    • MSL to AGL: altitudeAgl = altitudeMsl − GROUND_ELEVATION_FT where GROUND_ELEVATION_FT = 30 (line 22).
  5. Noise estimation (line 142): estimateGroundNoise(referenceDb, altitudeAgl) — altitude-only model, assumes listener directly below.
  6. Trail width (line 143): getTrailWidth(altitudeAgl).
  7. Trail color (line 144): getNoiseColorFromDb(noiseDb).

6c. Ground noise formula (trail-specific)

estimateGroundNoise() (trailNoise.ts:60–71):

effectiveAgl = max(aglFt, 50)
distanceAttenuation = 20 × log₁₀(effectiveAgl / 1000)
atmosphericAbsorption = 0.5 × (effectiveAgl / 1000)
groundDb = referenceDb − distanceAttenuation − atmosphericAbsorption

This is the altitude-only simplification: no horizontal distance, no lateral attenuation, no weather. The listener is assumed to be directly below the aircraft.

Minimum AGL clamp: 50 ft (line 62). Result rounded to 1 decimal place.

6d. Filtering

Track points are filtered before processing (trailNoise.ts:129–136):

  • altitude == null or altitude <= 0 → excluded (line 130).
  • altitude <= 1 (≤ 100 ft MSL) → excluded as ground/taxi (line 134).
  • altitude <= 2 (≤ 200 ft MSL) AND groundspeed < 60 kts → excluded as likely ground (line 135).

No time-window filtering — all valid track points from the FlightAware response are included.

6e. Color thresholds

getNoiseColorFromDb() (components/map/mapConstants.ts:17–22):

ThresholdColorCategory labelLine
≥ 85 dB#ef4444 (red)veryLoud18
≥ 75 dB#f97316 (orange)loud19
≥ 65 dB#eab308 (yellow)moderate20
< 65 dB#22c55e (green)quiet21

These are the same four colors used across the map UI. The 65/75/85 dB boundaries are defined in mapConstants.ts and imported by the trail computation.

6f. Trail width thresholds

getTrailWidth() (trailNoise.ts:77–82):

AGL altitudeWidthMeaning
≤ 500 ft12 pxVery low altitude, high ground impact
≤ 1,000 ft8 pxLow altitude
≤ 2,000 ft5 pxMedium altitude
> 2,000 ft3 pxHigh altitude, lower impact

Provenance: Not cited. These are visual design parameters.

6g. Animation rendering

components/map/AnimatedFlightLayer.tsx renders the computed trail data:

  • Glow layer (lines 140–156): 6 px width, 0.15–0.3 opacity, 4 px blur. Creates a soft halo effect.
  • Core line (lines 158–169): 2 px width, 0.7 opacity, round cap/join.
  • Aircraft dot: 4.5–6 px radius with white halo, noise-colored core.

All colors come from the pre-computed trail_color field on each NoiseTrailPoint.

6h. Data pipeline

Live tracks are served by GET /api/flights/live-tracks with a since parameter (ISO timestamp):

  • Default window: last 60 minutes.
  • Cache TTL: 30 seconds.
  • Max window: 120 minutes.
  • Each trail includes peak_noise_db (max across points) and avg_noise_db (mean across points).

6i. Smoothing and aggregation

None. Each track point is independently computed from its raw GPS position and altitude. There is no temporal smoothing, spatial averaging, or interpolation between points. The visualization shows raw per-point estimates.

6j. TODOs and known issues

  • No TODOs or FIXMEs in trailNoise.ts.
  • The trail model uses GROUND_ELEVATION_FT = 30 (line 22), while the footprint model uses KJPX_ELEVATION_FT = 55 (see Section 3c flag).
  • The trail model uses getAircraftNoiseProfile() (28-type client-side table) while trackNoiseCalculator.ts uses getEASANoiseProfile() (47-type EASA table). An aircraft type present in the EASA map but absent from the client-side table will get different dB values in trails vs. track-based analysis.
  • The point-ordering algorithm uses Euclidean distance on lat/lon (Math.sqrt((p.latitude - KJPX_LAT)² + (p.longitude - KJPX_LON)²), line 111), not Haversine. At KJPX's latitude (~41°N), one degree of longitude ≈ 0.755× one degree of latitude, introducing minor distortion. This is unlikely to cause incorrect ordering in practice.

7. High-Noise Events

7a. What the code does

A "high-noise event" is a flight that produces an estimated ground-level noise of ≥ 65 dB at a specific location. This threshold is the FAA's DNL significance threshold applied to individual flyover events. The feature counts how many flights exceeded this level at the user's address during a season.

7b. Threshold definition

PropertyValueSource
Threshold65 dBFAA_DNL_THRESHOLD = 65 in lib/noise/dnlCalculator.ts:30
UnitsdBA LAmax (A-weighted maximum sound level, estimated)Derived from EASA/FAA certification data
MeasurementPhysics-based estimate, not physical measurementNo noise monitors installed at KJPX

The 65 dB value comes from 14 CFR Part 150, which defines 65 dB DNL as the threshold for "significant" noise exposure. JPXWatch applies this same threshold to individual flyover events, not just to 24-hour DNL averages.

The About page confirms: "No monitors are installed at JPX" (about/page.tsx:1238). The file lib/noise/getNoiseDb.ts:15 states: "No physical noise monitoring equipment is currently installed at JPX."

7c. How events are measured

The noise at a location is computed using the propagation model (see Section 3):

groundDb = sourceDb − 20 × log₁₀(slantDist / 1000) − 0.5 × (slantDist / 1000)

Where slantDist = √(altitude_ft² + horizontalDistance_ft²).

For the "My Home" feature, noise is computed per track position against the user's saved address. For the residential analysis, noise is computed per flight per parcel using the same formula.

In trackNoiseCalculator.ts (line 539): events where estimate.db >= 65 accumulate timeAboveThreshold (counted in seconds, assuming 5-second position intervals, line 514).

7d. Configurability

The 65 dB threshold is not configurable:

  • Hardcoded as FAA_DNL_THRESHOLD in dnlCalculator.ts:30.
  • Hardcoded in UI tooltip text in components/noiserank/HighNoiseEventsCard.tsx (line 29): "Flights where estimated noise at your address exceeded 65 dB, the FAA's threshold for significant noise exposure."
  • No environment variable, feature flag, or per-user/per-location setting exists.
  • The threshold is the same globally for all users and all locations.

7e. How events are counted

Events are counted per aircraft pass per location. A single flight that produces ≥ 65 dB at one or more track positions at the user's location counts as one high-noise event. The system does not count per-dB-exceedance or per-time-window.

In the residential analysis (docs/high-noise-events-residential-analysis-v3.md), the counting methodology uses a two-stage filter:

  1. Range pre-filter: Each aircraft type has a pre-computed maximum horizontal distance at which it can produce ≥ 65 dB. Flight-parcel pairs beyond this range are skipped without full noise computation.
  2. Full attenuation filter: For pairs that pass the range check, the full propagation formula is applied. Only events ≥ 65 dB are counted.

7f. Noise Index (related metric)

The "Noise Index" is a related but different count, defined in lib/noise/getNoiseDb.ts:60–73:

  • Counts: all helicopter operations + jets where estimated noise ≥ 85 dB (LOUD_THRESHOLD_DB, line 22).
  • The 85 dB threshold is for aircraft-level source noise (at 1,000 ft reference), not ground-level received noise.
  • This is a fleet-composition metric (how many loud aircraft types operated), not a location-based impact metric.

7g. Display

components/noiserank/HighNoiseEventsCard.tsx:

  • Shows count of high-noise events out of total flights.
  • Text: "{count} of {total} flights above 65 dB at your address."

7h. About page discrepancy

FLAG — "Est. Noise" column inconsistency. The residential analysis document (docs/high-noise-events-residential-analysis-v3.md, lines 119–130) notes that the "My Home" feature's "Est. Noise" column displays raw certification noise (source dB at 1,000 ft reference) rather than distance-attenuated noise. This means the column shows e.g., 88 dB for an S76 regardless of the flight's actual distance from the user. The High-Noise Events count, by contrast, uses fully attenuated noise. This creates an inconsistency where the displayed "Est. Noise" for a flight may be well above 65 dB while the flight does not count as a high-noise event (because attenuated noise was below 65 dB), or vice versa for a flight that passed very close at low altitude.

7i. TODOs and known issues

  • No TODOs or FIXMEs in the high-noise event code paths.
  • The 65 dB threshold is borrowed from the FAA's DNL metric (a 24-hour energy average) and applied to individual flyover peak levels. These are different acoustic metrics — a single flight producing 65 dB LAmax at a point is not the same as experiencing 65 dB DNL over a full day. The code does not document this distinction.