Skip to content

About

One day of the D layer: the ionosphere's absorption law, measured with a hobby SDR and six time-station carriers

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

dlayer-diary — two ways to weigh an invisible layer in one day

The ionosphere's D layer sits 60–90 km up — too high to touch, too faint to see. On 2026-07-20/21 we measured it twice, two different ways, with the same rooftop antenna and consumer SDRs in suburban Virginia (DC metro):

Project site: felbs.software · Contact: E@felbs.software

  • By day we weighed its absorption — how hard it eats HF signals — by ladder-stepping six atomic-clock time-station carriers every 30 minutes and watching the low frequencies die toward noon and revive at dusk. The curve lands on the textbook 1/f² collision-physics law.
  • By night we weighed its height — how high the reflecting ceiling sits — by catching dispersed lightning "tweeks" and reading the Earth-ionosphere waveguide's cutoff frequency straight off their whistling tails. 77 tweeks put the nighttime ceiling at ~82 km.

Two measurements, one day, one layer, off a rooftop. Neither is an ionosonde; both fall within 10% of the physics.

Contents


Part 1 — the D layer by day: absorption ∝ 1/f²

On 2026-07-20, every 30 minutes from breakfast to bedtime, one SDR with a fixed gain ladder-stepped through six HF time-station carriers — WWV 2.5 / 5 / 10 / 15 / 20 MHz and CHU 7.85 MHz — and logged each carrier's SNR against its local noise floor. Rooftop discone, suburban Virginia, DC metro. Twenty-one captures, one antenna, one knob never touched.

The result is the textbook diurnal absorption curve of the ionosphere's D layer — measured, not illustrated:

six carriers across one day

Read it like a story:

  • 08:31 EDT, first capture: 20 MHz is barely there (12.7 dB). One hour later it is 61.5 dB — a 49 dB sunrise opening as the F layer ionizes enough to refract 20 MHz at all.
  • 10 MHz slides all day: 43.0 dB at breakfast, down through the 20s by lunch, bottoming at 6.3 dB at 15:01 EDT — the D layer eating it a little more every half hour, then letting go.
  • 2.5 MHz is not weak by day — it is absent. Every daytime capture reads ~3 dB, which is just the scatter of an empty FFT bin. Then two hours after sunset: 12.7 → 28.5 dB in 30 minutes, still climbing when the run ended.
  • Sunset 20:33 EDT: every rung below 15 MHz turns upward within ~90 minutes. The D layer is the only ionospheric layer that dies at night, and it dies fast.

Why the D layer absorbs (and why low frequencies die first)

The D layer lives at 60–90 km, far below the E and F layers that actually bend HF signals back to Earth. Daytime solar Lyman-α and X-rays ionize it; every HF skywave path must punch through it twice per hop.

At those altitudes the neutral atmosphere is still dense, so a free electron collides with a neutral molecule millions of times per second. Higher up (F layer), an electron shaken by a passing radio wave oscillates freely and re-radiates — energy is redirected, not lost. In the D layer, the electron picks up ordered energy from the wave and then smacks into a nitrogen molecule, converting your signal into a very small amount of heat.

The frequency dependence falls out of the electron's equation of motion: for wave frequency ω well above the collision rate ν, the absorption in dB per pass goes as

absorption ∝ N·ν / (ω² + ν²) ≈ 1/f²

where N is the electron density. Halve the frequency, quadruple the loss. That's why 2.5 MHz vanishes at sunrise while 15 MHz shrugs, and why AM broadcast goes long-distance only at night. After sunset the ionizing flux is gone, and because the air is dense, recombination is fast — the D layer evaporates within an hour or two, which is the recovery you see on the right side of the plot.

Why time stations are the perfect probe

  • Cesium-steady: WWV's carriers are locked to NIST's atomic timescale. Any dB that moves, moved on the path — not at the transmitter.
  • Always on, constant power: no schedule, no program-dependent modulation depth worth caring about at the carrier.
  • A frequency ladder from one site: WWV transmits 2.5–20 MHz from the same station in Fort Collins, Colorado (~2,400 km from here). Same path, same antenna, same receiver, same half-hour — the only variable left is frequency. That isolation is what makes a frequency-exponent fit meaningful at all.
  • CHU (Ottawa, ~750 km) rides along as a seventh-ish rung on a different path — in this run it never rose out of the noise (see honesty notes).

The fit: how close to 1/f²?

Take the midday SNR (median over 13:00–16:30 EDT, solar noon through the absorption plateau), subtract it from a night reference (mean of the last two captures, ~2 h after sunset), and call the difference the midday deficit. Fit deficit vs frequency to a power law:

absorption vs frequency

rung midday (median) night ref deficit in fit?
WWV 2.5 3.5 dB (empty bin) 20.6 dB (still rising) ≥ 17 dB no — daytime non-detection, deficit unmeasurable
WWV 5 8.4 dB 38.3 dB 29.9 dB shown as lower bound — night ref not yet recovered
CHU 7.85 2.7 dB 3.3 dB — no — never detected in this run
WWV 10 16.1 dB 48.4 dB 32.3 dB ✅
WWV 15 40.9 dB 57.0 dB 16.0 dB ✅
WWV 20 37.6 dB 46.7 dB 9.1 dB ✅

Result: deficit ∝ f−1.83 ± 0.07 on the three clean rungs, against the theoretical ~f−2. Three points and one afternoon; landing within 10% of the collision-physics exponent is more than this method deserves.

The censoring lesson is worth the price of admission: include the 5 MHz rung and the exponent flattens to −0.83 — not because the physics changed, but because both ends of that rung's deficit are clipped (daytime value snuggled against the detection floor, night reference measured while the band was still recovering). SNR deficits saturate exactly where absorption is strongest, so a naive fit is biased flat. Handle your censored rungs or they handle you.

What "absorption" honestly means here

The deficit is not pure D-layer absorption. It bundles together: D-layer loss (dominant on the low rungs), daytime vs nighttime propagation-mode changes (15 and 20 MHz need daytime F-layer ionization — their deficits partly reflect the night side weakening, which makes the measured exponent a touch shallow), and detection-floor clipping (fully censoring 2.5 MHz). This is a one-day, one-path, SNR-domain measurement — a demonstration that the 1/f² law is visible from a rooftop with consumer gear, not an ionosonde.

Honesty notes (Part 1)

  • No data before 12:31Z (08:31 EDT). The experiment started at breakfast; sunrise (~06:00 EDT) had already happened. The 20 MHz "opening" we caught is the tail of it.
  • 8 of 29 half-hour slots were missed — one radio, and the ladder yields to higher-priority work (a reservation lock arbitrates):
    • 15:31Z — satellite pass window
    • 19:31Z — radio busy, not logged
    • 22:31Z — evening listening session
    • 23:01Z, 23:31Z — HF band survey
    • 00:31Z, 01:01Z, 01:31Z — radio busy, not logged
  • Lines in the figure break at the gaps rather than interpolating across them.
  • The noise floor has its own diurnal curve: the floor_* columns show the floor running ~4 dB hotter through the working day than in the early evening on every rung (urban noise — the neighborhood is awake), and the low rungs climb ~4–5 dB again after dark as the ionosphere starts delivering everyone else's noise too. SNR is measured carrier-against-local-floor per capture, so this rise is divided out of the curves above — but it means the daytime deficits slightly overstate absorption (a hotter floor also lowers SNR), by roughly that same handful of dB spread across the ladder.
  • CHU 7.85 never made it out of the noise, day or night. We can't distinguish "shorter path, different antenna null, local QRM" from "bad luck" with one day of data, so it is excluded rather than explained.
  • The floor_* values are uncalibrated (dB relative to an arbitrary fixed-gain reference); only their differences mean anything.

Reproduce Part 1

python analyze.py

numpy + matplotlib; reads data/dlayer_ladder_20260720.csv, prints the slot bookkeeping and the fit, writes both figures.

To take your own day of data you need any SDR that tunes HF and any antenna that hears WWV at night. Every 30 minutes, at fixed gain: tune each carrier, measure the carrier-bin power against the median of the surrounding bins, log SNR and floor, repeat until bedtime. CSV columns: utc, snr_<MHz> ×6, floor_<MHz> ×6.


Part 2 — weighing the ceiling with lightning

Part 1 measured how hard the D layer absorbs. It says nothing about how high the ceiling is. For that you need a clock inside the ionosphere — and every thunderstorm on the continent is firing one a hundred times a second.

That same night, from 11:10 PM to 1:10 AM EDT, the roof discone recorded three 10-minute chunks of raw VLF audio (15 kHz center, 62.5 kS/s, AGC off). Buried in the crackle are tweeks: distant lightning sferics that have bounced their way here down the Earth-ionosphere waveguide and come out dispersed — the lowest frequencies smeared into a descending, whistling tail. That tail is a ruler for the height of the sky.

77 tweeks: dispersion fits and the fundamental cutoff

77 tweeks → fundamental cutoff fc ≈ 1.83 kHz → reflection height h = c / 2fc ≈ 82 km — squarely inside the known nighttime D/E band (83–90 km).

The physics: a sferic in a waveguide

The ground is a conductor. At night the bottom of the ionosphere — the residual D and lower E — is a conductor too. Between them, long-wavelength VLF energy is trapped in a parallel-plate waveguide a few Earth-radii wide and only ~85 km tall.

A lightning return stroke is a near-perfect broadband impulse — a single click containing everything from a few hundred Hz to tens of kHz. Launch that click into a waveguide and it does not all travel at the same speed. A waveguide has a cutoff frequency for each mode; the fundamental transverse mode can only propagate above

fc = c / (2h)

and near that cutoff the group velocity collapses:

vg = c · √(1 − (fc / f)²) → 0 as f → fc

The high frequencies race across the country at nearly c and arrive first; the frequencies just above cutoff crawl and arrive milliseconds late. A click goes in; a downward-swooping "tweek" comes out, piling up against a floor it can never cross. Turn the equation around and that floor is the cutoff, and the cutoff is the height:

h = c / (2 fc)

For fc = 1.83 kHz, h = (3×10⁸ m/s)/(2·1830 Hz) ≈ 82 km. By day the D layer is a lossy, lower, leakier lid and clean tweeks are rare; at night the lid firms up near the bottom of the E layer and the whole continent rings — which is exactly why tweeks are a nighttime phenomenon, and why the height we read is a nighttime height.

The measurement: 77 tweeks, one cutoff

Validation first. Before touching real data we built a synthetic dispersed sferic — a broadband impulse pushed through the analytic parallel-plate transfer function — and ran it through the same ridge extractor and fitter. Through a 60 Hz-comb-plus-noise gauntlet the recovered cutoff tracks truth to better than 40 Hz. The fitter is unbiased; what it reports on real tweeks, it earned.

Then the real pipeline, per capture:

  1. Detect sferics — impulse onsets that carry a strong, long-ringing tail in the 1.75–2.6 kHz band just above cutoff.
  2. Background-subtract the spectrogram — subtract each frequency's temporal median to erase the steady 60 Hz powerline comb and any fixed carriers, leaving only the transient.
  3. Extract the ridge — the arrival time of peak power at each frequency, t(f), with a monotone-descent constraint (dispersion only ever runs one way).
  4. Fit the dispersion law t(f) = t₀ + D · f / √(f² − fc²) with iterative outlier rejection, and read off fc.

Across the three captures, 77 tweeks cleared the plausibility gate (tight residual, physical height, a ridge that genuinely spans from the high band down to near cutoff). Their fitted cutoffs cluster at a median fc = 1.83 kHz (MAD 0.41 kHz), i.e. a median reflection height of ≈ 82 km. The per-tweek scatter is real — the MAD alone spans a height band of ~67–105 km — but the median is well-constrained precisely because the fitter is validated-unbiased and 77 independent flashes are averaging down.

Honesty notes (Part 2)

  • The per-tweek scatter is large and physical, not just noise. Real tweeks travel real, different paths — different storm distances, different ionospheric patches, occasional mode mixing — so a ±0.4 kHz spread in fc (a ~67–105 km height band) is expected. We quote the median, and only claim the median is tight. Any single tweek is a weak height estimate; 77 of them are a good one.

  • The through-night trend is suggestive, not established. The per-capture medians drift downward — h ≈ 91 → 82 → 75 km from 11 PM to 1 AM — which is the right direction for a still-settling nighttime ionosphere. But the per-file medians differ by less than half the per-event MAD. This is a hint that wants more nights, not a measured descent. We are not claiming it.

  • No clean second mode. A parallel-plate guide should also show a TM₂ knee at 2·fc ≈ 3.7 kHz; we could not cleanly isolate one in this busy data, so we fit the fundamental only.

  • Background subtraction was mandatory, not optional. This is a busy inland site — roughly 110 sferics per second — and a strong 60 Hz powerline comb dominates everything below ~2.5 kHz, right where the cutoff lives. Without per-frequency median subtraction the comb swamps the tail. The isolated, background-subtracted sferics below show both the mess we started from and the dispersed tails hooking toward cutoff:

    isolated background-subtracted sferics

  • One night, one antenna, one site. 82 km is a believable nighttime D/E reflection height and it lands where the textbooks put it — but it is a single night from a single rooftop, not a climatology.

Reproduce Part 2

python tweeks/analyze_tweeks.py

numpy + matplotlib (and scipy if you have the raw IQ). The script runs in either of two modes automatically:

  • Raw mode — if the three .cs16 captures are present it runs the full pipeline (detect → background-subtract → ridge → fit), draws the spectrograms, and rewrites tweeks/data/tweeks_fits.json.
  • JSON mode — otherwise it reloads the shipped per-tweek fit summary (tweeks/data/tweeks_fits.json) and redraws the dispersion curves and the fc/height histograms with no IQ needed.

The raw overnight captures (3 × ~150 MB CS16) are not shipped — they are too large for a git repo. Point --iq-dir at a folder holding them to run raw mode; everything else regenerates from the JSON.

To catch your own tweeks you need any VLF-capable receiver (a soundcard and a wire will do), a quiet-ish night, and a thunderstorm within a few thousand km. Record wideband audio, find the sferics, and fit the tail.


Family

Built by the tuna fleet — see the Radio Grid Atlas for the measured grids of WWV, WWVB, and a dozen other signals, and gr-radiotuna for the method's science deck.

License: MIT. The ionosphere itself is physics — nobody owns it.

About

One day of the D layer: the ionosphere's absorption law, measured with a hobby SDR and six time-station carriers

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages