00001 #pragma ident "$Id: TropModel.cpp 3140 2012-06-18 15:03:02Z susancummins $"
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00045 #include "TropModel.hpp"
00046 #include "EphemerisRange.hpp"
00047 #include "MathBase.hpp"
00048 #include "geometry.hpp"
00049 #include "GPSEllipsoid.hpp"
00050 #include "GNSSconstants.hpp"
00051 #include "YDSTime.hpp"
00052
00053 namespace gpstk
00054 {
00055
00056 static const double CELSIUS_TO_KELVIN = 273.15;
00057
00058
00059
00060
00061 double TropModel::correction(double elevation) const
00062 throw(TropModel::InvalidTropModel)
00063 {
00064 if(!valid)
00065 GPSTK_THROW(InvalidTropModel("Invalid model"));
00066
00067 if(elevation < 0.0)
00068 return 0.0;
00069
00070 return (dry_zenith_delay() * dry_mapping_function(elevation)
00071 + wet_zenith_delay() * wet_mapping_function(elevation));
00072
00073 }
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084 double TropModel::correction(const Position& RX,
00085 const Position& SV,
00086 const CommonTime& tt)
00087 throw(TropModel::InvalidTropModel)
00088 {
00089 if(!valid)
00090 GPSTK_THROW(InvalidTropModel("Invalid model"));
00091
00092 double c;
00093 try
00094 {
00095 c = correction(RX.elevation(SV));
00096 }
00097 catch(InvalidTropModel& e)
00098 {
00099 GPSTK_RETHROW(e);
00100 }
00101 return c;
00102 }
00103
00104
00105
00106
00107
00108
00109 void TropModel::setWeather(const double& T,
00110 const double& P,
00111 const double& H)
00112 throw(InvalidParameter)
00113 {
00114 temp = T + CELSIUS_TO_KELVIN;
00115 press = P;
00116 humid = H;
00117 if (temp < 0.0)
00118 {
00119 valid = false;
00120 GPSTK_THROW(InvalidParameter("Invalid temperature parameter."));
00121 }
00122 if (press < 0.0)
00123 {
00124 valid = false;
00125 GPSTK_THROW(InvalidParameter("Invalid pressure parameter."));
00126 }
00127 if (humid < 0.0 || humid > 100.0)
00128 {
00129 valid = false;
00130 GPSTK_THROW(InvalidParameter("Invalid humidity parameter."));
00131 }
00132 }
00133
00134
00135
00136
00137 void TropModel::setWeather(const WxObservation& wx)
00138 throw(InvalidParameter)
00139 {
00140 if (wx.isAllValid())
00141 {
00142 try
00143 {
00144 setWeather(wx.temperature, wx.pressure, wx.humidity);
00145 valid = true;
00146 }
00147 catch(InvalidParameter& e)
00148 {
00149 valid = false;
00150 GPSTK_RETHROW(e);
00151 }
00152 }
00153 else
00154 {
00155 valid = false;
00156 GPSTK_THROW(InvalidParameter("Invalid weather data"));
00157 }
00158 }
00165 void TropModel::weatherByStandardAtmosphereModel(const double& ht, double& T, double& P, double& H)
00166 {
00167
00168
00169 const double h0 = 0.0;
00170 const double Tr = +18.0;
00171 const double pr = 1013.25;
00172 const double Hr = 50;
00173
00174 T = Tr-0.0065*(ht-h0);
00175 P = pr * std::pow((1 - 0.0000226 * (ht - h0)), 5.225);
00176 H = Hr * std::exp(-0.0006396 * (ht - h0));
00177
00178 }
00179
00180
00181
00182
00183
00184 SimpleTropModel::SimpleTropModel(void)
00185 {
00186 setWeather(20.0, 980.0, 50.0);
00187 Cwetdelay = 0.122382715318184;
00188 Cdrydelay = 2.235486646978727;
00189 Cwetmap = 1.000282213715744;
00190 Cdrymap = 1.001012704615527;
00191 valid = true;
00192 }
00193
00194
00195
00196 SimpleTropModel::SimpleTropModel(const WxObservation& wx)
00197 throw(InvalidParameter)
00198 {
00199 setWeather(wx);
00200 valid = true;
00201 }
00202
00203
00204
00205
00206
00207 SimpleTropModel::SimpleTropModel(const double& T,
00208 const double& P,
00209 const double& H)
00210 throw(InvalidParameter)
00211 {
00212 setWeather(T,P,H);
00213 valid = true;
00214 }
00215
00216
00217
00218
00219
00220
00221 void SimpleTropModel::setWeather(const double& T,
00222 const double& P,
00223 const double& H)
00224 throw(InvalidParameter)
00225 {
00226 TropModel::setWeather(T,P,H);
00227 GPSEllipsoid ell;
00228 Cdrydelay = 2.343*(press/1013.25)*(temp-3.96)/temp;
00229 double tks = temp * temp;
00230 Cwetdelay = 8.952/tks*humid*std::exp(-37.2465+0.213166*temp-(0.256908e-3)*tks);
00231 Cdrymap =1.0+(0.15)*148.98*(temp-3.96)/ell.a();
00232 Cwetmap =1.0+(0.15)*12000.0/ell.a();
00233 valid = true;
00234 }
00235
00236
00237
00238
00239 void SimpleTropModel::setWeather(const WxObservation& wx)
00240 throw(InvalidParameter)
00241 {
00242 TropModel::setWeather(wx);
00243 }
00244
00245
00246 double SimpleTropModel::dry_zenith_delay(void) const
00247 throw(TropModel::InvalidTropModel)
00248 {
00249 if(!valid)
00250 GPSTK_THROW(InvalidTropModel("Invalid model"));
00251
00252 return Cdrydelay;
00253 }
00254
00255
00256 double SimpleTropModel::wet_zenith_delay(void) const
00257 throw(TropModel::InvalidTropModel)
00258 {
00259 if(!valid)
00260 GPSTK_THROW(InvalidTropModel("Invalid model"));
00261
00262 return Cwetdelay;
00263 }
00264
00265
00266
00267
00268
00269 double SimpleTropModel::dry_mapping_function(double elevation) const
00270 throw(TropModel::InvalidTropModel)
00271 {
00272 if(!valid)
00273 GPSTK_THROW(InvalidTropModel("Invalid model"));
00274
00275 if(elevation < 0.0) return 0.0;
00276
00277 double d = std::cos(elevation*DEG_TO_RAD);
00278 d /= Cdrymap;
00279 return (1.0/SQRT(1.0-d*d));
00280 }
00281
00282
00283
00284
00285
00286 double SimpleTropModel::wet_mapping_function(double elevation) const
00287 throw(TropModel::InvalidTropModel)
00288 {
00289 if(!valid)
00290 GPSTK_THROW(InvalidTropModel("Invalid model"));
00291
00292 if(elevation < 0.0) return 0.0;
00293
00294 double d = std::cos(elevation*DEG_TO_RAD);
00295 d /= Cwetmap;
00296 return (1.0/SQRT(1.0-d*d));
00297 }
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308 static const double GGdryscale = 8594.777388436570600;
00309 static const double GGwetscale = 2540.042008403690900;
00310
00311
00312 GGTropModel::GGTropModel(void)
00313 {
00314 TropModel::setWeather(20.0, 980.0, 50.0);
00315 Cdrydelay = 2.59629761092150147e-4;
00316 Cwetdelay = 4.9982784999977412e-5;
00317 Cdrymap = 42973.886942182834900;
00318 Cwetmap = 12700.210042018454260;
00319 valid = true;
00320 }
00321
00322
00323
00324 GGTropModel::GGTropModel(const WxObservation& wx)
00325 throw(InvalidParameter)
00326 {
00327 setWeather(wx);
00328 valid = true;
00329 }
00330
00331
00332
00333
00334
00335 GGTropModel::GGTropModel(const double& T,
00336 const double& P,
00337 const double& H)
00338 throw(InvalidParameter)
00339 {
00340 setWeather(T,P,H);
00341 valid = true;
00342 }
00343
00344 double GGTropModel::dry_zenith_delay(void) const
00345 throw(TropModel::InvalidTropModel)
00346 {
00347 if(!valid)
00348 GPSTK_THROW(InvalidTropModel("Invalid model"));
00349
00350 return (Cdrydelay * GGdryscale);
00351 }
00352
00353 double GGTropModel::wet_zenith_delay(void) const
00354 throw(TropModel::InvalidTropModel)
00355 {
00356 if(!valid)
00357 GPSTK_THROW(InvalidTropModel("Invalid model"));
00358
00359 return (Cwetdelay * GGwetscale);
00360 }
00361
00362 double GGTropModel::dry_mapping_function(double elevation) const
00363 throw(TropModel::InvalidTropModel)
00364 {
00365 if(!valid)
00366 GPSTK_THROW(InvalidTropModel("Invalid model"));
00367
00368 if(elevation < 0.0) return 0.0;
00369
00370 GPSEllipsoid ell;
00371 double ce=std::cos(elevation*DEG_TO_RAD), se=std::sin(elevation*DEG_TO_RAD);
00372 double ad = -se/Cdrymap;
00373 double bd = -ce*ce/(2.0*ell.a()*Cdrymap);
00374 double Rd = SQRT((ell.a()+Cdrymap)*(ell.a()+Cdrymap)
00375 - ell.a()*ell.a()*ce*ce) - ell.a()*se;
00376
00377 double Ad[9], ad2=ad*ad, bd2=bd*bd;
00378 Ad[0] = 1.0;
00379 Ad[1] = 4.0*ad;
00380 Ad[2] = 6.0*ad2 + 4.0*bd;
00381 Ad[3] = 4.0*ad*(ad2+3.0*bd);
00382 Ad[4] = ad2*ad2 + 12.0*ad2*bd + 6.0*bd2;
00383 Ad[5] = 4.0*ad*bd*(ad2+3.0*bd);
00384 Ad[6] = bd2*(6.0*ad2+4.0*bd);
00385 Ad[7] = 4.0*ad*bd*bd2;
00386 Ad[8] = bd2*bd2;
00387
00388
00389 double sumd=0.0;
00390 for(int j=9; j>=1; j--) {
00391 sumd += Ad[j-1]/double(j);
00392 sumd *= Rd;
00393 }
00394 return sumd/GGdryscale;
00395
00396 }
00397
00398
00399 double GGTropModel::wet_mapping_function(double elevation) const
00400 throw(TropModel::InvalidTropModel)
00401 {
00402 if(!valid)
00403 GPSTK_THROW(InvalidTropModel("Invalid model"));
00404
00405 if(elevation < 0.0) return 0.0;
00406
00407 GPSEllipsoid ell;
00408 double ce = std::cos(elevation*DEG_TO_RAD), se = std::sin(elevation*DEG_TO_RAD);
00409 double aw = -se/Cwetmap;
00410 double bw = -ce*ce/(2.0*ell.a()*Cwetmap);
00411 double Rw = SQRT((ell.a()+Cwetmap)*(ell.a()+Cwetmap)
00412 - ell.a()*ell.a()*ce*ce) - ell.a()*se;
00413
00414 double Aw[9], aw2=aw*aw, bw2=bw*bw;
00415 Aw[0] = 1.0;
00416 Aw[1] = 4.0*aw;
00417 Aw[2] = 6.0*aw2 + 4.0*bw;
00418 Aw[3] = 4.0*aw*(aw2+3.0*bw);
00419 Aw[4] = aw2*aw2 + 12.0*aw2*bw + 6.0*bw2;
00420 Aw[5] = 4.0*aw*bw*(aw2+3.0*bw);
00421 Aw[6] = bw2*(6.0*aw2+4.0*bw);
00422 Aw[7] = 4.0*aw*bw*bw2;
00423 Aw[8] = bw2*bw2;
00424
00425 double sumw=0.0;
00426 for(int j=9; j>=1; j--) {
00427 sumw += Aw[j-1]/double(j);
00428 sumw *= Rw;
00429 }
00430 return sumw/GGwetscale;
00431
00432 }
00433
00434 void GGTropModel::setWeather(const double& T,
00435 const double& P,
00436 const double& H)
00437 throw(InvalidParameter)
00438 {
00439 TropModel::setWeather(T,P,H);
00440 double th=300./temp;
00441
00442
00443
00444 double wvpp=2.409e9*humid*th*th*th*th*std::exp(-22.64*th);
00445 Cdrydelay = 7.7624e-5*press/temp;
00446 Cwetdelay = 1.0e-6*(-12.92+3.719e+05/temp)*(wvpp/temp);
00447 Cdrymap = (5.0*0.002277*press)/Cdrydelay;
00448 Cwetmap = (5.0*0.002277/Cwetdelay)*(1255.0/temp+0.5)*wvpp;
00449 valid = true;
00450 }
00451
00452
00453
00454
00455 void GGTropModel::setWeather(const WxObservation& wx)
00456 throw(InvalidParameter)
00457 {
00458 TropModel::setWeather(wx);
00459 }
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471 GGHeightTropModel::GGHeightTropModel(void)
00472 {
00473 validWeather = false;
00474 validHeights = false;
00475 validRxHeight = false;
00476 }
00477
00478
00479
00480 GGHeightTropModel::GGHeightTropModel(const WxObservation& wx)
00481 throw(InvalidParameter)
00482 {
00483 valid = validRxHeight = validHeights = false;
00484 setWeather(wx);
00485 }
00486
00487
00488
00489
00490
00491 GGHeightTropModel::GGHeightTropModel(const double& T,
00492 const double& P,
00493 const double& H)
00494 throw(InvalidParameter)
00495 {
00496 validRxHeight = validHeights = false;
00497 setWeather(T,P,H);
00498 }
00499
00500
00501
00502
00503
00504
00505
00506
00507 GGHeightTropModel::GGHeightTropModel(const double& T,
00508 const double& P,
00509 const double& H,
00510 const double hT,
00511 const double hP,
00512 const double hH)
00513 throw(InvalidParameter)
00514 {
00515 validRxHeight = false;
00516 setWeather(T,P,H);
00517 setHeights(hT,hP,hH);
00518 }
00519
00520
00521 double GGHeightTropModel::correction(double elevation) const
00522 throw(TropModel::InvalidTropModel)
00523 {
00524 if(!valid)
00525 {
00526 if(!validWeather)
00527 GPSTK_THROW(InvalidTropModel("Invalid GGH trop model: Weather"));
00528 if(!validHeights)
00529 GPSTK_THROW(InvalidTropModel("Invalid GGH trop model: Heights"));
00530 if(!validRxHeight)
00531 GPSTK_THROW(InvalidTropModel("Invalid GGH trop model: Rx Height"));
00532 }
00533 if(elevation < 0.0) return 0.0;
00534
00535 return (dry_zenith_delay() * dry_mapping_function(elevation)
00536 + wet_zenith_delay() * wet_mapping_function(elevation));
00537
00538 }
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549 double GGHeightTropModel::correction(const Position& RX,
00550 const Position& SV,
00551 const CommonTime& tt)
00552 throw(TropModel::InvalidTropModel)
00553 {
00554 if(!valid)
00555 {
00556 if(!validWeather)
00557 GPSTK_THROW(InvalidTropModel("Invalid GGH trop model: Weather"));
00558 if(!validHeights)
00559 GPSTK_THROW(InvalidTropModel("Invalid GGH trop model: Heights"));
00560 if(!validRxHeight)
00561 GPSTK_THROW(InvalidTropModel("Invalid GGH trop model: Rx Height"));
00562 }
00563
00564
00565 setReceiverHeight(RX.getHeight());
00566
00567 return TropModel::correction(RX.elevation(SV));
00568
00569 }
00570
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581 double GGHeightTropModel::correction(const Xvt& RX,
00582 const Xvt& SV,
00583 const CommonTime& tt)
00584 throw(TropModel::InvalidTropModel)
00585 {
00586 Position R(RX),S(SV);
00587 return GGHeightTropModel::correction(R,S,tt);
00588 }
00589
00590
00591 double GGHeightTropModel::dry_zenith_delay(void) const
00592 throw(TropModel::InvalidTropModel)
00593 {
00594 if(!valid) {
00595 if(!validWeather)
00596 GPSTK_THROW(InvalidTropModel("Invalid GGH trop model: Weather"));
00597 if(!validHeights)
00598 GPSTK_THROW(InvalidTropModel("Invalid GGH trop model: Heights"));
00599 if(!validRxHeight)
00600 GPSTK_THROW(InvalidTropModel("Invalid GGH trop model: Rx Height"));
00601 }
00602 double hrate=6.5e-3;
00603 double Ts=temp+hrate*height;
00604 double em=978.77/(2.8704e4*hrate);
00605 double Tp=Ts-hrate*hpress;
00606 double ps=press*std::pow(Ts/Tp,em)/1000.0;
00607 double rs=77.624e-3/Ts;
00608 double ho=11.385/rs;
00609 rs *= ps;
00610 double zen=(ho-height)/ho;
00611 zen = rs*zen*zen*zen*zen;
00612
00613 zen *= (ho-height)/5;
00614 return zen;
00615
00616 }
00617
00618
00619 double GGHeightTropModel::wet_zenith_delay(void) const
00620 throw(TropModel::InvalidTropModel)
00621 {
00622 if(!valid) {
00623 if(!validWeather)
00624 GPSTK_THROW(InvalidTropModel("Invalid GGH trop model: Weather"));
00625 if(!validHeights)
00626 GPSTK_THROW(InvalidTropModel("Invalid GGH trop model: Heights"));
00627 if(!validRxHeight)
00628 GPSTK_THROW(InvalidTropModel("Invalid GGH trop model: Rx Height"));
00629 }
00630 double hrate=6.5e-3;
00631 double Th=temp-273.15-hrate*(hhumid-htemp);
00632 double Ta=7.5*Th/(237.3+Th);
00633
00634 double e0=6.11e-5*humid*std::pow(10.0,Ta);
00635 double Ts=temp+hrate*htemp;
00636 double em=978.77/(2.8704e4*hrate);
00637 double Tk=Ts-hrate*hhumid;
00638 double es=e0*std::pow(Ts/Tk,4.0*em);
00639 double rs=(371900.0e-3/Ts-12.92e-3)/Ts;
00640 double ho=11.385*(1255/Ts+0.05)/rs;
00641 double zen=(ho-height)/ho;
00642 zen = rs*es*zen*zen*zen*zen;
00643
00644 zen *= (ho-height)/5;
00645 return zen;
00646
00647 }
00648
00649
00650
00651
00652
00653 double GGHeightTropModel::dry_mapping_function(double elevation) const
00654 throw(TropModel::InvalidTropModel)
00655 {
00656 if(!valid) {
00657 if(!validWeather)
00658 GPSTK_THROW(InvalidTropModel("Invalid GGH trop model: Weather"));
00659 if(!validHeights)
00660 GPSTK_THROW(InvalidTropModel("Invalid GGH trop model: Heights"));
00661 if(!validRxHeight)
00662 GPSTK_THROW(InvalidTropModel("Invalid GGH trop model: Rx Height"));
00663 }
00664 if(elevation < 0.0) return 0.0;
00665
00666 double hrate=6.5e-3;
00667 double Ts=temp+hrate*htemp;
00668 double ho=(11.385/77.624e-3)*Ts;
00669 double se=std::sin(elevation*DEG_TO_RAD);
00670 if(se < 0.0) se=0.0;
00671
00672 GPSEllipsoid ell;
00673 double rt,a,b,rn[8],al[8],er=ell.a();
00674 rt = (er+ho)/(er+height);
00675 rt = rt*rt - (1.0-se*se);
00676 if(rt < 0) rt=0.0;
00677 rt = (er+height)*(SQRT(rt)-se);
00678 a = -se/(ho-height);
00679 b = -(1.0-se*se)/(2.0*er*(ho-height));
00680 rn[0] = rt*rt;
00681 for(int j=1; j<8; j++) rn[j]=rn[j-1]*rt;
00682 al[0] = 2*a;
00683 al[1] = 2*a*a+4*b/3;
00684 al[2] = a*(a*a+3*b);
00685 al[3] = a*a*a*a/5+2.4*a*a*b+1.2*b*b;
00686 al[4] = 2*a*b*(a*a+3*b)/3;
00687 al[5] = b*b*(6*a*a+4*b)*0.1428571;
00688 if(b*b > 1.0e-35) {
00689 al[6] = a*b*b*b/2;
00690 al[7] = b*b*b*b/9;
00691 } else {
00692 al[6] = 0.0;
00693 al[7] = 0.0;
00694 }
00695 double map=rt;
00696 for(int k=0; k<8; k++) map += al[k]*rn[k];
00697
00698 double norm=(ho-height)/5;
00699 return map/norm;
00700
00701 }
00702
00703
00704
00705
00706
00707 double GGHeightTropModel::wet_mapping_function(double elevation) const
00708 throw(TropModel::InvalidTropModel)
00709 {
00710 if(!valid) {
00711 if(!validWeather)
00712 GPSTK_THROW(InvalidTropModel("Invalid GGH trop model: Weather"));
00713 if(!validHeights)
00714 GPSTK_THROW(InvalidTropModel("Invalid GGH trop model: Heights"));
00715 if(!validRxHeight)
00716 GPSTK_THROW(InvalidTropModel("Invalid GGH trop model: Rx Height"));
00717 }
00718 if(elevation < 0.0) return 0.0;
00719
00720 double hrate=6.5e-3;
00721 double Ts=temp+hrate*htemp;
00722 double rs=(371900.0e-3/Ts-12.92e-3)/Ts;
00723 double ho=11.385*(1255/Ts+0.05)/rs;
00724 double se=std::sin(elevation*DEG_TO_RAD);
00725 if(se < 0.0) se=0.0;
00726
00727 GPSEllipsoid ell;
00728 double rt,a,b,rn[8],al[8],er=ell.a();
00729 rt = (er+ho)/(er+height);
00730 rt = rt*rt - (1.0-se*se);
00731 if(rt < 0) rt=0.0;
00732 rt = (er+height)*(SQRT(rt)-se);
00733 a = -se/(ho-height);
00734 b = -(1.0-se*se)/(2.0*er*(ho-height));
00735 rn[0] = rt*rt;
00736 for(int i=1; i<8; i++) rn[i]=rn[i-1]*rt;
00737 al[0] = 2*a;
00738 al[1] = 2*a*a+4*b/3;
00739 al[2] = a*(a*a+3*b);
00740 al[3] = a*a*a*a/5+2.4*a*a*b+1.2*b*b;
00741 al[4] = 2*a*b*(a*a+3*b)/3;
00742 al[5] = b*b*(6*a*a+4*b)*0.1428571;
00743 if(b*b > 1.0e-35) {
00744 al[6] = a*b*b*b/2;
00745 al[7] = b*b*b*b/9;
00746 } else {
00747 al[6] = 0.0;
00748 al[7] = 0.0;
00749 }
00750 double map=rt;
00751 for(int j=0; j<8; j++) map += al[j]*rn[j];
00752
00753 double norm=(ho-height)/5;
00754 return map/norm;
00755
00756 }
00757
00758
00759
00760
00761
00762
00763 void GGHeightTropModel::setWeather(const double& T,
00764 const double& P,
00765 const double& H)
00766 throw(InvalidParameter)
00767 {
00768 try
00769 {
00770 TropModel::setWeather(T,P,H);
00771 validWeather = true;
00772 valid = validWeather && validHeights && validRxHeight;
00773 }
00774 catch(InvalidParameter& e)
00775 {
00776 valid = validWeather = false;
00777 GPSTK_RETHROW(e);
00778 }
00779 }
00780
00781
00782
00783
00784 void GGHeightTropModel::setWeather(const WxObservation& wx)
00785 throw(InvalidParameter)
00786 {
00787 try
00788 {
00789 TropModel::setWeather(wx);
00790 validWeather = true;
00791 valid = validWeather && validHeights && validRxHeight;
00792 }
00793 catch(InvalidParameter& e)
00794 {
00795 valid = validWeather = false;
00796 GPSTK_RETHROW(e);
00797 }
00798 }
00799
00800
00801
00802
00803
00804
00805
00806 void GGHeightTropModel::setHeights(const double& hT,
00807 const double& hP,
00808 const double& hH)
00809 {
00810 htemp = hT;
00811 hpress = hP;
00812 hhumid = hH;
00813 validHeights = true;
00814 valid = validWeather && validHeights && validRxHeight;
00815 }
00816
00817
00818
00819 void GGHeightTropModel::setReceiverHeight(const double& ht)
00820 {
00821 height = ht;
00822 validRxHeight = true;
00823 if(!validHeights) {
00824 htemp = hpress = hhumid = ht;
00825 validHeights = true;
00826 }
00827 valid = validWeather && validHeights && validRxHeight;
00828 }
00829
00830
00831
00832
00833
00834
00835
00836
00837
00838
00839
00840
00841
00842
00843
00844
00845
00846
00847 static const double NBRd=287.054;
00848 static const double NBg=9.80665;
00849 static const double NBk1=77.604;
00850 static const double NBk3p=382000;
00851
00852 static const double NBLat[5]={ 15.0, 30.0, 45.0, 60.0, 75.0};
00853
00854
00855 static const double NBZP0[5]={1013.25,1017.25,1015.75,1011.75,1013.00};
00856 static const double NBZT0[5]={ 299.65, 294.15, 283.15, 272.15, 263.65};
00857 static const double NBZW0[5]={ 26.31, 21.79, 11.66, 6.78, 4.11};
00858 static const double NBZB0[5]={6.30e-3,6.05e-3,5.58e-3,5.39e-3,4.53e-3};
00859 static const double NBZL0[5]={ 2.77, 3.15, 2.57, 1.81, 1.55};
00860
00861
00862 static const double NBZPa[5]={ 0.0, -3.75, -2.25, -1.75, -0.50};
00863 static const double NBZTa[5]={ 0.0, 7.0, 11.0, 15.0, 14.5};
00864 static const double NBZWa[5]={ 0.0, 8.85, 7.24, 5.36, 3.39};
00865 static const double NBZBa[5]={ 0.0,0.25e-3,0.32e-3,0.81e-3,0.62e-3};
00866 static const double NBZLa[5]={ 0.0, 0.33, 0.46, 0.74, 0.30};
00867
00868
00869 static const double NBMad[5]={1.2769934e-3,1.2683230e-3,1.2465397e-3,1.2196049e-3,
00870 1.2045996e-3};
00871 static const double NBMbd[5]={2.9153695e-3,2.9152299e-3,2.9288445e-3,2.9022565e-3,
00872 2.9024912e-3};
00873 static const double NBMcd[5]={62.610505e-3,62.837393e-3,63.721774e-3,63.824265e-3,
00874 64.258455e-3};
00875
00876
00877 static const double NBMaa[5]={0.0,1.2709626e-5,2.6523662e-5,3.4000452e-5,
00878 4.1202191e-5};
00879 static const double NBMba[5]={0.0,2.1414979e-5,3.0160779e-5,7.2562722e-5,
00880 11.723375e-5};
00881 static const double NBMca[5]={0.0,9.0128400e-5,4.3497037e-5,84.795348e-5,
00882 170.37206e-5};
00883
00884
00885 static const double NBMaw[5]={5.8021897e-4,5.6794847e-4,5.8118019e-4,5.9727542e-4,
00886 6.1641693e-4};
00887 static const double NBMbw[5]={1.4275268e-3,1.5138625e-3,1.4572752e-3,1.5007428e-3,
00888 1.7599082e-3};
00889 static const double NBMcw[5]={4.3472961e-2,4.6729510e-2,4.3908931e-2,4.4626982e-2,
00890 5.4736038e-2};
00891
00892
00893 enum TableEntry { ZP=1, ZT, ZW, ZB, ZL, Mad, Mbd, Mcd, Maw, Mbw, Mcw };
00894
00895
00896 static double NB_Interpolate(double lat, int doy, TableEntry entry)
00897 {
00898 const double *pave, *pamp;
00899 double ret, day=double(doy);
00900
00901
00902 switch(entry) {
00903 case ZP: pave=&NBZP0[0]; pamp=&NBZPa[0]; break;
00904 case ZT: pave=&NBZT0[0]; pamp=&NBZTa[0]; break;
00905 case ZW: pave=&NBZW0[0]; pamp=&NBZWa[0]; break;
00906 case ZB: pave=&NBZB0[0]; pamp=&NBZBa[0]; break;
00907 case ZL: pave=&NBZL0[0]; pamp=&NBZLa[0]; break;
00908 case Mad: pave=&NBMad[0]; pamp=&NBMaa[0]; break;
00909 case Mbd: pave=&NBMbd[0]; pamp=&NBMba[0]; break;
00910 case Mcd: pave=&NBMcd[0]; pamp=&NBMca[0]; break;
00911 case Maw: pave=&NBMaw[0]; break;
00912 case Mbw: pave=&NBMbw[0]; break;
00913 case Mcw: pave=&NBMcw[0]; break;
00914 }
00915
00916
00917 int i = int(ABS(lat)/15.0)-1;
00918
00919 if(i>=0 && i<=3) {
00920 double m=(ABS(lat)-NBLat[i])/(NBLat[i+1]-NBLat[i]);
00921 ret = pave[i]+m*(pave[i+1]-pave[i]);
00922 if(entry < Maw)
00923 ret -= (pamp[i]+m*(pamp[i+1]-pamp[i]))
00924 * std::cos(TWO_PI*(day-28.0)/365.25);
00925 }
00926 else {
00927 if(i<0) i=0; else i=4;
00928 ret = pave[i];
00929 if(entry < Maw)
00930 ret -= pamp[i]*std::cos(TWO_PI*(day-28.0)/365.25);
00931 }
00932
00933 return ret;
00934
00935 }
00936
00937
00938 NBTropModel::NBTropModel(void)
00939 {
00940 validWeather = false;
00941 validRxLatitude = false;
00942 validDOY = false;
00943 validRxHeight = false;
00944 }
00945
00946
00947
00948
00949
00950 NBTropModel::NBTropModel(const double& lat,
00951 const int& day)
00952 {
00953 validRxHeight = false;
00954 setReceiverLatitude(lat);
00955 setDayOfYear(day);
00956 setWeather();
00957 }
00958
00959
00960
00961
00962
00963 NBTropModel::NBTropModel(const double& lat,
00964 const int& day,
00965 const WxObservation& wx)
00966 throw(InvalidParameter)
00967 {
00968 validRxHeight = false;
00969 setReceiverLatitude(lat);
00970 setDayOfYear(day);
00971 setWeather(wx);
00972 }
00973
00974
00975
00976
00977
00978
00979
00980 NBTropModel::NBTropModel(const double& lat,
00981 const int& day,
00982 const double& T,
00983 const double& P,
00984 const double& H)
00985 throw(InvalidParameter)
00986 {
00987 validRxHeight = false;
00988 setReceiverLatitude(lat);
00989 setDayOfYear(day);
00990 setWeather(T,P,H);
00991 }
00992
00993
00994
00995
00996
00997
00998 NBTropModel::NBTropModel(const double& ht,
00999 const double& lat,
01000 const int& day)
01001 {
01002 setReceiverHeight(ht);
01003 setReceiverLatitude(lat);
01004 setDayOfYear(day);
01005 setWeather();
01006 }
01007
01008
01009 double NBTropModel::correction(double elevation) const
01010 throw(TropModel::InvalidTropModel)
01011 {
01012 if(!valid) {
01013 if(!validWeather)
01014 GPSTK_THROW(InvalidTropModel("Invalid NB trop model: weather"));
01015 if(!validRxLatitude)
01016 GPSTK_THROW(InvalidTropModel("Invalid NB trop model: Rx Latitude"));
01017 if(!validRxHeight)
01018 GPSTK_THROW(InvalidTropModel("Invalid NB trop model: Rx Height"));
01019 if(!validDOY)
01020 GPSTK_THROW(InvalidTropModel("Invalid NB trop model: day of year"));
01021 }
01022
01023 if(elevation < 0.0) return 0.0;
01024
01025 return (dry_zenith_delay() * dry_mapping_function(elevation)
01026 + wet_zenith_delay() * wet_mapping_function(elevation));
01027 }
01028
01029
01030
01031
01032
01033
01034
01035
01036
01037
01038 double NBTropModel::correction(const Position& RX,
01039 const Position& SV,
01040 const CommonTime& tt)
01041 throw(TropModel::InvalidTropModel)
01042 {
01043 if(!valid) {
01044 if(!validWeather)
01045
01046 if(!validRxLatitude)
01047 GPSTK_THROW(InvalidTropModel("Invalid NB trop model: Rx Latitude"));
01048 if(!validRxHeight)
01049 GPSTK_THROW(InvalidTropModel("Invalid NB trop model: Rx Height"));
01050 if(!validDOY)
01051 GPSTK_THROW(InvalidTropModel("Invalid NB trop model: day of year"));
01052 }
01053
01054
01055 setReceiverHeight(RX.getHeight());
01056 setReceiverLatitude(RX.getGeodeticLatitude());
01057
01058
01059 setDayOfYear(int((static_cast<YDSTime>(tt)).doy));
01060
01061 return TropModel::correction(RX.elevation(SV));
01062
01063 }
01064
01065
01066
01067
01068
01069
01070
01071
01072
01073
01074
01075 double NBTropModel::correction(const Xvt& RX,
01076 const Xvt& SV,
01077 const CommonTime& tt)
01078 throw(TropModel::InvalidTropModel)
01079 {
01080 Position R(RX),S(SV);
01081 return NBTropModel::correction(R,S,tt);
01082 }
01083
01084
01085 double NBTropModel::dry_zenith_delay(void) const
01086 throw(TropModel::InvalidTropModel)
01087 {
01088 if(!valid) {
01089 if(!validWeather)
01090 GPSTK_THROW(InvalidTropModel("Invalid NB trop model: weather"));
01091 if(!validRxLatitude)
01092 GPSTK_THROW(InvalidTropModel("Invalid NB trop model: Rx Latitude"));
01093 if(!validRxHeight)
01094 GPSTK_THROW(InvalidTropModel("Invalid NB trop model: Rx Height"));
01095 if(!validDOY)
01096 GPSTK_THROW(InvalidTropModel("Invalid NB trop model: day of year"));
01097 }
01098 double beta = NB_Interpolate(latitude,doy,ZB);
01099 double gm = 9.784*(1.0-2.66e-3*std::cos(2.0*latitude*DEG_TO_RAD)-2.8e-7*height);
01100
01101
01102
01103 double kd=1, base=std::log(1.0-beta*height/temp);
01104 if(interpolateWeather)
01105 kd = std::exp(base*NBg/(NBRd*beta));
01106
01107
01108 return ((1.0e-6*NBk1*NBRd/gm) * kd * press);
01109
01110 }
01111
01112
01113 double NBTropModel::wet_zenith_delay(void) const
01114 throw(TropModel::InvalidTropModel)
01115 {
01116 if(!valid) {
01117 if(!validWeather)
01118 GPSTK_THROW(InvalidTropModel("Invalid NB trop model: weather"));
01119 if(!validRxLatitude)
01120 GPSTK_THROW(InvalidTropModel("Invalid NB trop model: Rx Latitude"));
01121 if(!validRxHeight)
01122 GPSTK_THROW(InvalidTropModel("Invalid NB trop model: Rx Height"));
01123 if(!validDOY)
01124 GPSTK_THROW(InvalidTropModel("Invalid NB trop model: day of year"));
01125 }
01126 double beta = NB_Interpolate(latitude,doy,ZB);
01127 double lam = NB_Interpolate(latitude,doy,ZL);
01128 double gm = 9.784*(1.0-2.66e-3*std::cos(2.0*latitude*DEG_TO_RAD)-2.8e-7*height);
01129
01130
01131
01132 double kw=1, base=std::log(1.0-beta*height/temp);
01133 if(interpolateWeather)
01134 kw = std::exp(base*(-1.0+(lam+1)*NBg/(NBRd*beta)));
01135
01136
01137 return ((1.0e-6*NBk3p*NBRd/(gm*(lam+1)-beta*NBRd)) * kw * humid/temp);
01138
01139 }
01140
01141
01142
01143
01144
01145 double NBTropModel::dry_mapping_function(double elevation) const
01146 throw(TropModel::InvalidTropModel)
01147 {
01148 if(!valid) {
01149 if(!validWeather)
01150 GPSTK_THROW(InvalidTropModel("Invalid NB trop model: weather"));
01151 if(!validRxLatitude)
01152 GPSTK_THROW(InvalidTropModel("Invalid NB trop model: Rx Latitude"));
01153 if(!validRxHeight)
01154 GPSTK_THROW(InvalidTropModel("Invalid NB trop model: Rx Height"));
01155 if(!validDOY)
01156 GPSTK_THROW(InvalidTropModel("Invalid NB trop model: day of year"));
01157 }
01158 if(elevation < 0.0) return 0.0;
01159
01160 double a,b,c,se,map;
01161 se = std::sin(elevation*DEG_TO_RAD);
01162
01163 a = NB_Interpolate(latitude,doy,Mad);
01164 b = NB_Interpolate(latitude,doy,Mbd);
01165 c = NB_Interpolate(latitude,doy,Mcd);
01166 map = (1.0+a/(1.0+b/(1.0+c))) / (se+a/(se+b/(se+c)));
01167
01168 a = 2.53e-5;
01169 b = 5.49e-3;
01170 c = 1.14e-3;
01171 if(ABS(elevation)<=0.001) se=0.001;
01172 map += ((1.0/se)-(1.0+a/(1.0+b/(1.0+c)))/(se+a/(se+b/(se+c))))*height/1000.0;
01173
01174 return map;
01175
01176 }
01177
01178
01179
01180
01181
01182 double NBTropModel::wet_mapping_function(double elevation) const
01183 throw(TropModel::InvalidTropModel)
01184 {
01185 if(!valid) {
01186 if(!validWeather)
01187 GPSTK_THROW(InvalidTropModel("Invalid NB trop model: weather"));
01188 if(!validRxLatitude)
01189 GPSTK_THROW(InvalidTropModel("Invalid NB trop model: Rx Latitude"));
01190 if(!validRxHeight)
01191 GPSTK_THROW(InvalidTropModel("Invalid NB trop model: Rx Height"));
01192 if(!validDOY)
01193 GPSTK_THROW(InvalidTropModel("Invalid NB trop model: day of year"));
01194 }
01195 if(elevation < 0.0) return 0.0;
01196
01197 double a,b,c,se;
01198 se = std::sin(elevation*DEG_TO_RAD);
01199 a = NB_Interpolate(latitude,doy,Maw);
01200 b = NB_Interpolate(latitude,doy,Mbw);
01201 c = NB_Interpolate(latitude,doy,Mcw);
01202
01203 return ( (1.0+a/(1.0+b/(1.0+c))) / (se+a/(se+b/(se+c))) );
01204
01205 }
01206
01207
01208
01209
01210
01211
01212 void NBTropModel::setWeather(const double& T,
01213 const double& P,
01214 const double& H)
01215 throw(InvalidParameter)
01216 {
01217 interpolateWeather=false;
01218 TropModel::setWeather(T,P,H);
01219
01220 double th=300./temp;
01221 humid = 2.409e9*H*th*th*th*th*std::exp(-22.64*th);
01222 validWeather = true;
01223 valid = validWeather && validRxHeight && validRxLatitude && validDOY;
01224
01225 }
01226
01227
01228
01229
01230 void NBTropModel::setWeather(const WxObservation& wx)
01231 throw(InvalidParameter)
01232 {
01233 interpolateWeather = false;
01234 try
01235 {
01236 TropModel::setWeather(wx);
01237
01238 double th=300./temp;
01239 humid = 2.409e9*humid*th*th*th*th*std::exp(-22.64*th);
01240 validWeather = true;
01241 valid = validWeather && validRxHeight && validRxLatitude && validDOY;
01242 }
01243 catch(InvalidParameter& e)
01244 {
01245 valid = validWeather = false;
01246 GPSTK_RETHROW(e);
01247 }
01248 }
01249
01250
01251
01252 void NBTropModel::setWeather()
01253 throw(TropModel::InvalidTropModel)
01254 {
01255 interpolateWeather = true;
01256 if(!validRxLatitude)
01257 {
01258 valid = validWeather = false;
01259 GPSTK_THROW(InvalidTropModel(
01260 "NBTropModel must have Rx latitude before interpolating weather"));
01261 }
01262 if(!validDOY)
01263 {
01264 valid = validWeather = false;
01265 GPSTK_THROW(InvalidTropModel(
01266 "NBTropModel must have day of year before interpolating weather"));
01267 }
01268 temp = NB_Interpolate(latitude,doy,ZT);
01269 press = NB_Interpolate(latitude,doy,ZP);
01270 humid = NB_Interpolate(latitude,doy,ZW);
01271 validWeather = true;
01272 valid = validWeather && validRxHeight && validRxLatitude && validDOY;
01273 }
01274
01275
01276
01277 void NBTropModel::setReceiverHeight(const double& ht)
01278 {
01279 height = ht;
01280 validRxHeight = true;
01281 valid = validWeather && validRxHeight && validRxLatitude && validDOY;
01282 if(!validWeather && validRxLatitude && validDOY)
01283 setWeather();
01284 }
01285
01286
01287
01288 void NBTropModel::setReceiverLatitude(const double& lat)
01289 {
01290 latitude = lat;
01291 validRxLatitude = true;
01292 valid = validWeather && validRxHeight && validRxLatitude && validDOY;
01293 if(!validWeather && validRxLatitude && validDOY)
01294 setWeather();
01295 }
01296
01297
01298
01299 void NBTropModel::setDayOfYear(const int& d)
01300 {
01301 doy = d;
01302 if(doy > 0 && doy < 367) validDOY=true; else validDOY = false;
01303 valid = validWeather && validRxHeight && validRxLatitude && validDOY;
01304 if(!validWeather && validRxLatitude && validDOY)
01305 setWeather();
01306 }
01307
01308
01309
01310
01311
01312
01313
01314
01315
01316
01317
01318
01319
01320
01321
01322
01323
01324
01325
01326
01327
01328
01329
01330
01331
01332
01333
01334
01335
01336
01337
01338
01339 static const double SaasWetA[5]=
01340 { 0.00058021897, 0.00056794847, 0.00058118019, 0.00059727542, 0.00061641693 };
01341 static const double SaasWetB[5]=
01342 { 0.0014275268, 0.0015138625, 0.0014572752, 0.0015007428, 0.0017599082 };
01343 static const double SaasWetC[5]=
01344 { 0.043472961, 0.046729510, 0.043908931, 0.044626982, 0.054736038 };
01345
01346
01347 static const double SaasDryA[5]=
01348 { 0.0012769934, 0.0012683230, 0.0012465397, 0.0012196049, 0.0012045996 };
01349 static const double SaasDryB[5]=
01350 { 0.0029153695, 0.0029152299, 0.0029288445, 0.0029022565, 0.0029024912 };
01351 static const double SaasDryC[5]=
01352 { 0.062610505, 0.062837393, 0.063721774, 0.063824265, 0.064258455 };
01353
01354 static const double SaasDryA1[5]=
01355 { 0.0, 0.000012709626, 0.000026523662, 0.000034000452, 0.000041202191 };
01356 static const double SaasDryB1[5]=
01357 { 0.0, 0.000021414979, 0.000030160779, 0.000072562722, 0.00011723375 };
01358 static const double SaasDryC1[5]=
01359 { 0.0, 0.000090128400, 0.000043497037, 0.00084795348, 0.0017037206 };
01360
01361
01362 SaasTropModel::SaasTropModel(void)
01363 {
01364 validWeather = false;
01365 validRxLatitude = false;
01366 validDOY = false;
01367 validRxHeight = false;
01368 }
01369
01370
01371
01372
01373
01374 SaasTropModel::SaasTropModel(const double& lat,
01375 const int& day)
01376 {
01377 validWeather = false;
01378 validRxHeight = false;
01379 SaasTropModel::setReceiverLatitude(lat);
01380 SaasTropModel::setDayOfYear(day);
01381 }
01382
01383
01384
01385
01386
01387 SaasTropModel::SaasTropModel(const double& lat,
01388 const int& day,
01389 const WxObservation& wx)
01390 throw(InvalidParameter)
01391 {
01392 validRxHeight = false;
01393 SaasTropModel::setReceiverLatitude(lat);
01394 SaasTropModel::setDayOfYear(day);
01395 SaasTropModel::setWeather(wx);
01396 }
01397
01398
01399
01400
01401
01402
01403
01404 SaasTropModel::SaasTropModel(const double& lat,
01405 const int& day,
01406 const double& T,
01407 const double& P,
01408 const double& H)
01409 throw(InvalidParameter)
01410 {
01411 validRxHeight = false;
01412 SaasTropModel::setReceiverLatitude(lat);
01413 SaasTropModel::setDayOfYear(day);
01414 SaasTropModel::setWeather(T,P,H);
01415 }
01416
01417
01418 double SaasTropModel::correction(double elevation) const
01419 throw(TropModel::InvalidTropModel)
01420 {
01421 if(!valid) {
01422 if(!validWeather) GPSTK_THROW(
01423 InvalidTropModel("Invalid Saastamoinen trop model: weather"));
01424 if(!validRxLatitude) GPSTK_THROW(
01425 InvalidTropModel("Invalid Saastamoinen trop model: Rx Latitude"));
01426 if(!validRxHeight) GPSTK_THROW(
01427 InvalidTropModel("Invalid Saastamoinen trop model: Rx Height"));
01428 if(!validDOY) GPSTK_THROW(
01429 InvalidTropModel("Invalid Saastamoinen trop model: day of year"));
01430 GPSTK_THROW(
01431 InvalidTropModel("Valid flag corrupted in Saastamoinen trop model"));
01432 }
01433
01434 if(elevation < 0.0) return 0.0;
01435
01436 double corr=0.0;
01437 try {
01438 corr = (dry_zenith_delay() * dry_mapping_function(elevation)
01439 + wet_zenith_delay() * wet_mapping_function(elevation));
01440 }
01441 catch(Exception& e) { GPSTK_RETHROW(e); }
01442
01443 return corr;
01444
01445 }
01446
01447
01448
01449
01450
01451
01452
01453
01454
01455
01456 double SaasTropModel::correction(const Position& RX,
01457 const Position& SV,
01458 const CommonTime& tt)
01459 throw(TropModel::InvalidTropModel)
01460 {
01461 SaasTropModel::setReceiverHeight(RX.getHeight());
01462 SaasTropModel::setReceiverLatitude(RX.getGeodeticLatitude());
01463 SaasTropModel::setDayOfYear(int((static_cast<YDSTime>(tt).doy)));
01464
01465 if(!valid) {
01466 if(!validWeather) GPSTK_THROW(
01467 InvalidTropModel("Invalid Saastamoinen trop model: weather"));
01468 if(!validRxLatitude) GPSTK_THROW(
01469 InvalidTropModel("Invalid Saastamoinen trop model: Rx Latitude"));
01470 if(!validRxHeight) GPSTK_THROW(
01471 InvalidTropModel("Invalid Saastamoinen trop model: Rx Height"));
01472 if(!validDOY) GPSTK_THROW(
01473 InvalidTropModel("Invalid Saastamoinen trop model: day of year"));
01474 valid = true;
01475 }
01476
01477 double corr=0.0;
01478 try {
01479 corr = SaasTropModel::correction(RX.elevation(SV));
01480 }
01481 catch(Exception& e) { GPSTK_RETHROW(e); }
01482
01483 return corr;
01484
01485 }
01486
01487 double SaasTropModel::correction(const Xvt& RX,
01488 const Xvt& SV,
01489 const CommonTime& tt)
01490 throw(TropModel::InvalidTropModel)
01491 {
01492 Position R(RX),S(SV);
01493 return SaasTropModel::correction(R,S,tt);
01494 }
01495
01496
01497 double SaasTropModel::dry_zenith_delay(void) const
01498 throw(TropModel::InvalidTropModel)
01499 {
01500 if(!valid) {
01501 if(!validWeather) GPSTK_THROW(
01502 InvalidTropModel("Invalid Saastamoinen trop model: weather"));
01503 if(!validRxLatitude) GPSTK_THROW(
01504 InvalidTropModel("Invalid Saastamoinen trop model: Rx Latitude"));
01505 if(!validRxHeight) GPSTK_THROW(
01506 InvalidTropModel("Invalid Saastamoinen trop model: Rx Height"));
01507 if(!validDOY) GPSTK_THROW(
01508 InvalidTropModel("Invalid Saastamoinen trop model: day of year"));
01509 GPSTK_THROW(
01510 InvalidTropModel("Valid flag corrupted in Saastamoinen trop model"));
01511 }
01512
01513
01514
01515
01516
01517 double delay = 0.0022768 * press
01518 / (1 - 0.00266 * ::cos(2*latitude*DEG_TO_RAD) - 0.00028 * height/1000.);
01519
01520 return delay;
01521
01522 }
01523
01524
01525 double SaasTropModel::wet_zenith_delay(void) const
01526 throw(TropModel::InvalidTropModel)
01527 {
01528 if(!valid) {
01529 if(!validWeather) GPSTK_THROW(
01530 InvalidTropModel("Invalid Saastamoinen trop model: weather"));
01531 if(!validRxLatitude) GPSTK_THROW(
01532 InvalidTropModel("Invalid Saastamoinen trop model: Rx Latitude"));
01533 if(!validRxHeight) GPSTK_THROW(
01534 InvalidTropModel("Invalid Saastamoinen trop model: Rx Height"));
01535 if(!validDOY) GPSTK_THROW(
01536 InvalidTropModel("Invalid Saastamoinen trop model: day of year"));
01537 GPSTK_THROW(
01538 InvalidTropModel("Valid flag corrupted in Saastamoinen trop model"));
01539 }
01540
01541
01542 double delay = 0.0022768 * humid * 1255/((temp+CELSIUS_TO_KELVIN) + 0.05)
01543 / (1 - 0.00266 * ::cos(2*latitude*DEG_TO_RAD) - 0.00028 * height/1000.);
01544
01545 return delay;
01546
01547 }
01548
01549
01550
01551 double SaasTropModel::dry_mapping_function(double elevation) const
01552 throw(TropModel::InvalidTropModel)
01553 {
01554 if(!valid) {
01555 if(!validWeather) GPSTK_THROW(
01556 InvalidTropModel("Invalid Saastamoinen trop model: weather"));
01557 if(!validRxLatitude) GPSTK_THROW(
01558 InvalidTropModel("Invalid Saastamoinen trop model: Rx Latitude"));
01559 if(!validRxHeight) GPSTK_THROW(
01560 InvalidTropModel("Invalid Saastamoinen trop model: Rx Height"));
01561 if(!validDOY) GPSTK_THROW(
01562 InvalidTropModel("Invalid Saastamoinen trop model: day of year"));
01563 GPSTK_THROW(
01564 InvalidTropModel("Valid flag corrupted in Saastamoinen trop model"));
01565 }
01566 if(elevation < 0.0) return 0.0;
01567
01568 double lat,t,ct;
01569 lat = fabs(latitude);
01570 t = doy - 28.;
01571 if(latitude < 0)
01572 t += 365.25/2.;
01573 t *= 360.0/365.25;
01574 ct = ::cos(t*DEG_TO_RAD);
01575
01576 double a,b,c;
01577 if(lat < 15.) {
01578 a = SaasDryA[0];
01579 b = SaasDryB[0];
01580 c = SaasDryC[0];
01581 }
01582 else if(lat < 75.) {
01583 int i=int(lat/15.0)-1;
01584 double frac=(lat-15.*(i+1))/15.;
01585 a = SaasDryA[i] + frac*(SaasDryA[i+1]-SaasDryA[i]);
01586 b = SaasDryB[i] + frac*(SaasDryB[i+1]-SaasDryB[i]);
01587 c = SaasDryC[i] + frac*(SaasDryC[i+1]-SaasDryC[i]);
01588
01589 a -= ct * (SaasDryA1[i] + frac*(SaasDryA1[i+1]-SaasDryA1[i]));
01590 b -= ct * (SaasDryB1[i] + frac*(SaasDryB1[i+1]-SaasDryB1[i]));
01591 c -= ct * (SaasDryC1[i] + frac*(SaasDryC1[i+1]-SaasDryC1[i]));
01592 }
01593 else {
01594 a = SaasDryA[4] - ct * SaasDryA1[4];
01595 b = SaasDryB[4] - ct * SaasDryB1[4];
01596 c = SaasDryC[4] - ct * SaasDryC1[4];
01597 }
01598
01599 double se = ::sin(elevation*DEG_TO_RAD);
01600 double map = (1.+a/(1.+b/(1.+c)))/(se+a/(se+b/(se+c)));
01601
01602 a = 0.0000253;
01603 b = 0.00549;
01604 c = 0.00114;
01605 map += (height/1000.0)*(1./se-(1+a/(1.+b/(1.+c)))/(se+a/(se+b/(se+c))));
01606
01607 return map;
01608
01609 }
01610
01611
01612
01613 double SaasTropModel::wet_mapping_function(double elevation) const
01614 throw(TropModel::InvalidTropModel)
01615 {
01616 if(!valid) {
01617 if(!validWeather) GPSTK_THROW(
01618 InvalidTropModel("Invalid Saastamoinen trop model: weather"));
01619 if(!validRxLatitude) GPSTK_THROW(
01620 InvalidTropModel("Invalid Saastamoinen trop model: Rx Latitude"));
01621 if(!validRxHeight) GPSTK_THROW(
01622 InvalidTropModel("Invalid Saastamoinen trop model: Rx Height"));
01623 if(!validDOY) GPSTK_THROW(
01624 InvalidTropModel("Invalid Saastamoinen trop model: day of year"));
01625 GPSTK_THROW(
01626 InvalidTropModel("Valid flag corrupted in Saastamoinen trop model"));
01627 }
01628 if(elevation < 0.0) return 0.0;
01629
01630 double a,b,c,lat;
01631 lat = fabs(latitude);
01632 if(lat < 15.) {
01633 a = SaasWetA[0];
01634 b = SaasWetB[0];
01635 c = SaasWetC[0];
01636 }
01637 else if(lat < 75.) {
01638 int i=int(lat/15.0)-1;
01639 double frac=(lat-15.*(i+1))/15.;
01640 a = SaasWetA[i] + frac*(SaasWetA[i+1]-SaasWetA[i]);
01641 b = SaasWetB[i] + frac*(SaasWetB[i+1]-SaasWetB[i]);
01642 c = SaasWetC[i] + frac*(SaasWetC[i+1]-SaasWetC[i]);
01643 }
01644 else {
01645 a = SaasWetA[4];
01646 b = SaasWetB[4];
01647 c = SaasWetC[4];
01648 }
01649
01650 double se = ::sin(elevation*DEG_TO_RAD);
01651 double map = (1.+a/(1.+b/(1.+c)))/(se+a/(se+b/(se+c)));
01652
01653 return map;
01654
01655 }
01656
01657
01658
01659
01660
01661
01662 void SaasTropModel::setWeather(const double& T,
01663 const double& P,
01664 const double& H)
01665 throw(InvalidParameter)
01666 {
01667 temp = T;
01668 press = P;
01669
01670 double exp=7.5*T/(T+237.3);
01671 humid = 6.11 * (H/100.) * std::pow(10.0,exp);
01672
01673 validWeather = true;
01674 valid = (validWeather && validRxHeight && validRxLatitude && validDOY);
01675
01676 }
01677
01678
01679
01680
01681 void SaasTropModel::setWeather(const WxObservation& wx)
01682 throw(InvalidParameter)
01683 {
01684 try
01685 {
01686 SaasTropModel::setWeather(wx.temperature,wx.pressure,wx.humidity);
01687 }
01688 catch(InvalidParameter& e)
01689 {
01690 valid = validWeather = false;
01691 GPSTK_RETHROW(e);
01692 }
01693 }
01694
01695
01696
01697 void SaasTropModel::setReceiverHeight(const double& ht)
01698 {
01699 height = ht;
01700 validRxHeight = true;
01701 valid = (validWeather && validRxHeight && validRxLatitude && validDOY);
01702 }
01703
01704
01705
01706 void SaasTropModel::setReceiverLatitude(const double& lat)
01707 {
01708 latitude = lat;
01709 validRxLatitude = true;
01710 valid = (validWeather && validRxHeight && validRxLatitude && validDOY);
01711 }
01712
01713
01714
01715 void SaasTropModel::setDayOfYear(const int& d)
01716 {
01717 doy = d;
01718 if(doy > 0 && doy < 367) validDOY=true; else validDOY = false;
01719 valid = (validWeather && validRxHeight && validRxLatitude && validDOY);
01720 }
01721
01722
01723
01724
01725
01726
01727
01728
01729
01730
01731
01732
01733
01734
01735
01736
01737
01738
01739
01740
01741
01742
01743
01744
01745
01746
01747
01748
01749
01750
01751
01752
01753
01754
01755
01756
01757
01758
01759
01760
01761
01762
01763
01764
01765
01766
01767 GCATTropModel::GCATTropModel(const double& ht)
01768 {
01769 setReceiverHeight(ht);
01770 valid = true;
01771 }
01772
01773
01774
01775
01776
01777
01778
01779
01780
01781 double GCATTropModel::correction(double elevation) const
01782 throw(TropModel::InvalidTropModel)
01783 {
01784 if(!valid) throw InvalidTropModel("Invalid model");
01785
01786 if(elevation < 5.0) return 0.0;
01787
01788 return ( (dry_zenith_delay() + wet_zenith_delay()) *
01789 mapping_function(elevation));
01790 }
01791
01792
01793
01794
01795
01796
01797
01798
01799
01800
01801
01802 double GCATTropModel::correction( const Position& RX,
01803 const Position& SV )
01804 throw(TropModel::InvalidTropModel)
01805 {
01806
01807 try
01808 {
01809 setReceiverHeight( RX.getAltitude() );
01810 }
01811 catch(GeometryException& e)
01812 {
01813 valid = false;
01814 }
01815
01816 if(!valid) throw InvalidTropModel("Invalid model");
01817
01818 double c;
01819 try
01820 {
01821 c = correction(RX.elevationGeodetic(SV));
01822 }
01823 catch(InvalidTropModel& e)
01824 {
01825 GPSTK_RETHROW(e);
01826 }
01827
01828 return c;
01829
01830 }
01831
01832
01833
01834
01835
01836
01837
01838
01839
01840
01841
01842
01843
01844
01845
01846
01847 double GCATTropModel::correction( const Xvt& RX,
01848 const Xvt& SV,
01849 const CommonTime& tt )
01850 throw(TropModel::InvalidTropModel)
01851 {
01852
01853 Position R(RX),S(SV);
01854
01855 return GCATTropModel::correction(R,S);
01856 }
01857
01858
01859
01860
01861
01862 double GCATTropModel::dry_zenith_delay(void) const
01863 throw(TropModel::InvalidTropModel)
01864 {
01865 if(!valid) throw InvalidTropModel("Invalid model");
01866
01867 double ddry(2.29951*std::exp((-0.000116 * gcatHeight) ));
01868
01869 return ddry;
01870 }
01871
01872
01873
01874
01875
01876
01877 double GCATTropModel::mapping_function(double elevation) const
01878 throw(TropModel::InvalidTropModel)
01879 {
01880 if(!valid) throw InvalidTropModel("Invalid model");
01881
01882 if(elevation < 5.0) return 0.0;
01883
01884 double d = std::sin(elevation*DEG_TO_RAD);
01885 d = SQRT(0.002001+(d*d));
01886
01887 return (1.001/d);
01888 }
01889
01890
01891
01892
01893
01894
01895 void GCATTropModel::setReceiverHeight(const double& ht)
01896 {
01897 gcatHeight = ht;
01898 valid = true;
01899 }
01900
01901
01902
01903
01904
01905
01906
01907
01908
01909
01910
01911
01912
01913
01914
01915
01916
01917
01918
01919
01920
01921
01922
01923
01924
01925
01926
01927
01928
01929
01930
01931
01932
01933
01934
01935
01936
01937
01938
01939 static const double MOPSg=9.80665;
01940 static const double MOPSgm=9.784;
01941 static const double MOPSk1=77.604;
01942 static const double MOPSk2=382000.0;
01943 static const double MOPSRd=287.054;
01944
01945
01946
01947 MOPSTropModel::MOPSTropModel(void)
01948 {
01949 validHeight = false;
01950 validLat = false;
01951 validTime = false;
01952 valid = false;
01953 }
01954
01955
01956
01957
01958
01959
01960
01961
01962
01963
01964 MOPSTropModel::MOPSTropModel( const double& ht,
01965 const double& lat,
01966 const int& doy )
01967 {
01968 setReceiverHeight(ht);
01969 setReceiverLatitude(lat);
01970 setDayOfYear(doy);
01971 }
01972
01973
01974
01975
01976
01977
01978
01979
01980 MOPSTropModel::MOPSTropModel(const Position& RX, const CommonTime& time)
01981 {
01982 setReceiverHeight(RX.getAltitude());
01983 setReceiverLatitude(RX.getGeodeticLatitude());
01984 setDayOfYear(time);
01985 }
01986
01987
01988
01989
01990
01991
01992
01993 double MOPSTropModel::correction(double elevation) const
01994 throw(TropModel::InvalidTropModel)
01995 {
01996 if(!valid)
01997 {
01998 if(!validLat)
01999 throw InvalidTropModel("Invalid MOPS trop model: Rx Latitude");
02000 if(!validHeight)
02001 throw InvalidTropModel("Invalid MOPS trop model: Rx Height");
02002 if(!validTime)
02003 throw InvalidTropModel("Invalid MOPS trop model: day of year");
02004 }
02005
02006 if(elevation < 5.0) return 0.0;
02007
02008 double map = MOPSTropModel::mapping_function(elevation);
02009
02010
02011 double tropDelay = ( MOPSTropModel::dry_zenith_delay() +
02012 MOPSTropModel::wet_zenith_delay() ) * map;
02013
02014 return tropDelay;
02015
02016 }
02017
02018
02019
02020
02021
02022
02023
02024
02025
02026
02027 double MOPSTropModel::correction( const Position& RX,
02028 const Position& SV )
02029 throw(TropModel::InvalidTropModel)
02030 {
02031 try
02032 {
02033 setReceiverHeight( RX.getAltitude() );
02034 setReceiverLatitude(RX.getGeodeticLatitude());
02035 setWeather();
02036 }
02037 catch(GeometryException& e)
02038 {
02039 valid = false;
02040 }
02041
02042 if(!valid) throw InvalidTropModel("Invalid model");
02043
02044 double c;
02045 try
02046 {
02047 c = MOPSTropModel::correction(RX.elevationGeodetic(SV));
02048 }
02049 catch(InvalidTropModel& e)
02050 {
02051 GPSTK_RETHROW(e);
02052 }
02053
02054 return c;
02055
02056 }
02057
02058
02059
02060
02061
02062
02063
02064
02065
02066
02067 double MOPSTropModel::correction( const Position& RX,
02068 const Position& SV,
02069 const CommonTime& tt )
02070 throw(TropModel::InvalidTropModel)
02071 {
02072 setDayOfYear(tt);
02073
02074 return MOPSTropModel::correction(RX,SV);
02075 }
02076
02077
02078
02079
02080
02081
02082
02083
02084
02085
02086
02087 double MOPSTropModel::correction( const Position& RX,
02088 const Position& SV,
02089 const int& doy )
02090 throw(TropModel::InvalidTropModel)
02091 {
02092 setDayOfYear(doy);
02093
02094 return MOPSTropModel::correction(RX,SV);
02095 }
02096
02097
02098
02099
02100
02101
02102
02103
02104
02105
02106 double MOPSTropModel::correction( const Xvt& RX,
02107 const Xvt& SV )
02108 throw(TropModel::InvalidTropModel)
02109 {
02110 Position R(RX),S(SV);
02111
02112 return MOPSTropModel::correction(R,S);
02113 }
02114
02115
02116
02117
02118
02119
02120
02121
02122
02123
02124
02125
02126 double MOPSTropModel::correction( const Xvt& RX,
02127 const Xvt& SV,
02128 const CommonTime& tt )
02129 throw(TropModel::InvalidTropModel)
02130 {
02131 setDayOfYear(tt);
02132 Position R(RX),S(SV);
02133
02134 return MOPSTropModel::correction(R,S);
02135 }
02136
02137
02138
02139
02140
02141
02142
02143
02144
02145
02146
02147
02148
02149 double MOPSTropModel::correction( const Xvt& RX,
02150 const Xvt& SV,
02151 const int& doy )
02152 throw(TropModel::InvalidTropModel)
02153 {
02154 setDayOfYear(doy);
02155 Position R(RX),S(SV);
02156
02157 return MOPSTropModel::correction(R,S);
02158 }
02159
02160
02161
02162
02163 double MOPSTropModel::dry_zenith_delay(void) const
02164 throw(TropModel::InvalidTropModel)
02165 {
02166
02167 if(!valid) throw InvalidTropModel("Invalid model");
02168
02169 double ddry, zh_dry, exponent;
02170
02171
02172 double P = MOPSParameters(0);
02173 double T = MOPSParameters(1);
02174 double beta = MOPSParameters(3);
02175
02176
02177 zh_dry = 0.000001*(MOPSk1*MOPSRd)*P/MOPSgm;
02178
02179
02180
02181 exponent = MOPSg/MOPSRd/beta;
02182 ddry = zh_dry * std::pow( (1.0 - beta*MOPSHeight/T), exponent );
02183
02184 return ddry;
02185
02186 }
02187
02188
02189
02190
02191 double MOPSTropModel::wet_zenith_delay(void) const
02192 throw(TropModel::InvalidTropModel)
02193 {
02194
02195 if(!valid) throw InvalidTropModel("Invalid model");
02196
02197 double dwet, zh_wet, exponent;
02198
02199
02200 double T = MOPSParameters(1);
02201 double e = MOPSParameters(2);
02202 double beta = MOPSParameters(3);
02203 double lambda = MOPSParameters(4);
02204
02205
02206 zh_wet = (0.000001*MOPSk2)*MOPSRd/(MOPSgm*(lambda+1.0)-beta*MOPSRd)*e/T;
02207
02208
02209
02210 exponent = ( (lambda+1.0)*MOPSg/MOPSRd/beta)-1.0;
02211 dwet= zh_wet * std::pow( (1.0 - beta*MOPSHeight/T), exponent );
02212
02213 return dwet;
02214
02215 }
02216
02217
02218
02219
02220
02221 void MOPSTropModel::setWeather()
02222 throw(TropModel::InvalidTropModel)
02223 {
02224
02225 if(!validLat)
02226 {
02227 valid = false;
02228 throw InvalidTropModel(
02229 "MOPSTropModel must have Rx latitude before computing weather");
02230 }
02231
02232 if(!validTime)
02233 {
02234 valid = false;
02235 throw InvalidTropModel(
02236 "MOPSTropModel must have day of year before computing weather");
02237 }
02238
02239
02240
02241 try
02242 {
02243 prepareParameters();
02244 }
02245 catch(InvalidTropModel& e)
02246 {
02247 GPSTK_RETHROW(e);
02248 }
02249
02250 valid = validHeight && validLat && validTime;
02251 }
02252
02253
02254
02255
02256
02257
02258
02259 void MOPSTropModel::setReceiverHeight(const double& ht)
02260 {
02261 MOPSHeight = ht;
02262 validHeight = true;
02263
02264
02265 valid = validHeight && validLat && validTime;
02266
02267
02268 if (valid) setWeather();
02269
02270 }
02271
02272
02273
02274
02275
02276
02277
02278 void MOPSTropModel::setReceiverLatitude(const double& lat)
02279 {
02280 MOPSLat = lat;
02281 validLat = true;
02282
02283
02284 valid = validHeight && validLat && validTime;
02285
02286
02287 if (valid) setWeather();
02288
02289 }
02290
02291
02292
02293
02294
02295
02296
02297 void MOPSTropModel::setDayOfYear(const int& doy)
02298 {
02299
02300 if ( (doy>=1) && (doy<=366))
02301 {
02302 validTime = true;
02303 }
02304 else
02305 {
02306 validTime = false;
02307 }
02308
02309 MOPSTime = doy;
02310
02311
02312 valid = validHeight && validLat && validTime;
02313
02314
02315 if (valid) setWeather();
02316 }
02317
02318
02319
02320
02321
02322
02323
02324 void MOPSTropModel::setDayOfYear(const CommonTime& time)
02325 {
02326 MOPSTime = (int)(static_cast<YDSTime>(time)).doy;
02327 validTime = true;
02328
02329
02330 valid = validHeight && validLat && validTime;
02331
02332
02333 if (valid) setWeather();
02334
02335 }
02336
02337
02338
02339
02340
02341
02342
02343 void MOPSTropModel::setAllParameters( const CommonTime& time,
02344 const Position& rxPos )
02345 {
02346
02347 MOPSTime = (int)(static_cast<YDSTime>(time)).doy;
02348 validTime = true;
02349 MOPSLat = rxPos.getGeodeticLatitude();
02350 validHeight = true;
02351 MOPSLat = rxPos.getHeight();
02352 validLat = true;
02353
02354
02355 valid = validHeight && validLat && validTime;
02356
02357
02358 if (valid) setWeather();
02359
02360 }
02361
02362
02363
02364
02365
02366
02367 double MOPSTropModel::MOPSsigma2(double elevation)
02368 throw(TropModel::InvalidTropModel)
02369 {
02370
02371 double map_f;
02372
02373
02374
02375 if(elevation < 5.0)
02376 {
02377 return 9.9e9;
02378 }
02379 else
02380 {
02381 map_f = MOPSTropModel::mapping_function(elevation);
02382 }
02383
02384
02385 double MOPSsigma2trop = (0.12*map_f)*(0.12*map_f);
02386
02387 return MOPSsigma2trop;
02388
02389 }
02390
02391
02392
02393 void MOPSTropModel::prepareParameters(void)
02394 throw(TropModel::InvalidTropModel)
02395 {
02396
02397 if(!valid) throw InvalidTropModel("Invalid model");
02398
02399 try
02400 {
02401
02402 prepareTables();
02403
02404
02405 int idmin, j, index;
02406 double fact, axfi;
02407 Vector<double> avr0(5);
02408 Vector<double> svr0(5);
02409
02410
02411 MOPSParameters.resize(5);
02412
02413 if (MOPSLat >= 0.0)
02414 {
02415 idmin = 28;
02416 }
02417 else
02418 {
02419 idmin = 211;
02420 }
02421
02422
02423 fact = 2.0*PI*((double)(MOPSTime-idmin))/365.25;
02424
02425 axfi = ABS(MOPSLat);
02426
02427 if ( axfi <= 15.0 ) index=0;
02428 if ( (axfi > 15.0) && (axfi <= 30.0) ) index=1;
02429 if ( (axfi > 30.0) && (axfi <= 45.0) ) index=2;
02430 if ( (axfi > 45.0) && (axfi <= 60.0) ) index=3;
02431 if ( (axfi > 60.0) && (axfi < 75.0) ) index=4;
02432 if ( axfi >= 75.0 ) index=5;
02433
02434 for (j=0; j<5; j++)
02435 {
02436 if (index == 0) {
02437 avr0(j)=avr(index,j);
02438 svr0(j)=svr(index,j);
02439 }
02440 else
02441 {
02442 if (index < 5)
02443 {
02444 avr0(j) = avr(index-1,j) + (avr(index,j)-avr(index-1,j)) *
02445 (axfi-fi0(index-1))/(fi0( index)-fi0(index-1));
02446
02447 svr0(j) = svr(index-1,j) + (svr(index,j)-svr(index-1,j)) *
02448 (axfi-fi0(index-1))/(fi0( index)-fi0(index-1));
02449 }
02450 else
02451 {
02452 avr0(j) = avr(index-1,j);
02453 svr0(j) = svr(index-1,j);
02454 }
02455 }
02456
02457 MOPSParameters(j) = avr0(j)-svr0(j)*std::cos(fact);
02458 }
02459
02460 }
02461 catch (...)
02462 {
02463 InvalidTropModel e("Problem computing extra MOPS parameters.");
02464 GPSTK_RETHROW(e);
02465 }
02466
02467 }
02468
02469
02470
02471 void MOPSTropModel::prepareTables(void)
02472 {
02473 avr.resize(5,5);
02474 svr.resize(5,5);
02475 fi0.resize(5);
02476
02477
02478
02479
02480 avr(0,0) = 1013.25; avr(0,1) = 299.65; avr(0,2) = 26.31;
02481 avr(0,3) = 0.0063; avr(0,4) = 2.77;
02482
02483 avr(1,0) = 1017.25; avr(1,1) = 294.15; avr(1,2) = 21.79;
02484 avr(1,3) = 0.00605; avr(1,4) = 3.15;
02485
02486 avr(2,0) = 1015.75; avr(2,1) = 283.15; avr(2,2) = 11.66;
02487 avr(2,3) = 0.00558; avr(2,4) = 2.57;
02488
02489 avr(3,0) = 1011.75; avr(3,1) = 272.15; avr(3,2) = 6.78;
02490 avr(3,3) = 0.00539; avr(3,4) = 1.81;
02491
02492 avr(4,0) = 1013.00; avr(4,1) = 263.65; avr(4,2) = 4.11;
02493 avr(4,3) = 0.00453; avr(4,4) = 1.55;
02494
02495
02496
02497
02498 svr(0,0) = 0.00; svr(0,1) = 0.00; svr(0,2) = 0.00;
02499 svr(0,3) = 0.00000; svr(0,4) = 0.00;
02500
02501 svr(1,0) = -3.75; svr(1,1) = 7.00; svr(1,2) = 8.85;
02502 svr(1,3) = 0.00025; svr(1,4) = 0.33;
02503
02504 svr(2,0) = -2.25; svr(2,1) = 11.00; svr(2,2) = 7.24;
02505 svr(2,3) = 0.00032; svr(2,4) = 0.46;
02506
02507 svr(3,0) = -1.75; svr(3,1) = 15.00; svr(3,2) = 5.36;
02508 svr(3,3) = 0.00081; svr(3,4) = 0.74;
02509
02510 svr(4,0) = -0.50; svr(4,1) = 14.50; svr(4,2) = 3.39;
02511 svr(4,3) = 0.00062; svr(4,4) = 0.30;
02512
02513
02514
02515
02516 fi0(0) = 15.0; fi0(1) = 30.0; fi0(2) = 45.0;
02517 fi0(3) = 60.0; fi0(4) = 75.0;
02518
02519 }
02520
02521
02522
02523
02524
02525
02526
02527
02528
02529
02530
02531
02532
02533
02534
02535
02536
02537
02538
02539
02540
02541
02542
02543
02544
02545
02546
02547
02548
02549
02550
02551
02552
02553
02554
02555
02556
02557
02558
02559
02560
02561
02562
02563
02564
02565
02566
02567 NeillTropModel::NeillTropModel( const Position& RX,
02568 const CommonTime& time )
02569 {
02570 setReceiverHeight(RX.getAltitude());
02571 setReceiverLatitude(RX.getGeodeticLatitude( ));
02572 setDayOfYear(time);
02573 }
02574
02575
02576
02577
02578
02579 static const double NeillWetA[5] =
02580 { 0.00058021897, 0.00056794847, 0.00058118019,
02581 0.00059727542, 0.00061641693 };
02582 static const double NeillWetB[5] =
02583 { 0.0014275268, 0.0015138625, 0.0014572752,
02584 0.0015007428, 0.0017599082 };
02585 static const double NeillWetC[5] =
02586 { 0.043472961, 0.046729510, 0.043908931,
02587 0.044626982, 0.054736038 };
02588
02589
02590 static const double NeillDryA[5] =
02591 { 0.0012769934, 0.0012683230, 0.0012465397,
02592 0.0012196049, 0.0012045996 };
02593 static const double NeillDryB[5] =
02594 { 0.0029153695, 0.0029152299, 0.0029288445,
02595 0.0029022565, 0.0029024912 };
02596 static const double NeillDryC[5] =
02597 { 0.062610505, 0.062837393, 0.063721774,
02598 0.063824265, 0.064258455 };
02599
02600 static const double NeillDryA1[5] =
02601 { 0.0, 0.000012709626, 0.000026523662,
02602 0.000034000452, 0.000041202191 };
02603 static const double NeillDryB1[5] =
02604 { 0.0, 0.000021414979, 0.000030160779,
02605 0.000072562722, 0.00011723375 };
02606 static const double NeillDryC1[5] =
02607 { 0.0, 0.000090128400, 0.000043497037,
02608 0.00084795348, 0.0017037206 };
02609
02610
02611
02612
02613
02614
02615
02616
02617 double NeillTropModel::correction(double elevation) const
02618 throw(TropModel::InvalidTropModel)
02619 {
02620
02621 if(!valid)
02622 {
02623 if(!validLat)
02624 {
02625 throw InvalidTropModel("Invalid Neill trop model: Rx Latitude");
02626 }
02627
02628 if(!validHeight)
02629 {
02630 throw InvalidTropModel("Invalid Neill trop model: Rx Height");
02631 }
02632
02633 if(!validDOY)
02634 {
02635 throw InvalidTropModel("Invalid Neill trop model: day of year");
02636 }
02637 }
02638
02639
02640 if(elevation < 3.0)
02641 {
02642 return 0.0;
02643 }
02644
02645 double map_dry(NeillTropModel::dry_mapping_function(elevation));
02646
02647 double map_wet(NeillTropModel::wet_mapping_function(elevation));
02648
02649
02650 double tropDelay( (NeillTropModel::dry_zenith_delay() * map_dry) +
02651 (NeillTropModel::wet_zenith_delay() * map_wet) );
02652
02653 return tropDelay;
02654
02655 }
02656
02657
02658
02659
02660
02661
02662
02663
02664
02665
02666
02667
02668
02669
02670
02671
02672 double NeillTropModel::correction( const Position& RX,
02673 const Position& SV )
02674 throw(TropModel::InvalidTropModel)
02675 {
02676
02677 try
02678 {
02679 setReceiverHeight( RX.getAltitude() );
02680 setReceiverLatitude(RX.getGeodeticLatitude());
02681 setWeather();
02682 }
02683 catch(GeometryException& e)
02684 {
02685 valid = false;
02686 }
02687
02688 if(!valid)
02689 {
02690 throw InvalidTropModel("Invalid model");
02691 }
02692
02693 double c;
02694 try
02695 {
02696 c = NeillTropModel::correction(RX.elevationGeodetic(SV));
02697 }
02698 catch(InvalidTropModel& e)
02699 {
02700 GPSTK_RETHROW(e);
02701 }
02702
02703 return c;
02704
02705 }
02706
02707
02708
02709
02710
02711
02712
02713
02714
02715
02716
02717
02718
02719
02720 double NeillTropModel::correction( const Position& RX,
02721 const Position& SV,
02722 const CommonTime& tt )
02723 throw(TropModel::InvalidTropModel)
02724 {
02725
02726 setDayOfYear(tt);
02727
02728 return NeillTropModel::correction(RX,SV);
02729
02730 }
02731
02732
02733
02734
02735
02736
02737
02738
02739
02740
02741
02742
02743
02744
02745 double NeillTropModel::correction( const Position& RX,
02746 const Position& SV,
02747 const int& doy )
02748 throw(TropModel::InvalidTropModel)
02749 {
02750
02751 setDayOfYear(doy);
02752
02753 return NeillTropModel::correction(RX,SV);
02754
02755 }
02756
02757
02758
02759
02760
02761
02762
02763
02764
02765
02766
02767 double NeillTropModel::correction( const Xvt& RX,
02768 const Xvt& SV )
02769 throw(TropModel::InvalidTropModel)
02770 {
02771 Position R(RX),S(SV);
02772
02773 return NeillTropModel::correction(R,S);
02774
02775 }
02776
02777
02778
02779
02780
02781
02782
02783
02784
02785
02786
02787
02788
02789 double NeillTropModel::correction( const Xvt& RX,
02790 const Xvt& SV,
02791 const CommonTime& tt )
02792 throw(TropModel::InvalidTropModel)
02793 {
02794 setDayOfYear(tt);
02795 Position R(RX),S(SV);
02796
02797 return NeillTropModel::correction(R,S);
02798
02799 }
02800
02801
02802
02803
02804
02805
02806
02807
02808
02809
02810
02811
02812
02813 double NeillTropModel::correction( const Xvt& RX,
02814 const Xvt& SV,
02815 const int& doy )
02816 throw(TropModel::InvalidTropModel)
02817 {
02818 setDayOfYear(doy);
02819 Position R(RX),S(SV);
02820
02821 return NeillTropModel::correction(R,S);
02822
02823 }
02824
02825
02826
02827
02828 double NeillTropModel::dry_zenith_delay(void) const
02829 throw(TropModel::InvalidTropModel)
02830 {
02831
02832 if( !valid )
02833 {
02834 throw InvalidTropModel("Invalid model");
02835 }
02836
02837
02838 double ddry( 2.29951*std::exp( (-0.000116 * NeillHeight) ) );
02839
02840 return ddry;
02841
02842 }
02843
02844
02845
02846
02847
02848
02849
02850 double NeillTropModel::dry_mapping_function(double elevation) const
02851 throw(TropModel::InvalidTropModel)
02852 {
02853 if(!valid)
02854 {
02855 if(!validLat)
02856 {
02857 GPSTK_THROW( InvalidTropModel( "Invalid Neill trop model: Rx \
02858 Latitude" ) );
02859 }
02860
02861 if(!validHeight)
02862 {
02863 GPSTK_THROW( InvalidTropModel( "Invalid Neill trop model: Rx \
02864 Height" ) );
02865 }
02866
02867 if(!validDOY)
02868 {
02869 GPSTK_THROW( InvalidTropModel( "Invalid Neill trop model: day \
02870 of year" ) );
02871 }
02872
02873 GPSTK_THROW(InvalidTropModel( "Valid flag corrupted in Neill trop \
02874 model" ) );
02875 }
02876
02877 if(elevation < 3.0)
02878 {
02879 return 0.0;
02880 }
02881
02882 double lat, t, ct;
02883 lat = fabs(NeillLat);
02884 t = static_cast<double>(NeillDOY) - 28.0;
02885
02886 if(NeillLat < 0.0)
02887 {
02888 t += 365.25/2.;
02889 }
02890
02891 t *= 360.0/365.25;
02892 ct = ::cos(t*DEG_TO_RAD);
02893
02894 double a, b, c;
02895 if(lat < 15.0)
02896 {
02897 a = NeillDryA[0];
02898 b = NeillDryB[0];
02899 c = NeillDryC[0];
02900 }
02901 else if(lat < 75.)
02902 {
02903 int i=int(lat/15.0)-1;
02904 double frac=(lat-15.*(i+1))/15.;
02905 a = NeillDryA[i] + frac*(NeillDryA[i+1]-NeillDryA[i]);
02906 b = NeillDryB[i] + frac*(NeillDryB[i+1]-NeillDryB[i]);
02907 c = NeillDryC[i] + frac*(NeillDryC[i+1]-NeillDryC[i]);
02908
02909 a -= ct * (NeillDryA1[i] + frac*(NeillDryA1[i+1]-NeillDryA1[i]));
02910 b -= ct * (NeillDryB1[i] + frac*(NeillDryB1[i+1]-NeillDryB1[i]));
02911 c -= ct * (NeillDryC1[i] + frac*(NeillDryC1[i+1]-NeillDryC1[i]));
02912 }
02913 else
02914 {
02915 a = NeillDryA[4] - ct * NeillDryA1[4];
02916 b = NeillDryB[4] - ct * NeillDryB1[4];
02917 c = NeillDryC[4] - ct * NeillDryC1[4];
02918 }
02919
02920 double se = ::sin(elevation*DEG_TO_RAD);
02921 double map = (1.+a/(1.+b/(1.+c)))/(se+a/(se+b/(se+c)));
02922
02923 a = 0.0000253;
02924 b = 0.00549;
02925 c = 0.00114;
02926 map += ( NeillHeight/1000.0 ) *
02927 ( 1./se - ( (1.+a/(1.+b/(1.+c))) / (se+a/(se+b/(se+c))) ) );
02928
02929 return map;
02930
02931 }
02932
02933
02934
02935
02936
02937
02938
02939 double NeillTropModel::wet_mapping_function(double elevation) const
02940 throw(TropModel::InvalidTropModel)
02941 {
02942 if(!valid)
02943 {
02944 if(!validLat)
02945 {
02946 GPSTK_THROW( InvalidTropModel( "Invalid Neill trop model: Rx \
02947 Latitude" ) );
02948 }
02949 if(!validHeight)
02950 {
02951 GPSTK_THROW( InvalidTropModel( "Invalid Neill trop model: Rx \
02952 Height" ) );
02953 }
02954 if(!validDOY)
02955 {
02956 GPSTK_THROW( InvalidTropModel(" Invalid Neill trop model: day \
02957 of year" ) );
02958 }
02959
02960 GPSTK_THROW(InvalidTropModel( "Valid flag corrupted in Neill trop \
02961 model" ) );
02962 }
02963
02964 if(elevation < 3.0)
02965 {
02966 return 0.0;
02967 }
02968
02969 double a,b,c,lat;
02970 lat = fabs(NeillLat);
02971 if(lat < 15.0)
02972 {
02973 a = NeillWetA[0];
02974 b = NeillWetB[0];
02975 c = NeillWetC[0];
02976 }
02977 else if(lat < 75.)
02978 {
02979 int i=int(lat/15.0)-1;
02980 double frac=(lat-15.*(i+1))/15.;
02981 a = NeillWetA[i] + frac*(NeillWetA[i+1]-NeillWetA[i]);
02982 b = NeillWetB[i] + frac*(NeillWetB[i+1]-NeillWetB[i]);
02983 c = NeillWetC[i] + frac*(NeillWetC[i+1]-NeillWetC[i]);
02984 }
02985 else
02986 {
02987 a = NeillWetA[4];
02988 b = NeillWetB[4];
02989 c = NeillWetC[4];
02990 }
02991
02992 double se = ::sin(elevation*DEG_TO_RAD);
02993 double map = ( 1.+ a/ (1.+ b/(1.+c) ) ) / (se + a/(se + b/(se+c) ) );
02994
02995 return map;
02996
02997 }
02998
02999
03000
03001
03002
03003 void NeillTropModel::setWeather()
03004 throw(TropModel::InvalidTropModel)
03005 {
03006
03007 if(!validLat)
03008 {
03009 valid = false;
03010 throw InvalidTropModel( "NeillTropModel must have Rx latitude \
03011 before computing weather ");
03012 }
03013 if(!validDOY)
03014 {
03015 valid = false;
03016 throw InvalidTropModel( "NeillTropModel must have day of year \
03017 before computing weather" );
03018 }
03019
03020 valid = validHeight && validLat && validDOY;
03021
03022 }
03023
03024
03025
03026
03027
03028
03029
03030 void NeillTropModel::setReceiverHeight(const double& ht)
03031 {
03032 NeillHeight = ht;
03033 validHeight = true;
03034
03035
03036 valid = validHeight && validLat && validDOY;
03037
03038
03039 if (valid) setWeather();
03040
03041 }
03042
03043
03044
03045
03046
03047
03048 void NeillTropModel::setReceiverLatitude(const double& lat)
03049 {
03050 NeillLat = lat;
03051 validLat = true;
03052
03053
03054 valid = validHeight && validLat && validDOY;
03055
03056
03057 if (valid) setWeather();
03058
03059 }
03060
03061
03062
03063
03064
03065
03066 void NeillTropModel::setDayOfYear(const int& doy)
03067 {
03068
03069 if( (doy>=1) && (doy<=366) )
03070 {
03071 validDOY = true;
03072 }
03073 else
03074 {
03075 validDOY = false;
03076 }
03077
03078 NeillDOY = doy;
03079
03080
03081 valid = validHeight && validLat && validDOY;
03082
03083
03084 if (valid) setWeather();
03085
03086 }
03087
03088
03089
03090
03091
03092
03093 void NeillTropModel::setDayOfYear(const CommonTime& time)
03094 {
03095
03096 NeillDOY = static_cast<int>((static_cast<YDSTime>(time)).doy);
03097 validDOY = true;
03098
03099
03100 valid = validHeight && validLat && validDOY;
03101
03102
03103 if (valid) setWeather();
03104
03105 }
03106
03107
03108
03109
03110
03111
03112
03113 void NeillTropModel::setAllParameters( const CommonTime& time,
03114 const Position& rxPos )
03115 {
03116 YDSTime ydst = static_cast<YDSTime>(time);
03117 NeillDOY = static_cast<int>(ydst.doy);
03118 validDOY = true;
03119 NeillLat = rxPos.getGeodeticLatitude();
03120 validHeight = true;
03121 NeillLat = rxPos.getHeight();
03122 validLat = true;
03123
03124
03125 valid = validHeight && validLat && validDOY;
03126
03127
03128 if (valid) setWeather();
03129
03130 }
03131
03132
03133 }