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 gridsapi_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)
| File | Size | Uploaded | |
|---|---|---|---|
| gri_tropo-0.2.6.tar.gz | 70.4 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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}
|