Atmosphere
Atmospheric models for propagation and refraction.
Atmospheric models for tracking applications.
This module provides standard atmosphere models used for computing temperature, pressure, density, and other properties at various altitudes.
Submodules
models : Standard atmosphere models (US76, ISA) ionosphere : Ionospheric models for GPS/GNSS corrections
Atmospheric Models
Standard atmosphere and refraction models.
Atmospheric models for tracking applications.
This module provides standard atmosphere models used for computing temperature, pressure, and density at various altitudes.
- class pytcl.atmosphere.models.AtmosphereState(temperature, pressure, density, speed_of_sound)[source]
Bases:
NamedTupleAtmospheric state at a given altitude.
- Variables:
- pytcl.atmosphere.models.us_standard_atmosphere_1976(altitude)[source]
Compute atmospheric properties using US Standard Atmosphere 1976.
- Parameters:
altitude (array_like) – Geometric altitude in meters. Valid from 0 to ~86 km.
- Returns:
state – Atmospheric state containing temperature, pressure, density, and speed of sound.
- Return type:
Examples
>>> state = us_standard_atmosphere_1976(10000) >>> round(state.temperature, 3) 223.252 >>> round(state.pressure, 1) 26499.9
Notes
The US Standard Atmosphere 1976 is a model of the Earth’s atmosphere that defines temperature, pressure, and density as functions of altitude. It is valid from sea level to approximately 86 km altitude.
References
U.S. Standard Atmosphere, 1976, U.S. Government Printing Office, Washington, D.C., 1976.
- pytcl.atmosphere.models.isa_atmosphere(altitude, temperature_offset=0.0)[source]
Compute atmospheric properties using International Standard Atmosphere (ISA).
This is essentially the troposphere portion of US Standard Atmosphere 1976 with an optional temperature offset for non-standard days.
- Parameters:
altitude (array_like) – Geometric altitude in meters.
temperature_offset (float, optional) – Temperature offset from ISA conditions in Kelvin (default: 0). Positive values indicate warmer than standard day.
- Returns:
state – Atmospheric state.
- Return type:
Examples
>>> # Standard day at 5000m >>> state = isa_atmosphere(5000) >>> # Hot day (+15K) at 5000m >>> state = isa_atmosphere(5000, temperature_offset=15)
- pytcl.atmosphere.models.altitude_from_pressure(pressure)[source]
Compute geometric altitude from pressure (pressure altitude).
- Parameters:
pressure (array_like) – Atmospheric pressure in Pascals.
- Returns:
altitude – Geometric altitude in meters.
- Return type:
ndarray
Examples
>>> # Sea level pressure >>> bool(abs(altitude_from_pressure(101325)) < 1e-6) True >>> # Pressure at approximately 5000m >>> alt = altitude_from_pressure(54000) >>> 4800 < alt < 5200 True
Notes
This is an approximate inversion of the ISA model, valid primarily in the troposphere.
- pytcl.atmosphere.models.mach_number(velocity, altitude)[source]
Compute Mach number from velocity and altitude.
- Parameters:
velocity (array_like) – True airspeed in m/s.
altitude (array_like) – Geometric altitude in meters.
- Returns:
mach – Mach number.
- Return type:
ndarray
Examples
>>> # Aircraft at 300 m/s at sea level >>> mach_number(300, 0) 0.88... >>> # Same speed at 10 km altitude (lower speed of sound) >>> mach_number(300, 10000) 1.00...
- pytcl.atmosphere.models.true_airspeed_from_mach(mach, altitude)[source]
Compute true airspeed from Mach number and altitude.
- Parameters:
mach (array_like) – Mach number.
altitude (array_like) – Geometric altitude in meters.
- Returns:
velocity – True airspeed in m/s.
- Return type:
ndarray
Examples
>>> # Mach 0.8 at cruise altitude (10 km) >>> tas = true_airspeed_from_mach(0.8, 10000) >>> 230 < tas < 250 # approximately 240 m/s True >>> # Supersonic at sea level >>> true_airspeed_from_mach(1.0, 0) 340.2...
Thermosphere Model
Simplified barometric thermosphere density, temperature and composition
model with solar-activity and geomagnetic inputs. Not NRLMSISE-00: usable
above ~200 km (within ~2x of published NRLMSISE-00 values), up to 50x wrong
below ~86 km where us_standard_atmosphere_1976 should be used. Limits
are documented in the module and pinned by validation tests (gh-79).
Barometric thermosphere model.
This is not NRLMSISE-00. The module was named nrlmsise00 and described
itself as a high-fidelity NRL model until gh-79. NRLMSISE-00 requires harmonic
coefficient tables from NOAA which this library does not distribute; what is
implemented here is a set of per-species exponential profiles with
temperature-dependent scale heights, driven by F10.7 and Ap, each clamped to a
floor.
It is usable above roughly 200 km, where it agrees with published NRLMSISE-00
densities to within a factor of about two. Below that it is wrong by up to
50x, and pytcl.atmosphere.us_standard_atmosphere_1976() should be used
instead. SimplifiedThermosphere carries the measured comparison.
The references below describe the model this one approximates, not the one implemented here. They are retained because the temperature parameterisation and species set follow their structure.
References
Picone, J. M., A. E. Hedin, D. P. Drob, and A. C. Aikin (2002), “NRLMSISE-00 empirical model of the atmosphere: Statistical comparisons and scientific issues,” J. Geophys. Res., 107(A12), 1468, doi:10.1029/2002JA009430
NASA GSFC NRLMSISE-00 Model: https://ccmc.gsfc.nasa.gov/models/nrlmsise00
Drob, D. P., et al. (2008), “An update to the COSPAR International Reference Atmosphere model for the middle atmosphere,” Adv. Space Res., 43(12), 1747-1764
- class pytcl.atmosphere.thermosphere.SimplifiedThermosphere(use_meter_altitude=True)[source]
Bases:
objectBarometric thermosphere model with solar and geomagnetic coupling.
This is not NRLMSISE-00. It was named
NRLMSISE00and described as “a comprehensive thermosphere model” until gh-79; the real model requires harmonic coefficient tables from NOAA that this library does not ship. What this computes is a set of per-species exponential profiles with temperature-dependent scale heights, driven by F10.7 and Ap, with each species density clamped to a floor.Use it above roughly 200 km. Below that it is wrong, in places by more than an order of magnitude:
Altitude
This model
US Standard 1976
Ratio
0 km
0.682 kg/m^3
1.225 kg/m^3
0.56
10 km
0.253
0.414
0.61
30 km
0.0261
0.0184
1.42
50 km
1.044e-3
1.027e-3
1.02
80 km
3.69e-7
1.85e-5
0.02
For altitudes below about 86 km use
pytcl.atmosphere.us_standard_atmosphere_1976(), which is validated against the standard. In the thermosphere this model is reasonable: at 400 km with F10.7 = 150 it gives 2.9e-12 kg/m^3, against a published NRLMSISE-00 range of roughly 2-4e-12.The species floors are visible in the output.
h_densityreturns its floor of 1e8 m^-3 from 0 to 400 km, andhe_densityits floor of 1e10 m^-3 at 0 and 100 km, because the computed profile falls below the clamp. Those are not measurements of anything.The model implements: - Temperature profile with solar activity and magnetic coupling - Per-species barometric profiles with floors - Solar flux (F10.7) and magnetic activity (Ap) variations
- Parameters:
use_meter_altitude (bool, optional) – If True, expect altitude input in meters. If False, expect km. Default is True (meters).
Examples
>>> model = SimplifiedThermosphere() >>> output = model( ... latitude=np.radians(45), ... longitude=np.radians(-75), ... altitude=400_000, # 400 km ... year=2024, ... day_of_year=100, ... seconds_in_day=43200, ... f107=150, ... f107a=150, ... ap=5 ... ) >>> print(f"Density: {output.density:.2e} kg/m³") Density: 2.85e-12 kg/m³
Notes
This implementation uses empirical correlations for atmospheric properties as a function of geomagnetic and solar activity indices. For highest accuracy, use the original NRLMSISE-00 Fortran code from NASA/NOAA, which includes extensive coefficient tables.
- __call__(latitude, longitude, altitude, year, day_of_year, seconds_in_day, f107=150.0, f107a=150.0, ap=4.0)[source]
Compute atmospheric density and composition.
- Parameters:
latitude (array_like) – Geodetic latitude in radians.
longitude (array_like) – Longitude in radians.
altitude (array_like) – Altitude in meters (or km if use_meter_altitude=False).
year (int) – Year (e.g., 2024).
day_of_year (int) – Day of year (1-366).
seconds_in_day (float) – Seconds since midnight (0-86400).
f107 (float, optional) – 10.7 cm solar flux (daily value, SFU). Default 150.
f107a (float, optional) – 10.7 cm solar flux (81-day average, SFU). Default 150.
ap (float or array_like, optional) – Planetary magnetic index. Can be single value or 8-element array of 3-hour Ap values. Default 4.0.
- Returns:
output – Atmospheric properties (density, temperature, composition).
- Return type:
Notes
The model assumes hydrostatic equilibrium and uses empirical correlations for density and temperature variations.
- class pytcl.atmosphere.thermosphere.ThermosphereState(density, temperature, exosphere_temperature, he_density, o_density, n2_density, o2_density, ar_density, h_density, n_density)[source]
Bases:
NamedTupleOutput from NRLMSISE-00 atmospheric model.
- Variables:
density (float or ndarray) – Total atmospheric density in kg/m³.
temperature (float or ndarray) – Temperature at altitude (K).
exosphere_temperature (float or ndarray) – Exospheric temperature (K).
he_density (float or ndarray) – Helium density in m⁻³.
o_density (float or ndarray) – Atomic oxygen density in m⁻³.
n2_density (float or ndarray) – N₂ density in m⁻³.
o2_density (float or ndarray) – O₂ density in m⁻³.
ar_density (float or ndarray) – Argon density in m⁻³.
h_density (float or ndarray) – Hydrogen density in m⁻³.
n_density (float or ndarray) – Atomic nitrogen density in m⁻³.
- class pytcl.atmosphere.thermosphere.F107Index(f107, f107a, ap, ap_array=None)[source]
Bases:
NamedTupleSolar activity indices for NRLMSISE-00.
- Variables:
f107 (float) – 10.7 cm solar radio flux (daily, SFU).
f107a (float) – 10.7 cm solar radio flux (81-day average, SFU).
ap (float or ndarray) – Planetary magnetic index (Ap index).
ap_array (ndarray, optional) – Ap values for each 3-hour interval of the day (8 values). If not provided, derived from ap value.
- pytcl.atmosphere.thermosphere.simplified_thermosphere(latitude, longitude, altitude, year, day_of_year, seconds_in_day, f107=150.0, f107a=150.0, ap=4.0)[source]
Compute NRLMSISE-00 atmospheric properties.
This is a module-level convenience function wrapping the SimplifiedThermosphere class.
- Parameters:
latitude (array_like) – Geodetic latitude in radians.
longitude (array_like) – Longitude in radians.
altitude (array_like) – Altitude in meters.
year (int) – Year (e.g., 2024).
day_of_year (int) – Day of year (1-366).
seconds_in_day (float) – Seconds since midnight (0-86400).
f107 (float, optional) – 10.7 cm solar flux (daily value, SFU). Default 150.
f107a (float, optional) – 10.7 cm solar flux (81-day average, SFU). Default 150.
ap (float or array_like, optional) – Planetary magnetic index. Default 4.0.
- Returns:
output – Atmospheric properties.
- Return type:
Notes
See SimplifiedThermosphere class for more details.
Examples
>>> # ISS altitude (~400 km), magnetic latitude = 40°, quiet geomagnetic activity >>> output = simplified_thermosphere( ... latitude=np.radians(40), ... longitude=np.radians(-75), ... altitude=400_000, # 400 km ... year=2024, ... day_of_year=1, ... seconds_in_day=43200, ... f107=150, # Average solar activity ... f107a=150, ... ap=5 # Quiet conditions ... ) >>> print(f"Density at ISS: {output.density:.2e} kg/m³") Density at ISS: 2.89e-12 kg/m³
Ionosphere
Ionospheric models for radio propagation and navigation applications.
This module provides ionospheric models used for computing signal delays, electron density profiles, and Total Electron Content (TEC) estimates. These are essential for GPS/GNSS corrections and radio wave propagation.
Models
Klobuchar: GPS broadcast ionospheric model (single-frequency correction)
IRI: International Reference Ionosphere simplified model (
simple_iri)Dual-frequency TEC estimation and TEC-based delay
References
Klobuchar, J.A. (1987). “Ionospheric Time-Delay Algorithm for Single-Frequency GPS Users”. IEEE Transactions on Aerospace and Electronic Systems, AES-23(3), 325-331.
- class pytcl.atmosphere.ionosphere.IonosphereState(tec, delay_l1, delay_l2, f_peak, h_peak)[source]
Bases:
NamedTupleIonospheric state at a given location and time.
- Variables:
tec (float or ndarray) – Total Electron Content in TECU (10^16 electrons/m²).
delay_l1 (float or ndarray) – Ionospheric delay at L1 frequency in meters.
delay_l2 (float or ndarray) – Ionospheric delay at L2 frequency in meters.
f_peak (float or ndarray) – Critical frequency of F2 layer in MHz.
h_peak (float or ndarray) – Height of F2 layer peak in km.
- class pytcl.atmosphere.ionosphere.KlobucharCoefficients(alpha, beta)[source]
Bases:
NamedTupleKlobuchar ionospheric model coefficients.
These coefficients are broadcast by GPS satellites in the navigation message.
- Variables:
alpha (ndarray) – Amplitude coefficients (4 values) in seconds.
beta (ndarray) – Period coefficients (4 values) in seconds.
- pytcl.atmosphere.ionosphere.klobuchar_delay(latitude, longitude, elevation, azimuth, gps_time, coefficients=None)[source]
Compute ionospheric delay using the Klobuchar model.
The Klobuchar model is the standard GPS broadcast ionospheric correction model. It provides single-frequency ionospheric delay estimates accurate to about 50% RMS.
- Parameters:
latitude (array_like) – User geodetic latitude in radians.
longitude (array_like) – User geodetic longitude in radians.
elevation (array_like) – Satellite elevation angle in radians.
azimuth (array_like) – Satellite azimuth angle in radians.
gps_time (array_like) – GPS time of week in seconds.
coefficients (KlobucharCoefficients, optional) – Ionospheric coefficients from GPS navigation message. If None, uses default mid-latitude values.
- Returns:
delay – Ionospheric delay in meters (at L1 frequency).
- Return type:
ndarray
Examples
>>> # User at 40°N, 105°W, satellite at 45° elevation >>> delay = klobuchar_delay( ... np.radians(40), np.radians(-105), ... np.radians(45), np.radians(180), ... gps_time=43200 # Noon ... ) >>> delay > 0 True
Notes
The Klobuchar model assumes a thin-shell ionosphere at 350 km altitude and uses a cosine model for diurnal variation. It typically removes about 50% of the ionospheric delay.
References
IS-GPS-200, Interface Specification.
- pytcl.atmosphere.ionosphere.dual_frequency_tec(pseudorange_l1, pseudorange_l2)[source]
Compute Total Electron Content from dual-frequency pseudoranges.
This method uses the dispersive nature of the ionosphere to estimate TEC from the difference in L1 and L2 pseudoranges.
- Parameters:
pseudorange_l1 (array_like) – L1 pseudorange in meters.
pseudorange_l2 (array_like) – L2 pseudorange in meters.
- Returns:
tec – Total Electron Content in TECU (10^16 electrons/m²).
- Return type:
ndarray
Examples
>>> # Pseudorange measurements from dual-frequency receiver >>> p_l1 = 22000000.0 # L1 pseudorange in meters >>> p_l2 = 22000002.5 # L2 pseudorange (slightly delayed) >>> tec = dual_frequency_tec(p_l1, p_l2) >>> tec > 0 # TEC should be positive True
Notes
The ionospheric delay is proportional to TEC and inversely proportional to frequency squared:
delay = 40.3 * TEC / f²
The difference in delays at L1 and L2 gives:
P2 - P1 = 40.3 * TEC * (1/f1² - 1/f2²)
This is the standard dual-frequency ionospheric correction method.
- pytcl.atmosphere.ionosphere.ionospheric_delay_from_tec(tec, frequency=1575420000.0)[source]
Compute ionospheric delay from Total Electron Content.
- Parameters:
tec (array_like) – Total Electron Content in TECU (10^16 electrons/m²).
frequency (float, optional) – Signal frequency in Hz. Default is GPS L1.
- Returns:
delay – Ionospheric delay in meters.
- Return type:
ndarray
Examples
>>> # Typical mid-latitude TEC of 20 TECU >>> delay = ionospheric_delay_from_tec(20.0) >>> delay > 0 True >>> # Delay at L2 is larger than at L1 (lower frequency) >>> delay_l2 = ionospheric_delay_from_tec(20.0, frequency=F_L2) >>> delay_l2 > delay True
Notes
- The ionospheric delay for a signal is:
delay = 40.3 * TEC * 10^16 / f²
- pytcl.atmosphere.ionosphere.simple_iri(latitude, longitude, altitude, hour, month=6, solar_flux=150.0)[source]
Simplified International Reference Ionosphere (IRI) model.
This provides approximate electron density and TEC values based on simplified IRI physics. For accurate predictions, use the full IRI model or external services.
- Parameters:
latitude (array_like) – Geodetic latitude in radians.
longitude (array_like) – Geodetic longitude in radians.
altitude (array_like) – Altitude in meters.
hour (array_like) – Local hour (0-24).
month (int, optional) – Month of year (1-12). Default is 6 (June).
solar_flux (float, optional) – F10.7 solar flux in SFU. Default is 150 (moderate activity).
- Returns:
state – Ionospheric state with TEC, delays, and F2 layer parameters.
- Return type:
Notes
This is a simplified empirical model suitable for educational purposes and rough estimates. For operational use, the full IRI-2020 model should be employed.
Examples
>>> state = simple_iri(np.radians(40), np.radians(-105), 300e3, 12) >>> state.tec > 0 True
- pytcl.atmosphere.ionosphere.magnetic_latitude(latitude, longitude)[source]
Compute approximate geomagnetic latitude.
Uses a simple dipole approximation with the magnetic pole at approximately 80.5°N, 72.8°W.
- Parameters:
latitude (array_like) – Geodetic latitude in radians.
longitude (array_like) – Geodetic longitude in radians.
- Returns:
mag_lat – Geomagnetic latitude in radians.
- Return type:
ndarray
Examples
>>> import numpy as np >>> # New York City (40.7°N, 74°W) >>> mag_lat = magnetic_latitude(np.radians(40.7), np.radians(-74)) >>> np.degrees(mag_lat) 50.19... >>> # Equator at 0° longitude >>> mag_lat_eq = magnetic_latitude(0.0, 0.0) >>> np.abs(np.degrees(mag_lat_eq)) < 15 # Near magnetic equator True
- pytcl.atmosphere.ionosphere.scintillation_index(magnetic_latitude, hour, kp_index=3.0)[source]
Estimate ionospheric scintillation index S4.
Provides a rough estimate of amplitude scintillation based on geomagnetic latitude, local time, and geomagnetic activity.
- Parameters:
magnetic_latitude (array_like) – Geomagnetic latitude in radians.
hour (array_like) – Local hour (0-24).
kp_index (float, optional) – Kp geomagnetic activity index (0-9). Default is 3 (moderate).
- Returns:
s4 – S4 amplitude scintillation index (0-1).
- Return type:
ndarray
Examples
>>> import numpy as np >>> # Equatorial region at night (high scintillation risk) >>> s4 = scintillation_index(np.radians(10), 21, kp_index=5.0) >>> s4 > 0.3 # Moderate to strong scintillation True >>> # Mid-latitude during daytime (low scintillation) >>> s4_low = scintillation_index(np.radians(45), 12, kp_index=1.0) >>> s4_low < 0.2 True
Notes
S4 > 0.3 indicates moderate scintillation. S4 > 0.6 indicates strong scintillation that may affect receivers.