IonexData.cpp

Go to the documentation of this file.
00001 #pragma ident "$Id: IonexData.cpp 1802 2009-03-17 13:20:40Z coandrei $"
00002 
00008 //============================================================================
00009 //
00010 //  This file is part of GPSTk, the GPS Toolkit.
00011 //
00012 //  The GPSTk is free software; you can redistribute it and/or modify
00013 //  it under the terms of the GNU Lesser General Public License as published
00014 //  by the Free Software Foundation; either version 2.1 of the License, or
00015 //  any later version.
00016 //
00017 //  The GPSTk is distributed in the hope that it will be useful,
00018 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00019 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020 //  GNU Lesser General Public License for more details.
00021 //
00022 //  You should have received a copy of the GNU Lesser General Public
00023 //  License along with GPSTk; if not, write to the Free Software Foundation,
00024 //  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00025 //
00026 //  Octavian Andrei - FGI ( http://www.fgi.fi ). 2008, 2009
00027 //
00028 //============================================================================
00029 
00030 
00031 #include <cmath>
00032 
00033 #include "StringUtils.hpp"
00034 #include "IonexData.hpp"
00035 
00036 
00037 using namespace std;
00038 using namespace gpstk::StringUtils;
00039 
00040 
00041 namespace gpstk
00042 {
00043    const string IonexData::startTecMapString    =  "START OF TEC MAP";
00044    const string IonexData::startRmsMapString    =  "START OF RMS MAP";
00045    const string IonexData::startHgtMapString    =  "START OF HEIGHT MAP";
00046    const string IonexData::currentEpochString   =  "EPOCH OF CURRENT MAP";
00047    const string IonexData::dataBlockString      =  "LAT/LON1/LON2/DLON/H";
00048    const string IonexData::endTecMapString      =  "END OF TEC MAP";
00049    const string IonexData::endRmsMapString      =  "END OF RMS MAP";
00050    const string IonexData::endHgtMapString      =  "END OF HEIGHT MAP";
00051    const string IonexData::endOfFile            =  "END OF FILE";
00052 
00053    const IonexData::IonexValType IonexData::UN( "UN",
00054                                                 "Unknown or Invalid",
00055                                                 "unknown" );
00056 
00057    const IonexData::IonexValType IonexData::TEC( "TEC",
00058                                                  "Total Electron Content map",
00059                                                  "TECU" );
00060 
00061    const IonexData::IonexValType IonexData::RMS( "RMS",
00062                                                  "Root Mean Square error",
00063                                                  "TECU" );
00064 
00065 
00066    //--------------------------------------------------------------------
00067    //--------------------------------------------------------------------
00068    void IonexData::reallyPutRecord(FFStream& ffs) const
00069       throw( std::exception, FFStreamError, StringException )
00070    {
00071 
00072          // is there anything to write?
00073       if ( !valid || data.empty() )
00074          return;
00075 
00076       IonexStream& strm = dynamic_cast<IonexStream&>(ffs);
00077       string line;
00078 
00079 
00080          // write record opening TEC/RMS map
00081       line.clear();
00082       line += rightJustify( asString(mapID), 6 );
00083       line += string(54, ' ');
00084 
00085       if (type == IonexData::TEC)
00086       {
00087          line += leftJustify(startTecMapString,20);
00088       }
00089       else if (type == IonexData::RMS)
00090       {
00091          line += leftJustify(startRmsMapString,20);
00092       }
00093       else
00094       {
00095          FFStreamError err("This isn't a valid standard IONEX value type: " + 
00096             asString(type.description) );
00097          GPSTK_THROW(err);
00098       }
00099       strm << line << endl;
00100       strm.lineNumber++;
00101 
00102 
00103          // write epoch of current TEC/RMS map
00104       line.clear();
00105       line += writeTime(time);
00106       line += string(24, ' ');
00107       line += leftJustify(currentEpochString,20);
00108       strm << line << endl;
00109       strm.lineNumber++;
00110 
00111 
00112          // write TEC/RMS data sequence
00113       int nlat(dim[0]), nlon(dim[1]);
00114 
00115       for (int ilat = 0; ilat < nlat; ilat++)
00116       {
00117             // write record initializing a new TEC/RMS 
00118             // data block for latitude 'currLat'
00119          double currLat = lat[0] + ilat*lat[2];
00120 
00121          line.clear();
00122          line += string(2, ' ');
00123          line += rightJustify( asString(currLat,1), 6 );
00124          line += rightJustify( asString(lon[0],1), 6 );
00125          line += rightJustify( asString(lon[1],1), 6 );
00126          line += rightJustify( asString(lon[2],1), 6 );
00127          line += rightJustify( asString(hgt[0],1), 6 );
00128          line += string(28, ' ');
00129          line += leftJustify(dataBlockString,20);
00130          strm << line << endl;
00131          strm.lineNumber++;
00132 
00133 
00134             // write single TEC/RMS data block
00135          line.clear();
00136          for (int ilon = 0; ilon < nlon; ilon++)
00137          {
00138             int index = ilat*dim[1]+ilon;
00139 
00140             double val = (data[index] != 999.9) ?
00141                          std::pow(10.0,-exponent)*data[index] : 9999.0;
00142 
00143                // we need to put there an integer, i.e., the neareast integer
00144             int valint = (val > 0.0) ? 
00145                          static_cast<int>(val+0.5) : static_cast<int>(val-0.5);
00146             line += rightJustify( asString<short>(valint), 5 );
00147 
00148             if (line.size() == 80)  // maximum 16 values per record
00149             {
00150                strm << line << endl;
00151                strm.lineNumber++;
00152                line.clear();
00153             }
00154             else if (ilon == nlon-1)  // last longitude
00155             {
00156                line += string(80-line.size(), ' ');
00157                strm << line << endl;
00158                strm.lineNumber++;
00159                line.clear();
00160             }
00161 
00162          }  // End of 'for (int ilon = 0; ilon < nlon; ilon++)'
00163 
00164       }  // End of 'for (int ilat = 0; ilat < nlat; ilat++)'
00165 
00166 
00167          // write closing TEC/RMS map
00168       line.clear();
00169       line += rightJustify( asString(mapID), 6 );
00170       line += string(54, ' ');
00171 
00172       if (type == IonexData::TEC)
00173       {
00174          line += leftJustify(endTecMapString,20);
00175       }
00176       else if (type == IonexData::RMS)
00177       {
00178          line += leftJustify(endRmsMapString,20);
00179       }
00180       else
00181       {
00182          FFStreamError err("This isn't a valid standard IONEX value type: " + 
00183             asString(type.description) );
00184          GPSTK_THROW(err);
00185       }
00186       strm << line << endl;
00187       strm.lineNumber++;
00188 
00189    }  // End of method 'IonexData::reallyPutRecord()'
00190 
00191 
00192 
00204    void IonexData::reallyGetRecord(FFStream& ffs)
00205       throw( exception, FFStreamError, gpstk::StringUtils::StringException )
00206    {
00207 
00208       IonexStream& strm = dynamic_cast<IonexStream&>(ffs);
00209 
00210          // If the header has not been read, read it
00211       if(!strm.headerRead)
00212       {
00213          strm >> strm.header;
00214       }
00215 
00216          // Clear out this object
00217       IonexHeader& hdr = strm.header;
00218 
00219          // Let's be sure that nothing is set yet
00220       IonexData iod;
00221       *this = iod;
00222 
00223          // let's get some useful values from the header
00224       exponent = hdr.exponent;
00225       for (int i = 0; i < 3; i++)
00226       {
00227          lat[i] = hdr.lat[i];
00228          lon[i] = hdr.lon[i];
00229          hgt[i] = hdr.hgt[i];
00230       }
00231 
00232 
00233       string line;
00234 
00235          // some initializations before looping
00236 
00237          // ityp may be -1(initialize), 0(unknown), 1(TEC), 2(RMS), 3(HGT)
00238       int ityp(-1);
00239 
00240          // index for latitude
00241       int ilat(0);
00242 
00243       while (ityp != 0)
00244       {
00245 
00246          strm.formattedGetLine(line,true);
00247          StringUtils::stripTrailing(line);
00248 
00249          if (line.size() > 80)
00250          {
00251             FFStreamError e("Bad epoch line");
00252             GPSTK_THROW(e);
00253          }
00254 
00255             // skip empty lines
00256          if (line.length() == 0)
00257          {
00258             continue;
00259          }
00260 
00261          string label(line, 60, 20);
00262 
00263          if (label == startTecMapString)
00264          {
00265 
00266             type = IonexData::TEC;
00267             ityp = 1;
00268             mapID = asInt(line.substr(0,6));
00269             ilat = 0;
00270 
00271          }
00272          else if (label == startRmsMapString)
00273          {
00274 
00275             type = IonexData::RMS;
00276             ityp = 2;
00277             mapID = asInt(line.substr(0,6));
00278             ilat = 0;
00279 
00280          }
00281          else if (label == startHgtMapString)
00282          {
00283 
00284             ityp = 3;
00285             mapID = asInt(line.substr(0,6));
00286             ilat = 0;
00287 
00288          }
00289          else if (label == currentEpochString)
00290          {
00291 
00292             time = parseTime(line);
00293 
00294                // Get grid dimensions to know how much memory has to be
00295                // allocated for 'data' member of the object, and then
00296                // initialize all elements of the object member with dummy
00297                // values, i.e., 999.9
00298             dim[0] = static_cast<int>( ( hdr.lat[1] - hdr.lat[0] )
00299                             / hdr.lat[2] + 1 ); // consider Equator as well
00300 
00301             dim[1] = static_cast<int>( ( hdr.lon[1] - hdr.lon[0] )
00302                             / hdr.lon[2] + 1 ); // consider Greenwich meridian
00303 
00304             dim[2] = (hdr.hgt[2] == 0) ?
00305                      1 : static_cast<int>( (hdr.hgt[1]-hdr.hgt[0]) 
00306                                            / hdr.hgt[2]+1 );
00307 
00308             data.resize( dim[0]*dim[1]*dim[2], 999.9 );
00309 
00310          }
00311          else if (label == dataBlockString)
00312          {
00313 
00314             if (ityp == 0)
00315             {
00316                FFStreamError e(string("Map type undefined: " + line) );
00317                GPSTK_THROW(e);
00318             }
00319 
00320             double lat0,lon1,lon2,dlon,hgt;
00321 
00322             lat0 = asDouble(line.substr( 2,6));
00323             lon1 = asDouble(line.substr( 8,6));
00324             lon2 = asDouble(line.substr(14,6));
00325             dlon = asDouble(line.substr(20,6));
00326             hgt  = asDouble(line.substr(26,6));
00327 
00328                //read single data block
00329             for (int ival = 0,line_ndx = 0; ival < dim[1]; ival++, line_ndx++)
00330             {
00331 
00332                   // only 16 values per line - same as (line_ndx % 16 == 0)
00333                if (!(line_ndx % 16))
00334                {
00335 
00336                      // Get new line
00337                   strm.formattedGetLine(line);
00338 
00339                      // Set to zero again. There are only 16 values per line
00340                   line_ndx = 0;
00341 
00342                   if (line.size() > 80)
00343                   {
00344 
00345                     FFStreamError e("Error reading IONEX data. Bad epoch line");
00346                     GPSTK_THROW(e);
00347 
00348                   }
00349 
00350                      // skip empty lines if any
00351                   if (line.size() == 0)
00352                   {
00353                      continue;
00354                   }
00355 
00356                }  // End of 'if (!(line_ndx % 16))...'
00357 
00358 
00359                   // the 5th line doesn't have 80 characters, thus fill it
00360                line.resize(80, ' ');
00361 
00362                   // extract value
00363                int val = asInt(line.substr(line_ndx*5,5));
00364 
00365                   // add value
00366                data[ilat*dim[1]+ival] = (val != 9999) ?
00367                                         std::pow(10.0,exponent)*val : 999.9;
00368 
00369             }  // End of 'for (int ival = 0,line_ndx = 0; ival < dim[1];...'
00370 
00371                // next latitude
00372             ilat++;
00373 
00374          }  // End of 'if (label == dataBlockString)...'
00375          else if (label == endTecMapString)
00376          {
00377 
00378             ityp = 0;
00379             valid = true;
00380 
00381          }
00382          else if (label == endRmsMapString)
00383          {
00384 
00385             ityp = 0;
00386             valid = true;
00387 
00388          }
00389          else if (label == endOfFile)
00390          {
00391 
00392             // Remember that there is one more line in Ionex definition
00393             // before EOF. The IonexData object has been initialized already
00394             // but we mark the flag 'valid' as false. This helps when we shall
00395             // store this data into an IonexStore object.
00396             ityp = 0;
00397             valid = false;
00398 
00399          }
00400          else
00401          {
00402 
00403             FFStreamError e(string("Unidentified Ionex Data record " + line) );
00404             GPSTK_THROW(e);
00405 
00406          }
00407 
00408       }  // end of 'while (ityp != 0)' loop
00409 
00410       return;
00411 
00412    }  // End of method 'IonexData::reallyGetRecord()'
00413 
00414 
00415 
00416       // A debug output function.
00417    void IonexData::dump(std::ostream& os) const
00418    {
00419 
00420       os << endl;
00421       os << "IonexData dump() function"      << std::endl;
00422       os << "Epoch                       : " << time << std::endl;
00423       os << "Map index                   : " << mapID << std::endl;
00424       os << "Data type                   : " << type.type
00425                 << " (" << type.units << ")" << std::endl;
00426       os << "Grid size (lat x lon x hgt) : " << dim[0]
00427                 << " x " << dim[1]
00428                 << " x " << dim[2] << std::endl;
00429       os << "Number of values            : " << data.size()
00430                 << " values." << std::endl;
00431       os << "Valid object?               : " << isValid() << endl;
00432 
00433       return;
00434 
00435    }  // End of method 'IonexData::dump()'
00436 
00437 
00438 
00452    int IonexData::getIndex( const Triple& in,
00453                             const int& igp,
00454                             Triple& ABC ) const
00455       throw(InvalidRequest)
00456    {
00457 
00458          // grid dimensions
00459       int nlat = dim[0];
00460       int nlon = dim[1];
00461       int nhgt = dim[2];
00462 
00463          // useful variables
00464       int ilat, ilon, ihgt, ncyc;
00465       double xlat, xlon, xhgt;
00466 
00467          // latitude
00468       xlat = (in[0] - lat[0]) / lat[2] + 1.0;
00469 
00470       ilat = (igp == 1) ?
00471              static_cast<int>(xlat+0.5) : static_cast<int>(xlat);
00472 
00473       if (ilat >= 1 && ilat <= nlat)
00474       {
00475 
00476          ABC[0] = lat[0] + (ilat-1)*lat[2];
00477 
00478       }
00479       else
00480       {
00481 
00482          InvalidRequest e( "Irregular latitude. Latitude "
00483                            + asString(in[0]) + " DEG" );
00484 
00485          GPSTK_THROW(e);
00486 
00487       }
00488 
00489 
00490          // longitude
00491       xlon = (in[1] - lon[0]) / lon[2] + 1.0;
00492 
00493       ilon = (igp == 1) ?
00494              static_cast<int>(xlon + 0.5) : static_cast<int>(xlon);
00495 
00496          // Round to neareast integer
00497       ncyc = static_cast<int>( ( 360.0 / std::abs(lon[2]) ) + 0.5 );
00498 
00499 
00500       if (ilon < 1)
00501       {
00502 
00503          ilon = ilon + ncyc;
00504 
00505       }
00506       else if (ilon > nlon)
00507       {
00508 
00509          ilon = ilon - ncyc;
00510 
00511       }
00512 
00513 
00514       if ( (ilon >= 1) && (ilon <= nlon) )
00515       {
00516 
00517          ABC[1] = lon[0] + (ilon-1) * lon[2];
00518 
00519       }
00520       else
00521       {
00522 
00523          InvalidRequest e( "Irregular longitude. Longitude: "
00524                            + asString(in[1]) + " DEG" );
00525          GPSTK_THROW(e);
00526 
00527       }
00528 
00529          // height
00530       if (hgt[2] == 0)
00531       {
00532 
00533          ihgt = 1;
00534          ABC[2] = hgt[0];
00535 
00536       }
00537       else
00538       {
00539 
00540          xhgt = (in[2]/1000.0 - hgt[0]) / hgt[2] + 1.0;
00541 
00542          ihgt = (igp == 1) ?
00543                 static_cast<int>(xhgt + 0.5) : static_cast<int>(xhgt);
00544 
00545          if ( (ihgt >= 1) && (ihgt <= nhgt) )
00546          {
00547 
00548             ABC[2] = ( hgt[0] + (ihgt-1) * hgt[2] ) * 1000.0;  //meters
00549 
00550          }
00551          else
00552          {
00553 
00554             InvalidRequest e( "Irregular height. Height: "
00555                               + asString( in[2]/1000.0 ) + " km.");
00556 
00557             GPSTK_THROW(e);
00558 
00559          }  // End of 'if ( (ihgt >= 1) && (ihgt <= nhgt) )...'
00560 
00561       }  // End of 'if (hgt[2] == 0)...'
00562 
00563 
00564       return ( (ilon-1) + (ilat-1)*nlon + (ihgt-1)*nlon*nlat );
00565 
00566    }  // End of method 'IonexData::getIndex()'
00567 
00568 
00569 
00585    double IonexData::getValue( const Position& p ) const
00586       throw(InvalidRequest,FFStreamError)
00587    {
00588 
00589          // this never should happen but just in case
00590       Position pos(p);
00591       if ( pos.getSystemName() != "Geocentric" )
00592       {
00593 
00594          InvalidRequest e( "Position object is not in GEOCENTRIC coordinates");
00595 
00596          GPSTK_THROW(e);
00597 
00598       }
00599 
00600          // some useful declarations
00601       Triple ABC[4], inarg;
00602       int e[4];
00603       double xsum = 0.0;
00604 
00605          // the object is required for AEarth to be consistent with 
00606          // Position::getIonosphericPiercePoint()
00607       WGS84Geoid WGS84;
00608 
00609          // let's fetch the data
00610       double beta    = p.theArray[0];
00611       double lambda  = p.theArray[1];
00612       double height  = p.theArray[2]-WGS84.a();
00613 
00614          // we need this step because in the Position object the longitude is 
00615          // expressed in degrees E (i.e., [0 +360]), while in IONEX files 
00616          // longitude takes values within [-180 180]) (see IONEX manual) 
00617       if (lambda > 180.0)
00618       {
00619          lambda = lambda - 360.0;
00620       }
00621 
00622          // get position of lower left hand grid point E00
00623       inarg = Triple( beta, lambda, height);
00624       e[0] = getIndex( inarg, 2, ABC[0] );
00625 
00626 
00627          // compute factors P and Q
00628       double xp( (inarg[1] - ABC[0][1]) / lon[2] );
00629       double xq( (inarg[0] - ABC[0][0]) / lat[2] );
00630 
00631          // this never should happen but just in case
00632       if ( (xp < 0) || (xp > 1) || (xq < 0) || (xq > 1) )
00633       {
00634 
00635          throw(Exception("IonexData::getValue(): Wrong xp and xq factors!!!"));
00636 
00637       }
00638 
00639          // get E10's position index
00640       inarg = Triple( ABC[0][0], ABC[0][1]+lon[2], ABC[0][2] );
00641       e[1] = getIndex( inarg, 1, ABC[1] );
00642 
00643          // get E01's position index
00644       inarg = Triple( ABC[0][0]+lat[2], ABC[0][1], ABC[0][2] );
00645       e[2] = getIndex( inarg, 1, ABC[2] );
00646 
00647          // get E11's position index
00648       inarg = Triple( ABC[0][0]+lat[2], ABC[0][1]+lon[2], ABC[0][2] );
00649       e[3] = getIndex( inarg, 1, ABC[3] );
00650 
00651          // let's fetch the values
00652       double pntval[4];
00653       for (int i = 0; i < 4; i++)
00654       {
00655 
00656          double xval( data[e[i]] );
00657 
00658          if (xval != 999.9)
00659          {
00660             pntval[i] = xval;
00661          }
00662          else
00663          {
00664             FFStreamError e("Undefined TEC/RMS value(s).");
00665             GPSTK_THROW(e);
00666          }
00667 
00668       }  // End of 'for (int i = 0; i < 4; i++)...'
00669 
00670          // bivariate interpolation (pag.3, IONEX manual)
00671       xsum = (1.0-xp) * (1.0-xq) * pntval[0] +
00672                   xp  * (1.0-xq) * pntval[1] +
00673              (1.0-xp) *      xq  * pntval[2] +
00674                   xp  *      xq  * pntval[3];
00675 
00676       return xsum;
00677 
00678    }  // End of method 'IonexData::getValue()'
00679 
00680 
00681 
00687    DayTime IonexData::parseTime( const std::string& line ) const
00688    {
00689 
00690       int year, month, day, hour, min, sec;
00691 
00692       year  = asInt(line.substr( 0,6));
00693       month = asInt(line.substr( 6,6));
00694       day   = asInt(line.substr(12,6));
00695       hour  = asInt(line.substr(18,6));
00696       min   = asInt(line.substr(24,6));
00697       sec   = asInt(line.substr(30,6));
00698 
00699       return DayTime( year, month, day, hour, min, (double)sec );
00700 
00701    }  // End of method 'IonexData::parseTime()'
00702 
00703 
00704 
00710    string IonexData::writeTime(const DayTime& dt) const
00711       throw(gpstk::StringUtils::StringException)
00712    {
00713 
00714       if (dt == DayTime::BEGINNING_OF_TIME)
00715       {
00716          return string(36, ' ');
00717       }
00718 
00719       string line;
00720       line  = rightJustify(asString<short>(dt.year()), 6);
00721       line += rightJustify(asString<short>(dt.month()), 6);
00722       line += rightJustify(asString<short>(dt.day()), 6);
00723       line += rightJustify(asString<short>(dt.hour()), 6);
00724       line += rightJustify(asString<short>(dt.minute()), 6);
00725       line += rightJustify(asString (static_cast<int>(dt.second())), 6);
00726 
00727       return line;
00728 
00729    }  // End of method 'IonexData::writeTime()'
00730 
00731 
00732 
00733 }  // End of namespace gpstk

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