prop/docs/algo-reports/2026-05-04-recalibration.md
Graham McIntire 58f4a7714b
test: add Mox deps + SnmpClient poll/3 tests, RoadProximity, Buildings.Loader
- Add {:mox, "~> 1.0", only: :test} to deps
- Commercial.SnmpClient: injectable snmp_runner for poll/3 tests (62→92%)
- Rover.RoadProximity: pure road_distances/3 tests with injectable fetch (37→82%)
- Buildings.Loader: ensure_loaded/1 with real tile file + quadkey tests (69→100%)
- Buildings.Index: records_near, mark_loaded, loaded? tests (72→96%)

Coverage: 77.96% → 78.93% (+0.97%)
2026-05-07 13:36:22 -05:00

552 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Recalibration Analysis Report — 2026-05-04
> Auto-generated by `scripts/recalibrate_algo.py`. This report is the
> input for `algo.md` updates — diff against the prior dated section to
> see what's actually moved.
Connection: `postgres://prop:***@10.0.15.30:5432/prop`
Statement timeout: 20min
## Row counts and date ranges
| tbl | n | lo | hi |
|:---------------------|-------------:|:-----------|:-----------|
| contacts | 82084.000 | 1991-05-04 | 2026-05-02 |
| hrrr_native_profiles | 11472.000 | 2019-03-09 | 2025-09-21 |
| hrrr_profiles | 81302322.000 | 2016-09-17 | 2026-05-02 |
| iemre_observations | 28936.000 | | |
| narr_profiles | 395.000 | 1991-05-04 | 2014-09-19 |
| nexrad_observations | 7286.000 | 2019-08-17 | 2024-09-22 |
| propagation_scores | nan | | |
| soundings | 27745.000 | 1990-06-07 | 2026-05-04 |
| surface_observations | 648809.000 | 1991-05-03 | 2026-05-04 |
| terrain_profiles | 82084.000 | | |
## Data gaps and ingestion health
Severity legend: **BROKEN** = table is empty and feeds an active scoring path; **WARN** = populated but coverage is low enough to skew per-band statistics; **OK** = within spec or informational. Each non-OK row carries a one-line remediation hint.
| metric | value | severity | remediation |
|:-----------------------|--------:|:-----------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| narr_rows | 395 | OK | |
| narr_pre2014_rows | 395 | OK | |
| hrrr_climatology_rows | 0 | BROKEN | Should self-heal within 24h via the daily 02:30Z cron that runs AdminTaskWorker(task=climatology). If still 0 after a day, check Oban dashboard for failed admin jobs. |
| hrrr_complete_contacts | 81278 | OK | |
| hrrr_pending_contacts | 806 | OK | |
| pre2014_contacts | 207 | OK | |
HRRR enrichment completeness: **99.0%** (81,278 complete / 806 pending).
### NARR coverage by year
Year-bucketed counts. NARR is supposed to span 1979 → 2014-10 at 3-hourly cadence (~2,920 timestamps per year per grid cell). Years far below the others are partial backfills the worker hasn't finished.
| year | profiles |
|---------:|-----------:|
| 1991.000 | 31.000 |
| 1992.000 | 4.000 |
| 1993.000 | 18.000 |
| 1994.000 | 19.000 |
| 1996.000 | 3.000 |
| 1997.000 | 8.000 |
| 1998.000 | 2.000 |
| 1999.000 | 11.000 |
| 2000.000 | 10.000 |
| 2001.000 | 14.000 |
| 2002.000 | 12.000 |
| 2003.000 | 36.000 |
| 2004.000 | 23.000 |
| 2005.000 | 49.000 |
| 2006.000 | 20.000 |
| 2007.000 | 44.000 |
| 2008.000 | 15.000 |
| 2009.000 | 17.000 |
| 2010.000 | 31.000 |
| 2011.000 | 4.000 |
| 2012.000 | 15.000 |
| 2013.000 | 3.000 |
| 2014.000 | 6.000 |
### HRRR enrichment status by year
Locate the pending backlog: a year with high `queued`/`failed`/`unset` counts is where HrrrFetchWorker needs to be rerun. Recent years dominating `queued` usually means the live Oban queue is stuck rather than a historical backfill gap.
| year | complete | queued | failed | unset |
|---------:|-----------:|---------:|---------:|--------:|
| 2014.000 | 0.000 | 0.000 | 0.000 | 0.000 |
| 2015.000 | 0.000 | 0.000 | 0.000 | 0.000 |
| 2016.000 | 2.000 | 0.000 | 0.000 | 0.000 |
| 2017.000 | 26.000 | 0.000 | 0.000 | 0.000 |
| 2018.000 | 2658.000 | 0.000 | 0.000 | 0.000 |
| 2019.000 | 13036.000 | 0.000 | 0.000 | 0.000 |
| 2020.000 | 10300.000 | 0.000 | 0.000 | 0.000 |
| 2021.000 | 11266.000 | 0.000 | 0.000 | 0.000 |
| 2022.000 | 14487.000 | 2.000 | 0.000 | 0.000 |
| 2023.000 | 13461.000 | 5.000 | 0.000 | 0.000 |
| 2024.000 | 13378.000 | 0.000 | 0.000 | 0.000 |
| 2025.000 | 2640.000 | 0.000 | 0.000 | 0.000 |
| 2026.000 | 24.000 | 0.000 | 0.000 | 0.000 |
## Contacts by band (≥50 MHz)
| band_mhz | contacts | avg_km | max_km |
|-----------:|-----------:|---------:|---------:|
| 222 | 7595 | 229.000 | 2306.000 |
| 432 | 9177 | 200.000 | 1752.000 |
| 902 | 1794 | 171.000 | 915.000 |
| 1296 | 2996 | 160.000 | 1752.000 |
| 2304 | 749 | 153.000 | 655.000 |
| 3400 | 351 | 151.000 | 643.000 |
| 5760 | 336 | 126.000 | 505.000 |
| 10000 | 54280 | 211.000 | 2393.000 |
| 24000 | 3815 | 96.000 | 710.000 |
| 47000 | 774 | 65.000 | 343.000 |
| 75000 | 117 | 57.000 | 289.000 |
| 122000 | 70 | 18.000 | 139.000 |
| 134000 | 5 | 65.000 | 157.000 |
| 142000 | 4 | 47.000 | 79.700 |
| 145000 | 2 | 40.000 | 79.600 |
| 241000 | 14 | 31.000 | 114.400 |
| 288000 | 1 | 1.000 | 1.246 |
| 322000 | 1 | 1.000 | 1.400 |
| 403000 | 1 | 1.000 | 1.400 |
| 411000 | 1 | 0.000 | 0.050 |
## Monthly sounding ducting
| month | soundings | ducting_pct | avg_min_grad | avg_pwat |
|--------:|------------:|--------------:|---------------:|-----------:|
| 1 | 103 | 27.200 | -179.200 | 10.400 |
| 2 | 131 | 29.000 | -163.100 | 7.900 |
| 3 | 93 | 17.200 | -157.200 | 8.900 |
| 4 | 330 | 24.200 | -155.500 | 11.300 |
| 5 | 360 | 48.100 | -269.200 | 23.900 |
| 6 | 396 | 68.900 | -352.900 | 31.600 |
| 7 | 304 | 67.100 | -292.500 | 29.600 |
| 8 | 5045 | 55.200 | -255.800 | 35.100 |
| 9 | 2345 | 56.300 | -280.000 | 27.300 |
| 10 | 179 | 55.300 | -304.800 | 18.300 |
| 11 | 194 | 45.400 | -237.300 | 11.600 |
| 12 | 158 | 25.900 | -182.400 | 10.800 |
## HRRR native-profile duct distribution (best supportable band)
| band_bin | profiles | avg_inv_top_m | avg_theta_e_jump | avg_richardson |
|:-----------|-----------:|----------------:|-------------------:|-----------------:|
| 15-30 GHz | 10 | 2091.000 | 5.900 | 8.690 |
| 30-75 GHz | 4 | 3860.000 | 5.000 | 12.120 |
| 5-15 GHz | 98 | 4408.000 | 25.700 | 12.550 |
| <5 GHz | 1432 | 2097.000 | 5.400 | 18.420 |
| none | 9928 | 11497.000 | 80.500 | 38.270 |
## Contact ↔ HRRR per-band Pearson correlations (matched n=81547)
| band_mhz | n | rho_pr | rho_dpc | rho_pwat | rho_sref | rho_grad | rho_tc | rho_hpbl |
|-----------:|----------:|---------:|----------:|-----------:|-----------:|-----------:|---------:|-----------:|
| 222.000 | 7518.000 | -0.014 | 0.077 | 0.068 | 0.087 | 0.040 | -0.081 | -0.039 |
| 432.000 | 9073.000 | 0.074 | 0.134 | 0.111 | 0.132 | 0.008 | -0.088 | -0.063 |
| 902.000 | 1754.000 | 0.080 | 0.097 | 0.050 | 0.090 | -0.039 | -0.070 | -0.029 |
| 1296.000 | 2951.000 | 0.025 | 0.103 | 0.053 | 0.110 | -0.077 | -0.041 | -0.051 |
| 2304.000 | 719.000 | 0.079 | 0.165 | 0.130 | 0.192 | -0.126 | -0.059 | -0.086 |
| 3400.000 | 335.000 | -0.026 | 0.154 | 0.150 | 0.221 | 0.061 | -0.061 | -0.024 |
| 5760.000 | 320.000 | 0.156 | 0.076 | 0.099 | 0.091 | 0.003 | -0.068 | -0.057 |
| 10000.000 | 54150.000 | -0.086 | -0.022 | -0.029 | 0.000 | 0.030 | -0.004 | 0.007 |
| 24000.000 | 3794.000 | -0.221 | -0.338 | -0.362 | -0.241 | 0.023 | -0.211 | -0.009 |
| 47000.000 | 757.000 | -0.256 | -0.215 | -0.233 | -0.054 | -0.021 | -0.113 | -0.079 |
| 75000.000 | 109.000 | -0.768 | -0.699 | -0.620 | -0.582 | 0.434 | -0.680 | 0.365 |
| 122000.000 | 63.000 | -0.041 | -0.129 | -0.127 | -0.248 | 0.355 | 0.039 | 0.400 |
## Per-band HPBL bin distance distribution
One table per band with 50 matched contacts. A band-specific effect here means HPBL belongs in the scorer for that band.
**222 MHz:**
| bin | n | avg_km | p50_km |
|:----------|-----:|---------:|---------:|
| <200 | 1224 | 266.100 | 205.500 |
| 200-500 | 1556 | 225.600 | 167.000 |
| 500-1000 | 1754 | 213.300 | 167.500 |
| 1000-1500 | 1426 | 223.700 | 170.000 |
| 1500-2000 | 1239 | 224.700 | 178.000 |
| >=2000 | 319 | 233.800 | 158.000 |
**432 MHz:**
| bin | n | avg_km | p50_km |
|:----------|-----:|---------:|---------:|
| <200 | 1579 | 224.500 | 178.000 |
| 200-500 | 1867 | 205.800 | 153.000 |
| 500-1000 | 2114 | 189.900 | 139.800 |
| 1000-1500 | 1683 | 196.800 | 148.000 |
| 1500-2000 | 1475 | 192.600 | 145.000 |
| >=2000 | 355 | 171.700 | 124.000 |
**902 MHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <200 | 271 | 198.300 | 162.000 |
| 200-500 | 366 | 164.900 | 138.000 |
| 500-1000 | 433 | 166.500 | 131.600 |
| 1000-1500 | 356 | 166.800 | 136.500 |
| 1500-2000 | 282 | 174.900 | 152.000 |
| >=2000 | 46 | 153.500 | 126.500 |
**1.296 GHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <200 | 443 | 174.500 | 145.000 |
| 200-500 | 612 | 173.100 | 133.500 |
| 500-1000 | 735 | 152.500 | 116.000 |
| 1000-1500 | 585 | 158.200 | 131.000 |
| 1500-2000 | 479 | 152.600 | 122.000 |
| >=2000 | 97 | 143.800 | 108.000 |
**2.304 GHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <200 | 101 | 167.000 | 151.000 |
| 200-500 | 131 | 182.200 | 157.000 |
| 500-1000 | 188 | 140.800 | 122.000 |
| 1000-1500 | 159 | 146.700 | 135.000 |
| 1500-2000 | 123 | 148.300 | 135.000 |
| >=2000 | 17 | 137.400 | 140.000 |
**3.4 GHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <200 | 54 | 143.100 | 122.000 |
| 200-500 | 60 | 178.200 | 141.000 |
| 500-1000 | 74 | 145.600 | 108.000 |
| 1000-1500 | 84 | 149.400 | 133.000 |
| 1500-2000 | 48 | 165.100 | 138.700 |
| >=2000 | 15 | 121.800 | 128.000 |
**5.76 GHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <200 | 37 | 148.800 | 108.000 |
| 200-500 | 54 | 138.400 | 135.000 |
| 500-1000 | 85 | 113.300 | 108.000 |
| 1000-1500 | 74 | 130.900 | 127.500 |
| 1500-2000 | 54 | 121.200 | 122.000 |
| >=2000 | 16 | 124.200 | 125.000 |
**10 GHz:**
| bin | n | avg_km | p50_km |
|:----------|------:|---------:|---------:|
| <200 | 10466 | 212.500 | 182.600 |
| 200-500 | 14330 | 205.900 | 179.500 |
| 500-1000 | 16463 | 208.700 | 190.300 |
| 1000-1500 | 8712 | 212.400 | 198.500 |
| 1500-2000 | 2985 | 201.200 | 179.000 |
| >=2000 | 1191 | 228.700 | 193.500 |
**24 GHz:**
| bin | n | avg_km | p50_km |
|:----------|-----:|---------:|---------:|
| <200 | 731 | 100.200 | 92.200 |
| 200-500 | 1079 | 91.500 | 87.700 |
| 500-1000 | 1179 | 94.800 | 87.700 |
| 1000-1500 | 534 | 98.500 | 95.700 |
| 1500-2000 | 196 | 99.900 | 99.400 |
| >=2000 | 75 | 74.300 | 31.800 |
**47 GHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <200 | 172 | 67.100 | 63.600 |
| 200-500 | 237 | 62.600 | 61.300 |
| 500-1000 | 253 | 61.900 | 65.400 |
| 1000-1500 | 58 | 71.000 | 70.900 |
| 1500-2000 | 29 | 33.500 | 23.700 |
| >=2000 | 8 | 33.900 | 32.300 |
**75 GHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <200 | 19 | 18.600 | 19.600 |
| 200-500 | 31 | 44.300 | 36.700 |
| 500-1000 | 53 | 58.800 | 36.700 |
| 1000-1500 | 6 | 56.100 | 29.800 |
**122 GHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <200 | 7 | 4.400 | 5.000 |
| 200-500 | 27 | 8.800 | 8.000 |
| 500-1000 | 18 | 13.600 | 7.000 |
| 1000-1500 | 6 | 78.500 | 85.500 |
| 1500-2000 | 3 | 10.000 | 12.000 |
| >=2000 | 2 | 5.000 | 5.000 |
## Per-band pressure bin distance distribution
Surface pressure tends to proxy synoptic-scale stagnation, so we want to see longer distances under the 1015-1025 mb ridge at every band that ducts.
**222 MHz:**
| bin | n | avg_km | p50_km |
|:----------|-----:|---------:|---------:|
| <990 | 2720 | 227.200 | 178.000 |
| 990-1000 | 1926 | 255.100 | 201.000 |
| 1000-1010 | 1799 | 233.800 | 186.000 |
| 1010-1020 | 953 | 181.400 | 131.000 |
| >=1020 | 120 | 166.100 | 132.000 |
**432 MHz:**
| bin | n | avg_km | p50_km |
|:----------|-----:|---------:|---------:|
| <990 | 3536 | 188.800 | 141.000 |
| 990-1000 | 2278 | 226.700 | 178.000 |
| 1000-1010 | 1832 | 210.900 | 156.500 |
| 1010-1020 | 1264 | 174.100 | 128.000 |
| >=1020 | 163 | 159.000 | 121.000 |
**902 MHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <990 | 590 | 154.100 | 127.000 |
| 990-1000 | 510 | 203.200 | 157.500 |
| 1000-1010 | 369 | 189.200 | 148.000 |
| 1010-1020 | 249 | 139.200 | 143.000 |
| >=1020 | 36 | 79.700 | 51.500 |
**1.296 GHz:**
| bin | n | avg_km | p50_km |
|:----------|-----:|---------:|---------:|
| <990 | 1038 | 155.400 | 125.000 |
| 990-1000 | 800 | 183.100 | 146.000 |
| 1000-1010 | 620 | 165.400 | 117.000 |
| 1010-1020 | 444 | 132.400 | 114.000 |
| >=1020 | 49 | 119.800 | 101.000 |
**2.304 GHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <990 | 190 | 146.500 | 134.000 |
| 990-1000 | 250 | 155.000 | 122.000 |
| 1000-1010 | 144 | 172.600 | 134.000 |
| 1010-1020 | 121 | 147.600 | 152.000 |
| >=1020 | 14 | 127.600 | 137.000 |
**3.4 GHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <990 | 95 | 154.400 | 136.000 |
| 990-1000 | 159 | 159.200 | 134.000 |
| 1000-1010 | 45 | 166.000 | 140.000 |
| 1010-1020 | 27 | 111.700 | 98.000 |
| >=1020 | 9 | 114.600 | 131.000 |
**5.76 GHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <990 | 86 | 106.000 | 105.500 |
| 990-1000 | 139 | 133.200 | 111.000 |
| 1000-1010 | 51 | 136.100 | 104.000 |
| 1010-1020 | 34 | 143.200 | 152.000 |
| >=1020 | 10 | 140.600 | 139.000 |
**10 GHz:**
| bin | n | avg_km | p50_km |
|:----------|------:|---------:|---------:|
| <990 | 27142 | 221.800 | 213.500 |
| 990-1000 | 13469 | 181.200 | 158.600 |
| 1000-1010 | 7236 | 204.000 | 175.200 |
| 1010-1020 | 4658 | 218.700 | 175.300 |
| >=1020 | 1645 | 229.300 | 207.600 |
**24 GHz:**
| bin | n | avg_km | p50_km |
|:----------|-----:|---------:|---------:|
| <990 | 1485 | 100.900 | 96.500 |
| 990-1000 | 1462 | 87.200 | 81.000 |
| 1000-1010 | 463 | 114.400 | 113.400 |
| 1010-1020 | 258 | 70.900 | 72.100 |
| >=1020 | 126 | 102.700 | 89.600 |
**47 GHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <990 | 279 | 68.500 | 70.900 |
| 990-1000 | 297 | 58.000 | 57.300 |
| 1000-1010 | 63 | 69.600 | 68.000 |
| 1010-1020 | 89 | 60.600 | 65.400 |
| >=1020 | 29 | 43.400 | 42.000 |
**75 GHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <990 | 55 | 65.300 | 57.300 |
| 990-1000 | 32 | 17.500 | 10.400 |
| 1000-1010 | 12 | 71.800 | 89.800 |
| 1010-1020 | 8 | 18.300 | 19.600 |
| >=1020 | 2 | 11.700 | 11.700 |
**122 GHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <990 | 22 | 12.000 | 7.000 |
| 990-1000 | 19 | 27.300 | 7.000 |
| 1000-1010 | 10 | 8.400 | 6.000 |
| 1010-1020 | 10 | 13.400 | 8.000 |
| >=1020 | 2 | 12.000 | 12.000 |
## Contact ↔ NARR per-band Pearson correlations (pre-2014 only, matched n=201)
Historical sanity check: if the HRRR-era signs and magnitudes survive on 30+ years of NARR reanalysis, the scoring factors are physical rather than HRRR-artefact. Diverging signs = flag it.
| band_mhz | n | rho_pr | rho_dpc | rho_pwat | rho_sref | rho_grad | rho_tc | rho_hpbl |
|-----------:|--------:|---------:|----------:|-----------:|-----------:|-----------:|---------:|-----------:|
| 10000.000 | 103.000 | -0.402 | -0.402 | -0.402 | -0.234 | -0.222 | -0.402 | -0.402 |
## NEXRAD composite reflectivity vs distance (matched n=20525)
Rain-attenuation sanity check — one table per band with ≥50 matched contacts. At rain-sensitive bands (24+ GHz) higher `max_dbz` should map to shorter contacts; at 10 GHz and below the effect should be barely detectable.
**10 GHz:**
| bin | n | avg_km | p50_km |
|:-----------------|------:|---------:|---------:|
| none (<5) | 13803 | 207.900 | 193.500 |
| drizzle (5-20) | 1270 | 201.800 | 200.400 |
| light (20-30) | 1696 | 209.600 | 187.900 |
| moderate (30-45) | 1594 | 229.500 | 213.900 |
| heavy (>=45) | 675 | 191.600 | 146.300 |
**24 GHz:**
| bin | n | avg_km | p50_km |
|:-----------------|----:|---------:|---------:|
| none (<5) | 868 | 91.500 | 83.700 |
| drizzle (5-20) | 74 | 108.300 | 123.200 |
| light (20-30) | 151 | 88.200 | 70.900 |
| moderate (30-45) | 76 | 109.300 | 122.400 |
| heavy (>=45) | 49 | 86.600 | 102.300 |
**47 GHz:**
| bin | n | avg_km | p50_km |
|:-----------------|----:|---------:|---------:|
| none (<5) | 186 | 52.400 | 56.000 |
| light (20-30) | 30 | 59.300 | 66.300 |
| moderate (30-45) | 9 | 75.100 | 81.600 |
| heavy (>=45) | 15 | 71.400 | 82.800 |
## hrrr_native_profiles.best_duct_band_ghz vs distance (matched n=56845)
Validates the 1.15× boost in `Scorer.score_refractivity/4`: contacts where the native duct supports the target band should run longer than those where it does not. One table per band with ≥50 matched contacts.
**10 GHz:**
| bin | n | avg_km | p50_km |
|:----------|------:|---------:|---------:|
| none | 44655 | 211.200 | 192.400 |
| <5 GHz | 7073 | 200.000 | 169.300 |
| 5-10 GHz | 439 | 199.000 | 174.200 |
| 10-24 GHz | 146 | 204.700 | 242.700 |
| 24-47 GHz | 5 | 176.600 | 133.300 |
| >=47 GHz | 23 | 147.800 | 131.500 |
**24 GHz:**
| bin | n | avg_km | p50_km |
|:----------|---------:|---------:|---------:|
| none | 3183.000 | 94.800 | 92.200 |
| <5 GHz | 473.000 | 98.600 | 84.900 |
| 5-10 GHz | 36.000 | 97.500 | 113.700 |
| 10-24 GHz | 2.000 | 44.700 | 44.700 |
| >=47 GHz | 6.000 | 131.500 | 131.500 |
**47 GHz:**
| bin | n | avg_km | p50_km |
|:---------|--------:|---------:|---------:|
| none | 552.000 | 64.300 | 65.400 |
| <5 GHz | 131.000 | 56.800 | 61.800 |
| 5-10 GHz | 6.000 | 98.100 | 113.700 |
**75 GHz:**
| bin | n | avg_km | p50_km |
|:-------|-------:|---------:|---------:|
| none | 77.000 | 54.200 | 36.700 |
| <5 GHz | 15.000 | 13.900 | 14.600 |
## Commercial-link rx_power degradation vs contemporaneous DFW contacts (matched n=12)
Pearson(degradation_db, distance_km) = **0.318**
| bin | n | avg_km | p50_km |
|:---------------------|----:|---------:|---------:|
| negative/0 (no fade) | 10 | 134.500 | 122.500 |
| 3 dB noise floor | 2 | 119.500 | 119.500 |