Coordinate systems¶
canvod-auxiliary works with three coordinate systems. Knowing which one is used where prevents sign errors and unit confusion in downstream VOD calculations.
-
ECEF
Earth-Centered, Earth-Fixed Cartesian (X, Y, Z) in meters. Used in SP3 ephemerides and the internal satellite position pipeline.
-
Geodetic (WGS84)
Latitude, longitude (degrees), altitude above ellipsoid (meters). Stored in RINEX headers as the approximate receiver position.
-
Spherical (receiver-relative)
Slant range \(r\), polar angle \(\theta\), geographic azimuth \(\varphi\). This is the output added to the obs Dataset for VOD geometry.
ECEF coordinates¶
Satellite positions from SP3 files are ECEF Cartesian coordinates in meters, one position per satellite and epoch.
from canvod.auxiliary import ECEFPosition
# From raw values
ecef = ECEFPosition(x=4_075_539.8, y=931_735.3, z=4_801_629.6)
# From RINEX Dataset metadata (reads APPROX POSITION header)
ecef = ECEFPosition.from_ds_metadata(rinex_ds)
# Convert to geodetic
lat, lon, alt = ecef.to_geodetic()
Geodetic coordinates¶
WGS84 geodetic coordinates for the receiver position, read from the APPROX POSITION XYZ RINEX header field.
from canvod.auxiliary import GeodeticPosition
geo = GeodeticPosition(lat=48.2, lon=16.4, alt=200.0)
# Convert to ECEF for vector calculations
ecef = geo.to_ecef() # ECEFPosition
Spherical coordinates (receiver-relative)¶
The key output of the auxiliary pipeline, added to the obs Dataset as theta and phi coordinates for each (epoch, sid) cell.
| Variable | Symbol | Range | Convention |
|---|---|---|---|
| Slant range | \(r\) | \(\geq 0\) m | Distance from receiver antenna to satellite |
| Polar angle | \(\theta\) | \([0, \pi/2]\) | 0 = overhead (zenith), \(\pi/2\) = horizon |
| Geographic azimuth | \(\varphi\) | \([0, 2\pi)\) | 0 = North, \(\pi/2\) = East, clockwise |
All three are NaN while the satellite is below the receiver's horizon.
Polar angle vs elevation
VOD literature often uses elevation angle \(e = \pi/2 - \theta\). canvod-auxiliary stores polar angle \(\theta\) internally. The \(\cos\theta\) factor in the tau-omega formula uses \(\theta\) directly: \(\text{VOD} = -\ln(T) \cdot \cos\theta\).
Computing spherical coordinates¶
from canvod.auxiliary import compute_spherical_coordinates
r, theta, phi = compute_spherical_coordinates(
# satellite ECEF positions, arrays of any shape
sat_x,
sat_y,
sat_z,
receiver_position, # ECEFPosition
)
The function:
- Subtracts the receiver ECEF position from each satellite ECEF position.
- Rotates the difference vector into the local ENU (East-North-Up) frame.
- Converts ENU to spherical \((r, \theta, \varphi)\) and sets all three to NaN below the horizon.
Adding coordinates to datasets¶
In a run the interpolated satellite positions are first selected for each signal, so \((r, \theta, \varphi)\) come out on (epoch, sid) and are attached to the observation dataset:
from canvod.auxiliary import add_spherical_coords_to_dataset
augmented_ds = add_spherical_coords_to_dataset(rinex_ds, r, theta, phi)
# New data variables on (epoch, sid):
# augmented_ds["theta"] → polar angle [rad]
# augmented_ds["phi"] → geographic azimuth [rad]
# augmented_ds["r"] → slant range [m]
A run keeps r only when processing.params.store_radial_distance is
true (default false).