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