Position.cpp

Go to the documentation of this file.
00001 #pragma ident "$Id: Position.cpp 2944 2011-10-27 08:04:41Z yanweignss $"
00002 
00003 
00004 
00014 //============================================================================
00015 //
00016 //  This file is part of GPSTk, the GPS Toolkit.
00017 //
00018 //  The GPSTk is free software; you can redistribute it and/or modify
00019 //  it under the terms of the GNU Lesser General Public License as published
00020 //  by the Free Software Foundation; either version 2.1 of the License, or
00021 //  any later version.
00022 //
00023 //  The GPSTk is distributed in the hope that it will be useful,
00024 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00025 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00026 //  GNU Lesser General Public License for more details.
00027 //
00028 //  You should have received a copy of the GNU Lesser General Public
00029 //  License along with GPSTk; if not, write to the Free Software Foundation,
00030 //  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00031 //
00032 //  Copyright 2004, The University of Texas at Austin
00033 //
00034 //============================================================================
00035 
00036 #include "Position.hpp"
00037 #include "WGS84Geoid.hpp"
00038 #include "icd_200_constants.hpp"    // for TWO_PI, etc
00039 #include "geometry.hpp"             // for RAD_TO_DEG, etc
00040 #include "MiscMath.hpp"             // for RSS, SQRT
00041 
00042 namespace gpstk
00043 {
00044 
00045    using namespace std;
00046    using namespace StringUtils;
00047 
00048    // ----------- Part  1: coordinate systems --------------------------------
00049       // Labels for coordinate systems supported by Position
00050    static const char *SystemNames[] = {
00051       "Unknown",
00052       "Geodetic",
00053       "Geocentric",
00054       "Cartesian",
00055       "Spherical"};
00056 
00057       // return string giving name of coordinate system
00058    string Position::getSystemName()
00059       throw()
00060    { return SystemNames[system]; }
00061 
00062    // ----------- Part  2: tolerance -----------------------------------------
00063       // One millimeter tolerance.
00064    const double Position::ONE_MM_TOLERANCE = 0.001;
00065       // One centimeter tolerance.
00066    const double Position::ONE_CM_TOLERANCE = 0.01;
00067       // One micron tolerance.
00068    const double Position::ONE_UM_TOLERANCE = 0.000001;
00069 
00070       // Default tolerance for time equality in meters.
00071    double Position::POSITION_TOLERANCE = Position::ONE_MM_TOLERANCE;
00072 
00073       // Sets the tolerance for output and comparisons, for this object only.
00074       // See the constants in this file (e.g. ONE_MM_TOLERANCE)
00075       // for some easy to use tolerance values.
00076       // @param tol Tolerance in meters to be used by comparison operators.
00077    Position& Position::setTolerance(const double tol)
00078       throw()
00079    {
00080       tolerance = tol;
00081       return *this;
00082    }
00083 
00084    // ----------- Part  3: member functions: constructors --------------------
00085    //
00086       // Default constructor.
00087    Position::Position()
00088       throw()
00089    {
00090       WGS84Geoid WGS84;
00091       initialize(0.0,0.0,0.0,Unknown,&WGS84);
00092    }
00093 
00094    Position::Position(const double& a,
00095                       const double& b,
00096                       const double& c,
00097                       Position::CoordinateSystem s,
00098                       GeoidModel *geoid)
00099       throw(GeometryException)
00100    {
00101       try {
00102          initialize(a,b,c,s,geoid);
00103       }
00104       catch(GeometryException& ge) {
00105          GPSTK_RETHROW(ge);
00106       }
00107    }
00108 
00109    Position::Position(const double ABC[3],
00110                       CoordinateSystem s,
00111                       GeoidModel *geoid)
00112       throw(GeometryException)
00113    {
00114       double a=ABC[0];
00115       double b=ABC[1];
00116       double c=ABC[2];
00117       try {
00118          initialize(a,b,c,s,geoid);
00119       }
00120       catch(GeometryException& ge) {
00121          GPSTK_RETHROW(ge);
00122       }
00123    }
00124 
00125    Position::Position(const Triple& ABC,
00126                       CoordinateSystem s,
00127                       GeoidModel *geoid)
00128       throw(GeometryException)
00129    {
00130       double a=ABC[0];
00131       double b=ABC[1];
00132       double c=ABC[2];
00133       try {
00134          initialize(a,b,c,s,geoid);
00135       }
00136       catch(GeometryException& ge) {
00137          GPSTK_RETHROW(ge);
00138       }
00139    }
00140 
00141    Position::Position(const Xvt& xvt)
00142       throw()
00143    {
00144       double a=xvt.x[0];
00145       double b=xvt.x[1];
00146       double c=xvt.x[2];
00147       initialize(a,b,c,Cartesian);
00148    }
00149 
00150    // ----------- Part  4: member functions: arithmetic ----------------------
00151    //
00152    Position& Position::operator-=(const Position& right)
00153       throw()
00154    {
00155       Position r(right);
00156       CoordinateSystem savesys=system;    // save the original system
00157 
00158          // convert to cartestian and difference there
00159       transformTo(Cartesian);
00160       r.transformTo(Cartesian);
00161 
00162       for(int i=0; i<3; i++)
00163          theArray[i] -= r.theArray[i];
00164 
00165       transformTo(savesys);               // transform back to the original system
00166       return *this;
00167    }
00168 
00169    Position& Position::operator+=(const Position& right)
00170       throw()
00171    {
00172       Position r(right);
00173       CoordinateSystem savesys=system;    // save the original system
00174 
00175          // convert to cartestian and difference there
00176       transformTo(Cartesian);
00177       r.transformTo(Cartesian);
00178 
00179       for(int i=0; i<3; i++)
00180          theArray[i] += r.theArray[i];
00181 
00182       transformTo(savesys);               // transform back to the original system
00183       return *this;
00184    }
00185 
00186    Position operator-(const Position& left,
00187                             const Position& right)
00188       throw()
00189    {
00190       Position l(left),r(right);
00191          // convert both to Cartesian
00192       l.transformTo(Position::Cartesian);
00193       r.transformTo(Position::Cartesian);
00194          // difference
00195       l -= r;
00196 
00197       return l;
00198    }
00199 
00200    Position operator+(const Position& left,
00201                             const Position& right)
00202       throw()
00203    {
00204       Position l(left),r(right);
00205          // convert both to Cartesian
00206       l.transformTo(Position::Cartesian);
00207       r.transformTo(Position::Cartesian);
00208          // add
00209       l += r;
00210 
00211       return l;
00212    }
00213 
00214    // ----------- Part  5: member functions: comparisons ---------------------
00215    //
00216       // Equality operator. Returns false if geoid values differ.
00217    bool Position::operator==(const Position &right) const
00218       throw()
00219    {
00220       if(AEarth != right.AEarth || eccSquared != right.eccSquared)
00221          return false;
00222       if(range(*this,right) < tolerance)
00223          return true;
00224       else
00225          return false;
00226    }
00227 
00228       // Inequality operator. Returns true if geoid values differ.
00229    bool Position::operator!=(const Position &right) const
00230       throw()
00231    {
00232       return !(operator==(right));
00233    }
00234 
00235    // ----------- Part  6: member functions: coordinate transformations ------
00236    //
00237       // Transform coordinate system. Does nothing if sys already matches the
00238       // current value of member CoordinateSystem 'system'.
00239       // @param sys coordinate system into which *this is to be transformed.
00240       // @return *this
00241    Position Position::transformTo(CoordinateSystem sys)
00242       throw()
00243    {
00244       if(sys == Unknown || sys == system) return *this;
00245 
00246       // this copies geoid information and tolerance
00247       Position target(*this);
00248 
00249       // transform target.theArray and set target.system
00250       switch(system) {
00251          case Unknown:
00252             return *this;
00253          case Geodetic:
00254             // --------------- Geodetic to ... ------------------------
00255             switch(sys) {
00256                case Unknown: case Geodetic: return *this;
00257                case Geocentric:
00258                   convertGeodeticToGeocentric(*this,target,AEarth,eccSquared);
00259                   target.system = Geocentric;
00260                   break;
00261                case Cartesian:
00262                   convertGeodeticToCartesian(*this,target,AEarth,eccSquared);
00263                   target.system = Cartesian;
00264                   break;
00265                case Spherical:
00266                   convertGeodeticToGeocentric(*this,target,AEarth,eccSquared);
00267                   target.theArray[0] = 90 - target.theArray[0];   // geocen -> sph
00268                   target.system = Spherical;
00269                   break;
00270             }
00271             break;
00272          case Geocentric:
00273             // --------------- Geocentric to ... ----------------------
00274             switch(sys) {
00275                case Unknown: case Geocentric: return *this;
00276                case Geodetic:
00277                   convertGeocentricToGeodetic(*this,target,AEarth,eccSquared);
00278                   target.system = Geodetic;
00279                   break;
00280                case Cartesian:
00281                   convertGeocentricToCartesian(*this,target);
00282                   target.system = Cartesian;
00283                   break;
00284                case Spherical:
00285                   target.theArray[0] = 90 - target.theArray[0];   // geocen -> sph
00286                   target.system = Spherical;
00287                   break;
00288             }
00289             break;
00290          case Cartesian:
00291             // --------------- Cartesian to ... -----------------------
00292             switch(sys) {
00293                case Unknown: case Cartesian: return *this;
00294                case Geodetic:
00295                   convertCartesianToGeodetic(*this,target,AEarth,eccSquared);
00296                   target.system = Geodetic;
00297                   break;
00298                case Geocentric:
00299                   convertCartesianToGeocentric(*this,target);
00300                   target.system = Geocentric;
00301                   break;
00302                case Spherical:
00303                   convertCartesianToSpherical(*this,target);
00304                   target.system = Spherical;
00305                   break;
00306             }
00307             break;
00308          case Spherical:
00309             // --------------- Spherical to ... -----------------------
00310             switch(sys) {
00311                case Unknown: case Spherical: return *this;
00312                case Geodetic:
00313                   theArray[0] = 90 - theArray[0];   // sph -> geocen
00314                   convertGeocentricToGeodetic(*this,target,AEarth,eccSquared);
00315                   target.system = Geodetic;
00316                   break;
00317                case Geocentric:
00318                   target.theArray[0] = 90 - target.theArray[0];   // sph -> geocen
00319                   target.system = Geocentric;
00320                   break;
00321                case Cartesian:
00322                   convertSphericalToCartesian(*this,target);
00323                   target.system = Cartesian;
00324                   break;
00325             }
00326             break;
00327       }  // end switch(system)
00328 
00329       *this = target;
00330       return *this;
00331    }
00332 
00333    // ----------- Part  7: member functions: get -----------------------------
00334    //
00335    // These routines retrieve coordinate values in all coordinate systems.
00336    // Note that calling these will transform the Position to another coordinate
00337    // system if that is required.
00338    //
00339       // Get X coordinate (meters)
00340    double Position::X() const
00341       throw()
00342    {
00343       if(system == Cartesian)
00344          return theArray[0];
00345       Position t(*this);
00346       t.transformTo(Cartesian);
00347       return t.theArray[0];
00348    }
00349 
00350       // Get Y coordinate (meters)
00351    double Position::Y() const
00352       throw()
00353    {
00354       if(system == Cartesian)
00355          return theArray[1];
00356       Position t(*this);
00357       t.transformTo(Cartesian);
00358       return t.theArray[1];
00359    }
00360 
00361       // Get Z coordinate (meters)
00362    double Position::Z() const
00363       throw()
00364    {
00365       if(system == Cartesian)
00366          return theArray[2];
00367       Position t(*this);
00368       t.transformTo(Cartesian);
00369       return t.theArray[2];
00370    }
00371 
00372       // Get geodetic latitude (degrees North).
00373    double Position::geodeticLatitude() const
00374       throw()
00375    {
00376       if(system == Geodetic)
00377          return theArray[0];
00378       Position t(*this);
00379       t.transformTo(Geodetic);
00380       return t.theArray[0];
00381    }
00382 
00383       // Get geocentric latitude (degrees North),
00384       // equal to 90 degress - theta in regular spherical coordinates.
00385    double Position::geocentricLatitude() const
00386       throw()
00387    {
00388       if(system == Geocentric)
00389          return theArray[0];
00390       Position t(*this);
00391       t.transformTo(Geocentric);
00392       return t.theArray[0];
00393    }
00394 
00395       // Get spherical coordinate theta in degrees
00396    double Position::theta() const
00397       throw()
00398    {
00399       if(system == Spherical)
00400          return theArray[0];
00401       Position t(*this);
00402       t.transformTo(Spherical);
00403       return t.theArray[0];
00404    }
00405 
00406       // Get spherical coordinate phi in degrees
00407    double Position::phi() const
00408       throw()
00409    {
00410       if(system == Spherical)
00411          return theArray[1];
00412       Position t(*this);
00413       t.transformTo(Spherical);
00414       return t.theArray[1];
00415    }
00416 
00417       // Get longitude (degrees East),
00418       // equal to phi in regular spherical coordinates.
00419    double Position::longitude() const
00420       throw()
00421    {
00422       if(system != Cartesian)
00423          return theArray[1];
00424       Position t(*this);
00425       t.transformTo(Spherical);
00426       return t.theArray[1];
00427    }
00428 
00429       // Get radius or distance from the center of Earth (meters),
00430       // Same as radius in spherical coordinates.
00431    double Position::radius() const
00432       throw()
00433    {
00434       if(system == Spherical || system == Geocentric)
00435          return theArray[2];
00436       Position t(*this);
00437       t.transformTo(Spherical);
00438       return t.theArray[2];
00439    }
00440 
00441       // Get height above ellipsoid (meters) (Geodetic).
00442    double Position::height() const
00443       throw()
00444    {
00445       if(system == Geodetic)
00446          return theArray[2];
00447       Position t(*this);
00448       t.transformTo(Geodetic);
00449       return t.theArray[2];
00450    }
00451 
00452    // ----------- Part  8: member functions: set -----------------------------
00453    //
00454       // Set the geoid values for this Position given a geoid.
00455       // @param geoid Pointer to the GeoidModel.
00456       // @throw GeometryException if input is NULL.
00457    void Position::setGeoidModel(const GeoidModel *geoid)
00458       throw(GeometryException)
00459    {
00460       if(!geoid)
00461       {
00462          GeometryException ge("Given GeoidModel pointer is NULL.");
00463          GPSTK_THROW(ge);
00464       }
00465       AEarth = geoid->a();
00466       eccSquared = geoid->eccSquared();
00467    }
00468 
00469       // Set the Position given geodetic coordinates, system is set to Geodetic.
00470       // @param lat geodetic latitude in degrees North
00471       // @param lon geodetic longitude in degrees East
00472       // @param ht height above the ellipsoid in meters
00473       // @return a reference to this object.
00474       // @throw GeometryException on invalid input
00475    Position& Position::setGeodetic(const double lat,
00476                                    const double lon,
00477                                    const double ht,
00478                                    const GeoidModel *geoid)
00479       throw(GeometryException)
00480    {
00481       if(lat > 90 || lat < -90)
00482       {
00483          GeometryException ge("Invalid latitude in setGeodetic: "
00484                                  + StringUtils::asString(lat));
00485          GPSTK_THROW(ge);
00486       }
00487       theArray[0] = lat;
00488 
00489       theArray[1] = lon;
00490       if(theArray[1] < 0)
00491          theArray[1] += 360*(1+(unsigned long)(theArray[1]/360));
00492       else if(theArray[1] >= 360)
00493          theArray[1] -= 360*(unsigned long)(theArray[1]/360);
00494 
00495       theArray[2] = ht;
00496 
00497       if(geoid) {
00498          AEarth = geoid->a();
00499          eccSquared = geoid->eccSquared();
00500       }
00501       system = Geodetic;
00502 
00503       return *this;
00504    }
00505 
00506       // Set the Position given geocentric coordinates, system is set to Geocentric
00507       // @param lat geocentric latitude in degrees North
00508       // @param lon geocentric longitude in degrees East
00509       // @param rad radius from the Earth's center in meters
00510       // @return a reference to this object.
00511       // @throw GeometryException on invalid input
00512    Position& Position::setGeocentric(const double lat,
00513                                      const double lon,
00514                                      const double rad)
00515       throw(GeometryException)
00516    {
00517       if(lat > 90 || lat < -90)
00518       {
00519          GeometryException ge("Invalid latitude in setGeocentric: "
00520                                  + StringUtils::asString(lat));
00521          GPSTK_THROW(ge);
00522       }
00523       if(rad < 0)
00524       {
00525          GeometryException ge("Invalid radius in setGeocentric: "
00526                                           + StringUtils::asString(rad));
00527          GPSTK_THROW(ge);
00528       }
00529       theArray[0] = lat;
00530       theArray[1] = lon;
00531       theArray[2] = rad;
00532 
00533       if(theArray[1] < 0)
00534          theArray[1] += 360*(1+(unsigned long)(theArray[1]/360));
00535       else if(theArray[1] >= 360)
00536          theArray[1] -= 360*(unsigned long)(theArray[1]/360);
00537       system = Geocentric;
00538 
00539       return *this;
00540    }
00541 
00542       // Set the Position given spherical coordinates, system is set to Spherical
00543       // @param theta angle from the Z-axis (degrees)
00544       // @param phi angle from the X-axis in the XY plane (degrees)
00545       // @param rad radius from the center in meters
00546       // @return a reference to this object.
00547       // @throw GeometryException on invalid input
00548    Position& Position::setSpherical(const double theta,
00549                                     const double phi,
00550                                     const double rad)
00551       throw(GeometryException)
00552    {
00553       if(theta < 0 || theta > 180)
00554       {
00555          GeometryException ge("Invalid theta in setSpherical: "
00556                                  + StringUtils::asString(theta));
00557          GPSTK_THROW(ge);
00558       }
00559       if(rad < 0)
00560       {
00561          GeometryException ge("Invalid radius in setSpherical: "
00562                                           + StringUtils::asString(rad));
00563          GPSTK_THROW(ge);
00564       }
00565 
00566       theArray[0] = theta;
00567       theArray[1] = phi;
00568       theArray[2] = rad;
00569 
00570       if(theArray[1] < 0)
00571          theArray[1] += 360*(1+(unsigned long)(theArray[1]/360));
00572       else if(theArray[1] >= 360)
00573          theArray[1] -= 360*(unsigned long)(theArray[1]/360);
00574       system = Spherical;
00575 
00576       return *this;
00577    }
00578 
00579       // Set the Position given ECEF coordinates, system is set to Cartesian.
00580       // @param X ECEF X coordinate in meters.
00581       // @param Y ECEF Y coordinate in meters.
00582       // @param Z ECEF Z coordinate in meters.
00583       // @return a reference to this object.
00584    Position& Position::setECEF(const double X,
00585                                const double Y,
00586                                const double Z)
00587       throw()
00588    {
00589       theArray[0] = X;
00590       theArray[1] = Y;
00591       theArray[2] = Z;
00592       system = Cartesian;
00593       return *this;
00594    }
00595 
00596    // ----------- Part 9: member functions: setToString, printf -------------
00597    //
00598       // setToString, similar to scanf, this function takes a string and a
00599       // format describing string in order to define Position
00600       // values.  The parameters it can take are listed below and
00601       // described above with the printf() function.
00602       //
00603       // The specification must be sufficient to define a Position.
00604       // The following table lists combinations that give valid
00605       // Positions. Anything more or other combinations will give
00606       // unknown (read as: "bad") results so don't try it.  Anything
00607       // less will throw an exception.
00608       //
00609       // @code
00610       //  %X %Y %Z  (cartesian or ECEF)
00611       //  %x %y %z  (cartesian or ECEF)
00612       //  %a %l %r  (geocentric)
00613       //  %A %L %h  (geodetic)
00614       //  %t %p %r  (spherical)
00615       // @endcode
00616       //
00617       // So
00618       // @code
00619       // pos.setToString("123.4342,9328.1982,-128987.399", "%X,%Y,%Z");
00620       // @endcode
00621       //
00622       // works but
00623       //
00624       // @code
00625       // pos.setToString("123.4342,9328.1982", "%X,%Y");
00626       // @endcode
00627       // doesn't work (incomplete specification because it doesn't specify
00628       // a Position).
00629       //
00630       // Whitespace is unimportant here, the function will handle it.
00631       // The caller must ensure that that the extra characters in
00632       // the format string (ie '.' ',') are in the same relative
00633       // location as they are in the actual string, see the example above.
00634       //
00635       // @param str string from which to get the Position coordinates
00636       // @param fmt format to use to parse \c str.
00637       // @throw GeometryException if \c fmt is an incomplete or invalid specification
00638       // @throw FormatException if unable to scan \c str.
00639       // @throw StringException if an error occurs manipulating the
00640       // \c str or \c fmt strings.
00641       // @return a reference to this object.
00642    Position& Position::setToString(const std::string& str,
00643                                    const std::string& fmt)
00644       throw(GeometryException,
00645             DayTime::FormatException,
00646             StringUtils::StringException)
00647    {
00648       try {
00649             // make an object to return (so we don't fiddle with *this
00650             // until it's necessary)
00651          Position toReturn;
00652 
00653             // flags indicated these defined
00654          bool hX=false, hY=false, hZ=false;
00655          bool hglat=false, hlon=false, hht=false;
00656          bool hclat=false, hrad=false;
00657          bool htheta=false, hphi=false;
00658             // store input values
00659          double x,y,z,glat,lon,ht,clat,rad,theta,phi;
00660             // copy format and input string to parse
00661          string f = fmt;
00662          string s = str;
00663          stripLeading(s);
00664          stripTrailing(s);
00665 
00666             // parse strings...  As we process each part, it's removed from both
00667             // strings so when we reach 0, we're done
00668          while ( (s.size() > 0) && (f.size() > 0) )
00669          {
00670                // remove everything in f and s up to the first % in f
00671                // (these parts of the strings must be identical or this will break
00672                // after it tries to remove it!)
00673             while ( (s.length() != 0) && (f.length() != 0) && (f[0] != '%') )
00674             {
00675                   // remove that character now and other whitespace
00676                s.erase(0,1);
00677                f.erase(0,1);
00678                stripLeading(s);
00679                stripLeading(f);
00680             }
00681 
00682                // check just in case we hit the end of either string...
00683             if ( (s.length() == 0) || (f.length() == 0) )
00684                break;
00685 
00686                // lose the '%' in f...
00687             f.erase(0,1);
00688 
00689                // if the format string is like %03f, get '3' as the field
00690                // length.
00691             string::size_type fieldLength = string::npos;
00692 
00693             if (!isalpha(f[0]))
00694             {
00695                fieldLength = asInt(f);
00696 
00697                   // remove everything else up to the next character
00698                   // (in "%03f", that would be 'f')
00699                while ((!f.empty()) && (!isalpha(f[0])))
00700                   f.erase(0,1);
00701                if (f.empty())
00702                   break;
00703             }
00704 
00705                // finally, get the character that should end this field, if any
00706             char delimiter = 0;
00707             if (f.size() > 1)
00708             {
00709                if (f[1] != '%')
00710                {
00711                   delimiter = f[1];
00712 
00713                   if (fieldLength == string::npos)
00714                      fieldLength = s.find(delimiter,0);
00715                }
00716                   // if the there is no delimiter character and the next field
00717                   // is another part of the time to parse, assume the length
00718                   // of this field is 1
00719                else if (fieldLength == string::npos)
00720                {
00721                   fieldLength = 1;
00722                }
00723             }
00724 
00725                // figure out the next string to be removed.  if there is a field
00726                // length, use that first
00727             string toBeRemoved = s.substr(0, fieldLength);
00728 
00729                // based on char at f[0], we know what to do...
00730             switch (f[0])
00731             {
00732           //%x   X() (meters)
00733           //%y   Y() (meters)
00734           //%z   Z() (meters)
00735           //%X   X()/1000 (kilometers)
00736           //%Y   Y()/1000 (kilometers)
00737           //%Z   Z()/1000 (kilometers)
00738                case 'X':
00739                   x = asDouble(toBeRemoved) * 1000;
00740                   hX = true;
00741                   break;
00742                case 'x':
00743                   x = asDouble(toBeRemoved);
00744                   hX = true;
00745                   break;
00746                case 'Y':
00747                   y = asDouble(toBeRemoved) * 1000;
00748                   hY = true;
00749                   break;
00750                case 'y':
00751                   y = asDouble(toBeRemoved);
00752                   hY = true;
00753                   break;
00754                case 'Z':
00755                   z = asDouble(toBeRemoved) * 1000;
00756                   hZ = true;
00757                   break;
00758                case 'z':
00759                   z = asDouble(toBeRemoved);
00760                   hZ = true;
00761                   break;
00762           //%A   geodeticLatitude() (degrees North)
00763           //%a   geocentricLatitude() (degrees North)
00764                case 'A':
00765                   glat = asDouble(toBeRemoved);
00766                   if(glat > 90. || glat < -90.) {
00767                      DayTime::FormatException f(
00768                            "Invalid geodetic latitude for setTostring: "
00769                            + toBeRemoved);
00770                      GPSTK_THROW(f);
00771                   }
00772                   hglat = true;
00773                   break;
00774                case 'a':
00775                   clat = asDouble(toBeRemoved);
00776                   if(clat > 90. || clat < -90.) {
00777                      DayTime::FormatException f(
00778                            "Invalid geocentric latitude for setTostring: "
00779                            + toBeRemoved);
00780                      GPSTK_THROW(f);
00781                   }
00782                   hclat = true;
00783                   break;
00784           //%L   longitude() (degrees East)
00785           //%l   longitude() (degrees East)
00786           //%w   longitude() (degrees West)
00787           //%W   longitude() (degrees West)
00788                case 'L':
00789                case 'l':
00790                   lon = asDouble(toBeRemoved);
00791                   if(lon < 0)
00792                      lon += 360*(1+(unsigned long)(lon/360));
00793                   else if(lon >= 360)
00794                      lon -= 360*(unsigned long)(lon/360);
00795                   hlon = true;
00796                   break;
00797                case 'w':
00798                case 'W':
00799                   lon = 360.0 - asDouble(toBeRemoved);
00800                   if(lon < 0)
00801                      lon += 360*(1+(unsigned long)(lon/360));
00802                   else if(lon >= 360)
00803                      lon -= 360*(unsigned long)(lon/360);
00804                   hlon = true;
00805                   break;
00806           //%t   theta() (degrees)
00807           //%T   theta() (radians)
00808                case 't':
00809                   theta = asDouble(toBeRemoved);
00810                   if(theta > 180. || theta < 0.) {
00811                      DayTime::FormatException f("Invalid theta for setTostring: "
00812                                                 + toBeRemoved);
00813                      GPSTK_THROW(f);
00814                   }
00815                   htheta = true;
00816                   break;
00817                case 'T':
00818                   theta = asDouble(toBeRemoved) * RAD_TO_DEG;
00819                   if(theta > 90. || theta < -90.) {
00820                      DayTime::FormatException f("Invalid theta for setTostring: "
00821                                                 + toBeRemoved);
00822                      GPSTK_THROW(f);
00823                   }
00824                   htheta = true;
00825                   break;
00826           //%p   phi() (degrees)
00827           //%P   phi() (radians)
00828                case 'p':
00829                   phi = asDouble(toBeRemoved);
00830                   if(phi < 0)
00831                      phi += 360*(1+(unsigned long)(phi/360));
00832                   else if(phi >= 360)
00833                      phi -= 360*(unsigned long)(phi/360);
00834                   hphi = true;
00835                   break;
00836                case 'P':
00837                   phi = asDouble(toBeRemoved) * RAD_TO_DEG;
00838                   if(phi < 0)
00839                      phi += 360*(1+(unsigned long)(phi/360));
00840                   else if(phi >= 360)
00841                      phi -= 360*(unsigned long)(phi/360);
00842                   hphi = true;
00843                   break;
00844           //%r   radius() meters
00845           //%R   radius()/1000 kilometers
00846           //%h   height() meters
00847           //%H   height()/1000 kilometers
00848                case 'r':
00849                   rad = asDouble(toBeRemoved);
00850                   if(rad < 0.0) {
00851                      DayTime::FormatException f("Invalid radius for setTostring: "
00852                                                 + toBeRemoved);
00853                      GPSTK_THROW(f);
00854                   }
00855                   hrad = true;
00856                   break;
00857                case 'R':
00858                   rad = asDouble(toBeRemoved) * 1000;
00859                   if(rad < 0.0) {
00860                      DayTime::FormatException f("Invalid radius for setTostring: "
00861                                                 + toBeRemoved);
00862                      GPSTK_THROW(f);
00863                   }
00864                   hrad = true;
00865                   break;
00866                case 'h':
00867                   ht = asDouble(toBeRemoved);
00868                   hht = true;
00869                   break;
00870                case 'H':
00871                   ht = asDouble(toBeRemoved) * 1000;
00872                   hht = true;
00873                   break;
00874                default: // do nothing
00875                   break;
00876             }
00877                // remove the part of s that we processed
00878             stripLeading(s,toBeRemoved,1);
00879 
00880                // remove the character we processed from f
00881             f.erase(0,1);
00882 
00883                // check for whitespace again...
00884             stripLeading(f);
00885             stripLeading(s);
00886 
00887          }
00888 
00889          if ( s.length() != 0  )
00890          {
00891                // throw an error - something didn't get processed in the strings
00892             DayTime::FormatException fe(
00893                "Processing error - parts of strings left unread - " + s);
00894             GPSTK_THROW(fe);
00895          }
00896 
00897          if (f.length() != 0)
00898          {
00899                // throw an error - something didn't get processed in the strings
00900             DayTime::FormatException fe(
00901                "Processing error - parts of strings left unread - " + f);
00902             GPSTK_THROW(fe);
00903          }
00904 
00905             // throw if the specification is incomplete
00906          if ( !(hX && hY && hZ) && !(hglat && hlon && hht) &&
00907               !(hclat && hlon && hrad) && !(htheta && hphi && hrad)) {
00908             DayTime::FormatException fe("Incomplete specification for setToString");
00909             GPSTK_THROW(fe);
00910          }
00911 
00912             // define the Position toReturn
00913          if(hX && hY && hZ)
00914             toReturn.setECEF(x,y,z);
00915          else if(hglat && hlon && hht)
00916             toReturn.setGeodetic(glat,lon,ht);
00917          else if(hclat && hlon && hrad)
00918             toReturn.setGeocentric(clat,lon,rad);
00919          else if(htheta && hphi && hrad)
00920             toReturn.setSpherical(theta,phi,rad);
00921 
00922          *this = toReturn;
00923          return *this;
00924       }
00925       catch(gpstk::Exception& exc)
00926       {
00927          GeometryException ge(exc);
00928          ge.addText("Failed to convert string to Position");
00929          GPSTK_THROW(ge);
00930       }
00931       catch(std::exception& exc)
00932       {
00933          GeometryException ge(exc.what());
00934          ge.addText("Failed to convert string to Position");
00935          GPSTK_THROW(ge);
00936       }
00937    }
00938 
00939       // Format this Position into a string.
00940       //
00941       // Generate and return a string containing formatted
00942       // Position coordinates, formatted by the specification \c fmt.
00943       //
00944       // \li \%x   X() (meters)
00945       // \li \%y   Y() (meters)
00946       // \li \%z   Z() (meters)
00947       // \li \%X   X()/1000 (kilometers)
00948       // \li \%Y   Y()/1000 (kilometers)
00949       // \li \%Z   Z()/1000 (kilometers)
00950       // \li \%A   geodeticLatitude() (degrees North)
00951       // \li \%a   geocentricLatitude() (degrees North)
00952       // \li \%L   longitude() (degrees East)
00953       // \li \%l   longitude() (degrees East)
00954       // \li \%w   longitude() (degrees West)
00955       // \li \%W   longitude() (degrees West)
00956       // \li \%t   theta() (degrees)
00957       // \li \%T   theta() (radians)
00958       // \li \%p   phi() (degrees)
00959       // \li \%P   phi() (radians)
00960       // \li \%r   radius() meters
00961       // \li \%R   radius()/1000 kilometers
00962       // \li \%h   height() meters
00963       // \li \%H   height()/1000 kilometers
00964       //
00965       // @param fmt format to use for this time.
00966       // @return a string containing this Position in the
00967       // representation specified by \c fmt.
00968    std::string Position::printf(const char *fmt) const
00969       throw(StringUtils::StringException)
00970    {
00971       string rv = fmt;
00972       rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?x"),
00973                           string("xf"), X());
00974       rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?y"),
00975                           string("yf"), Y());
00976       rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?z"),
00977                           string("zf"), Z());
00978       rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?X"),
00979                           string("Xf"), X()/1000);
00980       rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?Y"),
00981                           string("Yf"), Y()/1000);
00982       rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?Z"),
00983                           string("Zf"), Z()/1000);
00984 
00985       rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?A"),
00986                           string("Af"), geodeticLatitude());
00987       rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?a"),
00988                           string("af"), geocentricLatitude());
00989       rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?L"),
00990                           string("Lf"), longitude());
00991       rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?l"),
00992                           string("lf"), longitude());
00993       rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?w"),
00994                           string("wf"), 360-longitude());
00995       rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?W"),
00996                           string("Wf"), 360-longitude());
00997 
00998       rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?t"),
00999                           string("tf"), theta());
01000       rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?T"),
01001                           string("Tf"), theta()*DEG_TO_RAD);
01002       rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?p"),
01003                           string("pf"), phi());
01004       rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?P"),
01005                           string("Pf"), phi()*DEG_TO_RAD);
01006       rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?r"),
01007                           string("rf"), radius());
01008       rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?R"),
01009                           string("Rf"), radius()/1000);
01010       rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?h"),
01011                           string("hf"), height());
01012       rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?H"),
01013                           string("Hf"), height()/1000);
01014       return rv;
01015    }
01016 
01017       // Returns the string that operator<<() would print.
01018    string Position::asString() const
01019       throw(StringUtils::StringException)
01020    {
01021       ostringstream o;
01022       o << *this;
01023       return o.str();
01024    }
01025 
01026    // ----------- Part 10: functions: fundamental conversions ---------------
01027    //
01028       // Fundamental conversion from spherical to cartesian coordinates.
01029       // @param trp (input): theta, phi, radius
01030       // @param xyz (output): X,Y,Z in units of radius
01031       // Algorithm references: standard geometry.
01032    void Position::convertSphericalToCartesian(const Triple& tpr,
01033                                               Triple& xyz)
01034       throw()
01035    {
01036       double st=::sin(tpr[0]*DEG_TO_RAD);
01037       xyz[0] = tpr[2]*st*::cos(tpr[1]*DEG_TO_RAD);
01038       xyz[1] = tpr[2]*st*::sin(tpr[1]*DEG_TO_RAD);
01039       xyz[2] = tpr[2]*::cos(tpr[0]*DEG_TO_RAD);
01040    }
01041 
01042       // Fundamental routine to convert cartesian to spherical coordinates.
01043       // @param xyz (input): X,Y,Z
01044       // @param trp (output): theta, phi (deg), radius in units of input
01045       // Algorithm references: standard geometry.
01046    void Position::convertCartesianToSpherical(const Triple& xyz,
01047                                               Triple& tpr)
01048       throw()
01049    {
01050       tpr[2] = RSS(xyz[0],xyz[1],xyz[2]);
01051       if(tpr[2] <= Position::POSITION_TOLERANCE/5) { // zero-length Cartesian vector
01052          tpr[0] = 90;
01053          tpr[1] = 0;
01054          return;
01055       }
01056       tpr[0] = ::acos(xyz[2]/tpr[2]);
01057       tpr[0] *= RAD_TO_DEG;
01058       if(RSS(xyz[0],xyz[1]) < Position::POSITION_TOLERANCE/5) {       // pole
01059          tpr[1] = 0;
01060          return;
01061       }
01062       tpr[1] = ::atan2(xyz[1],xyz[0]);
01063       tpr[1] *= RAD_TO_DEG;
01064       if(tpr[1] < 0) tpr[1] += 360;
01065    }
01066 
01067       // Fundamental routine to convert cartesian (ECEF) to geodetic coordinates,
01068       // (Geoid specified by semi-major axis and eccentricity squared).
01069       // @param xyz (input): X,Y,Z in meters
01070       // @param llh (output): geodetic lat(deg N), lon(deg E),
01071       //                             height above ellipsoid (meters)
01072       // @param A (input) Earth semi-major axis
01073       // @param eccSq (input) square of Earth eccentricity
01074       // Algorithm references:
01075    void Position::convertCartesianToGeodetic(const Triple& xyz,
01076                                              Triple& llh,
01077                                              const double A,
01078                                              const double eccSq)
01079       throw()
01080    {
01081       double p,slat,N,htold,latold;
01082       p = SQRT(xyz[0]*xyz[0]+xyz[1]*xyz[1]);
01083       if(p < Position::POSITION_TOLERANCE/5) {  // pole or origin
01084          llh[0] = (xyz[2] > 0 ? 90.0: -90.0);
01085          llh[1] = 0;                            // lon undefined, really
01086          llh[2] = ::fabs(xyz[2]) - A*SQRT(1.0-eccSq);
01087          return;
01088       }
01089       llh[0] = ::atan2(xyz[2], p*(1.0-eccSq));
01090       llh[2] = 0;
01091       for(int i=0; i<5; i++) {
01092          slat = ::sin(llh[0]);
01093          N = A / SQRT(1.0 - eccSq*slat*slat);
01094          htold = llh[2];
01095          llh[2] = p/::cos(llh[0]) - N;
01096          latold = llh[0];
01097          llh[0] = ::atan2(xyz[2], p*(1.0-eccSq*(N/(N+llh[2]))));
01098          if(::fabs(llh[0]-latold) < 1.0e-9 && fabs(llh[2]-htold) < 1.0e-9 * A) break;
01099       }
01100       llh[1] = ::atan2(xyz[1],xyz[0]);
01101       if(llh[1] < 0.0) llh[1] += TWO_PI;
01102       llh[0] *= RAD_TO_DEG;
01103       llh[1] *= RAD_TO_DEG;
01104    }
01105 
01106       // Fundamental routine to convert geodetic to cartesian (ECEF) coordinates,
01107       // (Geoid specified by semi-major axis and eccentricity squared).
01108       // @param llh (input): geodetic lat(deg N), lon(deg E),
01109       //            height above ellipsoid (meters)
01110       // @param xyz (output): X,Y,Z in meters
01111       // @param A (input) Earth semi-major axis
01112       // @param eccSq (input) square of Earth eccentricity
01113       // Algorithm references:
01114    void Position::convertGeodeticToCartesian(const Triple& llh,
01115                                              Triple& xyz,
01116                                              const double A,
01117                                              const double eccSq)
01118       throw()
01119    {
01120       double slat = ::sin(llh[0]*DEG_TO_RAD);
01121       double clat = ::cos(llh[0]*DEG_TO_RAD);
01122       double N = A/SQRT(1.0-eccSq*slat*slat);
01123       xyz[0] = (N+llh[2])*clat*::cos(llh[1]*DEG_TO_RAD);
01124       xyz[1] = (N+llh[2])*clat*::sin(llh[1]*DEG_TO_RAD);
01125       xyz[2] = (N*(1.0-eccSq)+llh[2])*slat;
01126    }
01127 
01128       // Fundamental routine to convert cartesian (ECEF) to geocentric coordinates.
01129       // @param xyz (input): X,Y,Z in meters
01130       // @param llr (output):
01131       //            geocentric lat(deg N),lon(deg E),radius (units of input)
01132    void Position::convertCartesianToGeocentric(const Triple& xyz,
01133                                                Triple& llr)
01134       throw()
01135    {
01136       convertCartesianToSpherical(xyz, llr);
01137       llr[0] = 90 - llr[0];         // convert theta to latitude
01138    }
01139 
01140       // Fundamental routine to convert geocentric to cartesian (ECEF) coordinates.
01141       // @param llr (input): geocentric lat(deg N),lon(deg E),radius
01142       // @param xyz (output): X,Y,Z (units of radius)
01143    void Position::convertGeocentricToCartesian(const Triple& llr,
01144                                                Triple& xyz)
01145       throw()
01146    {
01147       Triple llh(llr);
01148       llh[0] = 90 - llh[0];         // convert latitude to theta
01149       convertSphericalToCartesian(llh, xyz);
01150    }
01151 
01152       // Fundamental routine to convert geocentric to geodetic coordinates.
01153       // @param llr (input): geocentric Triple: lat(deg N),lon(deg E),radius (meters)
01154       // @param llh (output): geodetic latitude (deg N),
01155       //            longitude (deg E), and height above ellipsoid (meters)
01156       // @param A (input) Earth semi-major axis
01157       // @param eccSq (input) square of Earth eccentricity
01158    void Position::convertGeocentricToGeodetic(const Triple& llr,
01159                                                Triple& llh,
01160                                                const double A,
01161                                                const double eccSq)
01162       throw()
01163    {
01164       double cl,p,sl,slat,N,htold,latold;
01165       llh[1] = llr[1];     // longitude is unchanged
01166       cl = ::sin((90-llr[0])*DEG_TO_RAD);
01167       sl = ::cos((90-llr[0])*DEG_TO_RAD);
01168       if(llr[2] <= Position::POSITION_TOLERANCE/5) {
01169          // radius is below tolerance, hence assign zero-length
01170          // arbitrarily set latitude = longitude = 0
01171          llh[0] = llh[1] = 0;
01172          llh[2] = -A;
01173          return;
01174       }
01175       else if(cl < 1.e-10) {
01176          // near pole ... note that 1mm/radius(Earth) = 1.5e-10
01177          if(llr[0] < 0) llh[0] = -90;
01178          else           llh[0] =  90;
01179          llh[1] = 0;
01180          llh[2] = llr[2] - A*SQRT(1-eccSq);
01181          return;
01182       }
01183       llh[0] = ::atan2(sl, cl*(1.0-eccSq));
01184       p = cl*llr[2];
01185       llh[2] = 0;
01186       for(int i=0; i<5; i++) {
01187          slat = ::sin(llh[0]);
01188          N = A / SQRT(1.0 - eccSq*slat*slat);
01189          htold = llh[2];
01190          llh[2] = p/::cos(llh[0]) - N;
01191          latold = llh[0];
01192          llh[0] = ::atan2(sl, cl*(1.0-eccSq*(N/(N+llh[2]))));
01193          if(fabs(llh[0]-latold) < 1.0e-9 && ::fabs(llh[2]-htold) < 1.0e-9 * A) break;
01194       }
01195       llh[0] *= RAD_TO_DEG;
01196    }
01197 
01198       // Fundamental routine to convert geodetic to geocentric coordinates.
01199       // @param geodeticllh (input): geodetic latitude (deg N),
01200       //            longitude (deg E), and height above ellipsoid (meters)
01201       // @param llr (output): geocentric lat (deg N),lon (deg E),radius (meters)
01202       // @param A (input) Earth semi-major axis
01203       // @param eccSq (input) square of Earth eccentricity
01204    void Position::convertGeodeticToGeocentric(const Triple& llh,
01205                                               Triple& llr,
01206                                               const double A,
01207                                               const double eccSq)
01208       throw()
01209    {
01210       double slat = ::sin(llh[0]*DEG_TO_RAD);
01211       double N = A/SQRT(1.0-eccSq*slat*slat);
01212       // longitude is unchanged
01213       llr[1] = llh[1];
01214       // radius
01215       llr[2] = SQRT((N+llh[2])*(N+llh[2]) + N*eccSq*(N*eccSq-2*(N+llh[2]))*slat*slat);
01216       if(llr[2] <= Position::POSITION_TOLERANCE/5) {
01217          // radius is below tolerance, hence assign zero-length
01218          // arbitrarily set latitude = longitude = 0
01219          llr[0] = llr[1] = llr[2] = 0;
01220          return;
01221       }
01222       if(1-::fabs(slat) < 1.e-10) {             // at the pole
01223          if(slat < 0) llr[0] = -90;
01224          else         llr[0] =  90;
01225          llr[1] = 0.0;
01226          return;
01227       }
01228       // theta
01229       llr[0] = ::acos((N*(1-eccSq)+llh[2])*slat/llr[2]);
01230       llr[0] *= RAD_TO_DEG;
01231       llr[0] = 90 - llr[0];
01232    }
01233 
01234    // ----------- Part 11: operator<< and other useful functions -------------
01235    //
01236      // Stream output for Position objects.
01237      // @param s stream to append formatted Position to.
01238      // @param t Position to append to stream \c s.
01239      // @return reference to \c s.
01240    ostream& operator<<(ostream& s, const Position& p)
01241    {
01242       if(p.system == Position::Cartesian)
01243          s << p.printf("%.4x m %.4y m %.4z m");
01244       else if(p.system == Position::Geodetic)
01245          s << p.printf("%.8A degN %.8L degE %.4h m");
01246       else if(p.system == Position::Geocentric)
01247          s << p.printf("%.8a degN %.8L degE %.4r m");
01248       else if(p.system == Position::Spherical)
01249          s << p.printf("%.8t deg %.8p deg %.4r m");
01250       else
01251          s << " Unknown system! : " << p[0] << " " << p[1] << " " << p[2];
01252 
01253       return s;
01254    }
01255 
01256       // Compute the range in meters between this Position and
01257       // the Position passed as input.
01258       // @param right Position to which to find the range
01259       // @return the range (in meters)
01260       // @throw GeometryException if geoid values differ
01261    double range(const Position& A,
01262                 const Position& B)
01263       throw(GeometryException)
01264    {
01265       if(A.AEarth != B.AEarth || A.eccSquared != B.eccSquared)
01266       {
01267          GeometryException ge("Unequal geoids");
01268          GPSTK_THROW(ge);
01269       }
01270 
01271          Position L(A),R(B);
01272          L.transformTo(Position::Cartesian);
01273          R.transformTo(Position::Cartesian);
01274          double dif = RSS(L.X()-R.X(),L.Y()-R.Y(),L.Z()-R.Z());
01275          return dif;
01276       }
01277 
01278      // Compute the radius of the ellipsoidal Earth, given the geodetic latitude.
01279      // @param geolat geodetic latitude in degrees
01280      // @return the Earth radius (in meters)
01281    double Position::radiusEarth(const double geolat,
01282                                 const double A,
01283                                 const double eccSq)
01284       throw()
01285    {
01286       double slat=::sin(DEG_TO_RAD*geolat);
01287       double e=(1.0-eccSq);
01288       double f=(1.0+(e*e-1.0)*slat*slat)/(1.0-eccSq*slat*slat);
01289       return (A * SQRT(f));
01290    }
01291 
01292       // A member function that computes the elevation of the input
01293       // (Target) position as seen from this Position.
01294       // @param Target the Position which is observed to have the
01295       //        computed elevation, as seen from this Position.
01296       // @return the elevation in degrees
01297    double Position::elevation(const Position& Target) const
01298       throw(GeometryException)
01299    {
01300       Position R(*this),S(Target);
01301       R.transformTo(Cartesian);
01302       S.transformTo(Cartesian);
01303       // use Triple:: functions in cartesian coordinates (only)
01304       double elevation;
01305       try {
01306          elevation = R.elvAngle(S);
01307       }
01308       catch(GeometryException& ge)
01309       {
01310          GPSTK_RETHROW(ge);
01311       }
01312       return elevation;
01313    }
01314 
01315       // A member function that computes the elevation of the input
01316       // (Target) position as seen from this Position, using a Geodetic
01317       // (i.e. ellipsoidal) system.
01318       // @param Target the Position which is observed to have the
01319       //        computed elevation, as seen from this Position.
01320       // @return the elevation in degrees
01321    double Position::elevationGeodetic(const Position& Target) const
01322       throw(GeometryException)
01323    {
01324       Position R(*this),S(Target);
01325       double latGeodetic = R.getGeodeticLatitude()*DEG_TO_RAD;
01326       double longGeodetic = R.getLongitude()*DEG_TO_RAD;
01327       double localUp;
01328       double cosUp;
01329       R.transformTo(Cartesian);
01330       S.transformTo(Cartesian);
01331       Triple z;
01332       // Let's get the slant vector
01333       z = S.theArray - R.theArray;
01334 
01335       if (z.mag()<=1e-4) // if the positions are within .1 millimeter
01336       {
01337          GeometryException ge("Positions are within .1 millimeter");
01338          GPSTK_THROW(ge);
01339       }
01340 
01341       // Compute k vector in local North-East-Up (NEU) system
01342       Triple kVector(::cos(latGeodetic)*::cos(longGeodetic), ::cos(latGeodetic)*::sin(longGeodetic), ::sin(latGeodetic));
01343       // Take advantage of dot method to get Up coordinate in local NEU system
01344       localUp = z.dot(kVector);
01345       // Let's get cos(z), being z the angle with respect to local vertical (Up);
01346       cosUp = localUp/z.mag();
01347 
01348       return 90.0 - ((::acos(cosUp))*RAD_TO_DEG);
01349    }
01350 
01351       // A member function that computes the azimuth of the input
01352       // (Target) position as seen from this Position.
01353       // @param Target the Position which is observed to have the
01354       //        computed azimuth, as seen from this Position.
01355       // @return the azimuth in degrees
01356    double Position::azimuth(const Position& Target) const
01357       throw(GeometryException)
01358    {
01359       Position R(*this),S(Target);
01360       R.transformTo(Cartesian);
01361       S.transformTo(Cartesian);
01362       // use Triple:: functions in cartesian coordinates (only)
01363       double az;
01364       try
01365       {
01366          az = R.azAngle(S);
01367 
01368       }
01369       catch(GeometryException& ge)
01370       {
01371          GPSTK_RETHROW(ge);
01372       }
01373 
01374       return az;
01375    }
01376 
01377       // A member function that computes the azimuth of the input
01378       // (Target) position as seen from this Position, using a Geodetic
01379       // (i.e. ellipsoidal) system.
01380       // @param Target the Position which is observed to have the
01381       //        computed azimuth, as seen from this Position.
01382       // @return the azimuth in degrees
01383    double Position::azimuthGeodetic(const Position& Target) const
01384       throw(GeometryException)
01385    {
01386       Position R(*this),S(Target);
01387       double latGeodetic = R.getGeodeticLatitude()*DEG_TO_RAD;
01388       double longGeodetic = R.getLongitude()*DEG_TO_RAD;
01389       double localN, localE;
01390       R.transformTo(Cartesian);
01391       S.transformTo(Cartesian);
01392       Triple z;
01393       // Let's get the slant vector
01394       z = S.theArray - R.theArray;
01395 
01396       if (z.mag()<=1e-4) // if the positions are within .1 millimeter
01397       {
01398          GeometryException ge("Positions are within .1 millimeter");
01399          GPSTK_THROW(ge);
01400       }
01401 
01402       // Compute i vector in local North-East-Up (NEU) system
01403       Triple iVector(-::sin(latGeodetic)*::cos(longGeodetic), -::sin(latGeodetic)*::sin(longGeodetic), ::cos(latGeodetic));
01404       // Compute j vector in local North-East-Up (NEU) system
01405       Triple jVector(-::sin(longGeodetic), ::cos(longGeodetic), 0);
01406 
01407       // Now, let's use dot product to get localN and localE unitary vectors
01408       localN = (z.dot(iVector))/z.mag();
01409       localE = (z.dot(jVector))/z.mag();
01410 
01411       // Let's test if computing azimuth has any sense
01412       double test = fabs(localN) + fabs(localE);
01413 
01414       // Warning: If elevation is very close to 90 degrees, we will return azimuth = 0.0
01415       if (test < 1.0e-16) return 0.0;
01416 
01417       double alpha = ((::atan2(localE, localN)) * RAD_TO_DEG);
01418       if (alpha < 0.0)
01419       {
01420          return alpha + 360.0;
01421       }
01422       else
01423       {
01424          return alpha;
01425       }
01426    }
01427 
01428      // A member function that computes the point at which a signal, which
01429      // is received at *this Position and there is observed at the input
01430      // azimuth and elevation, crosses a model ionosphere that is taken to
01431      // be a uniform thin shell at the input height. This algorithm is done
01432      // in geocentric coordinates.
01433      // A member function that computes the point at which a signal, which
01434      // is received at *this Position and there is observed at the input
01435      // azimuth and elevation, crosses a model ionosphere that is taken to
01436      // be a uniform thin shell at the input height. This algorithm is done
01437      // in geocentric coordinates.
01438      // @param elev elevation angle of the signal at reception, in degrees
01439      // @param azim azimuth angle of the signal at reception, in degrees
01440      // @param ionoht height of the ionosphere, in meters
01441      // @return Position IPP the position of the ionospheric pierce point,
01442      //     in the same coordinate system as *this; *this is not modified.
01443    Position Position::getIonosphericPiercePoint(const double elev,
01444                                                 const double azim,
01445                                                 const double ionoht) const
01446       throw()
01447    {
01448       Position Rx(*this);
01449 
01450       // convert to Geocentric
01451       Rx.transformTo(Geocentric);
01452 
01453       // compute the geographic pierce point
01454       Position IPP(Rx);                   // copy system and geoid
01455       double el = elev * DEG_TO_RAD;
01456       // p is the angle subtended at Earth center by Rx and the IPP
01457       double p = PI/2.0 - el - ::asin(AEarth*::cos(el)/(AEarth+ionoht));
01458       double lat = Rx.theArray[0] * DEG_TO_RAD;
01459       double az = azim * DEG_TO_RAD;
01460       IPP.theArray[0] = std::asin(std::sin(lat)*std::cos(p) + std::cos(lat)*std::sin(p)*std::cos(az));
01461       IPP.theArray[1] = Rx.theArray[1]*DEG_TO_RAD
01462          + ::asin(::sin(p)*::sin(az)/::cos(IPP.theArray[0]));
01463 
01464       IPP.theArray[0] *= RAD_TO_DEG;
01465       IPP.theArray[1] *= RAD_TO_DEG;
01466       IPP.theArray[2] = AEarth + ionoht;
01467 
01468       // transform back
01469       IPP.transformTo(system);
01470 
01471       return IPP;
01472    }
01473 
01474    // ----------- Part 12: private functions and member data -----------------
01475    //
01476       // Initialization function, used by the constructors.
01477       // @param a coordinate [ X(m), or latitude (degrees N) ]
01478       // @param b coordinate [ Y(m), or longitude (degrees E) ]
01479       // @param c coordinate [ Z, height above ellipsoid or radius, in m ]
01480       // @param s CoordinateSystem, defaults to Cartesian
01481       // @param geiod pointer to a GeoidModel, default NULL (WGS84)
01482       // @throw GeometryException on invalid input.
01483    void Position::initialize(const double a,
01484                   const double b,
01485                   const double c,
01486                   Position::CoordinateSystem s,
01487                   GeoidModel *geoid)
01488       throw(GeometryException)
01489    {
01490       double bb(b);
01491       if(s == Geodetic || s==Geocentric)
01492       {
01493          if(a > 90 || a < -90)
01494          {
01495             GeometryException ge("Invalid latitude in constructor: "
01496                                     + StringUtils::asString(a));
01497             GPSTK_THROW(ge);
01498          }
01499          if(bb < 0)
01500             bb += 360*(1+(unsigned long)(bb/360));
01501          else if(bb >= 360)
01502             bb -= 360*(unsigned long)(bb/360);
01503       }
01504       if(s==Geocentric || s==Spherical)
01505       {
01506          if(c < 0)
01507          {
01508             GeometryException ge("Invalid radius in constructor: "
01509                                            + StringUtils::asString(c));
01510             GPSTK_THROW(ge);
01511          }
01512       }
01513       if(s==Spherical)
01514       {
01515          if(a < 0 || a > 180)
01516          {
01517             GeometryException ge("Invalid theta in constructor: "
01518                                     + StringUtils::asString(a));
01519             GPSTK_THROW(ge);
01520          }
01521          if(bb < 0)
01522             bb += 360*(1+(unsigned long)(bb/360));
01523          else if(bb >= 360)
01524             bb -= 360*(unsigned long)(bb/360);
01525       }
01526 
01527       theArray[0] = a;
01528       theArray[1] = bb;
01529       theArray[2] = c;
01530 
01531       if(geoid) {
01532          AEarth = geoid->a();
01533          eccSquared = geoid->eccSquared();
01534       }
01535       else {
01536          WGS84Geoid WGS84;
01537          AEarth = WGS84.a();
01538          eccSquared = WGS84.eccSquared();
01539       }
01540       system = s;
01541       tolerance = POSITION_TOLERANCE;
01542    }
01543 
01544 }  // namespace gpstk

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