Skip to content

Commit ebba5fb

Browse files
committed
Merge branch 'ryanpavlick-main'
2 parents a7e1d93 + 53714fd commit ebba5fb

File tree

4 files changed

+262
-1
lines changed

4 files changed

+262
-1
lines changed

.github/contributors.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Thanks to those who contributed code and ideas, including:
22

33
```
44
@aldebaran1 (robustness)
5-
@rpavlick (multiple features + functions)
5+
@ryanpavlick (.rsphere, .rcurve, .latitude, .lox, .los, .dca, and more)
66
@cchuravy (Ellipsoid parameters)
77
@jprMesh (more conversion functions)
88
@Fil (docs)

src/pymap3d/__init__.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,19 @@
3232
__version__ = "3.1.2"
3333

3434
from .aer import aer2ecef, aer2geodetic, ecef2aer, geodetic2aer
35+
from .dca import (
36+
enu2dca,
37+
dca2enu,
38+
dca2ned,
39+
ned2dca,
40+
ecef2dca,
41+
dca2ecef,
42+
geodetic2dca,
43+
dca2geodetic,
44+
aer2dca,
45+
dca2aer,
46+
)
47+
3548
from .ecef import (
3649
ecef2enu,
3750
ecef2enuv,
@@ -75,6 +88,8 @@
7588
geoc2geod,
7689
)
7790

91+
from .nvector import geodetic2nvector, nvector2geodetic, ecef2nvector, nvector2ecef
92+
7893
from .rcurve import parallel, meridian, transverse, geocentric_radius
7994

8095
__all__ = [
@@ -129,8 +144,23 @@
129144
"authalic2geodetic",
130145
"geod2geoc",
131146
"geoc2geod",
147+
"nvector2geodetic",
148+
"geodetic2nvector",
149+
"ecef2nvector",
150+
"nvector2ecef",
151+
"enu2dca",
152+
"dca2enu",
153+
"dca2ned",
154+
"ned2dca",
155+
"ecef2dca",
156+
"dca2ecef",
157+
"geodetic2dca",
158+
"dca2geodetic",
159+
"aer2dca",
160+
"dca2aer",
132161
]
133162

163+
134164
from .aer import aer2eci, eci2aer
135165
from .azelradec import azel2radec, radec2azel
136166
from .eci import ecef2eci, eci2ecef

src/pymap3d/dca.py

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
"""
2+
Transforms involving DCA (Downrange, Crossrange, Above)
3+
4+
This module provides functions to convert coordinates between DCA and
5+
other coordinate systems such as ENU (East, North, Up), NED (North, East, Down),
6+
ECEF (Earth-Centered, Earth-Fixed), and geodetic coordinates (latitude, longitude, altitude).
7+
8+
It also includes transformations to/from AER (Azimuth, Elevation, Range).
9+
10+
Functions:
11+
----------
12+
- enu2dca: Convert ENU to DCA coordinates.
13+
- dca2enu: Convert DCA to ENU coordinates.
14+
- dca2ned: Convert DCA to NED coordinates.
15+
- ned2dca: Convert NED to DCA coordinates.
16+
- ecef2dca: Convert ECEF to DCA coordinates.
17+
- dca2ecef: Convert DCA to ECEF coordinates.
18+
- geodetic2dca: Convert geodetic to DCA coordinates.
19+
- dca2geodetic: Convert DCA to geodetic coordinates.
20+
- aer2dca: Convert AER to DCA coordinates.
21+
- dca2aer: Convert DCA to AER coordinates.
22+
"""
23+
24+
from .mathfun import sin, cos, radians
25+
from .ecef import ecef2enu, enu2ecef
26+
from .enu import geodetic2enu, enu2geodetic, aer2enu, enu2aer
27+
from .ellipsoid import Ellipsoid
28+
29+
__all__ = [
30+
"enu2dca",
31+
"dca2enu",
32+
"dca2ned",
33+
"ned2dca",
34+
"ecef2dca",
35+
"dca2ecef",
36+
"geodetic2dca",
37+
"dca2geodetic",
38+
"aer2dca",
39+
"dca2aer",
40+
]
41+
42+
ELL = Ellipsoid.from_name("wgs84") # Default reference ellipsoid (WGS84)
43+
44+
45+
def enu2dca(e, n, u, heading, deg=True):
46+
"""
47+
Converts ENU (East, North, Up) coordinates to DCA (Downrange, Crossrange, Above).
48+
"""
49+
if deg:
50+
heading = radians(heading)
51+
dr = e * sin(heading) + n * cos(heading)
52+
cr = -e * cos(heading) + n * sin(heading)
53+
a = u
54+
return dr, cr, a
55+
56+
57+
def dca2enu(dr, cr, a, heading, deg=True):
58+
"""
59+
Converts DCA (Downrange, Crossrange, Above) coordinates to ENU (East, North, Up).
60+
"""
61+
if deg:
62+
heading = radians(heading)
63+
e = dr * sin(heading) - cr * cos(heading)
64+
n = dr * cos(heading) + cr * sin(heading)
65+
u = a
66+
return e, n, u
67+
68+
69+
def dca2ned(dr, cr, a, heading, deg=True):
70+
"""
71+
Converts DCA (Downrange, Crossrange, Above) coordinates to NED (North, East, Down).
72+
"""
73+
e, n, u = dca2enu(dr, cr, a, heading, deg=deg)
74+
return n, e, -u
75+
76+
77+
def ned2dca(n, e, d, heading, deg=True):
78+
"""
79+
Converts NED (North, East, Down) coordinates to DCA (Downrange, Crossrange, Above).
80+
"""
81+
dr, cr, a = enu2dca(e, n, -d, heading, deg=deg)
82+
return dr, cr, a
83+
84+
85+
def ecef2dca(x, y, z, lat0, lon0, h0, heading, ell=ELL, deg=True):
86+
"""
87+
Converts ECEF (Earth-Centered, Earth-Fixed) coordinates to DCA (Downrange, Crossrange, Above).
88+
"""
89+
e, n, u = ecef2enu(x, y, z, lat0, lon0, h0, ell, deg=deg)
90+
return enu2dca(e, n, u, heading, deg=deg)
91+
92+
93+
def dca2ecef(dr, cr, a, lat0, lon0, h0, heading, ell=ELL, deg=True):
94+
"""
95+
Converts DCA (Downrange, Crossrange, Above) coordinates to ECEF (Earth-Centered, Earth-Fixed) coordinates.
96+
"""
97+
e, n, u = dca2enu(dr, cr, a, heading, deg=deg)
98+
return enu2ecef(e, n, u, lat0, lon0, h0, ell, deg=deg)
99+
100+
101+
def geodetic2dca(lat, lon, h, lat0, lon0, h0, heading, ell=ELL, deg=True):
102+
"""
103+
Converts geodetic coordinates (latitude, longitude, altitude) to DCA (Downrange, Crossrange, Above) coordinates.
104+
"""
105+
e, n, u = geodetic2enu(lat, lon, h, lat0, lon0, h0, ell, deg=deg)
106+
return enu2dca(e, n, u, heading, deg=deg)
107+
108+
109+
def dca2geodetic(dr, cr, a, lat0, lon0, h0, heading, ell=ELL, deg=True):
110+
"""
111+
Converts DCA (Downrange, Crossrange, Above) coordinates to geodetic coordinates (latitude, longitude, altitude).
112+
"""
113+
e, n, u = dca2enu(dr, cr, a, heading, deg=deg)
114+
return enu2geodetic(e, n, u, lat0, lon0, h0, ell, deg=deg)
115+
116+
117+
def aer2dca(az, el, srange, heading, deg=True):
118+
"""
119+
Converts AER (Azimuth, Elevation, Range) coordinates to DCA (Downrange, Crossrange, Above).
120+
"""
121+
e, n, u = aer2enu(az, el, srange, deg=deg)
122+
return enu2dca(e, n, u, heading, deg=deg)
123+
124+
125+
def dca2aer(dr, cr, a, heading, deg=True):
126+
"""
127+
Converts DCA (Downrange, Crossrange, Above) coordinates to AER (Azimuth, Elevation, Range).
128+
"""
129+
e, n, u = dca2enu(dr, cr, a, heading, deg=deg)
130+
return enu2aer(e, n, u, deg=deg)

src/pymap3d/nvector.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import numpy as np
2+
import pymap3d as pm
3+
4+
def geodetic2nvector(lat, lon, ell=None, deg=True):
5+
"""
6+
Convert geodetic coordinates (latitude, longitude) to an n-vector.
7+
8+
Parameters:
9+
lat : float or array-like
10+
Geodetic latitude(s).
11+
lon : float or array-like
12+
Geodetic longitude(s).
13+
ell : str or tuple, optional
14+
Reference ellipsoid (default is None, which uses WGS84).
15+
deg : bool, optional
16+
If True (default), inputs are in degrees. If False, use radians.
17+
18+
Returns:
19+
n1, n2, n3 : ndarray
20+
Components of the n-vector in the Earth-Centered Earth-Fixed (ECEF) coordinate system.
21+
"""
22+
lat, lon = np.atleast_1d(lat), np.atleast_1d(lon)
23+
24+
if deg:
25+
lat, lon = np.radians(lat), np.radians(lon)
26+
27+
sin_lat, cos_lat = np.sin(lat), np.cos(lat)
28+
sin_lon, cos_lon = np.sin(lon), np.cos(lon)
29+
30+
n1 = cos_lat * cos_lon
31+
n2 = cos_lat * sin_lon
32+
n3 = sin_lat
33+
34+
return n1, n2, n3
35+
36+
def nvector2geodetic(n1, n2, n3, ell=None, deg=True):
37+
"""
38+
Convert an n-vector back to geodetic coordinates (latitude, longitude).
39+
40+
Parameters:
41+
n1, n2, n3 : float or array-like
42+
Components of the n-vector in the Earth-Centered Earth-Fixed (ECEF) coordinate system.
43+
ell : str or tuple, optional
44+
Reference ellipsoid (default is None, which uses WGS84).
45+
deg : bool, optional
46+
If True (default), returns latitude and longitude in degrees. If False, in radians.
47+
48+
Returns:
49+
lat, lon : ndarray
50+
Geodetic latitude(s) and longitude(s).
51+
"""
52+
n1, n2, n3 = np.atleast_1d(n1), np.atleast_1d(n2), np.atleast_1d(n3)
53+
54+
# Compute latitude and longitude from n-vector
55+
lat = np.arcsin(n3)
56+
lon = np.arctan2(n2, n1)
57+
58+
if deg:
59+
lat, lon = np.degrees(lat), np.degrees(lon)
60+
61+
return lat, lon
62+
63+
def ecef2nvector(x, y, z, ell=None, deg=True):
64+
"""
65+
Convert ECEF coordinates to an n-vector.
66+
67+
Parameters:
68+
x, y, z : float or array-like
69+
ECEF coordinates in meters.
70+
ell : str or tuple, optional
71+
Reference ellipsoid (default is None, which uses WGS84).
72+
deg : bool, optional
73+
If True (default), geodetic2nvector() inputs are in degrees. If False, in radians.
74+
75+
Returns:
76+
n1, n2, n3 : ndarray
77+
Components of the n-vector in the Earth-Centered Earth-Fixed (ECEF) coordinate system.
78+
"""
79+
lat, lon, _ = pm.ecef2geodetic(x, y, z, ell=ell, deg=deg)
80+
return geodetic2nvector(lat, lon, ell=ell, deg=deg)
81+
82+
def nvector2ecef(n1, n2, n3, alt=0, ell=None, deg=True):
83+
"""
84+
Convert an n-vector to ECEF coordinates.
85+
86+
Parameters:
87+
n1, n2, n3 : float or array-like
88+
Components of the n-vector in the Earth-Centered Earth-Fixed (ECEF) coordinate system.
89+
alt : float or array-like, optional
90+
Altitude in meters (default is 0).
91+
ell : str or tuple, optional
92+
Reference ellipsoid (default is None, which uses WGS84).
93+
deg : bool, optional
94+
If True (default), nvector2geodetic() outputs are in degrees. If False, in radians.
95+
96+
Returns:
97+
x, y, z : ndarray
98+
ECEF coordinates in meters.
99+
"""
100+
lat, lon = nvector2geodetic(n1, n2, n3, ell=ell, deg=deg)
101+
return pm.geodetic2ecef(lat, lon, alt, ell=ell, deg=deg)

0 commit comments

Comments
 (0)