AntexData.cpp

Go to the documentation of this file.
00001 #pragma ident "$Id: AntexData.cpp 2293 2010-02-12 18:14:16Z btolman $"
00002 
00011 //============================================================================
00012 //
00013 //  This file is part of GPSTk, the GPS Toolkit.
00014 //
00015 //  The GPSTk is free software; you can redistribute it and/or modify
00016 //  it under the terms of the GNU Lesser General Public License as published
00017 //  by the Free Software Foundation; either version 2.1 of the License, or
00018 //  any later version.
00019 //
00020 //  The GPSTk is distributed in the hope that it will be useful,
00021 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00022 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00023 //  GNU Lesser General Public License for more details.
00024 //
00025 //  You should have received a copy of the GNU Lesser General Public
00026 //  License along with GPSTk; if not, write to the Free Software Foundation,
00027 //  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00028 //  
00029 //  Copyright 2004, The University of Texas at Austin
00030 //
00031 //============================================================================
00032 
00033 //============================================================================
00034 //
00035 //This software developed by Applied Research Laboratories at the University of
00036 //Texas at Austin, under contract to an agency or agencies within the U.S. 
00037 //Department of Defense. The U.S. Government retains all rights to use,
00038 //duplicate, distribute, disclose, or release this software. 
00039 //
00040 //Pursuant to DoD Directive 523024 
00041 //
00042 // DISTRIBUTION STATEMENT A: This software has been approved for public 
00043 //                           release, distribution is unlimited.
00044 //
00045 //=============================================================================
00046 
00047 #include "AntexData.hpp"
00048 #include "AntexStream.hpp"
00049 #include "StringUtils.hpp"
00050 #include "geometry.hpp"
00051 
00052 using namespace gpstk::StringUtils;
00053 using namespace std;
00054 
00055 namespace gpstk
00056 {
00057    const string AntexData::startAntennaString = "START OF ANTENNA";
00058    const string AntexData::typeSerNumString   = "TYPE / SERIAL NO";
00059    const string AntexData::methodString       = "METH / BY / # / DATE";
00060    const string AntexData::daziString         = "DAZI";
00061    const string AntexData::zenithString       = "ZEN1 / ZEN2 / DZEN";
00062    const string AntexData::numFreqString      = "# OF FREQUENCIES";
00063    const string AntexData::validFromString    = "VALID FROM";
00064    const string AntexData::validUntilString   = "VALID UNTIL";
00065    const string AntexData::sinexCodeString    = "SINEX CODE";
00066    const string AntexData::dataCommentString  = "COMMENT";
00067    const string AntexData::startFreqString    = "START OF FREQUENCY";
00068    const string AntexData::neuFreqString      = "NORTH / EAST / UP";
00069    const string AntexData::endOfFreqString    = "END OF FREQUENCY";
00070    const string AntexData::startFreqRMSString = "START OF FREQ RMS";
00071    const string AntexData::neuFreqRMSString   = "NORTH / EAST / UP";
00072    const string AntexData::endOfFreqRMSString = "END OF FREQ RMS";
00073    const string AntexData::endOfAntennaString = "END OF ANTENNA";
00074 
00075    // NB. this dimension must be updated with the list
00077    const int Nsattype=11;
00078    string sattype[Nsattype] =
00079    { 
00080       string("BLOCK I"),         // 1
00081       string("BLOCK II"),        // 2
00082       string("BLOCK IIA"),       // 3
00083       string("BLOCK IIR"),       // 4
00084       string("BLOCK IIR-A"),     // 5
00085       string("BLOCK IIR-B"),     // 6
00086       string("BLOCK IIR-M"),     // 7
00087       string("BLOCK IIF"),       // 8
00088       string("GLONASS"),         // 9
00089       string("GLONASS-M"),       // 10
00090       string("GLONASS-K")        // 11
00091    };
00093    const vector<string> AntexData::SatelliteTypes(sattype,sattype+Nsattype);
00094 
00095    // ----------------------------------------------------------------------------
00096    bool AntexData::isValid(DayTime& time) const throw()
00097    {
00098       if(!isValid())
00099          return false;
00100       if(time == DayTime::BEGINNING_OF_TIME ||
00101             (!(valid & validFromValid) && !(valid & validUntilValid))) {
00102          return true;
00103       }
00104       if((valid & validFromValid) && time < validFrom) {
00105          return false;
00106       }
00107       if((valid & validUntilValid) && time > validUntil) {
00108          return false;
00109       }
00110 
00111       return true;
00112    }
00113 
00114    // ----------------------------------------------------------------------------
00115    double AntexData::getTotalPhaseCenterOffset(const int freq,
00116                                                const double azim,
00117                                                const double elev_nadir) const
00118       throw(Exception)
00119    {
00120       try {
00121          // these do all the checking and throwing
00122          double pcv = getPhaseCenterVariation(freq, azim, elev_nadir);
00123          Triple pco = getPhaseCenterOffset(freq);
00124 
00125          double elev = elev_nadir;
00126          if(!isRxAntenna)              // satellite : elev_nadir is 'nadir' angle:
00127             elev = 90. - elev_nadir;   //             from Z axis toward XY plane.
00128 
00129          double cosel = ::cos(elev * DEG_TO_RAD);
00130          double sinel = ::sin(elev * DEG_TO_RAD);
00131          double cosaz = ::cos(azim * DEG_TO_RAD);
00132          double sinaz = ::sin(azim * DEG_TO_RAD);
00133          
00134          // see doc for class AntexData for signs, etc
00135          return (-pcv + pco[0]*cosel*cosaz
00136                       + pco[1]*cosel*sinaz
00137                       + pco[2]*sinel);
00138       }
00139       catch(Exception& e) { GPSTK_RETHROW(e); }
00140    }
00141 
00142    // Access the PCO (only) values in the antenna coordinate system
00143    // (This does NOT include the phase center variations, which should
00144    // be computed using getPhaseCenterVariation() and added to the PCOs to get
00145    // the total phase center offset).
00146    Triple AntexData::getPhaseCenterOffset(const int freq) const
00147       throw(Exception)
00148    {
00149       if(!isValid()) {
00150          gpstk::Exception e("Invalid object");
00151          GPSTK_THROW(e);
00152       }
00153       if(freq <= 0 || freq > nFreq) {
00154          gpstk::Exception e("Invalid frequency");
00155          GPSTK_THROW(e);
00156       }
00157 
00158       // get the antennaPCOandPCVData for this frequency
00159       map<int, antennaPCOandPCVData>::const_iterator it = freqPCVmap.find(freq);
00160       if(it == freqPCVmap.end()) {
00161          gpstk::Exception e("Frequency " + gpstk::StringUtils::asString(freq)
00162                + " not found! object must be corrupted.");
00163          GPSTK_THROW(e);
00164       }
00165       //const antennaPCOandPCVData& antpco = it->second;
00166       
00167       Triple retval;
00168       for(int i=0; i<3; i++)
00169          retval[i] = it->second.PCOvalue[i];
00170 
00171       return retval;
00172    }
00173 
00174    // Compute the phase center variation (only) at the given azimuth and elevation
00175    // (receiver) or nadir (satellite) angles
00176    double AntexData::getPhaseCenterVariation(const int freq,
00177                                              const double azimuth,
00178                                              const double elev_nadir) const
00179       throw(Exception)
00180    {
00181       if(!isValid()) {
00182          Exception e("Invalid object");
00183          GPSTK_THROW(e);
00184       }
00185       if(freq <= 0 || freq > nFreq) {
00186          Exception e("Invalid frequency");
00187          GPSTK_THROW(e);
00188       }
00189       if(elev_nadir < 0.0 || elev_nadir > 90.0) {
00190          Exception e("Invalid elevation/nadir angle");
00191          GPSTK_THROW(e);
00192       }
00193 
00194       double retpco, azim, zen;
00195       zen = elev_nadir;             // satellite: elev_nadir is a zenith (nadir) angle
00196       if(isRxAntenna)               // receiver: elev_nadir is an elevation
00197          zen = 90. - elev_nadir;
00198 
00199       // ensure azim is within range
00200       if(azimuth < 0.0 || azimuth >= 360.0)
00201          azim = fmod(azimuth,360.0);
00202       else
00203          azim = azimuth;
00204 
00205       // find four points bracketing the point (azim,zen)
00206       //       zen
00207       //       ^
00208       // zn_hi | 0     1          have pco at 0,1,2,3
00209       //       |    x             have (azim,zen) at x, want retpco at x
00210       // zn_lo | 2     3
00211       //       |----------> azim
00212       //       az_lo  az_hi
00213       //
00214       // find bracketing azims within the map, then find bracketing zeniths and PCOs
00215       double az_lo,az_hi,zn_lo,zn_hi,pco[4];
00216       map<int, antennaPCOandPCVData>::const_iterator it;
00217       map<double, zenOffsetMap>::const_iterator jt_lo,jt_hi;
00218 
00219       it = freqPCVmap.find(freq);
00220       if(it == freqPCVmap.end()) {
00221          Exception e("Frequency " + StringUtils::asString(freq)
00222                + " not found! object must be corrupted.");
00223          GPSTK_THROW(e);
00224       }
00225 
00226       const antennaPCOandPCVData& antpco = it->second;
00227       const azimZenMap& azzenmap = antpco.PCVvalue;
00228 
00229       if(!antpco.hasAzimuth)
00230          jt_hi = azzenmap.begin(); // this will be only entry
00231       else
00232          jt_hi = azzenmap.find(azim); // find() returns end() unless exact match
00233 
00234       // either azimuth is not there, or there is an exact match in azimuth
00235       if(jt_hi != azzenmap.end()) {
00236          //az_lo = az_hi = azim;
00237          const zenOffsetMap& zenoffmap = jt_hi->second;
00238          // bracket zenith angle
00239          evaluateZenithMap(zen, zenoffmap, zn_lo, zn_hi, pco[2], pco[0]);
00240          if(zn_lo == zn_hi)                  // exact match in zenith angle
00241             retpco = pco[0];                 // TD use fabs(diff) < tolerance ?
00242          else                                // linear interpolation in zenith angle
00243             retpco = (pco[0]*(zen - zn_lo) + pco[2]*(zn_hi - zen))/(zn_hi - zn_lo);
00244       }
00245 
00246       else {                                 // must bracket in azimuth
00247          // lower_bound() returns first value >= azim (but == case handled above)
00248          jt_lo = jt_hi = azzenmap.lower_bound(azim);
00249 
00250          if(jt_lo == azzenmap.end()) {        // beyond the last value
00251             jt_lo--;                         // last value
00252             az_lo = jt_lo->first;
00253             jt_hi = azzenmap.begin();         // wrap around to beginning
00254             az_hi = jt_hi->first + 360.;
00255          }
00256          else if(jt_hi == azzenmap.begin()) { // before the first value
00257             az_hi = jt_hi->first;
00258             (jt_lo = azzenmap.end())--;       // wrap around to end
00259             az_lo = jt_lo->first - 360.;
00260          }
00261          else {                              // azim is bracketed
00262             az_hi = jt_hi->first;
00263             jt_lo--;
00264             az_lo = jt_lo->first;
00265          }
00266 
00267          // get zenith angles and pcos at upper and lower azimuths
00268          evaluateZenithMap(zen, jt_hi->second, zn_lo, zn_hi, pco[3], pco[1]);
00269          evaluateZenithMap(zen, jt_lo->second, zn_lo, zn_hi, pco[2], pco[0]);
00270 
00271          // interpolation
00272          if(zn_hi == zn_lo)   // zen exact match, linear interpolate in azimuth
00273             retpco = (pco[2]*(az_hi - azim) + pco[3]*(azim - az_lo))/(az_hi - az_lo);
00274          else                 // bi-linear interpolation
00275             retpco = ( pco[0] * (az_hi - azim)*(zen - zn_lo)
00276                      + pco[1] * (azim - az_lo)*(zen - zn_lo)
00277                      + pco[2] * (az_hi - azim)*(zn_hi - zen)
00278                      + pco[3] * (azim - az_lo)*(zn_hi - zen) )
00279                            / ( (az_hi - az_lo)*(zn_hi - zn_lo) );
00280       }
00281 
00282       // do not change the sign; just interpolate the map
00283       return retpco;
00284    }
00285 
00286    void AntexData::dump(ostream& s, int detail) const
00287    {
00288       map<int, antennaPCOandPCVData>::const_iterator it;
00289       map<double, zenOffsetMap>::const_iterator jt;
00290       map<double, double>::const_iterator kt;
00291 
00292       s << "Antenna Type/SN: [" << name() << "]";
00293       if(isRxAntenna)
00294          s << " (Receiver)" << endl;
00295       else {
00296          if(PRN != -1 || SVN != -1) {
00297            s << " (" << (type == string("GLONASS") ? "GLONASS" : "GPS");
00298            if(PRN != -1) s << string(" PRN ") + asString(PRN);
00299            if(SVN != -1) s << string(" SVN ") + asString(SVN);
00300            s << ")";
00301         }
00302         s << " Sat. code: " << satCode
00303           << " COSPAR ID: " << cospar << endl;
00304       }
00305 
00306       if(detail <= 0) return;
00307 
00308       s << "Method: " << method << "   Agency: " << agency
00309          << "   #Cal.Ant.s: " << noAntCalibrated << "   Date: " << date << endl;
00310       if(azimDelta > 0.0)
00311          s << "Azimuth dependence, delta azimuth = "
00312             << fixed << setprecision(1) << azimDelta << endl;
00313       else
00314          s << "No azimuth dependence" << endl;
00315       s << "Elevation dependence: from "
00316          << fixed << setprecision(1) << zenRange[0] << " to " << zenRange[1]
00317          << " in steps of " << zenRange[2] << " degrees." << endl;
00318       s << "Frequencies stored (" << nFreq << "): ";
00319 
00320       it = freqPCVmap.begin();
00321       while(it != freqPCVmap.end()) {
00322          s << " " << (serialNo[0]=='G' ? "L" : "") << it->first;
00323          it++;
00324       }
00325       s << endl;
00326       s << "Valid FROM "
00327          << (validFrom == DayTime::BEGINNING_OF_TIME ? " (all time) "
00328               : validFrom.printf("%02m/%02d/%04Y %02H:%02M:%.7s"))
00329          << " TO "
00330          << (validUntil == DayTime::END_OF_TIME ? " (all time) "
00331               : validUntil.printf("%02m/%02d/%04Y %02H:%02M:%.7s"))
00332          << endl;
00333       if(!sinexCode.empty())
00334          s << "SINEX code: " << sinexCode << endl;
00335       for(int i=0; i<commentList.size(); i++) {
00336          //if(i==0) s << "Comments:\n";
00337          s << "Comment " << setw(2) << i+1 << ": " << commentList[i] << endl;
00338       }
00339 
00340       if(detail == 1) {
00341          for(it = freqPCVmap.begin(); it != freqPCVmap.end(); it++) {
00342             const antennaPCOandPCVData& antpco = it->second;
00343             s << "PCO (" << (isRxAntenna ? "NEU from antenna reference position"
00344                                   : "body XYZ from center-of-mass") << ") (mm):"
00345               << " (freq " << it->first << ") "
00346               << fixed << setprecision(2) << setw(10) << antpco.PCOvalue[0]
00347               << ", " << setw(10) << antpco.PCOvalue[1]
00348               << ", " << setw(10) << antpco.PCOvalue[2] << endl;
00349          }
00350          return;
00351       }
00352 
00353       // loop over frequency
00354       for(it = freqPCVmap.begin(); it != freqPCVmap.end(); it++) {
00355          s << "Offset values for frequency: " << it->first
00356             << " (" << (it->second.hasAzimuth ? "has" : "does not have")
00357             << " azimuths)" << endl;
00358          const antennaPCOandPCVData& antpco = it->second;
00359 
00360          // PCOs
00361          s << "  PCO (" << (isRxAntenna ? "NEU from antenna reference position"
00362                                  : "body XYZ from center-of-mass") << ") (mm):"
00363            << fixed << setprecision(2) << setw(10) << antpco.PCOvalue[0]
00364            << ", " << setw(10) << antpco.PCOvalue[1]
00365            << ", " << setw(10) << antpco.PCOvalue[2] << endl;
00366 
00367          // RMS PCOs
00368          if(valid & neuFreqRMSValid)
00369             s << "  RMS PCO ("
00370               << (isRxAntenna ? "NEU from antenna reference position"
00371                               : "body XYZ from center-of-mass") << " (mm):"
00372               << fixed << setprecision(2) << setw(10) << antpco.PCOrms[0]
00373               << ", " << setw(10) << antpco.PCOrms[1]
00374               << ", " << setw(10) << antpco.PCOrms[2] << endl;
00375 
00376          // PCV array(s)
00377          const azimZenMap& azel = antpco.PCVvalue;
00378          s << fixed << setprecision(2);
00379          // header line
00380          jt = azel.begin();
00381          const zenOffsetMap& zenoffmap = jt->second;
00382          s << "  PCVs follow, one azimuth per row: AZ(deg) { PCVs(EL)(mm) .. .. }\n";
00383          s << "  EL(deg)";
00384          for(kt = zenoffmap.begin(); kt != zenoffmap.end(); kt++)
00385             s << setw(8) << kt->first;
00386          s << endl;
00387          // data values
00388          for(jt = azel.begin(); jt != azel.end(); jt++) {
00389             double azimuth = jt->first;
00390             const zenOffsetMap& zenoffmap = jt->second;
00391             if(azimuth == -1.0) s << "  (NOAZI)";
00392             else s << setw(9) << azimuth;
00393             for(kt = zenoffmap.begin(); kt != zenoffmap.end(); kt++)
00394                s << setw(8) << kt->second;
00395             s << endl;
00396          }
00397       }  // end loop over frequency
00398 
00399    }
00400 
00401    // ----------------------------------------------------------------------------
00402    // protected routines
00403    //
00404    // Find zenith angles bracketing the input zenith angle within the given map,
00405    // and the corresponding PCOs.
00406    void AntexData::evaluateZenithMap(const double& zen,
00407                                      const zenOffsetMap& eomap,
00408                                      double& zen_lo, double& zen_hi,
00409                                      double& pco_lo, double& pco_hi) const
00410       throw()
00411    {
00412       map<double, double>::const_iterator kt;
00413 
00414       // find() returns end() unless there is an exact match
00415       kt = eomap.find(zen);
00416       if(kt != eomap.end()) {                // exact match
00417          zen_lo = zen_hi = zen;
00418          pco_lo = pco_hi = kt->second;
00419          return;
00420       }
00421 
00422       // lower_bound() returns first value >= zen (but == case handled above)
00423       kt = eomap.lower_bound(zen);
00424 
00425       // zen is above the last, or below the first value - just take that value
00426       if(kt == eomap.end() || kt == eomap.begin()) {
00427          if(kt == eomap.end()) kt--;         // last value
00428          zen_lo = zen_hi = zen;
00429          pco_lo = pco_hi = kt->second;
00430          return;
00431       }
00432 
00433       // zen is bracketed
00434       zen_hi = kt->first;
00435       pco_hi = kt->second;
00436       kt--;
00437       zen_lo = kt->first;
00438       pco_lo = kt->second;
00439 
00440       return;
00441    }
00442 
00443    void AntexData::reallyPutRecord(FFStream& ffs) const 
00444       throw(exception, FFStreamError, StringException)
00445    {
00446       if(!isValid()) {
00447          FFStreamError fse(string("Cannot write invalid AntexData"));
00448          GPSTK_THROW(fse);
00449       }
00450 
00451       int i;
00452       string line;
00453       map<int, antennaPCOandPCVData>::const_iterator it;
00454       map<double, zenOffsetMap>::const_iterator jt;
00455       map<double, double>::const_iterator kt;
00456       AntexStream& strm = dynamic_cast<AntexStream&>(ffs);
00457 
00458       line = rightJustify(leftJustify(startAntennaString,20),80);
00459       strm << line << endl;
00460       strm.lineNumber++;
00461    
00462       line  = leftJustify(type,20);
00463       line += leftJustify(serialNo,20);
00464       line += leftJustify(satCode,10);
00465       line += leftJustify(cospar,10);
00466       line += typeSerNumString;     //"TYPE / SERIAL NO";
00467       strm << leftJustify(line,80) << endl;
00468       strm.lineNumber++;
00469 
00470       line  = leftJustify(method,20);
00471       line += leftJustify(agency,20);
00472       line += leftJustify(rightJustify(asString(noAntCalibrated),6),10);
00473       line += leftJustify(date,10);
00474       line += methodString;         // "METH / BY / # / DATE";
00475       strm << leftJustify(line,80) << endl;
00476       strm.lineNumber++;
00477 
00478       line  = string("  ");
00479       line += rightJustify(asString(azimDelta,1),6);
00480       line  = leftJustify(line,60);
00481       line += daziString;           // "DAZI";
00482       strm << leftJustify(line,80) << endl;
00483       strm.lineNumber++;
00484 
00485       line  = string("  ");
00486       line += rightJustify(asString(zenRange[0],1),6);
00487       line += rightJustify(asString(zenRange[1],1),6);
00488       line += rightJustify(asString(zenRange[2],1),6);
00489       line  = leftJustify(line,60);
00490       line += zenithString;         // "ZEN1 / ZEN2 / DZEN";
00491       strm << leftJustify(line,80) << endl;
00492       strm.lineNumber++;
00493 
00494       line  = rightJustify(asString(nFreq),6);
00495       line  = leftJustify(line,60);
00496       line += numFreqString;        // "# OF FREQUENCIES";
00497       strm << leftJustify(line,80) << endl;
00498       strm.lineNumber++;
00499 
00500       if(valid & validFromValid) {
00501          if(stringValidFrom.empty())
00502             line  = writeTime(validFrom);
00503          else
00504             line = stringValidFrom;
00505          line  = leftJustify(line,60);
00506          line += validFromString;      // "VALID FROM";
00507          strm << leftJustify(line,80) << endl;
00508          strm.lineNumber++;
00509       }
00510 
00511       if(valid & validUntilValid) {
00512          if(stringValidUntil.empty())
00513             line  = writeTime(validUntil);
00514          else
00515             line = stringValidUntil;
00516          line  = leftJustify(line,60);
00517          line += validUntilString;    // "VALID UNTIL";
00518          strm << leftJustify(line,80) << endl;
00519          strm.lineNumber++;
00520       }
00521 
00522       if(valid & sinexCodeValid) {
00523          line  = leftJustify(rightJustify(sinexCode,10),60);
00524          line += sinexCodeString;      // "SINEX CODE";
00525          strm << leftJustify(line,80) << endl;
00526          strm.lineNumber++;
00527       }
00528 
00529       for(i=0; i<commentList.size(); i++) {
00530          line  = leftJustify(commentList[i],60);
00531          line += dataCommentString;    // "COMMENT";
00532          strm << leftJustify(line,80) << endl;
00533          strm.lineNumber++;
00534       }
00535 
00536       // loop over frequency
00537       string freqStr;
00538       for(it = freqPCVmap.begin(); it != freqPCVmap.end(); it++) {
00539          const antennaPCOandPCVData& antpco = it->second;
00540 
00541          ostringstream oss; // b/c asString does not allow set fill char
00542          oss << systemChar << setfill('0') << setw(2) << it->first;
00543          freqStr = oss.str();
00544          line  = string("   ");
00545          line += freqStr;
00546          line  = leftJustify(line,60);
00547          line += startFreqString;      // "START OF FREQUENCY";
00548          strm << leftJustify(line,80) << endl;
00549          strm.lineNumber++;
00550 
00551          line = string();
00552          for(i=0; i<3; i++)
00553             line += rightJustify(asString(antpco.PCOvalue[i],2),10);
00554          line  = leftJustify(line,60);
00555          line += neuFreqString;        // "NORTH / EAST / UP";
00556          strm << leftJustify(line,80) << endl;
00557          strm.lineNumber++;
00558 
00559          // PCVs
00560          const azimZenMap& azel = antpco.PCVvalue;
00561          for(jt = azel.begin(); jt != azel.end(); jt++) {
00562             const zenOffsetMap& zenoffmap = jt->second;
00563             if(antpco.hasAzimuth && jt->first > -1.0)
00564                line = rightJustify(asString(jt->first,1),8);
00565             else
00566                line = string("   NOAZI");
00567             for(kt = zenoffmap.begin(); kt != zenoffmap.end(); kt++) {
00568                line += rightJustify(asString(kt->second,2),8);
00569             }
00570             strm << line << endl;
00571             strm.lineNumber++;
00572          }
00573 
00574          line  = string("   ");
00575          line += freqStr;
00576          line  = leftJustify(line,60);
00577          line += endOfFreqString;      // "END OF FREQUENCY";
00578          strm << leftJustify(line,80) << endl;
00579          strm.lineNumber++;
00580       }
00581 
00582       if(valid & (startFreqRMSValid|neuFreqRMSValid|endOfFreqRMSValid)) {
00583          // loop over frequency again
00584          for(it = freqPCVmap.begin(); it != freqPCVmap.end(); it++) {
00585             const antennaPCOandPCVData& antpco = it->second;
00586             ostringstream oss; // b/c asString does not allow set fill char
00587             oss << systemChar << setfill('0') << setw(2) << it->first;
00588             freqStr = oss.str();
00589             line  = string("   ");
00590             line += freqStr;
00591             line  = leftJustify(line,60);
00592             line += startFreqRMSString;   // "START OF FREQ RMS";
00593             strm << leftJustify(line,80) << endl;
00594             strm.lineNumber++;
00595 
00596             line = string();
00597             for(i=0; i<3; i++)
00598                line += rightJustify(asString(antpco.PCOrms[i],2),10);
00599             line  = leftJustify(line,60);
00600             line += neuFreqRMSString;     // "NORTH / EAST / UP";
00601             strm << leftJustify(line,80) << endl;
00602             strm.lineNumber++;
00603 
00604             // PCVs
00605             const azimZenMap& azel = antpco.PCVrms;
00606             for(jt = azel.begin(); jt != azel.end(); jt++) {
00607                const zenOffsetMap& zenoffmap = jt->second;
00608                if(antpco.hasAzimuth)
00609                   line = rightJustify(asString(jt->first,1),8);
00610                else
00611                   line = string("   NOAZI");
00612                for(kt = zenoffmap.begin(); kt != zenoffmap.end(); kt++) {
00613                   line += rightJustify(asString(kt->second,2),8);
00614                }
00615                strm << line << endl;
00616                strm.lineNumber++;
00617             }
00618 
00619             line  = string("   ");
00620             line += freqStr;
00621             line  = leftJustify(line,60);
00622             line += endOfFreqRMSString;   // "END OF FREQ RMS";
00623             strm << leftJustify(line,80) << endl;
00624             strm.lineNumber++;
00625          }
00626       }
00627 
00628       line = rightJustify(leftJustify(endOfAntennaString,20),80);
00629       strm << line << endl;
00630       strm.lineNumber++;
00631 
00632    }   // end AntexData::reallyPutRecord
00633 
00634 
00635    void AntexData::reallyGetRecord(FFStream& ffs) 
00636       throw(exception, FFStreamError, StringUtils::StringException)
00637    {
00638       AntexStream& strm = dynamic_cast<AntexStream&>(ffs);
00639       
00640          // If the header hasn't been read, read it...
00641       if(!strm.headerRead) strm >> strm.header;
00642       
00643          // Clear out this object
00644       AntexData ad;
00645       *this = ad;
00646       
00647       string line;
00648       
00649       while(!(valid & endOfAntennaValid)) {
00650          strm.formattedGetLine(line, true);
00651          stripTrailing(line);
00652 
00653          if(line.length() == 0)
00654             continue;
00655 
00656          try {
00657             ParseDataRecord(line);
00658          }
00659          catch(FFStreamError& e) {
00660             GPSTK_THROW(e);
00661          }
00662       }
00663       
00664    } // end of reallyGetRecord()
00665 
00666    // ----------------------------------------------------------------------------
00667    // private routines
00668    //
00669    // helper routine for ParseDataRecord
00670    // throw if valid contains test, i.e. !(test & valid)
00671    void AntexData::throwRecordOutOfOrder(unsigned long test, string& label)
00672    {
00673       if(test & valid) {
00674          FFStreamError fse(string("Records are out of order: detected at ") + label);
00675          GPSTK_THROW(fse);
00676       }
00677    }
00678 
00679    // for reallyGetRecord
00680    void AntexData::ParseDataRecord(string& line)
00681       throw(FFStreamError)
00682    {
00683    try {
00684       static bool hasAzim;
00685       static int freq;
00686       static string freqStr;
00687       string label(line, 60, 20);
00688 
00689       if(label == startAntennaString) {        // "START OF ANTENNA"
00690          throwRecordOutOfOrder(typeSerNumValid,label);
00691          valid |= startAntennaValid;
00692       }
00693       else if(label == typeSerNumString) {     // "TYPE / SERIAL NO"
00694          throwRecordOutOfOrder(methodValid,label);
00695          type = stripTrailing(stripLeading(line.substr(0,20)));
00696          // determine if satellite
00697          isRxAntenna = true;
00698          for(int i=0; i<AntexData::SatelliteTypes.size(); i++)
00699             if(type == AntexData::SatelliteTypes[i]) { isRxAntenna = false; break; }
00700          serialNo = stripTrailing(stripLeading(line.substr(20,20)));
00701          satCode = stripTrailing(stripLeading(line.substr(40,10)));
00702          cospar = stripTrailing(stripLeading(line.substr(50,10)));
00703          if(!isRxAntenna) {         // get the PRN and SVN numbers
00704             if(serialNo.length() > 1) PRN = asInt(serialNo.substr(1,2));
00705             else PRN = -1;
00706             if(satCode.length() > 1) SVN = asInt(satCode.substr(1,3));
00707             else SVN = -1;
00708          }
00709          valid |= typeSerNumValid;
00710       }
00711       else if(label == methodString) {         // "METH / BY / # / DATE"
00712          throwRecordOutOfOrder(daziValid,label);
00713          method = stripTrailing(stripLeading(line.substr(0,20)));
00714          agency = stripTrailing(stripLeading(line.substr(20,20)));
00715          noAntCalibrated = asInt(line.substr(40,6));
00716          date = stripTrailing(stripLeading(line.substr(50,10)));
00717          valid |= methodValid;
00718       }
00719       else if(label == daziString) {           // "DAZI"
00720          throwRecordOutOfOrder(zenithValid,label);
00721          azimDelta = asDouble(line.substr(2,6));
00722          if(azimDelta > 0.0) hasAzim = true; else hasAzim = false;
00723          valid |= daziValid;
00724       }
00725       else if(label == zenithString) {         // "ZEN1 / ZEN2 / DZEN"
00726          throwRecordOutOfOrder(numFreqValid,label);
00727          zenRange[0] = asDouble(line.substr(2,6)); // NB. zenith angles
00728          zenRange[1] = asDouble(line.substr(8,6)); // not elevation angles
00729          zenRange[2] = asDouble(line.substr(14,6));
00730          valid |= zenithValid;
00731       }
00732       else if(label == numFreqString) {        // "# OF FREQUENCIES"
00733          throwRecordOutOfOrder(validFromValid|validUntilValid|sinexCodeValid|
00734                dataCommentValid|startFreqValid,label);
00735          nFreq = (unsigned int)(asInt(line.substr(0,6)));
00736          valid |= numFreqValid;
00737       }
00738       else if(label == validFromString) {      // "VALID FROM"
00739          throwRecordOutOfOrder(validUntilValid|sinexCodeValid|
00740                dataCommentValid|startFreqValid,label);
00741          stringValidFrom = line.substr(0,43);
00742          validFrom = parseTime(line);
00743          valid |= validFromValid;
00744       }
00745       else if(label == validUntilString) {     // "VALID UNTIL"
00746          throwRecordOutOfOrder(sinexCodeValid|dataCommentValid|startFreqValid,label);
00747          stringValidUntil = line.substr(0,43);
00748          validUntil = parseTime(line);
00749          if(validUntil == DayTime::BEGINNING_OF_TIME)
00750             validUntil = DayTime::END_OF_TIME;
00751          valid |= validUntilValid;
00752       }
00753       else if(label == sinexCodeString) {      // "SINEX CODE"
00754          throwRecordOutOfOrder(dataCommentValid|startFreqValid,label);
00755          sinexCode = stripTrailing(stripLeading(line.substr(0,10)));
00756          valid |= sinexCodeValid;
00757       }
00758       else if(label == dataCommentString) {    // "COMMENT"
00759          throwRecordOutOfOrder(startFreqValid,label);
00760          string str = stripTrailing(line.substr(0,60));
00761          commentList.push_back(str);
00762          valid |= dataCommentValid;
00763       }
00764       else if(label == startFreqString) {      // "START OF FREQUENCY"
00765          throwRecordOutOfOrder(startFreqRMSValid|neuFreqRMSValid|endOfFreqRMSValid|
00766                endOfAntennaValid,label);
00767          freqStr = line.substr(3,3);
00768          systemChar = line[3];
00769          if(systemChar == ' ') systemChar = 'G';
00770          freq = asInt(line.substr(4,2));
00771          valid |= startFreqValid;
00772       }
00773       else if(label == neuFreqString) {        // "NORTH / EAST / UP"
00774          throwRecordOutOfOrder(startFreqRMSValid|neuFreqRMSValid|endOfFreqRMSValid|
00775                endOfAntennaValid,label);
00776          freqPCVmap[freq].PCOvalue[0] = asDouble(line.substr(0,10));
00777          freqPCVmap[freq].PCOvalue[1] = asDouble(line.substr(10,10));
00778          freqPCVmap[freq].PCOvalue[2] = asDouble(line.substr(20,10));
00779          valid |= neuFreqValid;
00780          // set flag here
00781          freqPCVmap[freq].hasAzimuth = hasAzim;
00782       }
00783       else if(label == endOfFreqString) {      // "END OF FREQUENCY"
00784          throwRecordOutOfOrder(startFreqRMSValid|neuFreqRMSValid|endOfFreqRMSValid|
00785                endOfAntennaValid,label);
00786          if(freqStr != line.substr(3,3)) {
00787             FFStreamError fse("START/END OF FREQ confused: "
00788                   + freqStr + " != " + line.substr(3,3));
00789             GPSTK_THROW(fse);
00790          }
00791          valid |= endOfFreqValid;
00792       }
00793       else if(label == startFreqRMSString) {   // "START OF FREQ RMS"
00794          throwRecordOutOfOrder(endOfAntennaValid,label);
00795          freqStr = line.substr(3,3);
00796          freq = asInt(line.substr(4,2));
00797          valid |= startFreqRMSValid;
00798       }
00799       else if(label == neuFreqRMSString) {     // "NORTH / EAST / UP"
00800          throwRecordOutOfOrder(endOfAntennaValid,label);
00801          freqPCVmap[freq].PCOrms[0] = asDouble(line.substr(0,10));
00802          freqPCVmap[freq].PCOrms[1] = asDouble(line.substr(10,10));
00803          freqPCVmap[freq].PCOrms[2] = asDouble(line.substr(20,10));
00804          valid |= neuFreqRMSValid;
00805       }
00806       else if(label == endOfFreqRMSString) {   // "END OF FREQ RMS"
00807          throwRecordOutOfOrder(endOfAntennaValid,label);
00808          if(freqStr != line.substr(3,3)) {
00809             FFStreamError fse("START/END OF FREQ RMS confused: "
00810                   + freqStr + " != " + line.substr(3,3));
00811             GPSTK_THROW(fse);
00812          }
00813          valid |= endOfFreqRMSValid;
00814       }
00815       else if(label == endOfAntennaString) {   // "END OF ANTENNA"
00816          valid |= endOfAntennaValid;
00817       }
00818       else {
00819          int i,n;
00820          string noazi = line.substr(3,5);
00821          double azim = asDouble(line.substr(0,8));
00822          if(!hasAzim && noazi != string("NOAZI")) {
00823             FFStreamError fse("Invalid format; zero delta azimuth without NOAZI");
00824             GPSTK_THROW(fse);
00825          }
00826 
00827          // NOAZI : data stored under azimuth = -1.0
00828          if(noazi == string("NOAZI")) azim = -1.0;
00829 
00830          n = StringUtils::numWords(line) - 1;
00831          if(n != 1+int((zenRange[1]-zenRange[0])/zenRange[2])) {
00832             FFStreamError fse("Invalid format; wrong number of zenith/offset values");
00833             GPSTK_THROW(fse);
00834          }
00835 
00836          // loop over values; format is 3x,a5,mf8.2
00837          for(i=1; i<=n; i++) {
00838             double value = asDouble(line.substr(8*i,8));
00839             double zen = zenRange[0] + (i-1)*zenRange[2];
00840             if(valid & neuFreqRMSValid)
00841                freqPCVmap[freq].PCVrms[azim][zen] = value;
00842             else if(valid & neuFreqValid)
00843                freqPCVmap[freq].PCVvalue[azim][zen] = value;
00844          }
00845 
00846       }  // end if/else if/else on record type
00847 
00848    }  // end try
00849    catch(FFStreamError& fse) { GPSTK_RETHROW(fse); }
00850    }  // end AntexData::ParseDataRecord
00851 
00852    DayTime AntexData::parseTime(const string& line) const
00853       throw(FFStreamError)
00854    {
00855       try
00856       {
00857          // default value
00858          DayTime time = DayTime::BEGINNING_OF_TIME;
00859 
00860          if (line.substr(0,42) == string(42,' '))
00861             return time;
00862 
00863          // check if the spaces are in the right place - an easy
00864          // way to check if there's corruption in the file
00865          // --YYYY----MM----DD----HH----MMsssss.sssssss-----------------
00866          // 012345678901234567890123456789012345678901234567890123456789
00867          if((line.substr( 0,2) != string(2,' ')) ||
00868             (line.substr( 6,4) != string(4,' ')) ||
00869             (line.substr(12,4) != string(4,' ')) ||
00870             (line.substr(18,4) != string(4,' ')) ||
00871             (line.substr(24,4) != string(4,' ')) ||
00872             (line[43] != ' '))
00873          {
00874             FFStreamError e("Invalid time format");
00875             GPSTK_THROW(e);
00876          }
00877 
00878          // parse the time
00879          int year, month, day, hour, min;
00880          double sec;
00881    
00882          year  = asInt(   line.substr( 2, 4));
00883          month = asInt(   line.substr(10, 4));
00884          day   = asInt(   line.substr(16, 4));
00885          hour  = asInt(   line.substr(22, 4));
00886          min   = asInt(   line.substr(28, 4));
00887          sec   = asDouble(line.substr(30,13));
00888 
00889          time.setYMDHMS(year, month, day, hour, min, sec);
00890 
00891          return time;
00892       }
00893       // string exceptions for substr are caught here
00894       catch (exception &e)
00895       {
00896          FFStreamError err("std::exception: " + string(e.what()));
00897          GPSTK_THROW(err);
00898       }
00899       catch (Exception& e)
00900       {
00901          string text;
00902          for(int i=0; i<e.getTextCount(); i++) text += e.getText(i);
00903          FFStreamError err("Exception in parseTime(): " + text);
00904          GPSTK_THROW(err);
00905       }
00906    }
00907 
00908    string AntexData::writeTime(const DayTime& dt) const
00909       throw(StringException)
00910    {
00911       if(dt == DayTime::BEGINNING_OF_TIME || dt == DayTime::END_OF_TIME)
00912          return string(43,' ');
00913 
00914       // --YYYY----MM----DD----HH----MMsssss.sssssss-----------------
00915       // 012345678901234567890123456789012345678901234567890123456789
00916       string line;
00917       line  = string(2,' ');
00918       line += rightJustify(asString<short>(dt.year()),4);
00919       line += string(4,' ');
00920       line += rightJustify(asString<short>(dt.month()),2);
00921       line += string(4,' ');
00922       line += rightJustify(asString<short>(dt.day()),2);
00923       line += string(4,' ');
00924       line += rightJustify(asString<short>(dt.hour()),2);
00925       line += string(4,' ');
00926       line += rightJustify(asString<short>(dt.minute()),2);
00927       line += rightJustify(asString(dt.second(),7),13);
00928 
00929       return line;
00930    }
00931 
00932 } // namespace

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