Skip to content

Commit

Permalink
feat: add calculation of zenith angle
Browse files Browse the repository at this point in the history
test: add zenith angle test
  • Loading branch information
tsutterley committed Nov 16, 2024
1 parent 872a091 commit fba5f35
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 9 deletions.
2 changes: 2 additions & 0 deletions doc/source/api_reference/spatial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,6 @@ General Methods

.. autofunction:: pyTMD.spatial.to_horizontal

.. autofunction:: pyTMD.spatial.to_zenith

.. autofunction:: pyTMD.spatial.scale_factors
27 changes: 18 additions & 9 deletions pyTMD/math.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@
]

# PURPOSE: calculate the sum of a polynomial function of time
def polynomial_sum(coefficients: list | np.ndarray, t: np.ndarray):
def polynomial_sum(
coefficients: list | np.ndarray,
t: np.ndarray
):
"""
Calculates the sum of a polynomial function using Horner's method [1]_
Expand All @@ -48,7 +51,10 @@ def polynomial_sum(coefficients: list | np.ndarray, t: np.ndarray):
t = np.atleast_1d(t)
return np.sum([c * (t ** i) for i, c in enumerate(coefficients)], axis=0)

def normalize_angle(theta: float | np.ndarray, circle: float = 360.0):
def normalize_angle(
theta: float | np.ndarray,
circle: float = 360.0
):
"""
Normalize an angle to a single rotation
Expand All @@ -61,15 +67,18 @@ def normalize_angle(theta: float | np.ndarray, circle: float = 360.0):
"""
return np.mod(theta, circle)

def rotate(theta: float | np.ndarray, axis: str = 'x'):
def rotate(
theta: float | np.ndarray,
axis: str = 'x'
):
"""
Rotate a 3-dimensional matrix about a given axis
Parameters
----------
theta: float or np.ndarray
Angle of rotation in radians
axis: str
axis: str, default 'x'
Axis of rotation (``'x'``, ``'y'``, or ``'z'``)
"""
# allocate for output rotation matrix
Expand Down Expand Up @@ -112,13 +121,13 @@ def legendre(
Parameters
----------
l: int
degree of Legrendre polynomials (0 to 3)
degree of the Legrendre polynomials (0 to 3)
x: np.ndarray
elements ranging from -1 to 1
Typically ``cos(theta)``, where ``theta`` is the colatitude in radians
m: int, default = 0
order of the Legendre polynomial
m: int, default 0
order of the Legendre polynomials (0 to ``l``)
Returns
-------
Expand Down Expand Up @@ -183,13 +192,13 @@ def sph_harm(
Parameters
----------
l: int
degree of spherical harmonics (0 to 3)
degree of the spherical harmonics (0 to 3)
theta: np.ndarray
colatitude in radians
phi: np.ndarray
longitude in radians
m: int, default 0
order of the spherical harmonics (0 to l)
order of the spherical harmonics (0 to ``l``)
Returns
-------
Expand Down
49 changes: 49 additions & 0 deletions pyTMD/spatial.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@
"to_ENU",
"from_ENU",
"to_horizontal",
"to_zenith",
"scale_areas",
"scale_factors",
]
Expand Down Expand Up @@ -2193,6 +2194,54 @@ def to_horizontal(
phi = np.mod(np.arctan2(E/D, N/D)*180.0/np.pi, 360.0)
return (alpha, phi, D)

def to_zenith(
x: np.ndarray,
y: np.ndarray,
z: np.ndarray,
lon0: float | np.ndarray = 0.0,
lat0: float | np.ndarray = 0.0,
h0: float | np.ndarray = 0.0,
a_axis: float = _wgs84.a_axis,
flat: float = _wgs84.flat,
):
"""
Calculate zenith angle of an object from Earth-Centered
Earth-Fixed (ECEF) cartesian coordinates
Parameters
----------
x, np.ndarray
cartesian x-coordinates
y, np.ndarray
cartesian y-coordinates
z, np.ndarray
cartesian z-coordinates
lon0: float or np.ndarray, default 0.0
reference longitude (degrees east)
lat0: float or np.ndarray, default 0.0
reference latitude (degrees north)
h0: float or np.ndarray, default 0.0
reference height (meters)
a_axis: float, default 6378137.0
semimajor axis of the ellipsoid
flat: float, default 1.0/298.257223563
ellipsoidal flattening
Returns
-------
zenith: np.ndarray
zenith angle of object in degrees
"""
# convert from ECEF to ENU
E, N, U = to_ENU(x, y, z, lon0=lon0, lat0=lat0, h0=h0,
a_axis=a_axis, flat=flat)
# convert from ENU to horizontal coordinates
alpha, phi, D = to_horizontal(E, N, U)
# calculate zenith angle in degrees
zenith = 90.0 - alpha
# return zenith angle
return zenith

def scale_areas(*args, **kwargs):
warnings.warn("Deprecated. Please use pyTMD.spatial.scale_factors instead",
DeprecationWarning)
Expand Down
11 changes: 11 additions & 0 deletions test/test_spatial.py
Original file line number Diff line number Diff line change
Expand Up @@ -478,9 +478,20 @@ def test_ECEF_to_horizontal():
# convert from ENU to horizontal coordinates
salt, saz, sdist = pyTMD.spatial.to_horizontal(SE, SN, SU)
lalt, laz, ldist = pyTMD.spatial.to_horizontal(LE, LN, LU)
# calculate zenith angle from ECEF coordinates
solar_zenith = pyTMD.spatial.to_zenith(SX, SY, SZ,
lon0=lon0, lat0=lat0, h0=h0)
lunar_zenith = pyTMD.spatial.to_zenith(LX, LY, LZ,
lon0=lon0, lat0=lat0, h0=h0)
# check solar azimuth and elevation
assert np.isclose(salt, -5.486, atol=0.001)
assert np.isclose(saz, 115.320, atol=0.001)
# check lunar azimuth and elevation
assert np.isclose(lalt, 36.381, atol=0.001)
assert np.isclose(laz, 156.297, atol=0.001)
# check solar and lunar zenith angles
assert np.isclose(solar_zenith, 95.486, atol=0.001)
assert np.isclose(lunar_zenith, 53.619, atol=0.001)
# verify relation between altitudes and zenith angles
assert solar_zenith == (90.0 - salt)
assert lunar_zenith == (90.0 - lalt)

0 comments on commit fba5f35

Please sign in to comment.