00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00036 #include "../Include/FlyLegacy.h"
00037 #include "../Include/Utility.h"
00038 #include <plib/sg.h>
00039
00040
00041
00042
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
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
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
00088
00089
00090
00091
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
00103
00104 SPosition GeocToGeod (SPosition pos)
00105 {
00106
00107
00108 double lat_geoc = pos.lat;
00109 double radius = FeetToMetres (pos.alt);
00110
00111
00112 double lat_geod, alt, sea_level_r;
00113
00114
00115
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 )
00121 || ( (PI_2 + lat_geoc) < ONE_ARCSEC ) )
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) );
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
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
00162
00163
00164
00165
00166
00167
00168
00169 SPosition GeodToGeoc (SPosition pos)
00170 {
00171
00172 double lat_geod = pos.lat * ARCSEC_TO_RAD;
00173 double alt = FeetToMetres (pos.alt);
00174
00175
00176 double sl_radius, lat_geoc;
00177
00178
00179
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) );
00184 sin_lambda_sl = sin( lambda_sl );
00185 cos_lambda_sl = cos( lambda_sl );
00186 sin_mu = sin(lat_geod);
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
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
00207
00208
00209 SVector GeocToCartesian (SPosition pos)
00210 {
00211
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
00221
00222
00223 void GeocToCartesianSgdVec3 (SPosition pos, sgdVec3 *v)
00224 {
00225
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
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
00250
00251
00252 SVector GeodToCartesian (SPosition pos)
00253 {
00254 SVector v;
00255
00256
00257 SPosition geoc = GeodToGeoc (pos);
00258
00259
00260 v = GeocToCartesian (geoc);
00261
00262 return v;
00263 }
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
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 ) {
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 ) {
00311
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
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
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
00339
00340 temp = sinu1*sinsig-cosu1*cossig*cosaz1;
00341 denom = (1.0-f)*sqrt(sinaz*sinaz+temp*temp);
00342
00343
00344 rnumer = sinu1*cossig+cosu1*sinsig*cosaz1;
00345 *lat2 = atan2(rnumer,denom)/RADDEG;
00346
00347
00348 rnumer = sinsig*sinaz1;
00349 denom = cosu1*cossig-sinu1*sinsig*cosaz1;
00350 dlams = atan2(rnumer,denom);
00351
00352
00353 tc = f*cos2saz*(4.0+f*(4.0-3.0*cos2saz))/16.0;
00354
00355
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
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 {
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
00379
00380
00381
00382
00383
00384
00385
00386
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
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
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
00427 *az1 = 0.0; *az2 = 0.0; *s = 0.0;
00428 return 0;
00429 } else if( fabs(cosphi1) < testv ) {
00430
00431 int k = geo_inverse_wgs_84( alt, lat2,lon2,lat1,lon1, az1,az2,s );
00432 k = k;
00433 b = *az1; *az1 = *az2; *az2 = b;
00434 return 0;
00435 } else if( fabs(cosphi2) < testv ) {
00436
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;
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
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
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
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
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
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
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
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
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
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604
00605
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680
00681
00682
00683
00684
00685
00686
00687
00688
00689
00690
00691
00692
00693
00694
00695
00696
00697
00698
00699
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710