Skip to content

Commit f2bd655

Browse files
committed
added coordinates module, with tests. Updated setup to include it
1 parent bce6c86 commit f2bd655

2 files changed

Lines changed: 951 additions & 0 deletions

File tree

skaero/geometry/coordinates.py

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
# coding: utf-8
2+
3+
"""
4+
Coordinate transformations used in flight dynamics
5+
"""
6+
7+
import numpy as np
8+
from numpy import sin, cos, deg2rad, array
9+
10+
11+
def lla2ecef(lat, lng, h):
12+
"""
13+
Calculates geocentric coordinates (ECEF - Earth Centered, Earth Fixed)
14+
for a given set of latitude, longitude and altitude inputs, following
15+
the WGS84 system.
16+
17+
Parameters
18+
----------
19+
lat : float
20+
latitude in degrees
21+
lng : float
22+
longitude in degrees
23+
h : float
24+
geometric altitude above sea level in meters
25+
26+
Returns
27+
-------
28+
array-like
29+
ECEF coordinates in meters
30+
"""
31+
if abs(lat) > 90:
32+
raise ValueError('latitude should be -90º <= latitude <= 90º')
33+
34+
if abs(lng) > 180:
35+
raise ValueError('longitude should be -180º <= longitude <= 180º')
36+
37+
if not (0 <= h <= 84852.05):
38+
msg = 'pressure model is only valid if 0 <= h <= 84852.05'
39+
raise ValueError(msg)
40+
41+
a = 6378137 # [m] Earth equatorial axis
42+
b = 6356752.3142 # [m] Earth polar axis
43+
e = 0.081819190842622 # Earth eccentricity
44+
45+
lat = deg2rad(lat) # degrees to radians
46+
lng = deg2rad(lng) # degrees to radians
47+
48+
N = a / (1 - (e * sin(lat))**2)**(.5)
49+
50+
x = (N + h) * cos(lat) * cos(lng)
51+
y = (N + h) * cos(lat) * sin(lng)
52+
z = (((b/a)**2) * N + h) * sin(lat)
53+
54+
return array([x, y, z])
55+
56+
57+
def ned2ecef(v_ned, lat, lng):
58+
"""
59+
Converts vector from local geodetic horizon reference frame (NED - North,
60+
East, Down) at a given latitude and longitude to geocentric coordinates
61+
(ECEF - Earth Centered, Earth Fixed).
62+
63+
Parameters
64+
----------
65+
v_ned: array-like
66+
vector expressed in NED coordinates
67+
lat : float
68+
latitude in degrees
69+
lng : float
70+
longitude in degrees
71+
72+
Returns
73+
-------
74+
v_ecef : array-like
75+
vector expressed in ECEF coordinates
76+
"""
77+
if abs(lat) > 90:
78+
raise ValueError('latitude should be -90º <= latitude <= 90º')
79+
80+
if abs(lng) > 180:
81+
raise ValueError('longitude should be -180º <= longitude <= 180º')
82+
83+
lat = deg2rad(lat)
84+
lng = deg2rad(lng)
85+
86+
Lne = array([[-sin(lat) * cos(lng), -sin(lat) * sin(lng), cos(lat)],
87+
[-sin(lng), cos(lng), 0],
88+
[-cos(lat) * cos(lng), -cos(lat) * sin(lng), -sin(lat)]])
89+
90+
Len = Lne.transpose()
91+
v_ecef = Len.dot(v_ned)
92+
93+
return v_ecef
94+
95+
96+
def body2ned(v_body, theta, phi, psi):
97+
"""
98+
Converts vector from body reference frame to local geodetic horizon
99+
reference frame (NED - North, East, Down)
100+
101+
Parameters
102+
----------
103+
v_body: array-like
104+
vector expressed in body coordinates
105+
theta : float
106+
pitch angle in radians
107+
phi : float
108+
bank angle in radians
109+
psi : float
110+
yaw angle in radians
111+
112+
Returns
113+
-------
114+
v_ned : array_like
115+
vector expressed in local horizon (NED) coordinates
116+
"""
117+
if abs(theta) > np.pi/2:
118+
raise ValueError('theta should be -pi/2 <= theta <= pi/2')
119+
120+
if abs(phi) > np.pi:
121+
raise ValueError('phi should be -pi <= phi <= pi')
122+
123+
if not 0 <= psi <= 2*np.pi:
124+
raise ValueError('psi should be 0 <= psi <= 2*pi')
125+
126+
Lnb = array([[cos(theta) * cos(psi),
127+
sin(phi) * sin(theta) * cos(psi) - cos(phi) * sin(psi),
128+
cos(phi) * sin(theta) * cos(psi) + sin(phi) * sin(psi)],
129+
[cos(theta) * sin(psi),
130+
sin(phi) * sin(theta) * sin(psi) + cos(phi) * cos(psi),
131+
cos(phi) * sin(theta) * sin(psi) - sin(phi) * cos(psi)],
132+
[-sin(theta),
133+
sin(phi) * cos(theta),
134+
cos(phi) * cos(theta)]])
135+
136+
v_ned = Lnb.dot(v_body)
137+
138+
return v_ned
139+
140+
141+
def ned2body(v_ned, theta, phi, psi):
142+
"""
143+
Converts vector from local geodetic horizon reference frame (NED -
144+
North, East, Down) to body reference frame
145+
146+
Parameters
147+
----------
148+
v_ned : array_like
149+
vector expressed in local horizon (NED) coordinates
150+
theta : float
151+
pitch angle in radians
152+
phi : float
153+
bank angle in radians
154+
psi : float
155+
yaw angle in radians
156+
157+
Returns
158+
-------
159+
v_body: array-like
160+
vector expressed in body coordinates
161+
"""
162+
if abs(theta) > np.pi/2:
163+
raise ValueError('theta should be -pi/2 <= theta <= pi/2')
164+
165+
if abs(phi) > np.pi:
166+
raise ValueError('phi should be -pi <= phi <= pi')
167+
168+
if not 0 <= psi <= 2*np.pi:
169+
raise ValueError('psi should be 0 <= psi <= 2*pi')
170+
171+
Lbn = array([[cos(theta) * cos(psi),
172+
cos(theta) * sin(psi),
173+
-sin(theta)],
174+
[sin(phi) * sin(theta) * cos(psi) - cos(phi) * sin(psi),
175+
sin(phi) * sin(theta) * sin(psi) + cos(phi) * cos(psi),
176+
sin(phi) * cos(theta)],
177+
[cos(phi) * sin(theta) * cos(psi) + sin(phi) * sin(psi),
178+
cos(phi) * sin(theta) * sin(psi) - sin(phi) * cos(psi),
179+
cos(phi) * cos(theta)]])
180+
181+
v_body = Lbn.dot(v_ned)
182+
183+
return v_body
184+
185+
186+
def body2wind(v_body, alpha, beta):
187+
"""
188+
Converts vector from body reference frame to wind reference frame
189+
190+
Parameters
191+
----------
192+
v_body : array_like
193+
vector expressed in body coordinates
194+
alpha : float
195+
angle of attack in radians
196+
beta : float
197+
sideslip angle in radians
198+
199+
Returns
200+
-------
201+
v_wind : array_like
202+
vector expressed in wind coordinates
203+
"""
204+
if abs(alpha) > np.pi/2:
205+
raise ValueError('alpha should be -pi/2 <= alpha <= pi/2')
206+
207+
if abs(beta) > np.pi:
208+
raise ValueError('beta should be -pi <= beta <= pi')
209+
210+
Lwb = array([[cos(alpha) * cos(beta), sin(beta), sin(alpha) * cos(beta)],
211+
[-cos(alpha) * sin(beta), cos(beta), -sin(alpha) * sin(beta)],
212+
[-sin(alpha), 0, cos(alpha)]])
213+
214+
v_wind = Lwb.dot(v_body)
215+
216+
return v_wind
217+
218+
219+
def wind2body(v_wind, alpha, beta):
220+
"""
221+
Converts vector from wind reference frame to body reference frame
222+
223+
Parameters
224+
----------
225+
v_wind : array_like
226+
vector expressed in wind coordinates
227+
alpha : float
228+
angle of attack in radians
229+
beta : float
230+
sideslip angle in radians
231+
232+
Returns
233+
-------
234+
v_body : array_like
235+
vector expressed in body coordinates
236+
"""
237+
if abs(alpha) > np.pi/2:
238+
raise ValueError('alpha should be -pi/2 <= alpha <= pi/2')
239+
240+
if abs(beta) > np.pi:
241+
raise ValueError('beta should be -pi <= beta <= pi')
242+
243+
Lbw = array([[cos(alpha) * cos(beta),
244+
-cos(alpha) * sin(beta),
245+
-sin(alpha)],
246+
[sin(beta), cos(beta), 0],
247+
[sin(alpha) * cos(beta),
248+
-sin(alpha) * sin(beta),
249+
cos(alpha)]])
250+
251+
v_body = Lbw.dot(v_wind)
252+
253+
return v_body

0 commit comments

Comments
 (0)