Skip to main content

GeoSol Research Logo

Tropo (Tropospheric Corrections)

Tropospheric delay and angular correction calculations from the empirical DGS model, with ERA5 reanalysis inputs. Requires Python 3.12+.

Overview

gri-tropo provides tools for calculating tropospheric corrections and accessing atmospheric refractivity data:

  • dgs: Calculate tropospheric angle and time corrections with the DGS model
  • rates: Time derivatives of those corrections, for Doppler, FDOA, and TDOA-rate observables
  • nsur: Access surface refractivity data from ERA5 reanalysis
  • refht: Access reference height data (scale height of exponential refractivity decay)

Mathematical Background

The troposphere (0-~12 km altitude) causes signal delay and angular bending due to atmospheric refractivity. Unlike the ionosphere, tropospheric refraction is non-dispersive (frequency-independent) and affects all electromagnetic signals equally.

The refractivity profile is modeled as an exponential decay:

N(h) = N_sur * exp(-h / h_ref)

where N_sur is the surface refractivity in N-units, h is altitude in km, and h_ref is the reference (scale) height in km. The signal delay and angular bending are computed by integrating the refractivity along the propagation path using ray-tracing methods.

N_sur values typically range from 250-400 N-units and vary with latitude, season, and local weather. The reference height h_ref is typically 6-8 km.

Corrections over time

Rate observables are biased by the rate at which the troposphere lengthens the optical path, not by the delay itself. Differentiating the path with moving endpoints gives:

f_rx = f_tx * (1 - range_rate / c - d(tau)/dt)

so the whole tropospheric frequency term is -f_tx * d(tau)/dt, where tau is the one-way excess delay. Both the ray bending at each endpoint and the reduced propagation speed are already contained in tau, which is why dgs_tropo_rates differentiates the delay model directly rather than rotating the line of sight by the bending angle and re-differencing the geometric range. That geometric approach captures only one endpoint's bending and omits the speed term.

Because the troposphere is non-dispersive, the delay rate is frequency-independent and is carried in seconds per second, matching gri_utils.observables.get_tdoa_dot. The carrier scaling belongs at the point of use.

Model limits

The DGS fit is valid over a bounded range, and both entry points enforce the same limits:

  • Emitter elevation is floored at -2 degrees.
  • The time correction is clamped to [0, 500] ns.
  • The emitter is the low end of the path. Only the refractive layer between the two altitudes counts: a satellite collector sees the whole column above the emitter, an aircraft the part below itself, and anything from 20.25 km up contributes nothing.
  • The fit falls away at high elevation: about 7.3 ns at 60 degrees, 2.4 ns at 70, and zero from about 84 degrees up.

dgs_tropo_rates differentiates the clamped model rather than the raw polynomials. Where the time correction is held at a limit, the delay rate is zero. Below the elevation floor the elevation dependence drops out of both rates, but neither goes to zero: the cone rate still follows the changing range, and both still follow the emitter altitude through the multiplicative correction.

References:

  • ITU-R P.453. "The radio refractive index: its formula and refractivity data."
  • ITU-R P.834. "Effects of tropospheric refraction on radiowave propagation."
  • Bean & Dutton (1966). "Radio Meteorology." National Bureau of Standards Monograph 92.

Documentation

The wheel ships its documentation inside the package, in gri_tropo/docs/, so it is available wherever the package is installed:

  • overview.md -- the corrections, the model and its limits, rates, and the ERA5 input grids
  • api_summary.md -- every public class, function, and signature (generated)

Print the directory with python -c "import gri_tropo, pathlib; print(pathlib.Path(gri_tropo.__file__).parent / 'docs')". Every example in those files is run by the test suite.

Installation

pip install gri-tropo

For development:

git clone https://gitlab.com/geosol-foss/python/gri-tropo.git
cd gri-tropo
uv sync

Usage

DGS - Tropospheric Corrections

Calculate tropospheric angle and time corrections for signal propagation:

from gri_tropo.dgs import dgs_tropo_corrections
import numpy as np

# Define emitter location [lat, lon, alt_m]
emitter_lla = np.array([40.0, -105.0, 1500.0])

# Define collector position in ECEF XYZ (meters)
collector_xyz = np.array([1000000.0, -5000000.0, 4000000.0])

# Calculate corrections with custom atmospheric parameters
cone_corr, time_corr = dgs_tropo_corrections(
    emitter_lla,
    collector_xyz,
    n_sur=315.0,    # Surface refractivity (N-units)
    ref_ht=7.35     # Reference height (km; RefHt returns meters)
)

# cone_corr: Angular deflection correction (radians)
# time_corr: Time delay correction (seconds)

Both outputs are one-way, single-path corrections for one emitter-to-collector leg. There is no built-in differential helper: for a TDOA, compute the time correction for each leg and subtract; for an AOA, use the cone correction directly. A single (3,) emitter returns floats, a (K, 3) batch returns two shape-(K,) arrays, and the batch axis is over emitters with one shared collector per call.

The n_sur and ref_ht defaults are global means. For location- and time-specific values, use the NSur and RefHt classes below.

DGS Rates - Tropospheric Corrections Over Time

Rate observables (Doppler, FDOA, TDOA-rate) are biased by the time derivative of the excess delay, not by the delay itself. dgs_tropo_rates returns the exact derivatives of the pair above for the same geometry:

from gri_tropo import dgs_tropo_rates
import numpy as np

emitter_lla = np.array([40.0, -105.0, 1500.0])
emitter_vel = np.zeros(3)                       # ECEF m/s
collector_xyz = np.array([1000000.0, -5000000.0, 4000000.0])
collector_vel = np.array([0.0, 7000.0, 1500.0])  # ECEF m/s

cone_rate, delay_rate = dgs_tropo_rates(
    emitter_lla,
    emitter_vel,
    collector_xyz,
    collector_vel,
)

# cone_rate:  radians per second
# delay_rate: seconds per second (dimensionless, frequency-independent)

The delay rate is frequency-independent because the troposphere is non-dispersive at RF, matching the convention of gri_utils.observables.get_tdoa_dot. Scale it by the carrier where it is used:

freq_bias_hz = -1.5e9 * delay_rate      # one-way bias at L-band

For an FDOA, compute the bias on each leg and subtract; the delay rate adds directly to a TDOA-rate observable.

NSur - Surface Refractivity Data

Access hourly surface refractivity data from ERA5:

from gri_tropo.nsur import NSur
from datetime import datetime

# Initialize with data directory
nsur = NSur("./data/n_sur")

# Get value for specific location and time
value = nsur.get_n_sur(
    lat=40.0,
    lon=254.0,  # -180 to 180 or 0 to 360; nearest whole degree
    dt=datetime(2025, 3, 15),
    hour=12
)

# Get values for multiple coordinates efficiently
coords = [
    (40.0, 254.0, datetime(2025, 3, 15), 12),
    (35.0, 250.0, datetime(2025, 3, 15), 14),
]
values = nsur.get_all_n_surs(coords)

RefHt - Reference Height Data

Access reference height data (scale height for exponential refractivity decay). Values are in meters; divide by 1000 for the ref_ht argument, which is in km:

from gri_tropo.refht import RefHt
from datetime import datetime

# Initialize with data directory
refht = RefHt("./data/ref_ht")

# Get value for specific location and date, in METERS
value = refht.get_ref_ht(
    lat=40.0,
    lon=254.0,  # -180 to 180 or 0 to 360; nearest whole degree
    dt=datetime(2025, 3, 15)
)

# Get values for multiple coordinates efficiently
coords = [
    (40.0, 254.0, datetime(2025, 3, 15)),
    (35.0, 250.0, datetime(2025, 3, 16)),
]
values = refht.get_all_ref_hts(coords)

Data Download and Processing

The NPZ data files required by this package are generated by the separate gri-tropo-data package. This separation keeps gri-tropo lightweight with minimal dependencies for users who just need tropospheric corrections.

For information on downloading ERA5 data and processing it to generate n_sur and ref_ht datasets, see the gri-tropo-data repository.

Dependencies

  • gri-utils: Coordinate conversions and geodetic utilities
  • numpy: Array operations
  • scipy: Scientific computing

Other Projects

Current list of other GRI FOSS Projects we are building and maintaining.

License

MIT License. See LICENSE for details.

Release files for gri-tropo 0.2.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for gri-tropo 0.2.6
File Size Uploaded
gri_tropo-0.2.6.tar.gz 70.4 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for gri-tropo 0.2.6
File Interpreter ABI Platform
gri_tropo-0.2.6-py3-none-any.whl Python 3 none any Details

Total release size: 70.4 MB

Release files / gri_tropo-0.2.6.tar.gz

Download URL gri_tropo-0.2.6.tar.gz
Size 70.4 MB
Tags Source
SHA-256 checksum
How to use checksums
96fc277e5667d6e028306dc3fccf7a1536a1f1c3f7529985faee80a95d7da2c8
BLAKE2b-256 checksum
How to use checksums
be21398b4acd982ccb7dfe21a3109b8827442b3319e9b645c5fd2d07dc7f808c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / gri_tropo-0.2.6-py3-none-any.whl

Download URL gri_tropo-0.2.6-py3-none-any.whl
Size 25.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6b1f16d10b6f95550e125e4fdd239113b2bc682c35dd4691a0da4858bdbd93cb
BLAKE2b-256 checksum
How to use checksums
9adcc755dda950fef9e24a55c0b3f4577d9a531acd933fd5b2b3110ed1da1f21
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.2.6 This release

2 release files

0.2.5

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page