# Microwave Propagation Algorithm v2 — Data-Driven, Per-Band

## Overview

This document revises the propagation scoring algorithm from algo1.md using empirical analysis of 57,492 QSOs with distance data across 13 ham radio bands from 10 GHz to 411 GHz. Weather data from 95 ASOS stations and 9 RAOB sounding stations is cross-referenced with QSO outcomes to validate and recalibrate the scoring model.

The algorithm serves two purposes:
1. **Current conditions display** — Real-time band-by-band propagation scores
2. **Short-term predictions** — Forecast for the next 3-6 hours based on diurnal trends, forecast data, and atmospheric trajectory

---

## Key Findings From QSO Data Analysis

### Dataset Summary

| Band (MHz) | QSOs | Median (km) | P90 (km) | P95 (km) | Max Tropo (km) |
|------------|------|-------------|----------|----------|----------------|
| 10,000 | 52,456 | 195 | 383 | 461 | 1,609 |
| 24,000 | 3,439 | 99 | 179 | 217 | 710 |
| 47,000 | 576 | 71 | 126 | 160 | 343 |
| 75,000 | 81 | 57 | 208 | 208 | 289 |
| 122,000 | 11 | 52 | 139 | 139 | 139 |
| 134,000 | 3 | 114 | 149 | 153 | 157 |
| 142,000 | 4 | 48 | 74 | 77 | 80 |
| 241,000 | 5 | 80 | 114 | 114 | 114 |

*Notes: All EME/satellite contacts filtered out. Very sparse data above 75 GHz.*

### Critical Finding: Humidity Effect Reverses By Frequency

**This is the most important discovery.** The relationship between moisture and distance INVERTS between 10 GHz and 24 GHz:

**10 GHz — Humidity HELPS:**
| Abs. Humidity | N | Avg Dist | P90 Dist |
|---------------|-------|----------|----------|
| 5-8 g/m³ | 2,375 | 193 km | 342 km |
| 8-11 g/m³ | 4,425 | 209 km | 376 km |
| 11-14 g/m³ | 24,272 | 215 km | 383 km |
| 14-17 g/m³ | 20,689 | 219 km | 385 km |
| 17+ g/m³ | 691 | 230 km | 519 km |

At 10 GHz, gaseous absorption is negligible (~0.01 dB/km), so extra moisture primarily INCREASES surface refractivity N, enhancing ducting and super-refraction. More moisture = more bending = longer distances.

**24 GHz — Humidity HURTS:**
| Abs. Humidity | N | Avg Dist | P90 Dist |
|---------------|-------|----------|----------|
| 5-8 g/m³ | 193 | 115 km | 154 km |
| 8-11 g/m³ | 370 | 143 km | 244 km |
| 11-14 g/m³ | 1,429 | 105 km | 174 km |
| 14-17 g/m³ | 1,390 | 89 km | 179 km |
| 17+ g/m³ | 50 | 53 km | 103 km |

At 24 GHz, proximity to the 22.235 GHz water vapor absorption line means humidity causes severe path loss (~0.1-0.15 dB/km at 7.5 g/m³). The absorption penalty outweighs refractivity benefits.

**47 GHz — Humidity hurts, but less severely than 24 GHz:**
| Abs. Humidity | N | Avg Dist | P90 Dist |
|---------------|-------|----------|----------|
| 0-5 g/m³ | 6 | 191 km | 234 km |
| 5-8 g/m³ | 39 | 91 km | 126 km |
| 8-11 g/m³ | 37 | 104 km | 216 km |
| 11-14 g/m³ | 222 | 71 km | 114 km |
| 14-17 g/m³ | 264 | 74 km | 122 km |

47 GHz sits in an atmospheric window between the 22 GHz H₂O peak and 60 GHz O₂ band. H₂O absorption is much less than at 24 GHz (~0.02 dB/km vs ~0.10), but O₂ wing absorption adds ~0.04-0.05 dB/km. Very dry conditions produce dramatically better results.

### Precipitable Water Column Confirms the Pattern

| PW (mm) | 10G Avg | 10G P90 | 24G Avg | 24G P90 | 47G Avg | 47G P90 |
|---------|---------|---------|---------|---------|---------|---------|
| <15 | 193 | 341 | 123 | 240 | 115 | 207 |
| 15-25 | 209 | 378 | 125 | 200 | 74 | 126 |
| 25-35 | 215 | 383 | 96 | 171 | 70 | 114 |
| 35-45 | 228 | 391 | 108 | 179 | 86 | 160 |

10 GHz: Wet atmosphere → longer paths (refractivity dominates).
24 GHz: Dry atmosphere → longer paths (absorption dominates).
47 GHz: Very dry → longest paths; also a secondary peak in wet conditions, likely from duct events.

### Boundary Layer Depth Matters More Than Ducting Detection

| BL Depth | 10G Avg | 10G P90 | 24G Avg | 24G P90 | 47G Avg | 47G P90 |
|----------|---------|---------|---------|---------|---------|---------|
| <200m (shallow) | — | — | — | — | — | — |
| 200-500m (low) | 214 | 382 | 92 | 159 | 70 | 114 |
| 500-1000m (medium) | 239 | 461 | 115 | 210 | 79 | 122 |
| 1-2km (deep) | 214 | 382 | 112 | 183 | 84 | 126 |

A moderate BL depth (500-1000m) is the sweet spot across all bands. This indicates an elevated inversion layer that's high enough to trap signals but not so deep that full convective mixing has occurred. This corresponds to morning conditions after sunrise when the nocturnal inversion is lifting but hasn't fully broken.

### Ducting Detection Alone Is a Weak Predictor

Binary "duct detected" from soundings shows surprisingly little correlation with distance:

| | 10G Duct | 10G No Duct | 24G Duct | 24G No Duct |
|---|---------|-------------|---------|-------------|
| Avg Dist | 214 | 216 | 101 | 100 |
| P90 Dist | 380 | 383 | 179 | 179 |

Why? Because (1) soundings are 12-hourly point samples while conditions evolve continuously, (2) the ±6hr matching window is too wide, and (3) contest operators work regardless of conditions, so the base population isn't filtered for "good" conditions. Duct characteristics (strength, height, thickness) matter more than binary detection.

### Sky Condition Impact Scales With Frequency

| Sky | 10G Avg | 24G Avg | 47G Avg |
|-----|---------|---------|---------|
| CLR | 213 | 101 | 73 |
| FEW | 216 | 100 | 71 |
| SCT | 216 | 99 | 69 |
| BKN | 215 | 96 | 69 |
| OVC | 215 | 93 | 68 |
| VV | 216 | 87 | 66 |

10 GHz: Sky condition is almost irrelevant (213-216 range).
24/47 GHz: Clear skies help modestly. VV (fog/very low visibility) is a moderate penalty, likely because it indicates high near-surface moisture content.

### Pressure: Low Pressure Correlates With Longer Distances

This contradicts algo1's scoring of high pressure as favorable:

| Pressure | 10G Avg | 10G P90 | 24G Avg | 47G Avg |
|----------|---------|---------|---------|---------|
| <1010 | 262 | 506 | 119 | 111 |
| 1010-1015 | 215 | 383 | 104 | 74 |
| 1015-1020 | 217 | 383 | 97 | 74 |
| 1020-1025 | 201 | 360 | 109 | 105 |
| 1025+ | 196 | 354 | 122 | — |

Low pressure systems bring frontal boundaries with strong temperature/moisture gradients, creating inversions and ducts. The key is not the absolute pressure but the gradient structure. However, extreme high pressure (ridge) can also trap air masses and create persistent inversions.

### Wind Impact Is Minimal

| Wind | 10G Avg | 24G Avg | 47G Avg |
|------|---------|---------|---------|
| Calm (0-3 kts) | 216 | 84 | — |
| Light (3-7 kts) | 214 | 100 | 77 |
| Moderate (7-12 kts) | 220 | 113 | 77 |

Wind shows no meaningful penalty for distance achieved. Algo1's heavy wind penalty (18% weight) is not supported by the data. Wind may reduce inversion quality, but it also creates boundary-layer dynamics (e.g., marine layer advection, frontal mixing) that can enhance propagation.

### Time of Day (10 GHz, Timed Contacts Only, CDT)

| Local Hour | N | Avg Dist | P90 Dist |
|------------|------|----------|----------|
| 22-23 (late evening) | 771 | 235 | 416 |
| 0-1 (overnight) | 299 | 212 | 370 |
| 5-6 (dawn) | 1,252 | 211 | 474 |
| 7-8 (early AM) | 5,848 | 216 | 394 |
| 9-12 (morning) | 16,383 | 211 | 373 |
| 13-16 (afternoon) | 17,432 | 213 | 373 |
| 17-21 (evening) | 12,401 | 213 | 396 |

Dawn (5-6 CDT) shows the highest P90 distances (474 km) but not the highest average — this confirms the inversion timing theory: dawn is when the strongest inversions exist, but they're spotty (high variance). Late evening (22-23) shows the best average. The afternoon minimum is real but modest (~5% lower than peak).

### Long-Range Contact Conditions

**10 GHz > 600 km**: Two primary mechanisms observed:
1. **Marine/coastal duct**: Hot, very dry (87°F, 27% RH), moderate wind. California/Mexico coast paths.
2. **Radiation inversion**: Cool, humid (64°F, 80% RH), calm. Northeast/Midwest overnight paths.

**24 GHz > 200 km**: Three mechanisms:
1. **Coastal duct in dry air**: Hot, very dry (81°F, 39% RH). West Coast marine layer.
2. **Cold dry air**: Cool, very dry (57°F, 33% RH). Winter enhanced refraction.
3. **Rain scatter**: One 710 km contact explicitly labeled "CW (Rainscatter)". Unique mechanism.

**47 GHz > 150 km**: Primarily dry-air events:
- Cool/dry (64°F, 60% RH) for the longest (343 km).
- Very dry winter (57°F, 33% RH) for 246 km.
- Exception: some medium-distance contacts in moderately humid conditions, suggesting occasional ducting.

---

## Frequency-Specific Physics

### Atmospheric Absorption

Based on ITU-R P.676-13 approximate values at sea level (15°C, 1013 hPa):

| Band | f (GHz) | O₂ (dB/km) | H₂O Coeff (dB/km per g/m³) | Total @ 7.5 g/m³ | Absorption Line Proximity |
|------|---------|------------|---------------------------|-------------------|--------------------------|
| 10G | 10.368 | 0.008 | 0.0005 | ~0.012 | Clear window |
| 24G | 24.192 | 0.015 | 0.012 | ~0.105 | 22.235 GHz H₂O shoulder |
| 47G | 47.088 | 0.045 | 0.003 | ~0.068 | Window (H₂O/O₂ gap) |
| 75G | 76.032 | 0.012 | 0.006 | ~0.057 | Window |
| 122G | 122.250 | 0.8 | 0.010 | ~0.875 | 118.75 GHz O₂ wing |
| 134G | 134.928 | 0.08 | 0.015 | ~0.193 | Between O₂ 118 & H₂O 183 |
| 142G | 142.000 | 0.05 | 0.025 | ~0.238 | Approaching H₂O 183 |
| 241G | 241.000 | 0.08 | 0.30 | ~2.33 | Between H₂O 183 & H₂O 325 |

### Rain Attenuation (ITU-R P.838-3)

γ_R = k × R^α (dB/km), R = rain rate in mm/hr:

| Band | k_H | α_H | Light (4 mm/hr) | Moderate (10 mm/hr) | Heavy (25 mm/hr) |
|------|------|------|-----------------|-------------------|------------------|
| 10G | 0.010 | 1.28 | 0.05 dB/km | 0.19 dB/km | 0.56 dB/km |
| 24G | 0.070 | 1.07 | 0.31 dB/km | 0.81 dB/km | 2.04 dB/km |
| 47G | 0.187 | 0.93 | 0.68 dB/km | 1.58 dB/km | 3.69 dB/km |
| 75G | 0.345 | 0.84 | 1.07 dB/km | 2.40 dB/km | 5.18 dB/km |
| 122G | 0.498 | 0.77 | 1.32 dB/km | 2.93 dB/km | 5.91 dB/km |
| 241G | 0.550 | 0.70 | 1.30 dB/km | 2.76 dB/km | 5.20 dB/km |

Above 75 GHz, even light rain effectively kills the path. At 10 GHz, moderate rain is tolerable. At 24/47 GHz, rain is a serious concern for paths > 100 km.

### Free-Space Path Loss (ITU-R P.525)

FSPL = 20·log₁₀(d_km) + 20·log₁₀(f_GHz) + 92.45 dB

| Band | FSPL @ 100 km | FSPL @ 200 km | FSPL @ 500 km |
|------|---------------|---------------|---------------|
| 10G | 152.8 dB | 158.8 dB | 166.4 dB |
| 24G | 160.1 dB | 166.1 dB | 173.4 dB |
| 47G | 165.9 dB | 171.9 dB | 179.4 dB |
| 75G | 170.0 dB | 176.0 dB | 183.5 dB |
| 122G | 174.2 dB | 180.2 dB | 187.6 dB |
| 241G | 180.1 dB | 186.1 dB | 193.5 dB |

Each doubling of frequency adds ~6 dB of path loss. Combined with higher atmospheric absorption, this explains why achievable distance falls rapidly with frequency.

---

## Revised Scoring Algorithm: Per-Band

### Design Principles

1. **Frequency-dependent humidity treatment** — moisture is beneficial at 10 GHz (refractivity), harmful at 24+ GHz (absorption)
2. **Reduced wind penalty** — data doesn't support the heavy wind penalty from algo1
3. **Pressure gradient over absolute** — frontal activity matters more than pressure value
4. **Boundary layer depth** — when sounding data available, use BL depth as a predictor
5. **Refractivity gradient** — super-refraction events are the strongest predictor

### Band Configuration

```elixir
@band_configs %{
  10_000 => %{
    label: "10 GHz",
    o2_db_km: 0.008,
    h2o_coeff: 0.0005,
    # Humidity INCREASES refractivity -> helps propagation
    # Net effect at 10G: more moisture = better
    humidity_effect: :beneficial,
    humidity_penalty: 0.0,    # No penalty
    humidity_bonus: 0.3,      # Bonus factor for moderate humidity
    rain_k: 0.010, rain_alpha: 1.28,
    seasonal_base: %{1 => 88, 2 => 84, 3 => 72, 4 => 62, 5 => 55,
                     6 => 42, 7 => 28, 8 => 28, 9 => 52, 10 => 68,
                     11 => 96, 12 => 88},
    seasonal_adj: %{},
    typical_range_km: 200,
    extended_range_km: 500,
    exceptional_range_km: 1000
  },
  24_000 => %{
    label: "24 GHz",
    o2_db_km: 0.015,
    h2o_coeff: 0.012,
    humidity_effect: :harmful,
    humidity_penalty: 1.6,    # Strong penalty near 22 GHz H2O line
    humidity_bonus: 0.0,
    rain_k: 0.070, rain_alpha: 1.07,
    seasonal_base: %{1 => 88, 2 => 84, 3 => 72, 4 => 62, 5 => 51,
                     6 => 34, 7 => 18, 8 => 18, 9 => 48, 10 => 68,
                     11 => 96, 12 => 88},
    seasonal_adj: %{5 => -4, 6 => -8, 7 => -10, 8 => -10, 9 => -4},
    typical_range_km: 100,
    extended_range_km: 250,
    exceptional_range_km: 500
  },
  47_000 => %{
    label: "47 GHz",
    o2_db_km: 0.045,
    h2o_coeff: 0.003,
    humidity_effect: :harmful,
    humidity_penalty: 1.0,    # Moderate — in atmospheric window
    humidity_bonus: 0.0,
    rain_k: 0.187, rain_alpha: 0.93,
    seasonal_base: %{1 => 90, 2 => 88, 3 => 78, 4 => 68, 5 => 55,
                     6 => 38, 7 => 22, 8 => 22, 9 => 48, 10 => 74,
                     11 => 96, 12 => 90},
    seasonal_adj: %{},
    typical_range_km: 70,
    extended_range_km: 150,
    exceptional_range_km: 300
  },
  75_000 => %{
    label: "75 GHz (4mm)",
    o2_db_km: 0.012,
    h2o_coeff: 0.006,
    humidity_effect: :harmful,
    humidity_penalty: 1.2,
    humidity_bonus: 0.0,
    rain_k: 0.345, rain_alpha: 0.84,
    seasonal_base: %{1 => 90, 2 => 90, 3 => 80, 4 => 68, 5 => 55,
                     6 => 38, 7 => 22, 8 => 22, 9 => 48, 10 => 74,
                     11 => 96, 12 => 90},
    seasonal_adj: %{},
    typical_range_km: 50,
    extended_range_km: 120,
    exceptional_range_km: 250
  },
  122_000 => %{
    label: "122 GHz (2.5mm)",
    o2_db_km: 0.80,
    h2o_coeff: 0.010,
    humidity_effect: :harmful,
    humidity_penalty: 1.0,
    humidity_bonus: 0.0,
    rain_k: 0.498, rain_alpha: 0.77,
    # O2 absorption near 118.75 GHz is the primary constraint
    # Winter months preferred (cold/dry = less O2 broadening)
    seasonal_base: %{1 => 92, 2 => 90, 3 => 78, 4 => 62, 5 => 45,
                     6 => 28, 7 => 15, 8 => 15, 9 => 38, 10 => 68,
                     11 => 92, 12 => 92},
    seasonal_adj: %{},
    typical_range_km: 30,
    extended_range_km: 80,
    exceptional_range_km: 140
  },
  134_000 => %{
    label: "134 GHz",
    o2_db_km: 0.08,
    h2o_coeff: 0.015,
    humidity_effect: :harmful,
    humidity_penalty: 1.3,
    humidity_bonus: 0.0,
    rain_k: 0.520, rain_alpha: 0.75,
    seasonal_base: %{1 => 92, 2 => 90, 3 => 78, 4 => 65, 5 => 48,
                     6 => 30, 7 => 18, 8 => 18, 9 => 42, 10 => 70,
                     11 => 92, 12 => 92},
    seasonal_adj: %{},
    typical_range_km: 40,
    extended_range_km: 100,
    exceptional_range_km: 160
  },
  241_000 => %{
    label: "241 GHz (1.2mm)",
    o2_db_km: 0.08,
    h2o_coeff: 0.30,
    humidity_effect: :harmful,
    humidity_penalty: 3.0,    # Extremely H2O sensitive (between 183 & 325 lines)
    humidity_bonus: 0.0,
    rain_k: 0.550, rain_alpha: 0.70,
    seasonal_base: %{1 => 95, 2 => 92, 3 => 75, 4 => 55, 5 => 35,
                     6 => 15, 7 => 8, 8 => 8, 9 => 30, 10 => 65,
                     11 => 95, 12 => 95},
    seasonal_adj: %{},
    typical_range_km: 10,
    extended_range_km: 50,
    exceptional_range_km: 115
  }
}
```

---

## Scoring Functions

### 1. Humidity Score (Revised — Frequency-Dependent)

```elixir
def score_humidity(abs_humidity_gm3, band_config) do
  case band_config.humidity_effect do
    :beneficial ->
      # 10 GHz: More moisture = more refractivity = better propagation
      # But extreme humidity (tropical) can cause scintillation
      cond do
        abs_humidity_gm3 < 4  -> 55   # Very dry = reduced refractivity
        abs_humidity_gm3 < 7  -> 70   # Dry
        abs_humidity_gm3 < 10 -> 82   # Moderate
        abs_humidity_gm3 < 14 -> 90   # Good refractivity
        abs_humidity_gm3 < 18 -> 95   # Excellent refractivity
        abs_humidity_gm3 < 22 -> 88   # High but starts to scatter
        true                  -> 75   # Tropical — scintillation risk
      end

    :harmful ->
      # 24+ GHz: H2O absorption dominates
      r = abs_humidity_gm3 * band_config.humidity_penalty
      cond do
        r <= 6  -> 100
        r <= 9  -> round(95 - (r - 6) / 3 * 20)
        r <= 13 -> round(75 - (r - 9) / 4 * 30)
        r <= 18 -> round(45 - (r - 13) / 5 * 35)
        true    -> max(0, round(10 - (r - 18) * 2))
      end
  end
end
```

### 2. Wind Score (Revised — Reduced Weight)

```elixir
def score_wind(speed_kts) do
  # Data shows wind has minimal impact on achieved distance
  # Retain a mild penalty for very high winds (turbulent scintillation)
  cond do
    speed_kts < 5  -> 100   # Calm/light — ideal for inversion
    speed_kts < 10 -> 90    # Light breeze — still good
    speed_kts < 15 -> 75    # Moderate — some mixing
    speed_kts < 20 -> 55    # Fresh — noticeable mixing
    speed_kts < 25 -> 35    # Strong — turbulent scintillation
    true           -> 15    # Gale — path degraded
  end
end
```

### 3. Sky Cover Score (Unchanged from algo1)

```elixir
def score_sky(condition) do
  case condition do
    c when c in ["CLR", "SKC"] -> 100
    "FEW" -> 88
    "SCT" -> 60
    "BKN" -> 25
    "OVC" -> 5
    "VV"  -> 5
    _     -> 50
  end
end
```

### 4. Time of Day Score (Revised — Data-Calibrated)

```elixir
def score_time_of_day(utc_hour, utc_minute, month) do
  # CDT/CST offset based on month
  is_dst = month >= 3 and month <= 10
  offset = if is_dst, do: -5, else: -6
  local = rem(utc_hour + utc_minute / 60 + offset + 24, 24)

  # Monthly sunrise table (local decimal hours)
  sunrise = [7.4, 7.3, 7.0, 6.7, 6.35, 6.25,
             6.35, 6.65, 6.9, 7.1, 7.35, 7.45]
            |> Enum.at(month - 1)

  d = local - sunrise  # Hours relative to sunrise

  cond do
    # Peak window: sunrise ±1.5 hours
    d >= -1.5 and d <= 1.5 ->
      {100, "Peak — inversion maximum"}

    # Good morning window: +1.5 to +3 hours
    d > 1.5 and d <= 3.0 ->
      {78, "Good — inversion eroding"}

    # Pre-dawn: -3 to -1.5 hours before sunrise
    d > -3.0 and d < -1.5 ->
      {82, "Pre-dawn — inversion building"}

    # Marginal morning: +3 to +6 hours
    d > 3.0 and d <= 6.0 ->
      {38, "Marginal — boundary layer mixing"}

    # Late evening: 20:00-00:00 local (data shows elevated P90)
    local >= 20.0 or local <= 1.0 ->
      {72, "Evening — cooling, inversion reforming"}

    # Afternoon: worst period
    d > 6.0 ->
      {18, "Afternoon — full convective mixing"}

    # Early night
    true ->
      {55, "Night — gradual cooling"}
  end
end
```

### 5. Temperature-Dewpoint Depression Score (Revised — Frequency-Split)

```elixir
def score_td_depression(temp_f, dewpoint_f, band_config) do
  dep = temp_f - dewpoint_f

  case band_config.humidity_effect do
    :beneficial ->
      # 10 GHz: Smaller depression (moist) can be BETTER due to refractivity
      # But very small depression (near fog) is bad (scintillation/scattering)
      cond do
        dep < 3   -> 40   # Near saturation — fog risk
        dep < 8   -> 75   # Moist — good refractivity
        dep < 14  -> 85   # Moderate — balanced
        dep < 22  -> 70   # Dry — reduced refractivity
        true      -> 55   # Very dry — poor refractivity
      end

    :harmful ->
      # 24+ GHz: Larger depression = drier = less absorption = better
      cond do
        dep > 22 -> 96
        dep > 14 -> 80
        dep > 8  -> 60
        dep > 4  -> 38
        true     -> 18
      end
  end
end
```

### 6. Season Score (Per-Band Lookup)

```elixir
def score_season(month, band_config) do
  base = Map.get(band_config.seasonal_base, month, 50)
  adj = Map.get(band_config.seasonal_adj, month, 0)
  max(0, min(100, base + adj))
end
```

### 7. Pressure Score (Revised — Gradient-Focused)

```elixir
def score_pressure(current_mb, previous_mb) do
  # Data shows frontal boundaries (low pressure, changing pressure) can
  # produce ducting. Steady high pressure is not as favorable as assumed.
  case previous_mb do
    nil ->
      # No trend data — score based on absolute (mild effect)
      cond do
        current_mb > 1025 -> 55    # Strong ridge — can trap but stagnant
        current_mb > 1018 -> 65    # Mild high — stable
        current_mb > 1010 -> 60    # Normal
        current_mb > 1005 -> 55    # Low — frontal activity possible
        true              -> 40    # Very low — active weather
      end

    prev ->
      delta = current_mb - prev
      cond do
        delta > 2.5  -> 80    # Rising rapidly — post-frontal clearing
        delta > 0.8  -> 70    # Rising — stabilizing
        delta > -0.5 -> 60    # Steady — neutral
        delta > -2.0 -> 65    # Falling slowly — approaching front, duct possible
        true         -> 45    # Falling rapidly — active weather
      end
  end
end
```

### 8. Rain/Precipitation Score (NEW — Critical for 24 GHz+)

```elixir
def score_rain(rain_rate_mmhr, band_config) do
  # Calculate path attenuation penalty
  if rain_rate_mmhr == nil or rain_rate_mmhr == 0 do
    100
  else
    gamma = band_config.rain_k * :math.pow(rain_rate_mmhr, band_config.rain_alpha)
    # Score based on additional dB/km penalty
    cond do
      gamma < 0.1  -> 95   # Negligible rain effect
      gamma < 0.5  -> 75   # Minor rain effect
      gamma < 1.0  -> 50   # Moderate rain — paths significantly degraded
      gamma < 2.0  -> 25   # Heavy rain — short paths only
      gamma < 5.0  -> 10   # Very heavy — marginal
      true         -> 0    # Extreme — path destroyed
    end
  end
end
```

### 9. Refractivity/Sounding Score (NEW — When Available)

```elixir
def score_refractivity(sounding, band_config) do
  # Use sounding-derived parameters when available
  cond do
    sounding == nil -> 50  # No data — neutral

    sounding.min_refractivity_gradient < -500 ->
      # Super-refraction detected
      case band_config.humidity_effect do
        :beneficial -> 98   # 10 GHz loves super-refraction
        :harmful    -> 85   # Higher bands benefit too but less
      end

    sounding.min_refractivity_gradient < -200 ->
      # Enhanced refraction
      80

    sounding.boundary_layer_depth_m != nil and
    sounding.boundary_layer_depth_m >= 500 and
    sounding.boundary_layer_depth_m <= 1000 ->
      # Sweet-spot BL depth
      78

    sounding.ducting_detected ->
      # Duct detected but not super-refraction
      70

    true ->
      50  # Normal conditions
  end
end
```

---

## Composite Score: Revised Weights

### Weight Rationale Changes From algo1

| Factor | algo1 | algo2 | Rationale |
|--------|-------|-------|-----------|
| Humidity | 26% | 22% | Still important but split role (helpful at 10G) |
| Wind | 18% | 8% | Data shows minimal impact |
| Sky | 15% | 10% | Modest effect, mainly at higher frequencies |
| Time of Day | 14% | 18% | Strongest diurnal predictor in data |
| Td Depression | 11% | 14% | Proxy for humidity aloft — strong signal |
| Season | 9% | 10% | Long-term baseline |
| Pressure | 7% | 5% | Weak standalone predictor |
| Rain | 0% (new) | 8% | Critical for 24+ GHz |
| Refractivity | 0% (new) | 5% | Best predictor when available |

### Composite Function

```elixir
def composite_score(factors) do
  round(
    factors.humidity     * 0.22 +
    factors.wind         * 0.08 +
    factors.sky          * 0.10 +
    factors.time_of_day  * 0.18 +
    factors.td_depression * 0.14 +
    factors.season       * 0.10 +
    factors.pressure     * 0.05 +
    factors.rain         * 0.08 +
    factors.refractivity * 0.05
  )
end
# Total: 100%
```

### Score Interpretation (Per-Band Distance Estimates)

| Score | Label | 10G Range | 24G Range | 47G Range | 75G Range |
|-------|-------|-----------|-----------|-----------|-----------|
| 80-100 | EXCELLENT | 400-1000+ km | 200-500 km | 120-300 km | 80-200+ km |
| 65-79 | GOOD | 250-400 km | 120-200 km | 80-120 km | 50-80 km |
| 50-64 | MARGINAL | 150-250 km | 70-120 km | 50-80 km | 30-50 km |
| 33-49 | POOR | 80-150 km | 40-70 km | 25-50 km | 15-30 km |
| 0-32 | NEGLIGIBLE | <80 km | <40 km | <25 km | <15 km |

---

## Short-Term Prediction Model

### Concept

Current conditions are sampled every 5 minutes from ASOS. The prediction model extrapolates conditions forward 1-6 hours using:

1. **Diurnal temperature curve** — predict T and Td based on time of day and current trajectory
2. **Inversion lifecycle** — model inversion formation (evening), strengthening (overnight), peak (sunrise), and breakup (mid-morning)
3. **Pressure tendency** — extrapolate from last 3 hours of observations
4. **Moisture trajectory** — absolute humidity changes slowly; project forward from current trend
5. **Forecast data** — when available, use NWS forecast (cloud cover, wind, precipitation probability)

### Prediction Algorithm

```elixir
def predict_score(current_obs, recent_obs_3hr, forecast, band_config) do
  # Calculate current trajectory
  temp_trend = (current_obs.temp_f - recent_obs_3hr.temp_f) / 3  # °F/hr
  dp_trend = (current_obs.dewpoint_f - recent_obs_3hr.dewpoint_f) / 3
  pressure_trend = (current_obs.slp - recent_obs_3hr.slp) / 3  # mb/hr

  # Generate predictions for +1, +2, +3, +4, +5, +6 hours
  for hours_ahead <- 1..6 do
    future_time = DateTime.add(current_obs.observed_at, hours_ahead * 3600)
    {future_hour, future_minute} = {future_time.hour, future_time.minute}
    month = future_time.month

    # Project temperature using diurnal model
    projected_temp = project_temperature(current_obs.temp_f, temp_trend,
                                         hours_ahead, future_hour, month)
    projected_dp = project_dewpoint(current_obs.dewpoint_f, dp_trend,
                                    hours_ahead)
    projected_slp = current_obs.slp + pressure_trend * hours_ahead

    # Use forecast data for sky/rain if available
    projected_sky = forecast_sky(forecast, hours_ahead) || current_obs.sky_condition
    projected_rain = forecast_rain(forecast, hours_ahead) || 0

    projected_wind = project_wind(current_obs.wind_speed_kts, forecast,
                                  hours_ahead)

    # Calculate projected abs humidity
    tc = (projected_temp - 32) * 5 / 9
    es = 6.112 * :math.exp(17.67 * tc / (tc + 243.5))
    td_c = (projected_dp - 32) * 5 / 9
    ed = 6.112 * :math.exp(17.67 * td_c / (td_c + 243.5))
    rh = min(100, ed / es * 100)
    abs_hum = 217 * (rh / 100) * es / (tc + 273.15)

    # Score each factor
    factors = %{
      humidity: score_humidity(abs_hum, band_config),
      wind: score_wind(projected_wind),
      sky: score_sky(projected_sky),
      time_of_day: score_time_of_day(future_hour, future_minute, month) |> elem(0),
      td_depression: score_td_depression(projected_temp, projected_dp, band_config),
      season: score_season(month, band_config),
      pressure: score_pressure(projected_slp, current_obs.slp),
      rain: score_rain(projected_rain, band_config),
      refractivity: 50  # Can't predict refractivity from surface obs
    }

    %{
      hours_ahead: hours_ahead,
      time: future_time,
      score: composite_score(factors),
      factors: factors,
      confidence: prediction_confidence(hours_ahead)
    }
  end
end
```

### Diurnal Temperature Projection

```elixir
def project_temperature(current_temp, trend_per_hour, hours_ahead,
                        future_local_hour, month) do
  # Diurnal temperature follows a sinusoidal pattern
  # Max temp ~15:00 local, min temp ~06:00 local
  sunrise = [7.4, 7.3, 7.0, 6.7, 6.35, 6.25,
             6.35, 6.65, 6.9, 7.1, 7.35, 7.45]
            |> Enum.at(month - 1)

  # After sunset: cooling rate ~1-2°F/hr
  # After sunrise: warming rate ~2-4°F/hr
  # Near max/min: rate approaches 0

  # Blend between current trend and diurnal model
  diurnal_rate = cond do
    future_local_hour < sunrise - 1 -> -0.5      # Pre-dawn: slow cooling
    future_local_hour < sunrise + 2 -> 0.0       # Around sunrise: transition
    future_local_hour < 15 -> 2.0                # Morning: warming
    future_local_hour < 18 -> 0.5                # Late afternoon: slow warming
    future_local_hour < 21 -> -1.5               # Evening: cooling
    true -> -1.0                                 # Night: slow cooling
  end

  # Weight: current trend dominates short-term, diurnal dominates long-term
  weight = min(1.0, hours_ahead / 4.0)
  blended_rate = trend_per_hour * (1 - weight) + diurnal_rate * weight

  current_temp + blended_rate * hours_ahead
end
```

### Prediction Confidence

```elixir
def prediction_confidence(hours_ahead) do
  # Confidence decreases with time
  case hours_ahead do
    1 -> 0.90
    2 -> 0.80
    3 -> 0.65
    4 -> 0.50
    5 -> 0.40
    6 -> 0.30
    _ -> 0.20
  end
end
```

---

## Band-Specific Propagation Mechanisms

### 10 GHz (3cm)

**Primary mechanisms:** Tropospheric ducting, enhanced refraction
**Key factor:** Refractivity profile (N-units), NOT humidity absorption
**Best conditions:**
- Moderate to high humidity (12-20 g/m³) — increases surface N
- Temperature inversions — radiation (overnight) or advection (marine layer)
- Stable atmosphere — LI > 0, moderate BL depth 500-1000m
- Any time of day can work, but late evening through early morning peaks

**Unique considerations:**
- 10 GHz is largely insensitive to rain (< 0.2 dB/km at 10 mm/hr)
- Cloud/fog does NOT significantly attenuate — can propagate through cloud decks
- Marine ducting can produce 1000+ km paths along coastlines
- Frontal boundaries create strong refractive gradients

**Prediction emphasis:** Focus on inversion formation/destruction cycle, not moisture trends.

### 24 GHz (1.2cm)

**Primary mechanisms:** Tropospheric ducting (reduced by absorption), enhanced refraction
**Key factor:** Absolute humidity — the 22.235 GHz H₂O line makes this THE most humidity-sensitive band
**Best conditions:**
- Very dry air (< 8 g/m³ absolute humidity)
- Cold season (Nov-Mar) when humidity is naturally low
- Clear skies with strong radiation inversions
- Pre-dawn through early morning

**Unique considerations:**
- 24 GHz is 10x more sensitive to water vapor than 10 GHz
- Summer is terrible (Gulf moisture floods the boundary layer)
- Rain scatter is a viable mechanism at this frequency (one 710 km QSO documented)
- The humidity penalty effectively limits summer range to ~50% of winter potential

**Prediction emphasis:** Track humidity trajectory closely. A drying trend is the most actionable prediction.

### 47 GHz (6mm)

**Primary mechanisms:** Tropospheric ducting (in atmospheric window), line-of-sight with enhanced refraction
**Key factor:** Balance of humidity (absorption) and refractivity (ducting)
**Best conditions:**
- Dry air (< 8 g/m³) — moderate H₂O sensitivity
- Clear skies — no precipitation
- Strong inversions — required for anything beyond ~100 km
- Early morning, pre-dawn

**Unique considerations:**
- Sits in a window between 22 GHz H₂O and 60 GHz O₂ bands
- O₂ absorption is significant (~0.045 dB/km) — cannot be reduced by weather
- Very dry winter conditions can produce surprising distances (246 km documented)
- Ducting events are the ONLY way to exceed ~150 km

**Prediction emphasis:** Monitor for ducting conditions (inversions + dry air). Predict morning inversion peak.

### 75 GHz (4mm)

**Primary mechanisms:** Tropospheric ducting (rare), enhanced LOS
**Key factor:** Dry air + path geometry
**Best conditions:**
- Very dry air (< 5 g/m³)
- No precipitation whatsoever
- Strong elevated inversions
- Winter months

**Unique considerations:**
- 289 km record on this band (California marine duct)
- Only 81 contacts with distance data — sparse empirical basis
- Rain attenuation is severe (~1 dB/km at 4 mm/hr)
- Most contacts are during summer contests but winter records are longer
- Equipment is rare, so opportunities to collect data are limited

**Prediction emphasis:** Same as 47 GHz but stricter rain avoidance. Track precipitation probability.

### 122 GHz (2.5mm)

**Primary mechanisms:** Enhanced LOS, rare ducting
**Key factor:** O₂ absorption from 118.75 GHz line
**Best conditions:**
- Cold temperatures (reduce O₂ line broadening)
- Low altitude paths (O₂ absorption decreases with altitude... actually it doesn't scale simply)
- Very dry air
- No rain or precipitation

**Unique considerations:**
- The 118.75 GHz O₂ absorption line creates ~0.8 dB/km baseline absorption
- This CANNOT be improved by any weather conditions — O₂ is well-mixed
- Cold temperatures narrow the O₂ absorption lines slightly, reducing wing absorption
- 139 km record (California, February) — cold dry conditions
- Practically limited to ~50 km for reliable paths

**Prediction emphasis:** Temperature trajectory (cold = slightly better O₂ absorption), rain avoidance.

### 134 GHz

**Primary mechanisms:** Enhanced LOS
**Key factor:** Between O₂ 118 and H₂O 183 lines — a mini window
**Best conditions:**
- Cold, dry air
- No precipitation
- Clear LOS paths

**Unique considerations:**
- 157 km record (Germany, March) — cold European winter
- Better than 122 GHz because farther from O₂ 118.75 line
- H₂O 183 line begins to affect from this side
- Very few active stations worldwide

### 241 GHz (1.2mm)

**Primary mechanisms:** LOS only (atmospheric absorption too high for long paths)
**Key factor:** H₂O absorption is dominant (~0.3 dB/km per g/m³)
**Best conditions:**
- Extremely dry air (< 3 g/m³) — essentially winter-only in most US locations
- High altitude stations (thinner atmosphere)
- Short paths (< 20 km typical, < 50 km extended)

**Unique considerations:**
- 114 km record (Virginia, January) — WA1ZMS/W4WWQ path, likely elevated terrain + extreme cold/dry
- Between H₂O 183 and H₂O 325 lines — significant absorption from both wings
- Total path loss at 100 km: FSPL + gas ≈ 180 + 230 = 410 dB (impractical without ducting + dry air)
- Realistic prediction: display "Path viable" / "Path not viable" for specific planned paths

---

## Implementation Notes

### Data Sources for Current Conditions

1. **ASOS surface observations** (every 5-20 min): temp, dewpoint, wind, pressure, sky, visibility
2. **RAOB soundings** (00Z and 12Z): refractivity profile, BL depth, inversions, ducts
3. **NWS forecast** (hourly): precipitation probability, wind, cloud cover (for predictions)

### Computation Flow

```
Every 5 minutes:
  1. Fetch latest ASOS observations for nearby stations
  2. Compute absolute humidity from temp + dewpoint
  3. Compute per-band scores using scoring functions
  4. Generate composite score per band
  5. If sounding data available (within last 12 hrs), incorporate refractivity score
  6. Generate 6-hour forecast per band
  7. Display current + forecast in UI

Every 12 hours (00Z, 12Z):
  1. Fetch new sounding data
  2. Recompute refractivity/ducting parameters
  3. Update refractivity score component
```

### Display: Band Conditions Panel

For each band, show:
- **Current score** (0-100, colored badge)
- **Estimated range** (km, based on score tier)
- **Key limiting factor** (e.g., "High humidity: 16 g/m³", "Strong inversion detected")
- **Trend arrow** (improving/stable/degrading based on last hour)
- **6-hour sparkline** or simple timeline of predicted scores

### Display: Prediction Timeline

```
Band      Now   +1h   +2h   +3h   +4h   +5h   +6h
10 GHz    78 ▲  82    85    88    90    88    82
24 GHz    45 ▼  42    38    35    32    35    42
47 GHz    52 →  54    55    58    60    58    52
75 GHz    40 ▼  38    35    32    30    32    38
```

Confidence indicators: bold for +1-2h (high confidence), normal for +3-4h, dim for +5-6h.

---

## Validation Against Known Contacts

### Calibration Points

| Contact | Band | Distance | Conditions | algo1 Score | algo2 Score |
|---------|------|----------|------------|-------------|-------------|
| W5LUA-K0VXM Jun 2024 | 10G | 1,609 km | 87°F, 49% RH, wind 8 kts | ~50 | ~75 |
| WA5WCP-W5VY Sep 2022 | 10G | 1,265 km | 69°F, 75% RH, wind 5 kts | ~55 | ~72 |
| W4DEX-K1WHS Sep | 10G | 1,212 km | 64°F, 80% RH, wind 3 kts | ~48 | ~78 |
| WB6CWN-AD6FP Aug | 24G | 526 km | 81°F, 39% RH, wind 11 kts | ~55 | ~82 |
| W6QI-AD6FP Oct | 47G | 343 km | 64°F, 60% RH, wind 8 kts | ~62 | ~78 |
| W0EOM-KF6KVG Feb | 47G | 246 km | 57°F, 33% RH, wind 6 kts | ~68 | ~85 |

algo2 better captures the favorable conditions for each band's physics:
- 10 GHz long contacts in humid conditions now score well (algo1 penalized humidity)
- 24/47 GHz dry contacts score well (algo1 also scored these well, but algo2 adds rain penalty and better humidity model)

---

## Future Improvements

1. **Machine learning model** — With 50K+ 10 GHz QSOs, train a gradient-boosted model directly on weather features → distance outcome
2. **Path-specific prediction** — Use actual terrain profiles and station locations instead of generic area scores
3. **Sounding interpolation** — Temporal interpolation between 00Z and 12Z soundings using surface obs trends
4. **NWS model soundings** — Use RAP/HRRR forecast soundings for refractivity predictions
5. **Rain radar integration** — Real-time NEXRAD data for precipitation scoring
6. **Regional calibration** — Different weights for coastal vs inland vs mountain regions

---

## ITU-R References

- P.453-14: Radio refractivity
- P.525: Free-space path loss
- P.676-13: Gaseous absorption (O₂ + H₂O)
- P.838-3: Specific rain attenuation model
- P.526: Diffraction
- P.452: Prediction procedure for evaluation of interference between stations

## Data References

- ARRL Microwave Contest QSO database: 58,282 contacts (2019-2024)
- IEM ASOS observations: 95 stations, ±2hr window match
- IEM RAOB soundings: 9 stations, ±6hr window match
