Add interactive NLOS propagation post

New post "why-signals-bend" with ten hand-rolled canvas scenes
(LOS, Earth curvature, Fresnel, knife-edge and rounded diffraction,
refractivity profile, ray tracing, duct trapping, duct types,
and the real 65 km QTH-to-W5HN/B path with 4/3-Earth bulge).

Also drop QSO/em-dash conventions from prop1 and the
"written by humans" footer line.
This commit is contained in:
Graham McIntire 2026-04-22 10:29:20 -05:00
parent 1a9b796140
commit 725857594e
No known key found for this signature in database
GPG key ID: F4ABF488E6029E59
5 changed files with 1961 additions and 15 deletions

View file

@ -23,7 +23,7 @@ favorable conditions. An easy visual analogy: ships sometimes appear to float ab
![Atmospheric ducted ship](/images/atmospheric_ducting.jpg)
This is because there's a duct with favorable conditions for refracting visible light the same
This is because there's a duct with favorable conditions for refracting visible light, the same
thing happens with microwave signals.
At one of the [NTMS](https://ntms.org) meetings, KM5PO presented on this exact topic and had used
@ -33,7 +33,7 @@ kicked off this project.
Fast forward a few weeks and I've now built an Elixir/Phoenix app at <https://prop.w5isp.com> that
collects and processes everything. For a quick import of some known contacts, I pulled in results
from the ARRL 10 GHz and Up and 222 MHz and Up contests VHF/UHF/microwave events where stations
from the ARRL 10 GHz and Up and 222 MHz and Up contests, VHF/UHF/microwave events where stations
log long-distance contacts on those bands.
With known contacts in place, it was time to backfill them with data. To predict ducting we need
@ -41,23 +41,23 @@ atmospheric data at fine vertical resolution, so the app pulls from a mix of wea
surface observations, upper-air soundings, and terrain:
### Atmospheric
* HRRR (NOAA 3 km) hourly f00f18 propagation scoring hot path; native hybrid-sigma levels for fine-grained duct detection
* GEFS (NOAA 0.5° ensemble) Day 27 extended-horizon outlook
* HRRR native (hybrid σ) best-duct-band per cell, refractivity gradient at 1050 m resolution
* IEMRE (Iowa State 0.125° reanalysis) — gridded hourly weather for per-QSO enrichment
* NARR (NCEI 32 km reanalysis) — pre-2014 historical backfill for old QSOs (HRRR archive doesn't cover them)
* HRRR (NOAA 3 km), hourly f00f18 propagation scoring hot path; native hybrid-sigma levels for fine-grained duct detection
* GEFS (NOAA 0.5° ensemble), Day 27 extended-horizon outlook
* HRRR native (hybrid σ), best-duct-band per cell, refractivity gradient at 1050 m resolution
* IEMRE (Iowa State 0.125° reanalysis), gridded hourly weather for per-contact enrichment
* NARR (NCEI 32 km reanalysis), pre-2014 historical backfill for old contacts (HRRR archive doesn't cover them)
### Surface observations
* ASOS (via IEM) — airport station surface weather, used by per-QSO enrichment and the 10-minute score nudge
* MRMS (NOAA PrecipRate) 2-minute radar-derived rain rate that overlays HRRR's hourly precip for fast-moving cells
* NEXRAD (Level II composite reflectivity) current-hour precip cell detection for f00 scoring
* ASOS (via IEM), airport station surface weather, used by per-contact enrichment and the 10-minute score nudge
* MRMS (NOAA PrecipRate), 2-minute radar-derived rain rate that overlays HRRR's hourly precip for fast-moving cells
* NEXRAD (Level II composite reflectivity), current-hour precip cell detection for f00 scoring
### Upper air
* RAOB (US radiosondes via IEM) twice-daily soundings for duct/inversion analysis
* UWYO (Canadian radiosondes) 00/12 Z Canadian profiles
* RAOB (US radiosondes via IEM), twice-daily soundings for duct/inversion analysis
* UWYO (Canadian radiosondes), 00/12 Z Canadian profiles
### Terrain & geography
* SRTM (90 m local tile cache + fallback API) ITU-R P.526-16 path diffraction + viewshed
* SRTM (90 m local tile cache + fallback API), ITU-R P.526-16 path diffraction + viewshed
It's taken a mountain of work and time to get all of this implemented, but most of the contacts
have now been backfilled with this information. Feeding all of this in to Claude, we end up with an
@ -77,7 +77,7 @@ since both endpoints are known and fixed.
The effort has been growing and we now have help from a senior meteorologist in Lubbock, TX and
many hams submitting verified contacts. If you'd like to help out, we need as many contacts as
possible during non-contest times contest weekends concentrate activity into a few days, but
possible during non-contest times, contest weekends concentrate activity into a few days, but
the model needs samples spread across all conditions. We also need volunteers for future
automated beacon monitoring. You can reach me at my first name at my last name dot me (it's in
the footer).

View file

@ -0,0 +1,111 @@
+++
title = "Why signals bend (and why your 10 GHz signal sometimes goes through a hill)"
date = 2026-04-22
draft = false
+++
<link rel="stylesheet" href="/css/nlos.css">
<div class="nlos" data-nlos>
A line-of-sight microwave link is easy to reason about. Two antennas, straight line, if nothing touches the line the path works.
Ham radio doesn't cooperate with that story. People working the [ARRL 10 GHz and Up contest](https://www.arrl.org/10-ghz-up) log contacts over hundreds of kilometers where a string between the two stations would go straight through an Oklahoma mesa. The path profile looks broken. The contact happens anyway.
Below, one layer at a time, with scenes you can interact with. We start from the straight-line-and-obstacle model and break it four times.
## Start simple: a line between two antennas
Two antennas, flat ground, a hill in the middle. If the hill sticks above the line, you get nothing.
<div id="scene-los"></div>
That model is how nearly every commercial point-to-point microwave link is designed. WISPs, utility SCADA, carrier backhaul, all of it. It hides two lies though. The Earth isn't flat, and the signal isn't a line.
## Earth gets in the way too
Zoom out far enough and the ground curves. From an antenna of height *h* the geometric horizon sits at about √(2·R·h). Two 30-meter towers can each see a horizon roughly 20 km away. Push them 80 km apart and the Earth bulges more than 120 meters up between them.
<div id="scene-curve"></div>
No mast is tall enough to beat that on its own.
## The signal is fatter than a line
Here's the first thing in the geometry-only model that helps NLOS along a little. Radio energy doesn't ride a pencil-thin ray. It fills an ellipsoid between the antennas called the first Fresnel zone, and you want about 60% of that zone clear for the path to behave like free space.
The ellipsoid is fattest at the middle of the path. Its radius there is √(λ·d/4), so it grows with wavelength. A 40 km path on 50 MHz has a midspan radius around 24 meters. Same path on 10 GHz is 1.7 meters.
<div id="scene-fresnel"></div>
This is why microwave links behave so much worse than HF over the same obstructed path. Higher frequency, skinnier ellipsoid, easier to clip with a ridge that doesn't look like much on the path profile.
## Ridges leak signal
When the line of sight is fully blocked, a sharp ridge doesn't eat everything. The wavefront hitting that edge keeps going, and some energy diffracts into the shadow. The usual way to describe it is the Fresnel-Kirchhoff parameter ν, which mashes together the geometry and the wavelength. ITU-R [P.526](https://www.itu.int/rec/R-REC-P.526/) gives the loss curve, shown on the right.
<div id="scene-knife"></div>
A knife edge grazing the LOS already costs 6 dB. Stick it one Fresnel zone above and you're down 20+. It's cheap power on VHF. On 10 GHz the same geometry is almost never workable on its own.
## Real hills are rounded, which costs more
Knife edges are a convenient fiction. A rounded summit scatters more energy away from the shadow zone, so actual loss is worse than the knife-edge baseline. P.526 tacks on an extra term based on the crest's radius of curvature.
<div id="scene-round"></div>
This is where path profile software starts lying to you. If it says a ridge clears by 5 meters and you'll be +3 dB over the noise floor, you can usually subtract another 510 dB once you account for the rounding. Most of the time that subtraction puts the link under water. Except the link works. So something else is going on.
## Air isn't empty
Microwave waves travel a hair slower through air than through vacuum, and the slowness changes with pressure, temperature, and humidity. Radio engineers express it as N-units:
> N = (n 1) · 10⁶
where *n* is the refractive index. At sea level N is usually around 315. It falls off with altitude, and the slope of that fall is what bends rays.
<div id="scene-refractivity"></div>
The dashed line is the international standard atmosphere. Drag the slope and the regime label changes with it. Most of the useful ham band magic happens on the far left side of that slider.
## Rays follow the gradient
A horizontal ray in a medium whose refractive index falls with altitude bends downward. The tighter the gradient, the tighter the curve. Under the standard atmosphere the bend is gentle, about a quarter of Earth's own curvature, and engineers paper over it by pretending the Earth is 4/3 its real size and the rays are straight. That's the familiar k = 4/3 factor.
<div id="scene-raytrace"></div>
Push the slope below 79 N/km and rays start bending faster than standard. A lot of VHF/UHF tropo propagation lives in that regime. Push below 157 N/km and rays curve tighter than the Earth curves away. The ray can't climb out anymore.
## The signal gets trapped
Here's where a ham-friendly explanation helps. Propagation models talk about "rays" because the math is a lot easier when you treat a radio wave as a bundle of straight (or curving) lines, each representing the direction the wavefront is moving at that point. In practice, those lines are the actual paths your RF follows out of the antenna. When the atmosphere bends them, your signal goes where the lines go.
A *duct* is a layer of atmosphere where the refractivity drops steeply enough that any signal launched into the layer at a shallow enough elevation angle can't escape upward. Picture a slice of your antenna's radiation pattern between about 0.5° and +0.5° elevation. Normally that energy just keeps rising into space and is lost. Under a duct, it climbs into the layer, curves back down, hits the ground (or a lower boundary of the duct) and reflects, climbs again, curves back down again, and keeps repeating. The signal skips along inside the duct for hundreds of kilometers, losing very little on each bounce because the reflections are shallow and the layer is keeping the wavefront collimated.
<div id="scene-duct"></div>
Green paths in the scene are the rays (ray = the direction the wavefront is traveling) that stay trapped inside the duct. Orange ones escape. The "critical angle" is the elevation window that stays trapped, and it's tiny, usually a fraction of a degree. That's why ducting is so fickle. Your antenna has to be radiating useful power inside that window, *and* the duct has to exist above your station, *and* the station on the other end has to be inside it or close enough for its energy to enter. When all three line up, your 10 GHz rig works a contact 400 km away. When any one of them is off, the path behaves like the terrain profile says it should.
## Flavors of duct
Three of these show up in the wild, all the same physics, different ways the atmosphere sets them up.
<div id="scene-ducttypes"></div>
Surface ducts form overnight as the ground cools faster than the air above it, and they collapse once the sun warms things back up. Elevated ducts sit aloft under a subsidence inversion, typical under a high-pressure ridge. Both ends of the link usually need to be inside the layer, or at least angled into it. Evaporation ducts are the interesting one: over warm oceans the humidity gradient in the first 5-40 meters is so sharp that a thin duct is there nearly 24/7. Every over-water microwave link above a few GHz lives inside one.
## A real path
This is the 65 km path I've been staring at: my QTH to the W5HN/B beacon on top of the TWU dorms in Denton. Terrain heights are the actual SRTM elevations along the great circle between the two antennas. Earth's curvature (using the standard k = 4/3 effective Earth) is added on top, which is why the middle of the profile bulges up about 62 m above the endpoints. The terrain obstructs the straight LOS. On the right mornings the path works anyway, because an elevated duct parks over North Texas and both ends get into it.
<div id="scene-real"></div>
Figuring out whether that duct is there on any given day is the whole point of <https://prop.w5isp.com>. The app pulls HRRR at native hybrid-sigma levels, corrects against ASOS and radiosonde profiles, and computes the refractivity gradient along every path we track. When a duct is sitting on the path the score lights up. When it isn't, the path should behave the way the terrain profile says.
The part that keeps me working on it is how often it *doesn't* behave that way. Ducting, super-refraction, and enhanced tropo scatter combine in ways that still surprise the old guys who've been operating these bands for forty years. The more verified contacts we log with good atmospheric data behind them, the better the model gets.
If you run 50 MHz or up, submit your contacts or beacons at <https://prop.w5isp.com>. A continuous, known-distance path is worth a lot more to the model than a pile of contest contacts.
</div>
<script src="/js/nlos.js" defer></script>

131
static/css/nlos.css Normal file
View file

@ -0,0 +1,131 @@
/* Styles scoped to the NLOS propagation post. Only loaded when the post's
<link> tag is on the page. */
.nlos {
--nlos-bg: #1f232b;
--nlos-panel: #242932;
--nlos-fg: #d4d8df;
--nlos-dim: #7d8594;
--nlos-grid: #323844;
--nlos-blue: #61afef;
--nlos-cyan: #56b6c2;
--nlos-green: #98c379;
--nlos-orange: #d19a66;
--nlos-red: #e06c75;
--nlos-yellow: #e5c07b;
--nlos-magenta: #c678dd;
}
.nlos-scene {
margin: 2.5rem 0;
background: var(--nlos-bg);
border: 1px solid var(--nlos-grid);
border-radius: 6px;
padding: 0.75rem 0.75rem 1rem;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.35);
}
.nlos-scene canvas {
display: block;
width: 100%;
height: auto;
touch-action: none;
cursor: grab;
background: transparent;
}
.nlos-scene canvas:active {
cursor: grabbing;
}
.nlos-controls {
display: grid;
grid-template-columns: 1fr;
gap: 0.5rem;
margin-top: 0.75rem;
font-size: 0.9rem;
}
@media (min-width: 640px) {
.nlos-controls {
grid-template-columns: repeat(2, 1fr);
}
.nlos-controls.nlos-controls--three {
grid-template-columns: repeat(3, 1fr);
}
}
.nlos-control {
display: flex;
flex-direction: column;
gap: 0.2rem;
}
.nlos-control label {
color: var(--nlos-dim);
font-size: 0.8rem;
display: flex;
justify-content: space-between;
gap: 0.5rem;
}
.nlos-control label .nlos-value {
color: var(--nlos-fg);
font-variant-numeric: tabular-nums;
}
.nlos-control input[type="range"] {
width: 100%;
accent-color: var(--nlos-cyan);
}
.nlos-scene .nlos-caption {
margin-top: 0.5rem;
font-size: 0.85rem;
color: var(--nlos-dim);
line-height: 1.45;
}
.nlos-scene .nlos-readout {
display: flex;
flex-wrap: wrap;
gap: 0.75rem 1.5rem;
padding: 0.5rem 0;
margin-top: 0.25rem;
color: var(--nlos-fg);
font-size: 0.85rem;
font-variant-numeric: tabular-nums;
}
.nlos-scene .nlos-readout .nlos-readout-item span:first-child {
color: var(--nlos-dim);
margin-right: 0.35rem;
}
.nlos-buttons {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-top: 0.5rem;
}
.nlos-buttons button {
background: var(--nlos-panel);
color: var(--nlos-fg);
border: 1px solid var(--nlos-grid);
border-radius: 4px;
padding: 0.35rem 0.65rem;
font-size: 0.85rem;
cursor: pointer;
font-family: inherit;
}
.nlos-buttons button:hover {
border-color: var(--nlos-cyan);
}
.nlos-buttons button[aria-pressed="true"] {
background: var(--nlos-cyan);
color: #111;
border-color: var(--nlos-cyan);
}

1705
static/js/nlos.js Normal file

File diff suppressed because it is too large Load diff

View file

@ -76,7 +76,6 @@
</main>
<hr>
<footer style="margin-top: 3rem;">
<p>All content written by humans</p>
<p>&copy; Copyright Graham McIntire {{ now() | date(format="%Y") }}</p>
</footer>
<script src='https://storage.ko-fi.com/cdn/scripts/overlay-widget.js'></script>