Coverage for src/gncpy/coordinate_transforms.py: 0%
69 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-13 06:15 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-13 06:15 +0000
1"""Implements functions for useful coordinate transforms."""
2import numpy as np
4import gncpy.wgs84 as wgs84
7def ecef_to_LLA(pos):
8 """Convert an ECEF position to LLA coordinates.
10 Parameters
11 ----------
12 pos : 3 x 1 numpy array
13 Position in meters in the ECEF frame
15 Returns
16 -------
17 3 x 1 numpy array
18 Latitude (rad), Longitude (rad), Altitude (m)
19 """
20 xyz = pos.copy().squeeze()
21 lon = np.arctan2(xyz[1], xyz[0])
23 p = np.sqrt(xyz[0] ** 2 + xyz[1] ** 2)
24 E = np.sqrt(wgs84.EQ_RAD ** 2 - wgs84.POL_RAD ** 2)
25 F = 54 * (wgs84.POL_RAD * xyz[2]) ** 2
26 G = (
27 p ** 2
28 + (1 - wgs84.ECCENTRICITY ** 2) * (xyz[2] ** 2)
29 - (wgs84.ECCENTRICITY * E) ** 2
30 )
31 c = wgs84.ECCENTRICITY ** 4 * F * p ** 2 / G ** 3
32 s = (1 + c + np.sqrt(c ** 2 + 2 * c)) ** (1 / 3)
33 P = (F / (3 * G ** 2)) / (s + 1 / s + 1) ** 2
34 Q = np.sqrt(1 + 2 * wgs84.ECCENTRICITY ** 4 * P)
35 k1 = -P * wgs84.ECCENTRICITY ** 2 * p / (1 + Q)
36 k2 = 0.5 * wgs84.EQ_RAD ** 2 * (1 + 1 / Q)
37 k3 = -P * (1 - wgs84.ECCENTRICITY ** 2) * xyz[2] ** 2 / (Q * (1 + Q))
38 k4 = -0.5 * P * p ** 2
39 k5 = p - wgs84.ECCENTRICITY ** 2 * (k1 + np.sqrt(k2 + k3 + k4))
40 U = np.sqrt(k5 ** 2 + xyz[2] ** 2)
41 V = np.sqrt(k5 ** 2 + (1 - wgs84.ECCENTRICITY ** 2) * xyz[2] ** 2)
42 alt = U * (1 - wgs84.POL_RAD ** 2 / (wgs84.EQ_RAD * V))
44 z0 = wgs84.POL_RAD ** 2 * xyz[2] / (wgs84.EQ_RAD * V)
45 ep = wgs84.EQ_RAD / wgs84.POL_RAD * wgs84.ECCENTRICITY
46 lat = np.arctan((xyz[2] + z0 * ep ** 2) / p)
48 return np.array([lat, lon, alt]).reshape((3, 1))
51def lla_to_ECEF(lat, lon, alt):
52 """Convert LLA coordinates to ECEF coordinates.
54 Parameters
55 ----------
56 lat : float
57 Latitude in radians
58 lon : float
59 Longitude in radians
60 alt : float
61 Altitude in meters
63 Returns
64 -------
65 3 x 1 numpy array
66 ECEF position in meters
67 """
68 re = wgs84.calc_ew_rad(lat)
69 c_lat = np.cos(lat)
70 s_lat = np.sin(lat)
71 c_lon = np.cos(lon)
72 s_lon = np.sin(lon)
74 x = (re + alt) * c_lat * c_lon
75 y = (re + alt) * c_lat * s_lon
76 z = ((1 - wgs84.ECCENTRICITY ** 2) * re + alt) * s_lat
77 return np.array([x, y, z]).reshape((3, 1))
80def lla_to_NED(ref_lat, ref_lon, ref_alt, pos_lat, pos_lon, pos_alt):
81 """Convert LLA to NED coordinates.
83 Parameters
84 ----------
85 ref_lat : float
86 Reference latitude (rad)
87 ref_lon : float
88 Reference longitude (rad)
89 ref_alt : float
90 Reference altitude (m)
91 pos_lat : float
92 Latitude (rad)
93 pos_lon : float
94 Longitude (rad)
95 pos_alt : float
96 Altitude (m)
98 Returns
99 -------
100 3 x 1 numpy array
101 Position in NED coordinates
102 """
103 c_lat = np.cos(ref_lat)
104 s_lat = np.sin(ref_lat)
105 c_lon = np.cos(ref_lon)
106 s_lon = np.sin(ref_lon)
107 R = np.array(
108 [
109 [-s_lat * c_lon, -s_lon, -c_lat * c_lon],
110 [-s_lat * s_lon, c_lon, -c_lat * s_lon],
111 [c_lat, 0, -s_lat],
112 ]
113 )
114 ref_E = lla_to_ECEF(ref_lat, ref_lon, ref_alt)
115 pos_E = lla_to_ECEF(pos_lat, pos_lon, pos_alt)
116 return R.T @ (pos_E - ref_E)
119def ecef_to_NED(ref_xyz, pos_xyz):
120 """Convert an ECEF position to the NED frame.
122 Parameters
123 ----------
124 ref_xyz : 3 x 1 numpy array
125 Reference position (m) in the ECEF frame
126 pos_xyz : 3 x 1 numpy array
127 Position (m) in the ECEF frame
129 Returns
130 -------
131 3 x 1 numpy array
132 Position (m) in the NED frame
133 """
134 ref_LLA = ecef_to_LLA(ref_xyz).squeeze()
135 c_lat = np.cos(ref_LLA[0])
136 s_lat = np.sin(ref_LLA[0])
137 c_lon = np.cos(ref_LLA[1])
138 s_lon = np.sin(ref_LLA[1])
139 R = np.array(
140 [
141 [-s_lat * c_lon, -s_lon, -c_lat * c_lon],
142 [-s_lat * s_lon, c_lon, -c_lat * s_lon],
143 [c_lat, 0, -s_lat],
144 ]
145 )
146 return R.T @ (pos_xyz - ref_xyz)
149def ned_to_LLA(ned, ref_lat, ref_lon, ref_alt):
150 """Convert NED to LLA.
152 Parameters
153 ----------
154 ned : numpy array
155 NED positon.
156 ref_lat : flaot
157 Reference latitude (radians).
158 ref_lon : flaot
159 Reference longitude (radians).
160 ref_alt : float
161 Reference altitude (meters).
163 Returns
164 -------
165 numpy array
166 Lat/lon/alt in rad/rad/m.
167 """
168 alt = ref_alt + -ned[2]
169 if isinstance(alt, np.ndarray):
170 alt = alt.item()
171 f_fact = 2 * wgs84.FLATTENING - wgs84.FLATTENING ** 2
172 s_lat2 = np.sin(ref_lat) ** 2
173 Rn = wgs84.EQ_RAD / np.sqrt(1 - f_fact * s_lat2)
174 Rm = Rn * ((1 - f_fact) / (1 - f_fact * s_lat2))
175 dlat = ned[0] * np.arctan2(1, Rm)
176 dlon = ned[1] * np.arctan2(1, Rn * np.cos(ref_lat))
177 lat = ref_lat + dlat
178 lon = ref_lon + dlon
179 if isinstance(lat, np.ndarray):
180 lat = lat.item()
181 if isinstance(lon, np.ndarray):
182 lon = lon.item()
183 return np.array([lat, lon, alt]).reshape((3, 1))