TropModel.cpp

Go to the documentation of this file.
00001 #pragma ident "$Id: TropModel.cpp 2741 2011-06-22 16:37:02Z nwu $"
00002 
00003 //============================================================================
00004 //
00005 //  This file is part of GPSTk, the GPS Toolkit.
00006 //
00007 //  The GPSTk is free software; you can redistribute it and/or modify
00008 //  it under the terms of the GNU Lesser General Public License as published
00009 //  by the Free Software Foundation; either version 2.1 of the License, or
00010 //  any later version.
00011 //
00012 //  The GPSTk is distributed in the hope that it will be useful,
00013 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 //  GNU Lesser General Public License for more details.
00016 //
00017 //  You should have received a copy of the GNU Lesser General Public
00018 //  License along with GPSTk; if not, write to the Free Software Foundation,
00019 //  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020 //
00021 //  Copyright 2004, The University of Texas at Austin
00022 //
00023 //============================================================================
00024 
00025 //============================================================================
00026 //
00027 //This software developed by Applied Research Laboratories at the University of
00028 //Texas at Austin, under contract to an agency or agencies within the U.S.
00029 //Department of Defense. The U.S. Government retains all rights to use,
00030 //duplicate, distribute, disclose, or release this software.
00031 //
00032 //Pursuant to DoD Directive 523024
00033 //
00034 // DISTRIBUTION STATEMENT A: This software has been approved for public
00035 //                           release, distribution is unlimited.
00036 //
00037 //=============================================================================
00038 
00045 #include "TropModel.hpp"
00046 #include "EphemerisRange.hpp"             // for Elevation()
00047 #include "MathBase.hpp"                   // SQRT
00048 #include "geometry.hpp"                   // DEG_TO_RAD
00049 #include "GPSGeoid.hpp"                   // geoid.a() = R earth
00050 #include "icd_200_constants.hpp"          // TWO_PI
00051 #include "Geodetic.hpp"
00052 #include "ECEF.hpp"
00053 
00054 namespace gpstk
00055 {
00056       // for temperature conversion from Celcius to Kelvin
00057    static const double CELSIUS_TO_KELVIN = 273.15;
00058 
00059       // Compute and return the full tropospheric delay. Typically call
00060       // setWeather(T,P,H) before making this call.
00061       // @param elevation Elevation of satellite as seen at receiver, in degrees
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    }  // end TropModel::correction(elevation)
00075 
00076       // Compute and return the full tropospheric delay, given the positions of
00077       // receiver and satellite and the time tag. This version is most useful
00078       // within positioning algorithms, where the receiver position and timetag may
00079       // vary; it computes the elevation (and other receiver location information)
00080       // and passes them to appropriate set...() routines and the
00081       // correction(elevation) routine.
00082       // @param RX  Receiver position in ECEF cartesian coordinates (meters)
00083       // @param SV  Satellite position in ECEF cartesian coordinates (meters)
00084       // @param tt  Time tag of the signal
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    }  // end TropModel::correction(RX,SV,TT)
00104 
00105       // Re-define the tropospheric model with explicit weather data.
00106       // Typically called just before correction().
00107       // @param T temperature in degrees Celsius
00108       // @param P atmospheric pressure in millibars
00109       // @param H relative humidity in percent
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    }  // end TropModel::setWeather(T,P,H)
00134 
00135       // Re-define the tropospheric model with explicit weather data.
00136       // Typically called just before correction().
00137       // @param wx the weather to use for this correction
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          // reference height and it's relate weather(T P H)
00170       const double h0  = 0.0;                      // meter
00171       const double Tr  = +18.0;       // Celsius
00172       const double pr  = 1013.25;               // millibarc
00173       const double Hr  = 50;                       // humidity
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    // Simple Black model. This has been used as the 'default' for many years.
00183 
00184       // Default constructor
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       // Creates a trop model from a weather observation
00196       // @param wx the weather to use for this correction.
00197    SimpleTropModel::SimpleTropModel(const WxObservation& wx)
00198       throw(InvalidParameter)
00199    {
00200       setWeather(wx);
00201       valid = true;
00202    }
00203 
00204       // Create a tropospheric model from explicit weather data
00205       // @param T temperature in degrees Celsius
00206       // @param P atmospheric pressure in millibars
00207       // @param H relative humidity in percent
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       // Re-define the tropospheric model with explicit weather data.
00218       // Typically called just before correction().
00219       // @param T temperature in degrees Celsius
00220       // @param P atmospheric pressure in millibars
00221       // @param H relative humidity in percent
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    }  // end SimpleTropModel::setWeather(T,P,H)
00236 
00237       // Re-define the tropospheric model with explicit weather data.
00238       // Typically called just before correction().
00239       // @param wx the weather to use for this correction
00240    void SimpleTropModel::setWeather(const WxObservation& wx)
00241       throw(InvalidParameter)
00242    {
00243       TropModel::setWeather(wx);
00244    }
00245 
00246       // Compute and return the zenith delay for dry component of the troposphere
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       // Compute and return the zenith delay for wet component of the troposphere
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       // Compute and return the mapping function for dry component
00267       // of the troposphere
00268       // @param elevation is the Elevation of satellite as seen at receiver,
00269       //                  in degrees
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       // Compute and return the mapping function for wet component
00284       // of the troposphere
00285       // @param elevation is the Elevation of satellite as seen at receiver,
00286       //                  in degrees
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    // Tropospheric model based on Goad and Goodman(1974),
00302    // "A Modified Hopfield Tropospheric Refraction Correction Model," Paper
00303    // presented at the Fall Annual Meeting of the American Geophysical Union,
00304    // San Francisco, December 1974.
00305    // See Leick, "GPS Satellite Surveying," Wiley, NY, 1990, Chapter 9,
00306    // particularly Table 9.1.
00307    // ------------------------------------------------------------------------
00308 
00309    static const double GGdryscale = 8594.777388436570600;
00310    static const double GGwetscale = 2540.042008403690900;
00311 
00312       // Default constructor
00313    GGTropModel::GGTropModel(void)
00314    {
00315       TropModel::setWeather(20.0, 980.0, 50.0);
00316       Cdrydelay = 2.59629761092150147e-4;    // zenith delay, dry
00317       Cwetdelay = 4.9982784999977412e-5;     // zenith delay, wet
00318       Cdrymap = 42973.886942182834900;       // height for mapping, dry
00319       Cwetmap = 12700.210042018454260;       // height for mapping, wet
00320       valid = true;
00321    }  // end GGTropModel::GGTropModel()
00322 
00323       // Creates a trop model from a weather observation
00324       // @param wx the weather to use for this correction.
00325    GGTropModel::GGTropModel(const WxObservation& wx)
00326       throw(InvalidParameter)
00327    {
00328       setWeather(wx);
00329       valid = true;
00330    }  // end GGTropModel::GGTropModel(weather)
00331 
00332       // Create a tropospheric model from explicit weather data
00333       // @param T temperature in degrees Celsius
00334       // @param P atmospheric pressure in millibars
00335       // @param H relative humidity in percent
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    } // end GGTropModel::GGTropModel()
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    }  // end GGTropModel::dry_zenith_delay()
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    }  // end GGTropModel::wet_zenith_delay()
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          // compute dry component of the mapping function
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    }  // end GGTropModel::dry_mapping_function(elev)
00398 
00399       // compute wet component of the mapping function
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    }  // end GGTropModel::wet_mapping_function(elev)
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          // water vapor partial pressure (mb)
00443          // this comes from Leick and is not good.
00444          // double wvpp=6.108*(RHum*0.01)*exp((17.15*Tk-4684.0)/(Tk-38.45));
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    }  // end GGTropModel::setWeather(T,P,H)
00452 
00453       // Re-define the tropospheric model with explicit weather data.
00454       // Typically called just before correction().
00455       // @param wx the weather to use for this correction
00456    void GGTropModel::setWeather(const WxObservation& wx)
00457       throw(InvalidParameter)
00458    {
00459       TropModel::setWeather(wx);
00460    }
00461 
00462    // ------------------------------------------------------------------------
00463    // Tropospheric model with heights based on Goad and Goodman(1974),
00464    // "A Modified Hopfield Tropospheric Refraction Correction Model," Paper
00465    // presented at the Fall Annual Meeting of the American Geophysical Union,
00466    // San Francisco, December 1974.
00467    // (Not the same as GGTropModel because this has height dependence,
00468    // and the computation of this model does not break cleanly into
00469    // wet and dry components.)
00470 
00471       // Default constructor
00472    GGHeightTropModel::GGHeightTropModel(void)
00473    {
00474       validWeather = false; //setWeather(20.0,980.0,50.0);
00475       validHeights = false; //setHeights(0.0,0.0,0.0);
00476       validRxHeight = false;
00477    }
00478 
00479       // Creates a trop model from a weather observation
00480       // @param wx the weather to use for this correction.
00481    GGHeightTropModel::GGHeightTropModel(const WxObservation& wx)
00482       throw(InvalidParameter)
00483    {
00484       valid = validRxHeight = validHeights = false;
00485       setWeather(wx);
00486    }
00487 
00488       // Create a tropospheric model from explicit weather data
00489       // @param T temperature in degrees Celsius
00490       // @param P atmospheric pressure in millibars
00491       // @param H relative humidity in percent
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       // Create a valid model from explicit input.
00502       // @param T temperature in degrees Celsius
00503       // @param P atmospheric pressure in millibars
00504       // @param H relative humidity in percent
00505       // @param hT height at which temperature applies in meters.
00506       // @param hP height at which atmospheric pressure applies in meters.
00507       // @param hH height at which relative humidity applies in meters.
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       // re-define this to get the throws
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    }  // end GGHeightTropModel::correction(elevation)
00540 
00541       // Compute and return the full tropospheric delay, given the positions of
00542       // receiver and satellite and the time tag. This version is most useful
00543       // within positioning algorithms, where the receiver position and timetag
00544       // may vary; it computes the elevation (and other receiver location
00545       // information) and passes them to appropriate set...() routines and
00546       // the correction(elevation) routine.
00547       // @param RX  Receiver position
00548       // @param SV  Satellite position
00549       // @param tt  Time tag of the signal
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       // compute height from RX
00566       setReceiverHeight(RX.getHeight());
00567 
00568       return TropModel::correction(RX.elevation(SV));
00569 
00570    }  // end GGHeightTropModel::correction(RX,SV,TT)
00571 
00572       // Compute and return the full tropospheric delay, given the positions of
00573       // receiver and satellite and the time tag. This version is most useful
00574       // within positioning algorithms, where the receiver position and timetag
00575       // may vary; it computes the elevation (and other receiver location
00576       // information) and passes them to appropriate set...() routines and
00577       // the correction(elevation) routine.
00578       // @param RX  Receiver position in ECEF cartesian coordinates (meters)
00579       // @param SV  Satellite position in ECEF cartesian coordinates (meters)
00580       // @param tt  Time tag of the signal
00581       // This function is deprecated; use the Position version
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       // Compute and return the zenith delay for dry component of the troposphere
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          // normalize
00614       zen *= (ho-height)/5;
00615       return zen;
00616 
00617    }  // end GGHeightTropModel::dry_zenith_delay()
00618 
00619       // Compute and return the zenith delay for wet component of the troposphere
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; //   deg K / m
00632       double Th=temp-273.15-hrate*(hhumid-htemp);
00633       double Ta=7.5*Th/(237.3+Th);
00634          // water vapor partial pressure
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          //normalize
00645       zen *= (ho-height)/5;
00646       return zen;
00647 
00648    }  // end GGHeightTropModel::wet_zenith_delay()
00649 
00650       // Compute and return the mapping function for dry component
00651       // of the troposphere
00652       // @param elevation Elevation of satellite as seen at receiver,
00653       //                  in degrees
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          // normalize
00699       double norm=(ho-height)/5;
00700       return map/norm;
00701 
00702    }  // end GGHeightTropModel::dry_mapping_function(elevation)
00703 
00704       // Compute and return the mapping function for wet component
00705       // of the troposphere
00706       // @param elevation Elevation of satellite as seen at receiver,
00707       //                  in degrees
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          // normalize map function
00754       double norm=(ho-height)/5;
00755       return map/norm;
00756 
00757    }  // end GGHeightTropModel::wet_mapping_function(elevation)
00758 
00759       // Re-define the weather data.
00760       // Typically called just before correction().
00761       // @param T temperature in degrees Celsius
00762       // @param P atmospheric pressure in millibars
00763       // @param H relative humidity in percent
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    }  // end GGHeightTropModel::setWeather(T,P,H)
00781 
00782       // Re-define the tropospheric model with explicit weather data.
00783       // Typically called just before correction().
00784       // @param wx the weather to use for this correction
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       // Re-define the heights at which the weather parameters apply.
00803       // Typically called just before correction().
00804       // @param hT height (m) at which temperature applies
00805       // @param hP height (m) at which atmospheric pressure applies
00806       // @param hH height (m) at which relative humidity applies
00807    void GGHeightTropModel::setHeights(const double& hT,
00808                                       const double& hP,
00809                                       const double& hH)
00810    {
00811       htemp = hT;                 // height (m) at which temp applies
00812       hpress = hP;                // height (m) at which press applies
00813       hhumid = hH;                // height (m) at which humid applies
00814       validHeights = true;
00815       valid = validWeather && validHeights && validRxHeight;
00816    }  // end GGHeightTropModel::setHeights(hT,hP,hH)
00817 
00818       // Define the receiver height; this required before calling
00819       // correction() or any of the zenith_delay or mapping_function routines.
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    }  // end GGHeightTropModel::setReceiverHeight(const double& ht)
00830 
00831    // ------------------------------------------------------------------------
00832    // Tropospheric model developed by University of New Brunswick and described in
00833    // "A Tropospheric Delay Model for the User of the Wide Area Augmentation
00834    // System," J. Paul Collins and Richard B. Langley, Technical Report No. 187,
00835    // Dept. of Geodesy and Geomatics Engineering, University of New Brunswick,
00836    // 1997. See particularly Appendix C.
00837    //
00838    // This model includes a wet and dry component, and was designed for the user
00839    // without access to measurements of temperature, pressure and relative humidity
00840    // at ground level. It requires input of the latitude, day of year and height
00841    // above the ellipsoid, and it interpolates a table of values, using these
00842    // inputs, to get the ground level weather parameters plus other parameters and
00843    // the mapping function constants.
00844    //
00845    // NB in this model, units of temp are degrees Celsius, and humid is the water
00846    // vapor partial pressure.
00847 
00848    static const double NBRd=287.054;   // J/kg/K = m*m*K/s*s
00849    static const double NBg=9.80665;    // m/s*s
00850    static const double NBk1=77.604;    // K/mbar
00851    static const double NBk3p=382000;   // K*K/mbar
00852 
00853    static const double NBLat[5]={   15.0,   30.0,   45.0,   60.0,   75.0};
00854 
00855    // zenith delays, averages
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    // zenith delays, amplitudes
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    // mapping function, dry, averages
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    // mapping function, dry, amplitudes
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    // mapping function, wet, averages (there are no amplitudes for wet)
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    // labels for use with the interpolation routine
00894    enum TableEntry { ZP=1, ZT, ZW, ZB, ZL, Mad, Mbd, Mcd, Maw, Mbw, Mcw };
00895 
00896    // the interpolation routine
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          // assign pointer to the right array
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          // find the index i such that NBLat[i] <= lat < NBLat[i+1]
00918       int i = int(ABS(lat)/15.0)-1;
00919 
00920       if(i>=0 && i<=3) {               // mid-latitude -> regular interpolation
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 {                           // < 15 or > 75 -> simpler interpolation
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    }  // end double NB_Interpolate(lat,doy,entry)
00937 
00938       // Default constructor
00939    NBTropModel::NBTropModel(void)
00940    {
00941       validWeather = false;
00942       validRxLatitude = false;
00943       validDOY = false;
00944       validRxHeight = false;
00945    } // end NBTropModel::NBTropModel()
00946 
00947       // Create a trop model using the minimum information: latitude and doy.
00948       // Interpolate the weather unless setWeather (optional) is called.
00949       // @param lat Latitude of the receiver in degrees.
00950       // @param day Day of year.
00951    NBTropModel::NBTropModel(const double& lat,
00952                             const int& day)
00953    {
00954       validRxHeight = false;
00955       setReceiverLatitude(lat);
00956       setDayOfYear(day);
00957       setWeather();
00958    }  // end NBTropModel::NBTropModel(lat, day);
00959 
00960       // Create a trop model with weather.
00961       // @param lat Latitude of the receiver in degrees.
00962       // @param day Day of year.
00963       // @param wx the weather to use for this correction.
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    }  // end NBTropModel::NBTropModel(weather)
00974 
00975       // Create a tropospheric model from explicit weather data
00976       // @param lat Latitude of the receiver in degrees.
00977       // @param day Day of year.
00978       // @param T temperature in degrees Celsius
00979       // @param P atmospheric pressure in millibars
00980       // @param H relative humidity in percent
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    } // end NBTropModel::NBTropModel()
00993 
00994       // Create a valid model from explicit input (weather will be estimated
00995       // internally by this model).
00996       // @param ht Height of the receiver in meters.
00997       // @param lat Latitude of the receiver in degrees.
00998       // @param d Day of year.
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       // re-define this to get the throws
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    }  // end NBTropModel::correction(elevation)
01029 
01030       // Compute and return the full tropospheric delay, given the positions of
01031       // receiver and satellite and the time tag. This version is most useful
01032       // within positioning algorithms, where the receiver position and timetag
01033       // may vary; it computes the elevation (and other receiver location
01034       // information) and passes them to appropriate set...() routines
01035       // and the correction(elevation) routine.
01036       // @param RX  Receiver position
01037       // @param SV  Satellite position
01038       // @param tt  Time tag of the signal
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          // compute height and latitude from RX
01056       setReceiverHeight(RX.getHeight());
01057       setReceiverLatitude(RX.getGeodeticLatitude());
01058 
01059          // compute day of year from tt
01060       setDayOfYear(int(tt.DOYday()));
01061 
01062       return TropModel::correction(RX.elevation(SV));
01063 
01064    }  // end NBTropModel::correction(RX,SV,TT)
01065 
01066       // Compute and return the full tropospheric delay, given the positions of
01067       // receiver and satellite and the time tag. This version is most useful
01068       // within positioning algorithms, where the receiver position and timetag
01069       // may vary; it computes the elevation (and other receiver location
01070       // information) and passes them to appropriate set...() routines
01071       // and the correction(elevation) routine.
01072       // @param RX  Receiver position in ECEF cartesian coordinates (meters)
01073       // @param SV  Satellite position in ECEF cartesian coordinates (meters)
01074       // @param tt  Time tag of the signal
01075       // This function is deprecated; use the Position version
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       // Compute and return the zenith delay for dry component of the troposphere
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          // scale factors for height above mean sea level
01103          // if weather is given, assume it's measured at ht -> kw=kd=1
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          // compute the zenith delay for dry component
01109       return ((1.0e-6*NBk1*NBRd/gm) * kd * press);
01110 
01111    }  // end NBTropModel::dry_zenith_delay()
01112 
01113       // Compute and return the zenith delay for wet component of the troposphere
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          // scale factors for height above mean sea level
01132          // if weather is given, assume it's measured at ht -> kw=kd=1
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          // compute the zenith delay for wet component
01138       return ((1.0e-6*NBk3p*NBRd/(gm*(lam+1)-beta*NBRd)) * kw * humid/temp);
01139 
01140    }  // end NBTropModel::wet_zenith_delay()
01141 
01142       // Compute and return the mapping function for dry component
01143       // of the troposphere
01144       // @param elevation Elevation of satellite as seen at receiver,
01145       //                  in degrees
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    }  // end NBTropModel::dry_mapping_function()
01178 
01179       // Compute and return the mapping function for wet component
01180       // of the troposphere
01181       // @param elevation Elevation of satellite as seen at receiver,
01182       //                  in degrees
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    }  // end NBTropModel::wet_mapping_function()
01207 
01208       // Re-define the weather data.
01209       // If called, typically called before any calls to correction().
01210       // @param T temperature in degrees Celsius
01211       // @param P atmospheric pressure in millibars
01212       // @param H relative humidity in percent
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             // humid actually stores water vapor partial pressure
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    }  // end NBTropModel::setWeather()
01227 
01228       // Re-define the tropospheric model with explicit weather data.
01229       // Typically called just before correction().
01230       // @param wx the weather to use for this correction
01231    void NBTropModel::setWeather(const WxObservation& wx)
01232       throw(InvalidParameter)
01233    {
01234       interpolateWeather = false;
01235       try
01236       {
01237          TropModel::setWeather(wx);
01238             // humid actually stores vapor partial pressure
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       // configure the model to estimate the weather from the internal model,
01252       // using lat and doy
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       // Define the receiver height; this required before calling
01277       // correction() or any of the zenith_delay or mapping_function routines.
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    }  // end NBTropModel::setReceiverHeight()
01286 
01287       // Define the latitude of the receiver; this is required before calling
01288       // correction() or any of the zenith_delay or mapping_function routines.
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    }  // end NBTropModel::setReceiverLatitude(lat)
01297 
01298       // Define the day of year; this is required before calling
01299       // correction() or any of the zenith_delay or mapping_function routines.
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    }  // end NBTropModel::setDayOfYear(doy)
01308 
01309    // ------------------------------------------------------------------------
01310    // Saastamoinen tropospheric model.
01311    // This model needs work; it is not the Saastamoinen model, but appears to be
01312    // a combination of the Neill mapping functions and an unknown delay model.
01313    // Based on Saastamoinen, J., 'Atmospheric
01314    // Correction for the Troposphere and Stratosphere in Radio Ranging of
01315    // Satellites,' Geophysical Monograph 15, American Geophysical Union, 1972,
01316    // and Ch. 9 of McCarthy, D. and Petit, G., IERS Conventions (2003), IERS
01317    // Technical Note 32, IERS, 2004. The mapping functions are from
01318    // Neill, A.E., 1996, 'Global Mapping Functions for the Atmosphere Delay of
01319    // Radio Wavelengths,' J. Geophys. Res., 101, pp. 3227-3246 (also see IERS TN 32).
01320    //
01321    // This model includes a wet and dry component, and requires input of the
01322    // geodetic latitude, day of year and height above the ellipsoid of the receiver.
01323    //
01324    // Usually, the caller will set the latitude and day of year at the same
01325    // time the weather is set
01326    //   SaasTropModel stm;
01327    //   stm.setReceiverLatitude(lat);
01328    //   stm.setDayOfYear(doy);
01329    //   stm.setWeather(T,P,H);
01330    // Then, when the correction (and/or delay and map) is computed, receiver height
01331    // should be set before the call to correction(elevation):
01332    //   stm.setReceiverHeight(height);
01333    //   trop_corr = stm.correction(elevation);
01334    //
01335    // NB in this model, units of 'temp' are degrees Celcius and
01336    // humid actually stores water vapor partial pressure in mbars
01337    //
01338 
01339    // constants for wet mapping function
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    // constants for dry mapping function
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       // Default constructor
01363    SaasTropModel::SaasTropModel(void)
01364    {
01365       validWeather = false;
01366       validRxLatitude = false;
01367       validDOY = false;
01368       validRxHeight = false;
01369    } // end SaasTropModel::SaasTropModel()
01370 
01371       // Create a trop model using the minimum information: latitude and doy.
01372       // Interpolate the weather unless setWeather (optional) is called.
01373       // @param lat Latitude of the receiver in degrees.
01374       // @param day Day of year.
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    } // end SaasTropModel::SaasTropModel
01383 
01384       // Create a trop model with weather.
01385       // @param lat Latitude of the receiver in degrees.
01386       // @param day Day of year.
01387       // @param wx the weather to use for this correction.
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    }  // end SaasTropModel::SaasTropModel(weather)
01398 
01399       // Create a tropospheric model from explicit weather data
01400       // @param lat Latitude of the receiver in degrees.
01401       // @param day Day of year.
01402       // @param T temperature in degrees Celsius
01403       // @param P atmospheric pressure in millibars
01404       // @param H relative humidity in percent
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    } // end SaasTropModel::SaasTropModel()
01417 
01418       // re-define this to get the throws correct
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    }  // end SaasTropModel::correction(elevation)
01447 
01448       // Compute and return the full tropospheric delay, given the positions of
01449       // receiver and satellite and the time tag. This version is most useful
01450       // within positioning algorithms, where the receiver position and timetag
01451       // may vary; it computes the elevation (and other receiver location
01452       // information) and passes them to appropriate set...() routines
01453       // and the correction(elevation) routine.
01454       // @param RX  Receiver position
01455       // @param SV  Satellite position
01456       // @param tt  Time tag of the signal
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    }  // end SaasTropModel::correction(RX,SV,TT)
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       // Compute and return the zenith delay for dry component of the troposphere
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       // correct pressure for height
01515       //double press_at_h =
01516       //   press * std::pow((temp+273.16-4.5*height/1000.0)/(temp+273.16),34.1/4.5);
01517       // humid is zero for the dry component
01518       double delay = 0.0022768 * press //_at_h
01519             / (1 - 0.00266 * ::cos(2*latitude*DEG_TO_RAD) - 0.00028 * height/1000.);
01520 
01521       return delay;
01522 
01523    }  // end SaasTropModel::dry_zenith_delay()
01524 
01525       // Compute and return the zenith delay for wet component of the troposphere
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       // press is zero for the wet component
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    }  // end SaasTropModel::wet_zenith_delay()
01549 
01550       // Compute and return the mapping function for dry component of the troposphere
01551       // @param elevation Elevation of satellite as seen at receiver, in degrees
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);         // degrees
01571       t = doy - 28.;                // mid-winter
01572       if(latitude < 0)              // southern hemisphere
01573          t += 365.25/2.;
01574       t *= 360.0/365.25;            // convert to degrees
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.) {          // coefficients are for 15,30,45,60,75 deg
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    }  // end SaasTropModel::dry_mapping_function()
01611 
01612       // Compute and return the mapping function for wet component of the troposphere
01613       // @param elevation Elevation of satellite as seen at receiver, in degrees.
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);         // degrees
01633       if(lat < 15.) {
01634          a = SaasWetA[0];
01635          b = SaasWetB[0];
01636          c = SaasWetC[0];
01637       }
01638       else if(lat < 75.) {          // coefficients are for 15,30,45,60,75 deg
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    }  // end SaasTropModel::wet_mapping_function()
01657 
01658       // Re-define the weather data.
01659       // If called, typically called before any calls to correction().
01660       // @param T temperature in degrees Celsius
01661       // @param P atmospheric pressure in millibars
01662       // @param H relative humidity in percent
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          // humid actually stores water vapor partial pressure
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    }  // end SaasTropModel::setWeather()
01678 
01679       // Re-define the tropospheric model with explicit weather data.
01680       // Typically called just before correction().
01681       // @param wx the weather to use for this correction
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       // Define the receiver height; this required before calling
01697       // correction() or any of the zenith_delay or mapping_function routines.
01698    void SaasTropModel::setReceiverHeight(const double& ht)
01699    {
01700       height = ht;
01701       validRxHeight = true;
01702       valid = (validWeather && validRxHeight && validRxLatitude && validDOY);
01703    }  // end SaasTropModel::setReceiverHeight()
01704 
01705       // Define the latitude of the receiver; this is required before calling
01706       // correction() or any of the zenith_delay or mapping_function routines.
01707    void SaasTropModel::setReceiverLatitude(const double& lat)
01708    {
01709       latitude = lat;
01710       validRxLatitude = true;
01711       valid = (validWeather && validRxHeight && validRxLatitude && validDOY);
01712    }  // end SaasTropModel::setReceiverLatitude(lat)
01713 
01714       // Define the day of year; this is required before calling
01715       // correction() or any of the zenith_delay or mapping_function routines.
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    }  // end SaasTropModel::setDayOfYear(doy)
01722 
01723 
01724    //-----------------------------------------------------------------------
01725       /* Tropospheric model implemented in "GPS Code Analysis Tool" (GCAT)
01726        * software.
01727        *
01728        * This model is described in the book "GPS Data processing: code and
01729        * phase Algorithms, Techniques and Recipes" by Hernandez-Pajares, M.,
01730        * J.M. Juan-Zornoza and Sanz-Subirana, J. See Chapter 5.
01731        *
01732        * This book and associated software are freely available at:
01733        *
01734        * http://gage152.upc.es/~manuel/tdgps/tdgps.html
01735        *
01736        * This is a simple but efective model composed of the wet and dry
01737        * vertical tropospheric delays as defined in Gipsy/Oasis-II GPS
01738        * analysis software, and the mapping function as defined by Black and
01739        * Eisner (H. D. Black, A. Eisner. Correcting Satellite Doppler
01740        * Data for Tropospheric Effects. Journal of  Geophysical Research.
01741        * Vol 89. 1984.) and used in MOPS (RTCA/DO-229C) standards.
01742        *
01743        * Usually, the caller will set the receiver height using
01744        * setReceiverHeight() method, and then call the correction() method
01745        * with the satellite elevation as parameter.
01746        *
01747        * @code
01748        *   GCATTropModel gcatTM();
01749        *   ...
01750        *   gcatTM.setReceiverHeight(150.0);
01751        *   trop = gcatTM.correction(elevation);
01752        * @endcode
01753        *
01754        * Another posibility is to set the receiver height when calling
01755        * the constructor.
01756        *
01757        * @code
01758        *   GCATTropModel gcatTM(150.0);    // Receiver height is 150.0 meters
01759        *   ...
01760        *   trop = gcatTM.correction(elevation);
01761        * @endcode
01762        */
01763 
01764       // Constructor to create a GCAT trop model providing  the height of the
01765       // receiver above mean sea level (as defined by ellipsoid model).
01766       //
01767       // @param ht Height of the receiver above mean sea level, in meters.
01768    GCATTropModel::GCATTropModel(const double& ht)
01769    {
01770       setReceiverHeight(ht);
01771       valid = true;
01772    }
01773 
01774 
01775       /* Compute and return the full tropospheric delay. The receiver height
01776        * must has been provided before, whether using the appropriate
01777        * constructor or with the setReceiverHeight() method.
01778        *
01779        * @param elevation  Elevation of satellite as seen at receiver, in
01780        *                   degrees
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    }  // end GCATTropModel::correction(elevation)
01792 
01793 
01794       /* Compute and return the full tropospheric delay, given the positions of
01795        * receiver and satellite. This version is most useful within positioning
01796        * algorithms, where the receiver position may vary; it computes the
01797        * elevation and the receiver height and passes them to appropriate
01798        * set...() routines and the correction(elevation) routine.
01799        *
01800        * @param RX  Receiver position in ECEF cartesian coordinates (meters)
01801        * @param SV  Satellite position in ECEF cartesian coordinates (meters)
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    }  // end GCATTropModel::correction(RX,SV,TT)
01832 
01833 
01834       /* Compute and return the full tropospheric delay, given the positions of
01835        * receiver and satellite and the time tag. This version is most useful
01836        * within positioning algorithms, where the receiver position and timetag
01837        * may vary; it computes the elevation (and other receiver location
01838        * information) and passes them to appropriate set...() routines and the
01839        * correction(elevation) routine.
01840        *
01841        * @param RX  Receiver position in ECEF cartesian coordinates (meters)
01842        * @param SV  Satellite position in ECEF cartesian coordinates (meters)
01843        * @param tt  Time. In this model, tt is a dummy parameter kept only for
01844        *            consistency
01845        *
01846        * This function is deprecated; use the Position version
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    }  // end GCATTropModel::correction(RX,SV,tt)
01858 
01859 
01860       /* Compute and return the zenith delay for the dry component of the
01861        * troposphere.
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    }  // end GCATTropModel::dry_zenith_delay()
01872 
01873 
01874       /* Compute and return the mapping function of the troposphere
01875        * @param elevation  Elevation of satellite as seen at receiver,
01876        *                   in degrees
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    }  // end GCATTropModel::mapping_function(elevation)
01890 
01891 
01892       /* Define the receiver height; this is required before calling
01893        * correction() or any of the zenith_delay or mapping_function routines.
01894        * @param ht Height of the receiver above mean sea level, in meters.
01895        */
01896    void GCATTropModel::setReceiverHeight(const double& ht)
01897    {
01898       gcatHeight = ht;
01899       valid = true;
01900    }
01901 
01902 
01903    //---------------------------------------------------------------
01904       /* Tropospheric model implemented in the RTCA "Minimum Operational
01905        * Performance Standards" (MOPS), version C.
01906        *
01907        * This model is described in the RTCA "Minimum Operational Performance
01908        * Standards" (MOPS), version C (RTCA/DO-229C), in Appendix A.4.2.4.
01909        * Although originally developed for SBAS systems (EGNOS, WAAS), it may
01910        * be suitable for other uses as well.
01911        *
01912        * This model needs the day of year, satellite elevation (degrees),
01913        * receiver height over mean sea level (meters) and receiver latitude in
01914        * order to start computing.
01915        *
01916        * On the other hand, the outputs are the tropospheric correction (in
01917        * meters) and the sigma-squared of tropospheric delay residual error
01918        * (meters^2).
01919        *
01920        * A typical way to use this model follows:
01921        *
01922        * @code
01923        *   MOPSTropModel mopsTM;
01924        *   mopsTM.setReceiverLatitude(lat);
01925        *   mopsTM.setReceiverHeight(height);
01926        *   mopsTM.setDayOfYear(doy);
01927        * @endcode
01928        *
01929        * Once all the basic model parameters are set (latitude, height and day
01930        * of year), then we are able to compute the tropospheric correction as
01931        * a function of elevation:
01932        *
01933        * @code
01934        *   trop = mopsTM.correction(elevation);
01935        * @endcode
01936        *
01937        */
01938 
01939       // Some specific constants
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       // Empty constructor
01948    MOPSTropModel::MOPSTropModel(void)
01949    {
01950       validHeight = false;
01951       validLat    = false;
01952       validTime   = false;
01953       valid       = false;
01954    }
01955 
01956 
01957       /* Constructor to create a MOPS trop model providing the height of
01958        *  the receiver above mean sea level (as defined by ellipsoid model),
01959        *  its latitude and the day of year.
01960        *
01961        * @param ht   Height of the receiver above mean sea level, in meters.
01962        * @param lat  Latitude of receiver, in degrees.
01963        * @param doy  Day of year.
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       /* Constructor to create a MOPS trop model providing the position of
01976        *  the receiver and current time.
01977        *
01978        * @param RX   Receiver position.
01979        * @param time Time.
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       // Compute and return the full tropospheric delay. The receiver height,
01990       // latitude and Day oy Year must has been set before using the
01991       // appropriate constructor or the provided methods.
01992       // @param elevation Elevation of satellite as seen at receiver, in
01993       // degrees
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          // Compute tropospheric delay
02012       double tropDelay = ( MOPSTropModel::dry_zenith_delay() +
02013                            MOPSTropModel::wet_zenith_delay() ) * map;
02014 
02015       return tropDelay;
02016 
02017    }  // end MOPSTropModel::correction(elevation)
02018 
02019 
02020       // Compute and return the full tropospheric delay, given the positions of
02021       // receiver and satellite. This version is most useful within
02022       // positioning algorithms, where the receiver position may vary; it
02023       // computes the elevation (and other receiver location information as
02024       // height and latitude) and passes them to appropriate methods. You must
02025       // set time using method setDayOfYear() before calling this method.
02026       // @param RX  Receiver position
02027       // @param SV  Satellite position
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    }  // end MOPSTropModel::correction(RX,SV)
02058 
02059 
02060       // Compute and return the full tropospheric delay, given the positions of
02061       // receiver and satellite and the time tag. This version is most useful
02062       // within positioning algorithms, where the receiver position may vary;
02063       // it computes the elevation (and other receiver location information as
02064       // height  and latitude) and passes them to appropriate methods.
02065       // @param RX  Receiver position in ECEF cartesian coordinates (meters)
02066       // @param SV  Satellite position in ECEF cartesian coordinates (meters)
02067       // @param tt  Time (DayTime object).
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    }  // end MOPSTropModel::correction(RX,SV,TT)
02077 
02078 
02079       // Compute and return the full tropospheric delay, given the positions of
02080       // receiver and satellite and the day of the year. This version is most
02081       // useful within positioning algorithms, where the receiver position may
02082       // vary; it computes the elevation (and other receiver location
02083       // information as height and latitude) and passes them to appropriate
02084       // methods.
02085       // @param RX  Receiver position in ECEF cartesian coordinates (meters)
02086       // @param SV  Satellite position in ECEF cartesian coordinates (meters)
02087       // @param doy Day of year.
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    }  // end MOPSTropModel::correction(RX,SV,doy)
02097 
02098 
02099 
02100       // deprecated
02101       // Compute and return the full tropospheric delay, given the positions of
02102       // receiver and satellite. You must set time using method setDayOfYear()
02103       // before calling this method.
02104       // @param RX  Receiver position in ECEF cartesian coordinates (meters)
02105       // @param SV  Satellite position in ECEF cartesian coordinates (meters)
02106       // This function is deprecated; use the Position version
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    }  // end MOPSTropModel::correction(RX,SV)
02115 
02116 
02117       // deprecated
02118       // Compute and return the full tropospheric delay, given the positions of
02119       // receiver and satellite and the time tag. This version is most useful
02120       // within positioning algorithms, where the receiver position may vary;
02121       // it computes the elevation (and other receiver location information as
02122       // height and latitude) and passes them to appropriate methods.
02123       // @param RX  Receiver position in ECEF cartesian coordinates (meters)
02124       // @param SV  Satellite position in ECEF cartesian coordinates (meters)
02125       // @param tt  Time (DayTime object).
02126       // This function is deprecated; use the Position version
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    }  // end MOPSTropModel::correction(RX,SV,tt)
02137 
02138 
02139       // deprecated
02140       // Compute and return the full tropospheric delay, given the positions of
02141       // receiver and satellite and the day of the year. This version is most
02142       // useful within positioning algorithms, where the receiver position may
02143       // vary; it computes the elevation (and other receiver location
02144       // information as height and latitude) and passes them to appropriate
02145       // methods.
02146       // @param RX  Receiver position in ECEF cartesian coordinates (meters)
02147       // @param SV  Satellite position in ECEF cartesian coordinates (meters)
02148       // @param doy Day of year.
02149       // This function is deprecated; use the Position version
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    }  // end MOPSTropModel::correction(RX,SV,doy)
02160 
02161 
02162       // Compute and return the zenith delay for the dry component of the
02163       // troposphere
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          // Set the extra parameters
02173       double P = MOPSParameters(0);
02174       double T = MOPSParameters(1);
02175       double beta = MOPSParameters(3);
02176 
02177          // Zero-altitude dry zenith delay:
02178       zh_dry = 0.000001*(MOPSk1*MOPSRd)*P/MOPSgm;
02179 
02180          // Zenith delay terms at MOPSHeight meters of height above mean sea
02181          // level
02182       exponent = MOPSg/MOPSRd/beta;
02183       ddry = zh_dry * std::pow( (1.0 - beta*MOPSHeight/T), exponent );
02184 
02185       return ddry;
02186 
02187    }  // end MOPSTropModel::dry_zenith_delay()
02188 
02189 
02190       // Compute and return the zenith delay for the wet component of the
02191       // troposphere
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          // Set the extra parameters
02201       double T = MOPSParameters(1);
02202       double e = MOPSParameters(2);
02203       double beta = MOPSParameters(3);
02204       double lambda = MOPSParameters(4);
02205 
02206          // Zero-altitude wet zenith delay:
02207       zh_wet = (0.000001*MOPSk2)*MOPSRd/(MOPSgm*(lambda+1.0)-beta*MOPSRd)*e/T;
02208 
02209          // Zenith delay terms at MOPSHeight meters of height above mean sea
02210          // level
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    }  // end MOPSTropModel::wet_zenith_delay()
02217 
02218 
02219       // This method configure the model to estimate the weather using height,
02220       // latitude and day of year (DOY). It is called automatically when
02221       // setting those parameters.
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          // In order to compute tropospheric delay we need to compute some
02241          // extra parameters
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       /* Define the receiver height; this is required before calling
02256        *  correction() or any of the zenith_delay routines.
02257        *
02258        * @param ht   Height of the receiver above mean sea level, in meters.
02259        */
02260    void MOPSTropModel::setReceiverHeight(const double& ht)
02261    {
02262       MOPSHeight = ht;
02263       validHeight = true;
02264 
02265          // Change the value of field "valid" if everything is already set
02266       valid = validHeight && validLat && validTime;
02267 
02268          // If model is valid, set the appropriate parameters
02269       if (valid) setWeather();
02270 
02271    }
02272 
02273 
02274       /* Define the receiver latitude; this is required before calling
02275        *  correction() or any of the zenith_delay routines.
02276        *
02277        * @param lat  Latitude of receiver, in degrees.
02278        */
02279    void MOPSTropModel::setReceiverLatitude(const double& lat)
02280    {
02281       MOPSLat = lat;
02282       validLat = true;
02283 
02284          // Change the value of field "valid" if everything is already set
02285       valid = validHeight && validLat && validTime;
02286 
02287          // If model is valid, set the appropriate parameters
02288       if (valid) setWeather();
02289 
02290    }
02291 
02292 
02293       /* Set the time when tropospheric correction will be computed for, in
02294        *  days of the year.
02295        *
02296        * @param doy  Day of the year.
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          // Change the value of field "valid" if everything is already set
02313       valid = validHeight && validLat && validTime;
02314 
02315          // If model is valid, set the appropriate parameters
02316       if (valid) setWeather();
02317    }
02318 
02319 
02320       /* Set the time when tropospheric correction will be computed for, in
02321        *  days of the year.
02322        *
02323        * @param time  Time object.
02324        */
02325    void MOPSTropModel::setDayOfYear(const DayTime& time)
02326    {
02327       MOPSTime = (int)time.DOY();
02328       validTime = true;
02329 
02330          // Change the value of field "valid" if everything is already set
02331       valid = validHeight && validLat && validTime;
02332 
02333          // If model is valid, set the appropriate parameters
02334       if (valid) setWeather();
02335 
02336    }
02337 
02338 
02339       /* Convenient method to set all model parameters in one pass.
02340        *
02341        * @param time  Time object.
02342        * @param rxPos Receiver position object.
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          // Change the value of field "valid" if everything is already set
02356       valid = validHeight && validLat && validTime;
02357 
02358          // If model is valid, set the appropriate parameters
02359       if (valid) setWeather();
02360 
02361    }
02362 
02363 
02364       // Compute and return the sigma-squared value of tropospheric delay
02365       // residual error (meters^2)
02366       // @param elevation  Elevation of satellite as seen at receiver,
02367       //                   in degrees
02368    double MOPSTropModel::MOPSsigma2(double elevation)
02369       throw(TropModel::InvalidTropModel)
02370    {
02371 
02372       double map_f;
02373 
02374          // If elevation is below bounds, fail in a sensible way returning a
02375          // very big sigma value
02376       if(elevation < 5.0)
02377       {
02378          return 9.9e9;
02379       }
02380       else
02381       {
02382          map_f = MOPSTropModel::mapping_function(elevation);
02383       }
02384 
02385          // Compute residual error for tropospheric delay
02386       double MOPSsigma2trop = (0.12*map_f)*(0.12*map_f);
02387 
02388       return MOPSsigma2trop;
02389 
02390    }  // end MOPSTropModel::MOPSsigma(elevation)
02391 
02392 
02393       // The MOPS tropospheric model needs to compute several extra parameters
02394    void MOPSTropModel::prepareParameters(void)
02395       throw(TropModel::InvalidTropModel)
02396    {
02397 
02398       if(!valid) throw InvalidTropModel("Invalid model");
02399 
02400       try
02401       {
02402             // We need to read some data
02403          prepareTables();
02404 
02405             // Declare some variables
02406          int idmin, j, index;
02407          double fact, axfi;
02408          Vector<double> avr0(5);
02409          Vector<double> svr0(5);
02410 
02411             // Resize MOPSParameters as appropriate
02412          MOPSParameters.resize(5);
02413 
02414          if (MOPSLat >= 0.0)
02415          {
02416             idmin = 28;
02417          }
02418          else
02419          {
02420             idmin = 211;
02421          }
02422 
02423             // Fraction of the year in radians
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       } // end try
02462       catch (...)
02463       {
02464          InvalidTropModel e("Problem computing extra MOPS parameters.");
02465          GPSTK_RETHROW(e);
02466       }
02467 
02468    }  // end MOPSTropModel::prepareParameters()
02469 
02470 
02471       // The MOPS tropospheric model uses several predefined data tables
02472    void MOPSTropModel::prepareTables(void)
02473    {
02474       avr.resize(5,5);
02475       svr.resize(5,5);
02476       fi0.resize(5);
02477 
02478 
02479          // Table avr (Average):
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          // Table svr (Seasonal Variation):
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          // Table fi0 (Latitude bands):
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       /* Tropospheric model based in the Neill mapping functions.
02525        *
02526        * This model uses the mapping functions developed by A.E. Niell and
02527        * published in Neill, A.E., 1996, 'Global Mapping Functions for the
02528        * Atmosphere Delay of Radio Wavelengths,' J. Geophys. Res., 101,
02529        * pp. 3227-3246 (also see IERS TN 32).
02530        *
02531        * The coefficients of the hydrostatic mapping function depend on the
02532        * latitude and height above sea level of the receiver station, and on
02533        * the day of the year. On the other hand, the wet mapping function
02534        * depends only on latitude.
02535        *
02536        * This mapping is independent from surface meteorology, while having
02537        * comparable accuracy and precision to those that require such data.
02538        * This characteristic makes this model very useful, and it is
02539        * implemented in geodetic software such as JPL's Gipsy/OASIS.
02540        *
02541        * A typical way to use this model follows:
02542        *
02543        * @code
02544        *   NeillTropModel neillTM;
02545        *   neillTM.setReceiverLatitude(lat);
02546        *   neillTM.setReceiverHeight(height);
02547        *   neillTM.setDayOfYear(doy);
02548        * @endcode
02549        *
02550        * Once all the basic model parameters are set (latitude, height and
02551        * day of year), then we are able to compute the tropospheric correction
02552        * as a function of elevation:
02553        *
02554        * @code
02555        *   trop = neillTM.correction(elevation);
02556        * @endcode
02557        *
02558        * @warning The Neill mapping functions are defined for elevation
02559        * angles down to 3 degrees.
02560        *
02561        */
02562 
02563       // Constructor to create a Neill trop model providing the position
02564       // of the receiver and current time.
02565       //
02566       // @param RX   Receiver position.
02567       // @param time Time.
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       // Parameters borrowed from Saastamoinen tropospheric model
02579       // Constants for wet mapping function
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       // constants for dry mapping function
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       // Compute and return the full tropospheric delay. The receiver height,
02613       // latitude and Day oy Year must has been set before using the
02614       // appropriate constructor or the provided methods.
02615       //
02616       // @param elevation Elevation of satellite as seen at receiver,
02617       // in degrees
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          // Neill mapping functions work down to 3 degrees of elevation
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          // Compute tropospheric delay
02651       double tropDelay( (NeillTropModel::dry_zenith_delay() * map_dry) +
02652                         (NeillTropModel::wet_zenith_delay() * map_wet) );
02653 
02654       return tropDelay;
02655 
02656    }  // end NeillTropModel::correction(elevation)
02657 
02658 
02659       /* Compute and return the full tropospheric delay, given the
02660        * positions of receiver and satellite.
02661        *
02662        * This version is more useful within positioning algorithms, where
02663        * the receiver position may vary; it computes the elevation (and
02664        * other receiver location information as height and latitude) and
02665        * passes them to appropriate methods.
02666        *
02667        * You must set time using method setReceiverDOY() before calling
02668        * this method.
02669        *
02670        * @param RX  Receiver position.
02671        * @param SV  Satellite position.
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    }  // end NeillTropModel::correction(RX,SV)
02707 
02708 
02709       /* Compute and return the full tropospheric delay, given the
02710        * positions of receiver and satellite and the time tag.
02711        *
02712        * This version is more useful within positioning algorithms, where
02713        * the receiver position may vary; it computes the elevation (and
02714        * other receiver location information as height and latitude), and
02715        * passes them to appropriate methods.
02716        *
02717        * @param RX  Receiver position.
02718        * @param SV  Satellite position.
02719        * @param tt  Time (DayTime object).
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    }  // end NeillTropModel::correction(RX,SV,TT)
02732 
02733 
02734       /* Compute and return the full tropospheric delay, given the
02735        * positions of receiver and satellite and the day of the year.
02736        *
02737        * This version is more useful within positioning algorithms, where
02738        * the receiver position may vary; it computes the elevation (and
02739        * other receiver location information as height and latitude), and
02740        * passes them to appropriate methods.
02741        *
02742        * @param RX  Receiver position.
02743        * @param SV  Satellite position.
02744        * @param doy Day of year.
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    }  // end NeillTropModel::correction(RX,SV,doy)
02757 
02758 
02759 
02760       // deprecated
02761       // Compute and return the full tropospheric delay, given the positions
02762       // of receiver and satellite. . You must set time using method
02763       // setDayOfYear() before calling this method.
02764       //
02765       // @param RX  Receiver position in ECEF cartesian coordinates (meters)
02766       // @param SV  Satellite position in ECEF cartesian coordinates (meters)
02767       // This function is deprecated; use the Position version
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    }  // end NeillTropModel::correction(RX,SV)
02777 
02778 
02779       // deprecated
02780       // Compute and return the full tropospheric delay, given the positions of
02781       // receiver and satellite and the time tag. This version is more useful
02782       // within positioning algorithms, where the receiver position may vary;
02783       // it computes the elevation (and other receiver location information as
02784       // height and latitude) and passes them to appropriate methods.
02785       //
02786       // @param RX  Receiver position in ECEF cartesian coordinates (meters)
02787       // @param SV  Satellite position in ECEF cartesian coordinates (meters)
02788       // @param tt  Time (DayTime object).
02789       // This function is deprecated; use the Position version
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    }  // end NeillTropModel::correction(RX,SV,tt)
02801 
02802 
02803       // deprecated
02804       // Compute and return the full tropospheric delay, given the positions of
02805       // receiver and satellite and the day of the year. This version is more
02806       // within positioning algorithms, where the receiver position may vary;
02807       // it computes the elevation (and other receiver location information as
02808       // height and latitude) and passes them to appropriate methods.
02809       //
02810       // @param RX  Receiver position in ECEF cartesian coordinates (meters)
02811       // @param SV  Satellite position in ECEF cartesian coordinates (meters)
02812       // @param doy Day of year.
02813       // This function is deprecated; use the Position version
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    }  // end NeillTropModel::correction(RX,SV,doy)
02825 
02826 
02827       // Compute and return the zenith delay for the dry component of
02828       // the troposphere.
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          // Note: 1.013*2.27 = 2.29951
02839       double ddry( 2.29951*std::exp( (-0.000116 * NeillHeight) ) );
02840 
02841       return ddry;
02842 
02843    }  // end NeillTropModel::dry_zenith_delay()
02844 
02845 
02846       // Compute and return the mapping function for dry component of
02847       // the troposphere.
02848       //
02849       // @param elevation Elevation of satellite as seen at receiver, in
02850       //                  degrees
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);         // degrees
02885       t = static_cast<double>(NeillDOY) - 28.0;  // mid-winter
02886 
02887       if(NeillLat < 0.0)              // southern hemisphere
02888       {
02889          t += 365.25/2.;
02890       }
02891 
02892       t *= 360.0/365.25;            // convert to degrees
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.)      // coefficients are for 15,30,45,60,75 deg
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    }  // end NeillTropModel::dry_mapping_function()
02933 
02934 
02935       // Compute and return the mapping function for wet component of the
02936       // troposphere.
02937       //
02938       // @param elevation Elevation of satellite as seen at receiver,
02939       //                  in degrees.
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);         // degrees
02972       if(lat < 15.0)
02973       {
02974          a = NeillWetA[0];
02975          b = NeillWetB[0];
02976          c = NeillWetC[0];
02977       }
02978       else if(lat < 75.)          // coefficients are for 15,30,45,60,75 deg
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    }  // end NeillTropModel::wet_mapping_function()
02999 
03000 
03001       // This method configure the model to estimate the weather using height,
03002       // latitude and day of year (DOY). It is called automatically when
03003       // setting those parameters.
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       // Define the receiver height; this is required before calling
03027       // correction() or any of the zenith_delay routines.
03028       //
03029       // @param ht   Height of the receiver above mean sea level,
03030       //             in meters.
03031    void NeillTropModel::setReceiverHeight(const double& ht)
03032    {
03033       NeillHeight = ht;
03034       validHeight = true;
03035 
03036          // Change the value of field "valid" if everything is already set
03037       valid = validHeight && validLat && validDOY;
03038 
03039          // If model is valid, set the appropriate parameters
03040       if (valid) setWeather();
03041 
03042    }
03043 
03044 
03045       // Define the receiver latitude; this is required before calling
03046       // correction() or any of the zenith_delay routines.
03047       //
03048       // @param lat  Latitude of receiver, in degrees.
03049    void NeillTropModel::setReceiverLatitude(const double& lat)
03050    {
03051       NeillLat = lat;
03052       validLat = true;
03053 
03054          // Change the value of field "valid" if everything is already set
03055       valid = validHeight && validLat && validDOY;
03056 
03057          // If model is valid, set the appropriate parameters
03058       if (valid) setWeather();
03059 
03060    }
03061 
03062 
03063       // Set the time when tropospheric correction will be computed for,
03064       // in days of the year.
03065       //
03066       // @param doy  Day of the year.
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          // Change the value of field "valid" if everything is already set
03082       valid = validHeight && validLat && validDOY;
03083 
03084          // If model is valid, set the appropriate parameters
03085       if (valid) setWeather();
03086 
03087    }
03088 
03089 
03090       // Set the time when tropospheric correction will be computed for,
03091       // in days of the year.
03092       //
03093       // @param time  Time object.
03094    void NeillTropModel::setDayOfYear(const DayTime& time)
03095    {
03096 
03097       NeillDOY = static_cast<int>(time.DOY());
03098       validDOY = true;
03099 
03100          // Change the value of field "valid" if everything is already set
03101       valid = validHeight && validLat && validDOY;
03102 
03103          // If model is valid, set the appropriate parameters
03104       if (valid) setWeather();
03105 
03106    }
03107 
03108 
03109       /* Convenient method to set all model parameters in one pass.
03110        *
03111        * @param time  Time object.
03112        * @param rxPos Receiver position object.
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          // Change the value of field "valid" if everything is already set
03125       valid = validHeight && validLat && validDOY;
03126 
03127          // If model is valid, set the appropriate parameters
03128       if (valid) setWeather();
03129 
03130    }
03131 
03132 
03133 } // end namespace gpstk

Generated on Wed Feb 8 03:31:03 2012 for GPS ToolKit Software Library by  doxygen 1.3.9.1