ModelSpace
All Classes Namespaces Functions Variables Enumerations Pages
planetrelutils.hpp
1/******************************************************************************
2* Copyright (c) ATTX LLC 2024. All Rights Reserved.
3*
4* This software and associated documentation (the "Software") are the
5* proprietary and confidential information of ATTX, LLC. The Software is
6* furnished under a license agreement between ATTX and the user organization
7* and may be used or copied only in accordance with the terms of the agreement.
8* Refer to 'license/attx_license.adoc' for standard license terms.
9*
10* EXPORT CONTROL NOTICE: THIS SOFTWARE MAY INCLUDE CONTENT CONTROLLED UNDER THE
11* INTERNATIONAL TRAFFIC IN ARMS REGULATIONS (ITAR) OR THE EXPORT ADMINISTRATION
12* REGULATIONS (EAR99). No part of the Software may be used, reproduced, or
13* transmitted in any form or by any means, for any purpose, without the express
14* written permission of ATTX, LLC.
15******************************************************************************/
16/*
17Planet relative state Utils header file
18---------------------------------------
19The planet relative state utilities header file contains
20utiltities for converting between planet-based spherical
21coordinates and planet-centered rotating frame coordinates.
22
23Author: Alex Reynolds
24
25Update Log
26----------
27May 9, 2024, Gabriel A. Heredia Acevedo
28
29Update tools to support planet (celestial) relative states model
30*/
31
32#ifndef PLANET_RELATIVE_STATE_UTILS_HPP
33#define PLANET_RELATIVE_STATE_UTILS_HPP
34
35#include <vector>
36
37#include "core/macros.h"
38#include "core/clockwerkerrors.h"
39#include "core/CartesianVector.hpp"
40#include "six_dof_dynamics/Frame.hpp"
41#include <iomanip>
42
43namespace clockwerk {
44
45 /// @brief Function to calculate detic LLA from PCR state
46 /// @param pos__pcr The position of the spacecraft in PCR frame
47 /// @param r_planet_equatorial The equatorial radius of the planet
48 /// @param flattening The flattening value of the planet
49 /// @param lat_rad Implicit return of latitude in radians
50 /// @param lon_rad Implicit return of longitude in radians
51 /// @param alt Implicit return of altitude
52 template <typename T>
53 int pcrDeticToLla(CartesianVector<T, 3> pos__pcr, T r_planet_equatorial, T flattening, T& lat_rad, T& lon_rad, T& alt);
54 /// @brief Wrapper around pcrDeticToLLA for bulk conversion of x, y, z coordinates
55 /// @param pcr_x Vector of x PCR frame values
56 /// @param pcr_y Vector of y PCR frame values
57 /// @param pcr_z Vector of z PCR frame values
58 /// @param a Semimajor axis of the planet. Defaults to WGS84
59 /// @param flattening Flattening of the planet. Defaults to WGS84
60 /// @return Vector of vectors of coordinatess containing lat and lon in radians and altitude
61 /// @note NOT embedded safe! Does not error check.
62 std::vector<std::vector<double>> pcr2lla(const std::vector<double> &pcr_x,
63 const std::vector<double> &pcr_y,
64 const std::vector<double> &pcr_z,
65 double a=6378137.0,
66 double flattening=(1.0/298.257223563));
67
68 /// @brief Function to return the PCR position of an object from centric lat, lon, alt
69 /// @param r_planet The radius of the planet relative to which LLA is measured
70 /// @param lat_rad The centric latitude in radians
71 /// @param lon_rad The longitude in radians
72 /// @param alt The centric altitude in the same units as the radius
73 /// @return The PCR position of the object
74 template <typename T>
75 CartesianVector<T, 3> llaCentricToPCR(T r_planet, T lat_rad, T lon_rad, T alt);
76
77 /// @brief Function to calculate the East, North, Up frame attitude from centric lat and lon
78 /// @param lat_rad The centric latitude in radians
79 /// @param lon_rad The longitude in radians
80 /// @return DCM corresponding to ENU frame attitude relative to PCR
81 template <typename T>
82 DCM<T> enuFromLatLonCentric(T lat_rad, T lon_rad);
83
84 /// @brief Function to calculate the East, North, Up frame attitude from detic lat and lon
85 /// @param lat_rad The detic latitude in radians
86 /// @param lon_rad The longitude in radians
87 /// @return DCM corresponding to ENU frame attitude relative to PCR
88 template <typename T>
89 DCM<T> enuFromLatLonDetic(T lat_rad, T lon_rad);
90
91 /// @brief Function to calculate the North, East, Down frame attitude from centric lat and lon
92 /// @param lat_rad The centric latitude in radians
93 /// @param lon_rad The longitude in radians
94 /// @return DCM corresponding to NED frame attitude relative to PCR
95 template <typename T>
96 DCM<T> nedFromLatLonCentric(T lat_rad, T lon_rad);
97
98 /// @brief Function to calculate the North, East, Down frame attitude from detic lat and lon
99 /// @param lat_rad The detic latitude in radians
100 /// @param lon_rad The longitude in radians
101 /// @return DCM corresponding to NED frame attitude relative to PCR
102 template <typename T>
103 DCM<T> nedFromLatLonDetic(T lat_rad, T lon_rad);
104
105 /// @brief Function to calculate detic LLA from PCR state using the Heikkinen algorithm
106 /// @param pos__pcr The position of the spacecraft in PCR frame
107 /// @param r_planet_equatorial The equatorial radius of the planet
108 /// @param flattening The flattening value of the planet
109 /// @param lat_rad Implicit return of latitude in radians
110 /// @param lon_rad Implicit return of longitude in radians
111 /// @param alt Implicit return of altitude
112 template <typename T>
113 int heikkinenLla(CartesianVector<T, 3> pos__pcr, T r_planet_equatorial, T flattening, T& lat_rad, T& lon_rad, T& alt);
114
115 /// @brief Function to calculate centric LLA from PCR state
116 /// @param pos__pcr The position of the spacecraft in PCR frame
117 /// @param r_planet The radius of the spherical planet
118 /// @param lat_rad Implicit return of latitude in radians
119 /// @param lon_rad Implicit return of longitude in radians
120 /// @param alt Implicit return of altitude
121 template <typename T>
122 int pcrToLlaCentric(CartesianVector<T, 3> pos__pcr, T r_planet, T& lat_rad, T& lon_rad, T& alt);
123
124 template <typename T>
125 int pcrDeticToLla(CartesianVector<T, 3> pos__pcr, T r_planet_equatorial, T flattening, T& lat_rad, T& lon_rad, T& alt) {
126 T nav_e2 = (2.0 - flattening)*flattening; // also e^2
127
128 // Get our longitude from y and x pcr position
129 lon_rad = atan2(pos__pcr[1], pos__pcr[0]);
130
131 // Make initial lat and alt guesses based on spherical earth.
132 T rhosqrd = pos__pcr[0]*pos__pcr[0] + pos__pcr[1]*pos__pcr[1];
133 T rho;
134 int _error = safeSqrt(rhosqrd, rho);
135 if(_error) {return _error;}
136
137 T templat = atan2(pos__pcr[2], rho);
138 T tempalt = sqrt(rhosqrd + pos__pcr[2]*pos__pcr[2]) - r_planet_equatorial;
139 T rhoerror = 1000.0;
140 T zerror = 1000.0;
141
142 int iter = 0; // number of iterations
143
144 // Newton's method iteration on templat and tempalt makes
145 // the residuals on rho and z progressively smaller. Loop
146 // is implemented as a 'while' instead of a 'do' to simplify
147 // porting to MATLAB
148 //TODO: need a global tolerance parameter
149 T slat, clat, sqrt_q, q, r_n, drdl, aa, bb, cc, dd, invdet;
150 while((abs(rhoerror) > 1e-6) || (abs(zerror) > 1e-6)) {
151 slat = sin(templat);
152 clat = cos(templat);
153 q = 1.0 - nav_e2*slat*slat;
154 _error = safeSqrt(q, sqrt_q);
155 if(_error) {return _error;}
156 _error = safeDivide(r_planet_equatorial, sqrt_q, r_n);
157 if(_error) {return _error;}
158 _error = safeDivide(r_n*nav_e2*slat*clat , q, drdl);
159 if(_error) {return _error;}
160
161 rhoerror = (r_n + tempalt)*clat - rho;
162 zerror = (r_n*(1.0 - nav_e2) + tempalt)*slat - pos__pcr[2];
163
164 aa = drdl*clat - (r_n + tempalt)*slat;
165 bb = clat;
166 cc = (1.0 - nav_e2)*(drdl*slat + r_n*clat);
167 dd = slat;
168
169 _error = safeDivide(1.0, aa*dd - bb*cc, invdet);
170 if(_error) {return _error;}
171 templat = templat - invdet*(dd*rhoerror - bb*zerror);
172 tempalt = tempalt - invdet*(-cc*rhoerror + aa*zerror);
173
174 iter++;
175
176 if (iter > 20){return ERROR_INVALID_RANGE;}
177 }
178
179 lat_rad = templat;
180 alt = tempalt;
181
182 return NO_ERROR;
183 }
184
185 template <typename T>
186 CartesianVector<T, 3> llaCentricToPCR(T r_planet, T lat_rad, T lon_rad, T alt) {
187 T r_tot = r_planet + alt;
188 return CartesianVector<T, 3>({r_tot*cos(lat_rad)*cos(lon_rad),
189 r_tot*cos(lat_rad)*sin(lon_rad),
190 r_tot*sin(lat_rad)});
191 }
192
193 template <typename T>
194 DCM<T> enuFromLatLonCentric(T lat_rad, T lon_rad) {
195 return DCM<T>({{-sin(lon_rad), cos(lon_rad), 0.0},
196 {-cos(lon_rad)*sin(lat_rad), -sin(lon_rad)*sin(lat_rad), cos(lat_rad)},
197 {cos(lon_rad)*cos(lat_rad), sin(lon_rad)*cos(lat_rad), sin(lat_rad)}});
198 }
199
200 template <typename T>
201 int heikkinenLla(CartesianVector<T, 3> pos__pcr, T r_planet_equatorial, T flattening, T& lat_rad, T& lon_rad, T& alt){
202 int _error;
203 T b = r_planet_equatorial*(1.0-flattening);
204 T e_sqr = (2.0 - flattening)*flattening; // also e^2
205 T E_sqr = e_sqr*r_planet_equatorial*r_planet_equatorial;
206 T ep_sqr = E_sqr/b/b; // also e^2
207 T z_sqr = std::pow(pos__pcr[2],2.0);
208 T r;
209 _error = safeSqrt(std::pow(pos__pcr[0],2.0)+std::pow(pos__pcr[1],2.0), r);
210 if (_error) {return ERROR_NEGATIVE_SQRT;}
211 T F = 54.0*b*b*z_sqr;
212 T G = r*r+(1-e_sqr)*z_sqr-e_sqr*E_sqr;
213 T c;
214 _error = safeDivide(e_sqr*e_sqr*F*r*r,std::pow(G,3.0),c);
215 if (_error) {return ERROR_DIVIDE_BY_ZERO;}
216
217 T tmpRes0;
218 T tmpRes1;
219 _error = safeSqrt(c*c+2*c,tmpRes0);
220 if (_error) {return ERROR_NEGATIVE_SQRT;}
221 T s;
222 _error = safeNroot(1.0+c+tmpRes0,3,s);
223 if (_error) {return _error;} // Different types of error within function
224 T P;
225 _error = safeDivide(1.0,s,tmpRes1);
226 if (_error) {return ERROR_DIVIDE_BY_ZERO;}
227 _error = safeDivide(F,3.0*std::pow(s+tmpRes1+1.0,2.0)*G*G,P);
228 if (_error) {return ERROR_DIVIDE_BY_ZERO;}
229 T Q;
230 _error = safeSqrt(1.0+2.0*e_sqr*e_sqr*P,Q);
231 if (_error) {return ERROR_NEGATIVE_SQRT;}
232
233 _error = safeDivide(P*(1.0-e_sqr)*z_sqr,Q*(1.0+Q),tmpRes0);
234 if (_error) {return ERROR_DIVIDE_BY_ZERO;}
235 T r_0;
236 _error = safeDivide(1.0,Q,tmpRes1);
237 if (_error) {return ERROR_DIVIDE_BY_ZERO;}
238 _error = safeSqrt(0.5*r_planet_equatorial*r_planet_equatorial*(1.0+tmpRes1)-tmpRes0-0.5*P*r*r,r_0);
239 if (_error) {return ERROR_NEGATIVE_SQRT;}
240 _error = safeDivide(P*e_sqr*r,1.0+Q,tmpRes0);
241 if (_error) {return ERROR_DIVIDE_BY_ZERO;}
242 r_0 -= tmpRes0;
243
244 tmpRes0 = std::pow(r-e_sqr*r_0,2.0);
245
246 // U and V are, by definition, greater than or equal to zero numbers
247 T U = std::sqrt(tmpRes0+z_sqr);
248 T V = std::sqrt(tmpRes0+(1.0-e_sqr)*z_sqr);
249 _error = safeDivide(b*b,r_planet_equatorial*V,tmpRes0);
250 if (_error) {return ERROR_DIVIDE_BY_ZERO;}
251
252 T z_0 = pos__pcr[2]*tmpRes0;
253
254 lat_rad = atan2(pos__pcr[2] + ep_sqr*z_0, r);
255 lon_rad = atan2(pos__pcr[1], pos__pcr[0]);
256 alt = U*(1.0-tmpRes0);
257
258 return NO_ERROR;
259 };
260
261 template <typename T>
262 int pcrToLlaCentric(CartesianVector<T, 3> pos__pcr, T r_planet, T& lat_rad, T& lon_rad, T& alt){
263 int _error;
264 double r_pos;
265 double tmpVal0;
266
267 _error = pos__pcr.normSquared(r_pos);
268 if (_error) {return _error;}
269
270 _error = safeSqrt(r_pos,alt);
271 if (_error) {return ERROR_NEGATIVE_SQRT;}
272
273 alt -= r_planet;
274
275 lon_rad = atan2(pos__pcr[1],pos__pcr[0]);
276
277 _error = safeSqrt(std::pow(pos__pcr[0],2.0)+std::pow(pos__pcr[1],2.0),tmpVal0);
278 if (_error) {return ERROR_NEGATIVE_SQRT;}
279
280 //TODO: atan2 needs a safe operator
281 lat_rad = atan2(pos__pcr[2],tmpVal0);
282
283 return NO_ERROR;
284 };
285
286 //TODO: This is a redundant definition to enuFromLatLonCentric. Both functions should be generalized into one!
287 template <typename T>
288 DCM<T> enuFromLatLonDetic(T lat_rad, T lon_rad) {
289 return DCM<T>({{-sin(lon_rad), cos(lon_rad), 0.0},
290 {-cos(lon_rad)*sin(lat_rad), -sin(lon_rad)*sin(lat_rad), cos(lat_rad)},
291 {cos(lon_rad)*cos(lat_rad), sin(lon_rad)*cos(lat_rad), sin(lat_rad)}});
292 }
293
294 template <typename T>
295 DCM<T> nedFromLatLonCentric(T lat_rad, T lon_rad) {
296 return DCM<T>({{-sin(lat_rad)*cos(lon_rad), -sin(lat_rad)*sin(lon_rad), cos(lat_rad)},
297 {-sin(lon_rad), cos(lon_rad), 0.0},
298 {-cos(lat_rad)*cos(lon_rad), -cos(lat_rad)*sin(lon_rad), -sin(lat_rad)}});
299 }
300
301 //TODO: This is a redundant definition to nedFromLatLonCentric. Both functions should be generalized into one!
302 template <typename T>
303 DCM<T> nedFromLatLonDetic(T lat_rad, T lon_rad) {
304 return DCM<T>({{-sin(lat_rad)*cos(lon_rad), -sin(lat_rad)*sin(lon_rad), cos(lat_rad)},
305 {-sin(lon_rad), cos(lon_rad), 0.0},
306 {-cos(lat_rad)*cos(lon_rad), -cos(lat_rad)*sin(lon_rad), -sin(lat_rad)}});
307 }
308
309}
310
311#endif
Standard vector class derived from Matrix.
Definition CartesianVector.hpp:39
Class defining a direction cosine matrix inherited from Matrix.
Definition DCM.hpp:71
#define NO_ERROR
Definition clockwerkerrors.h:31
#define ERROR_NEGATIVE_SQRT
Definition clockwerkerrors.h:54
#define ERROR_INVALID_RANGE
Definition clockwerkerrors.h:50
#define ERROR_DIVIDE_BY_ZERO
Definition clockwerkerrors.h:46