Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members | Related Pages

Geodesy.cpp

Go to the documentation of this file.
00001 /*
00002  * Geodesy.cpp
00003  *
00004  * Part of Fly! Legacy project
00005  *
00006  * Copyright 2003 Chris Wallace
00007  *
00008  * Fly! Legacy is free software; you can redistribute it and/or modify
00009  *   it under the terms of the GNU General Public License as published by
00010  *   the Free Software Foundation; either version 2 of the License, or
00011  *   (at your option) any later version.
00012  *
00013  * Fly! Legacy is distributed in the hope that it will be useful,
00014  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  *   GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  *   along with Fly! Legacy; if not, write to the Free Software
00020  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021  *
00022  */
00023 
00036 #include "../Include/FlyLegacy.h"
00037 #include "../Include/Utility.h"
00038 #include <plib/sg.h>
00039 
00040 
00041 //
00042 // Test function for debugging Geodesy.cpp functions
00043 //
00044 void test_geodesy (void)
00045 {
00046   SPosition geod;
00047   geod.lat = 45 * 3600;
00048   geod.lon = 135 * 3600;
00049   geod.alt = 5000;
00050 
00051   SPosition geoc = GeodToGeoc (geod);
00052   geod = GeocToGeod (geoc);
00053 
00054   SVector cart;
00055   cart = GeocToCartesian (geoc);
00056   geoc = CartesianToGeoc (cart);
00057   geod = GeocToGeod (geoc);
00058 
00059   char s[80];
00060   FormatPosition (geod, s);
00061 }
00062 
00063 
00064 //
00065 // By convention:
00066 //
00067 //  SPosition structs representing locations in geodetic coordinate system:
00068 //    lat = arcseconds of latitude N (+) or S (-) of the equator
00069 //    lon = arcseconds of longitude E of the Prime Meridian
00070 //    alt = altitude in feet above MSL
00071 //
00072 //  SPosition structs representing locations in geocentric coordinate system
00073 //    lat = radians of latitude N (+) or S (-) of the equator
00074 //    lon = radians of longitude E of the Prime Meridian
00075 //    alt = distance in feet from center of the Earth
00076 //
00077 
00078 
00079 #define PI_2            (SGD_PI * 2)
00080 #define ARCSEC_360DEG       (360 * 3600)
00081 #define ARCSEC_TO_RAD       (PI_2 / ARCSEC_360DEG)
00082 #define ONE_ARCSEC          (ARCSEC_TO_RAD)
00083 #define EQUATORIAL_RADIUS_FEET    (3963.19 * 5280)
00084 #define EQUATORIAL_RADIUS_M     (EQUATORIAL_RADIUS_FEET * METRES_PER_FOOT)
00085 #define EQ_RAD_SQUARE_M       (EQUATORIAL_RADIUS_M * EQUATORIAL_RADIUS_M)
00086 
00087 // Value of earth flattening parameter from ref [8] 
00088 //
00089 //      Note: FP = f
00090 //            E  = 1-f
00091 //            EPS = sqrt(1-(1-f)^2)
00092 //
00093 
00094 static const double FP =   0.003352813178;
00095 static const double E  =   0.996647186;
00096 static const double EPS =  0.081819221;
00097 static const double INVG = 0.031080997;
00098 
00099 
00100 
00101 //
00102 // Convert an SPosition from geocentric to (WGS84) geodetic systems
00103 //
00104 SPosition GeocToGeod (SPosition pos)
00105 {
00106   // Input SPosition is in geocentric coordinates just need to convert
00107   //   altitude from feet to meters for compatibility with the algorithm
00108   double lat_geoc = pos.lat;
00109   double radius = FeetToMetres (pos.alt);
00110 
00111   // Define output parameters for algorithm
00112   double lat_geod, alt, sea_level_r;
00113 
00114   //
00115   // Conversion algorithm taken from SimGear 0.3.3 by sg_geodesy.cxx
00116   //
00117     double t_lat, x_alpha, mu_alpha, delt_mu, r_alpha, l_point, rho_alpha;
00118     double sin_mu_a, denom,delt_lambda, lambda_sl, sin_lambda_sl;
00119 
00120     if( ( (PI_2 - lat_geoc) < ONE_ARCSEC )        // near North pole
00121   || ( (PI_2 + lat_geoc) < ONE_ARCSEC ) )   // near South pole
00122     {
00123     lat_geod = lat_geoc;
00124     sea_level_r = EQUATORIAL_RADIUS_M*E;
00125     alt = radius - sea_level_r;
00126     } else {
00127     t_lat = tan(lat_geoc);
00128     x_alpha = E*EQUATORIAL_RADIUS_M/sqrt(t_lat*t_lat + E*E);
00129 
00130     double tmp = sqrt(EQ_RAD_SQUARE_M - x_alpha * x_alpha);
00131     if ( tmp < 0.0 ) { tmp = 0.0; }
00132 
00133     mu_alpha = atan2(tmp,E*x_alpha);
00134     if (lat_geoc < 0) mu_alpha = - mu_alpha;
00135     sin_mu_a = sin(mu_alpha);
00136     delt_lambda = mu_alpha - lat_geoc;
00137     r_alpha = x_alpha/cos(lat_geoc);
00138     l_point = radius - r_alpha;
00139     alt = l_point*cos(delt_lambda);
00140 
00141     denom = sqrt(1-EPS*EPS*sin_mu_a*sin_mu_a);
00142 
00143     rho_alpha = EQUATORIAL_RADIUS_M*(1-EPS)/(denom*denom*denom);
00144     delt_mu = atan2(l_point*sin(delt_lambda),rho_alpha + alt);
00145     lat_geod = mu_alpha - delt_mu;
00146     lambda_sl = atan( E*E * tan(lat_geod) ); // SL geoc. latitude
00147     sin_lambda_sl = sin( lambda_sl );
00148     sea_level_r = sqrt(EQ_RAD_SQUARE_M / (1 + ((1/(E*E))-1)*sin_lambda_sl*sin_lambda_sl));
00149     }
00150 
00151   // Prepare return value
00152   SPosition result;
00153   result.lat = lat_geod / ARCSEC_TO_RAD;
00154   result.lon = pos.lon / ARCSEC_TO_RAD;
00155   result.alt = MetresToFeet (alt);
00156 
00157   return result;
00158 }
00159 
00160 //
00161 // Convert an SPosition from WGS84 geodetic to geocentric systems
00162 //
00163 // Inputs:
00164 //  pos     Geodetic position, lat/lon in arcsec, alt in feet ASL
00165 //
00166 // Returns:
00167 //  SPosition Geocentric position, lat/lon in radians, alt in feet from earth centre
00168 //
00169 SPosition GeodToGeoc (SPosition pos)
00170 {
00171   // Prepare arguments for the algorithm
00172   double lat_geod = pos.lat * ARCSEC_TO_RAD;
00173   double alt = FeetToMetres (pos.alt);
00174 
00175   // Declare return values from algorithm
00176   double sl_radius, lat_geoc;
00177 
00178   //
00179   // Conversion algorithm taken from SimGear 0.3.3  sg_geodesy.cxx
00180   //
00181   double lambda_sl, sin_lambda_sl, cos_lambda_sl, sin_mu, cos_mu, px, py;
00182     
00183   lambda_sl = atan( E*E * tan(lat_geod) ); // sea level geocentric latitude
00184   sin_lambda_sl = sin( lambda_sl );
00185   cos_lambda_sl = cos( lambda_sl );
00186   sin_mu = sin(lat_geod);                  // Geodetic (map makers') latitude
00187   cos_mu = cos(lat_geod);
00188   sl_radius = 
00189     sqrt(EQ_RAD_SQUARE_M / (1 + ((1/(E*E))-1)*sin_lambda_sl*sin_lambda_sl));
00190 
00191   py = sl_radius * sin_lambda_sl + alt * sin_mu;
00192   px = sl_radius * cos_lambda_sl + alt * cos_mu;
00193   lat_geoc = atan2( py, px );
00194 
00195   // Prepare return value
00196   SPosition result;
00197   result.lat = lat_geoc;
00198   result.lon = pos.lon * ARCSEC_TO_RAD;
00199   result.alt = pos.alt + MetresToFeet (sl_radius);
00200 
00201   return result;
00202 }
00203 
00204 
00205 //
00206 // Convert a geocentric SPosition (lat,lon,alt) to cartesian coordinates
00207 //   in an SVector struct
00208 //
00209 SVector GeocToCartesian (SPosition pos)
00210 {
00211   // Convert to cartesian coordinates
00212   SVector v;
00213   v.x = cos (pos.lon) * cos (pos.lat) * pos.alt;
00214   v.y = sin (pos.lon) * cos (pos.lat) * pos.alt;
00215   v.z = sin (pos.lat) * pos.alt;
00216   return v;
00217 }
00218 
00219 //
00220 // Convert a geocentric SPosition (lat,lon,alt) to cartesian coordinates
00221 //   in an sgdVec3 struct
00222 //
00223 void GeocToCartesianSgdVec3 (SPosition pos, sgdVec3 *v)
00224 {
00225   // Convert to cartesian coordinates
00226   double x = cos (pos.lon) * cos (pos.lat) * pos.alt;
00227   double y = sin (pos.lon) * cos (pos.lat) * pos.alt;
00228   double z = sin (pos.lat) * pos.alt;
00229 
00230   sgdSetVec3 (*v, x, y, z);
00231 }
00232 
00233 //
00234 // Convert a cartesian SVector (x,y,z) to geocentric SPosition (lat,lon,alt)
00235 //
00236 SPosition CartesianToGeoc (SVector v)
00237 {
00238   SPosition result;
00239 
00240   result.lon = atan2 (v.y, v.x);
00241   result.lat = PI/2 - atan2 (sqrt(v.x*v.x + v.y*v.y), v.z);
00242   result.alt = sqrt (v.x*v.x + v.y*v.y + v.z*v.z);
00243 
00244   return result;
00245 }
00246 
00247 
00248 //
00249 // One-stop conversion of a geodetic position to cartesian coordinates
00250 //
00251 // In the "flat earth" model, 
00252 SVector GeodToCartesian (SPosition pos)
00253 {
00254   SVector v;
00255 
00256   // First convert to geocentric position
00257   SPosition geoc = GeodToGeoc (pos);
00258 
00259   // Then convert to cartesian coordinates
00260   v = GeocToCartesian (geoc);
00261 
00262   return v;
00263 }
00264 
00265 
00266 //
00267 // This algorithm was taken from SimGear 0.3.3 module "sg_geodesy.cxx",
00268 // originally based on:
00269 //
00270 // Proceedings of the 7th International Symposium on Geodetic
00271 // Computations, 1985
00272 //
00273 // "The Nested Coefficient Method for Accurate Solutions of Direct and
00274 // Inverse Geodetic Problems With Any Length"
00275 //
00276 // Zhang Xue-Lian
00277 // pp 747-763
00278 //
00279 // Modified for FlightGear to use WGS84 only -- Norman Vine
00280 //
00281 
00282 static inline double M0( double e2 ) {
00283     return SGD_PI*(1.0 - e2*( 1.0/4.0 + e2*( 3.0/64.0 + 
00284               e2*(5.0/256.0) )))/2.0;
00285 }
00286 
00287 static int geo_direct_wgs_84 (const double& alt, const double& lat1,
00288                 const double& lon1, const double& az1,
00289                 const double& s, double *lat2, double *lon2,
00290                 double *az2 )
00291 {
00292   double a = 6378137.000, rf = 298.257223563;
00293   double RADDEG = (SGD_PI)/180.0, testv = 1.0E-10;
00294   double f = ( rf > 0.0 ? 1.0/rf : 0.0 );
00295   double b = a*(1.0-f);
00296   double e2 = f*(2.0-f);
00297   double phi1 = lat1*RADDEG, lam1 = lon1*RADDEG;
00298   double sinphi1 = sin(phi1), cosphi1 = cos(phi1);
00299   double azm1 = az1*RADDEG;
00300   double sinaz1 = sin(azm1), cosaz1 = cos(azm1);
00301   
00302   if( fabs(s) < 0.01 ) {  // distance < centimeter => congruency
00303     *lat2 = lat1;
00304     *lon2 = lon1;
00305     *az2 = 180.0 + az1;
00306     if( *az2 > 360.0 ) *az2 -= 360.0;
00307 
00308     return 0;
00309 
00310   } else if( cosphi1 ) {  // non-polar origin
00311     // u1 is reduced latitude
00312     double tanu1 = sqrt(1.0-e2)*sinphi1/cosphi1;
00313     double sig1 = atan2(tanu1,cosaz1);
00314     double cosu1 = 1.0/sqrt( 1.0 + tanu1*tanu1 ), sinu1 = tanu1*cosu1;
00315     double sinaz =  cosu1*sinaz1, cos2saz = 1.0-sinaz*sinaz;
00316     double us = cos2saz*e2/(1.0-e2);
00317 
00318     // Terms
00319     double  ta = 1.0+us*(4096.0+us*(-768.0+us*(320.0-175.0*us)))/16384.0,
00320     tb = us*(256.0+us*(-128.0+us*(74.0-47.0*us)))/1024.0,
00321     tc = 0;
00322 
00323     // FIRST ESTIMATE OF SIGMA (SIG)
00324     double first = s/(b*ta);  // !!
00325     double sig = first;
00326     double c2sigm, sinsig,cossig, temp,denom,rnumer, dlams, dlam;
00327     do {
00328       c2sigm = cos(2.0*sig1+sig);
00329       sinsig = sin(sig); cossig = cos(sig);
00330       temp = sig;
00331       sig = first +
00332         tb*sinsig*(c2sigm+tb*(cossig*(-1.0+2.0*c2sigm*c2sigm) - 
00333         tb*c2sigm*(-3.0+4.0*sinsig*sinsig)
00334         * (-3.0+4.0*c2sigm*c2sigm)/6.0)
00335         / 4.0);
00336     } while( fabs(sig-temp) > testv);
00337 
00338     // LATITUDE OF POINT 2
00339     // DENOMINATOR IN 2 PARTS (TEMP ALSO USED LATER)
00340     temp = sinu1*sinsig-cosu1*cossig*cosaz1;
00341     denom = (1.0-f)*sqrt(sinaz*sinaz+temp*temp);
00342 
00343     // NUMERATOR
00344     rnumer = sinu1*cossig+cosu1*sinsig*cosaz1;
00345     *lat2 = atan2(rnumer,denom)/RADDEG;
00346 
00347     // DIFFERENCE IN LONGITUDE ON AUXILARY SPHERE (DLAMS )
00348     rnumer = sinsig*sinaz1;
00349     denom = cosu1*cossig-sinu1*sinsig*cosaz1;
00350     dlams = atan2(rnumer,denom);
00351 
00352     // TERM C
00353     tc = f*cos2saz*(4.0+f*(4.0-3.0*cos2saz))/16.0;
00354 
00355     // DIFFERENCE IN LONGITUDE
00356     dlam = dlams-(1.0-tc)*f*sinaz*(sig+tc*sinsig* (c2sigm+tc*cossig*(-1.0+2.0*c2sigm*c2sigm)));
00357     *lon2 = (lam1+dlam)/RADDEG;
00358     if (*lon2 > 180.0  ) *lon2 -= 360.0;
00359     if (*lon2 < -180.0 ) *lon2 += 360.0;
00360 
00361     // AZIMUTH - FROM NORTH
00362     *az2 = atan2(-sinaz,temp)/RADDEG;
00363     if ( fabs(*az2) < testv ) *az2 = 0.0;
00364     if ( *az2 < 0.0) *az2 += 360.0;
00365 
00366     return 0;
00367   } else {      // phi1 == 90 degrees, polar origin
00368     double dM = a*M0(e2) - s;
00369     double paz = ( phi1 < 0.0 ? 180.0 : 0.0 );
00370     double zero = 0.0f;
00371     return geo_direct_wgs_84( alt, zero, lon1, paz, dM, lat2, lon2, az2 );
00372     } 
00373 }
00374 
00375 
00376 SPosition GreatCirclePosition(SPosition *from, SVector *polar)
00377 {
00378   // The algorithm expects the following input arguments:
00379   //  double  alt     Starting altitude in meters
00380   //  double  lat1    Starting latitude in degrees
00381   //  double  lon1    Starting longitude in degrees
00382   //  double  az1     Starting azimuth in degrees
00383   //  double  s     Distance in meters
00384   //  double  lat2    Destination latitude in degrees
00385   //  double  lon2    Destination longitude in degrees
00386   //  double  az2     Destination azimuth in degrees
00387   //
00388   double alt = FeetToMetres (from->alt);
00389   double lat1 = from->lat / 3600;
00390   double lon1 = from->lon / 3600;
00391   double az1 = polar->h;
00392   double s = polar->r;
00393   double lat2, lon2, az2;
00394 
00395   geo_direct_wgs_84 (alt, lat1, lon1, az1, s, &lat2, &lon2, &az2);
00396 
00397   // Convert output parameters back into SPosition
00398   SPosition rc;
00399   rc.lat = lat2 * 3600;
00400   rc.lon = lon2 * 3600;
00401   rc.alt = from->alt;
00402 
00403   return rc;
00404 }
00405 
00406 
00407 static int geo_inverse_wgs_84(const double& alt, const double& lat1,
00408                 const double& lon1, const double& lat2,
00409                 const double& lon2, double *az1, double *az2,
00410                 double *s )
00411 {
00412     double a = 6378137.000, rf = 298.257223563;
00413     int iter=0;
00414     double RADDEG = (SGD_PI)/180.0, testv = 1.0E-10;
00415     double f = ( rf > 0.0 ? 1.0/rf : 0.0 );
00416     double b = a*(1.0-f);
00417     // double e2 = f*(2.0-f); // unused in this routine
00418     double phi1 = lat1*RADDEG, lam1 = lon1*RADDEG;
00419     double sinphi1 = sin(phi1), cosphi1 = cos(phi1);
00420     double phi2 = lat2*RADDEG, lam2 = lon2*RADDEG;
00421     double sinphi2 = sin(phi2), cosphi2 = cos(phi2);
00422   
00423     if( (fabs(lat1-lat2) < testv && 
00424    ( fabs(lon1-lon2) < testv) || fabs(lat1-90.0) < testv ) )
00425     { 
00426   // TWO STATIONS ARE IDENTICAL : SET DISTANCE & AZIMUTHS TO ZERO */
00427   *az1 = 0.0; *az2 = 0.0; *s = 0.0;
00428   return 0;
00429     } else if(  fabs(cosphi1) < testv ) {
00430   // initial point is polar
00431   int k = geo_inverse_wgs_84( alt, lat2,lon2,lat1,lon1, az1,az2,s );
00432   k = k; // avoid compiler error since return result is unused
00433   b = *az1; *az1 = *az2; *az2 = b;
00434   return 0;
00435     } else if( fabs(cosphi2) < testv ) {
00436   // terminal point is polar
00437         double _lon1 = lon1 + 180.0f;
00438   int k = geo_inverse_wgs_84( alt, lat1, lon1, lat1, _lon1, 
00439             az1, az2, s );
00440   k = k; // avoid compiler error since return result is unused
00441   *s /= 2.0;
00442   *az2 = *az1 + 180.0;
00443   if( *az2 > 360.0 ) *az2 -= 360.0; 
00444   return 0;
00445     } else if( (fabs( fabs(lon1-lon2) - 180 ) < testv) && 
00446          (fabs(lat1+lat2) < testv) ) 
00447     {
00448   // Geodesic passes through the pole (antipodal)
00449   double s1,s2;
00450   geo_inverse_wgs_84( alt, lat1,lon1, lat1,lon2, az1,az2, &s1 );
00451   geo_inverse_wgs_84( alt, lat2,lon2, lat1,lon2, az1,az2, &s2 );
00452   *az2 = *az1;
00453   *s = s1 + s2;
00454   return 0;
00455     } else {
00456   // antipodal and polar points don't get here
00457   double dlam = lam2 - lam1, dlams = dlam;
00458   double sdlams,cdlams, sig,sinsig,cossig, sinaz,
00459       cos2saz, c2sigm;
00460   double tc,temp, us,rnumer,denom, ta,tb;
00461   double cosu1,sinu1, sinu2,cosu2;
00462 
00463   // Reduced latitudes
00464   temp = (1.0-f)*sinphi1/cosphi1;
00465   cosu1 = 1.0/sqrt(1.0+temp*temp);
00466   sinu1 = temp*cosu1;
00467   temp = (1.0-f)*sinphi2/cosphi2;
00468   cosu2 = 1.0/sqrt(1.0+temp*temp);
00469   sinu2 = temp*cosu2;
00470     
00471   do {
00472       sdlams = sin(dlams), cdlams = cos(dlams);
00473       sinsig = sqrt(cosu2*cosu2*sdlams*sdlams+
00474         (cosu1*sinu2-sinu1*cosu2*cdlams)*
00475         (cosu1*sinu2-sinu1*cosu2*cdlams));
00476       cossig = sinu1*sinu2+cosu1*cosu2*cdlams;
00477       
00478       sig = atan2(sinsig,cossig);
00479       sinaz = cosu1*cosu2*sdlams/sinsig;
00480       cos2saz = 1.0-sinaz*sinaz;
00481       c2sigm = (sinu1 == 0.0 || sinu2 == 0.0 ? cossig : 
00482           cossig-2.0*sinu1*sinu2/cos2saz);
00483       tc = f*cos2saz*(4.0+f*(4.0-3.0*cos2saz))/16.0;
00484       temp = dlams;
00485       dlams = dlam+(1.0-tc)*f*sinaz*
00486     (sig+tc*sinsig*
00487      (c2sigm+tc*cossig*(-1.0+2.0*c2sigm*c2sigm)));
00488       if (fabs(dlams) > SGD_PI && iter++ > 50) {
00489     return iter;
00490       }
00491   } while ( fabs(temp-dlams) > testv);
00492 
00493   us = cos2saz*(a*a-b*b)/(b*b); // !!
00494   // BACK AZIMUTH FROM NORTH
00495   rnumer = -(cosu1*sdlams);
00496   denom = sinu1*cosu2-cosu1*sinu2*cdlams;
00497   *az2 = atan2(rnumer,denom)/RADDEG;
00498   if( fabs(*az2) < testv ) *az2 = 0.0;
00499   if(*az2 < 0.0) *az2 += 360.0;
00500 
00501   // FORWARD AZIMUTH FROM NORTH
00502   rnumer = cosu2*sdlams;
00503   denom = cosu1*sinu2-sinu1*cosu2*cdlams;
00504   *az1 = atan2(rnumer,denom)/RADDEG;
00505   if( fabs(*az1) < testv ) *az1 = 0.0;
00506   if(*az1 < 0.0) *az1 += 360.0;
00507 
00508   // Terms a & b
00509   ta = 1.0+us*(4096.0+us*(-768.0+us*(320.0-175.0*us)))/
00510       16384.0;
00511   tb = us*(256.0+us*(-128.0+us*(74.0-47.0*us)))/1024.0;
00512 
00513   // GEODETIC DISTANCE
00514   *s = b*ta*(sig-tb*sinsig*
00515        (c2sigm+tb*(cossig*(-1.0+2.0*c2sigm*c2sigm)-tb*
00516              c2sigm*(-3.0+4.0*sinsig*sinsig)*
00517              (-3.0+4.0*c2sigm*c2sigm)/6.0)/
00518         4.0));
00519   return 0;
00520     }
00521 }
00522 
00523 
00524 SVector GreatCirclePolar(SPosition *from, SPosition *to)
00525 {
00526   double alt = FeetToMetres (from->alt);
00527   double lat1 = from->lat / 3600;
00528   double lon1 = from->lon / 3600;
00529   double lat2 = to->lat / 3600;
00530   double lon2 = to->lon / 3600;
00531   double az1, az2, s;
00532 
00533   geo_inverse_wgs_84(alt, lat1, lon1, lat2, lon2, &az1, &az2, &s);
00534   
00535   // Convert results to SVector return code
00536   SVector v;
00537   v.h = az1;
00538   v.p = 0;
00539   v.r = MetresToFeet (s);
00540 
00541   return v;
00542 }
00543 
00544 
00545 
00546 /*
00547 
00548 //
00549 // geoc_to_geod (lat_geoc, radius, *lat_geod, *Alt, *seal_level_r)
00550 //
00551 // Convert geodetic (WGS84) coordinates to geocentric coordinate system
00552 //
00553 //     INPUTS:  
00554 //         lat_geoc Geocentric latitude, radians, + = North
00555 //         radius C.G. radius to earth center (meters)
00556 //
00557 //     OUTPUTS:
00558 //         lat_geod Geodetic latitude, radians, + = North
00559 //         alt    C.G. altitude above mean sea level (meters)
00560 //         sea_level_r  radius from earth center to sea level at
00561 //                      local vertical (surface normal) of C.G. (meters)
00562 
00563 void geoc_to_geod( const double& lat_geoc, const double& radius,
00564                    double *lat_geod, double *alt, double *sea_level_r )
00565 {
00566     double t_lat, x_alpha, mu_alpha, delt_mu, r_alpha, l_point, rho_alpha;
00567     double sin_mu_a, denom,delt_lambda, lambda_sl, sin_lambda_sl;
00568 
00569     if( ( (SGD_PI_2 - lat_geoc) < SG_ONE_SECOND )        // near North pole
00570   || ( (SGD_PI_2 + lat_geoc) < SG_ONE_SECOND ) )   // near South pole
00571     {
00572     *lat_geod = lat_geoc;
00573     *sea_level_r = SG_EQUATORIAL_RADIUS_M*E;
00574     *alt = radius - *sea_level_r;
00575     } else {
00576     // cout << "  lat_geoc = " << lat_geoc << endl;
00577     t_lat = tan(lat_geoc);
00578     // cout << "  tan(t_lat) = " << t_lat << endl;
00579     x_alpha = E*SG_EQUATORIAL_RADIUS_M/sqrt(t_lat*t_lat + E*E);
00580     // cout << "  x_alpha = " << x_alpha << endl;
00581     double tmp = sqrt(SG_EQ_RAD_SQUARE_M - x_alpha * x_alpha);
00582     if ( tmp < 0.0 ) { tmp = 0.0; }
00583     mu_alpha = atan2(tmp,E*x_alpha);
00584     if (lat_geoc < 0) mu_alpha = - mu_alpha;
00585     sin_mu_a = sin(mu_alpha);
00586     delt_lambda = mu_alpha - lat_geoc;
00587     r_alpha = x_alpha/cos(lat_geoc);
00588     l_point = radius - r_alpha;
00589     *alt = l_point*cos(delt_lambda);
00590 
00591     denom = sqrt(1-EPS*EPS*sin_mu_a*sin_mu_a);
00592     rho_alpha = SG_EQUATORIAL_RADIUS_M*(1-EPS)/
00593       (denom*denom*denom);
00594     delt_mu = atan2(l_point*sin(delt_lambda),rho_alpha + *alt);
00595     *lat_geod = mu_alpha - delt_mu;
00596     lambda_sl = atan( E*E * tan(*lat_geod) ); // SL geoc. latitude
00597     sin_lambda_sl = sin( lambda_sl );
00598     *sea_level_r = 
00599       sqrt(SG_EQ_RAD_SQUARE_M / (1 + ((1/(E*E))-1)*sin_lambda_sl*sin_lambda_sl));
00600     }
00601 }
00602 
00603 
00604 // geod_to_geoc( lat_geod, alt, *sl_radius, *lat_geoc )
00605 //
00606 // Convert geodetic (WGS84) coordinates to geocentric coordinate system
00607 //
00608 //     INPUTS:  
00609 //         lat_geod Geodetic latitude, radians, + = North
00610 //         alt    C.G. altitude above mean sea level (meters)
00611 //
00612 //     OUTPUTS:
00613 //         sl_radius  SEA LEVEL radius to earth center (meters)
00614 //                      (add Altitude to get true distance from earth center.
00615 //         lat_geoc Geocentric latitude, radians, + = North
00616 //
00617 
00618 
00619 void geod_to_geoc ( const double& lat_geod, const double& alt, double *sl_radius,
00620           double *lat_geoc )
00621 {
00622     double lambda_sl, sin_lambda_sl, cos_lambda_sl, sin_mu, cos_mu, px, py;
00623     
00624     lambda_sl = atan( E*E * tan(lat_geod) ); // sea level geocentric latitude
00625     sin_lambda_sl = sin( lambda_sl );
00626     cos_lambda_sl = cos( lambda_sl );
00627     sin_mu = sin(lat_geod);                  // Geodetic (map makers') latitude
00628     cos_mu = cos(lat_geod);
00629     *sl_radius = 
00630   sqrt(SG_EQ_RAD_SQUARE_M / (1 + ((1/(E*E))-1)*sin_lambda_sl*sin_lambda_sl));
00631     py = *sl_radius*sin_lambda_sl + alt*sin_mu;
00632     px = *sl_radius*cos_lambda_sl + alt*cos_mu;
00633     *lat_geoc = atan2( py, px );
00634 }
00635 
00636 */
00637 
00638 
00639 /***************************************************************************
00640 
00641   TITLE:  ls_geodesy
00642   
00643 ----------------------------------------------------------------------------
00644 
00645   FUNCTION: Converts geocentric coordinates to geodetic positions
00646 
00647 ----------------------------------------------------------------------------
00648 
00649   MODULE STATUS:  developmental
00650 
00651 ----------------------------------------------------------------------------
00652 
00653   GENEALOGY:  Written as part of LaRCSim project by E. B. Jackson
00654 
00655 ----------------------------------------------------------------------------
00656 
00657   DESIGNED BY:  E. B. Jackson
00658   
00659   CODED BY: E. B. Jackson
00660   
00661   MAINTAINED BY:  E. B. Jackson
00662 
00663 ----------------------------------------------------------------------------
00664 
00665   MODIFICATION HISTORY:
00666   
00667   DATE  PURPOSE           BY
00668   
00669   930208  Modified to avoid singularity near polar region.  EBJ
00670   930602  Moved backwards calcs here from ls_step.    EBJ
00671   931214  Changed erroneous Latitude and Altitude variables to 
00672     *lat_geod and *alt in routine ls_geoc_to_geod.    EBJ
00673   940111  Changed header files from old ls_eom.h style to ls_types, 
00674     and ls_constants.  Also replaced old DATA type with new
00675     SCALAR type.            EBJ
00676 
00677 ----------------------------------------------------------------------------
00678 
00679   REFERENCES:
00680 
00681     [ 1]  Stevens, Brian L.; and Lewis, Frank L.: "Aircraft 
00682       Control and Simulation", Wiley and Sons, 1992.
00683       ISBN 0-471-61397-5          
00684 
00685 
00686 ----------------------------------------------------------------------------
00687 
00688   CALLED BY:  ls_aux
00689 
00690 ----------------------------------------------------------------------------
00691 
00692   CALLS TO:
00693 
00694 ----------------------------------------------------------------------------
00695 
00696   INPUTS: 
00697     lat_geoc  Geocentric latitude, radians, + = North
00698     radius    C.G. radius to earth center, ft
00699 
00700 ----------------------------------------------------------------------------
00701 
00702   OUTPUTS:
00703     lat_geod  Geodetic latitude, radians, + = North
00704     alt   C.G. altitude above mean sea level, ft
00705     sea_level_r radius from earth center to sea level at
00706         local vertical (surface normal) of C.G.
00707 
00708 --------------------------------------------------------------------------*/
00709 
00710 
SourceForge.net Logo Documentation generated by doxygen