00001 #pragma ident "$Id: IonexData.cpp 1802 2009-03-17 13:20:40Z coandrei $"
00002
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
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
00073 if ( !valid || data.empty() )
00074 return;
00075
00076 IonexStream& strm = dynamic_cast<IonexStream&>(ffs);
00077 string line;
00078
00079
00080
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
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
00113 int nlat(dim[0]), nlon(dim[1]);
00114
00115 for (int ilat = 0; ilat < nlat; ilat++)
00116 {
00117
00118
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
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
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)
00149 {
00150 strm << line << endl;
00151 strm.lineNumber++;
00152 line.clear();
00153 }
00154 else if (ilon == nlon-1)
00155 {
00156 line += string(80-line.size(), ' ');
00157 strm << line << endl;
00158 strm.lineNumber++;
00159 line.clear();
00160 }
00161
00162 }
00163
00164 }
00165
00166
00167
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 }
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
00211 if(!strm.headerRead)
00212 {
00213 strm >> strm.header;
00214 }
00215
00216
00217 IonexHeader& hdr = strm.header;
00218
00219
00220 IonexData iod;
00221 *this = iod;
00222
00223
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
00236
00237
00238 int ityp(-1);
00239
00240
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
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
00295
00296
00297
00298 dim[0] = static_cast<int>( ( hdr.lat[1] - hdr.lat[0] )
00299 / hdr.lat[2] + 1 );
00300
00301 dim[1] = static_cast<int>( ( hdr.lon[1] - hdr.lon[0] )
00302 / hdr.lon[2] + 1 );
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
00329 for (int ival = 0,line_ndx = 0; ival < dim[1]; ival++, line_ndx++)
00330 {
00331
00332
00333 if (!(line_ndx % 16))
00334 {
00335
00336
00337 strm.formattedGetLine(line);
00338
00339
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
00351 if (line.size() == 0)
00352 {
00353 continue;
00354 }
00355
00356 }
00357
00358
00359
00360 line.resize(80, ' ');
00361
00362
00363 int val = asInt(line.substr(line_ndx*5,5));
00364
00365
00366 data[ilat*dim[1]+ival] = (val != 9999) ?
00367 std::pow(10.0,exponent)*val : 999.9;
00368
00369 }
00370
00371
00372 ilat++;
00373
00374 }
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
00393
00394
00395
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 }
00409
00410 return;
00411
00412 }
00413
00414
00415
00416
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 }
00436
00437
00438
00452 int IonexData::getIndex( const Triple& in,
00453 const int& igp,
00454 Triple& ABC ) const
00455 throw(InvalidRequest)
00456 {
00457
00458
00459 int nlat = dim[0];
00460 int nlon = dim[1];
00461 int nhgt = dim[2];
00462
00463
00464 int ilat, ilon, ihgt, ncyc;
00465 double xlat, xlon, xhgt;
00466
00467
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
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
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
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;
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 }
00560
00561 }
00562
00563
00564 return ( (ilon-1) + (ilat-1)*nlon + (ihgt-1)*nlon*nlat );
00565
00566 }
00567
00568
00569
00585 double IonexData::getValue( const Position& p ) const
00586 throw(InvalidRequest,FFStreamError)
00587 {
00588
00589
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
00601 Triple ABC[4], inarg;
00602 int e[4];
00603 double xsum = 0.0;
00604
00605
00606
00607 WGS84Geoid WGS84;
00608
00609
00610 double beta = p.theArray[0];
00611 double lambda = p.theArray[1];
00612 double height = p.theArray[2]-WGS84.a();
00613
00614
00615
00616
00617 if (lambda > 180.0)
00618 {
00619 lambda = lambda - 360.0;
00620 }
00621
00622
00623 inarg = Triple( beta, lambda, height);
00624 e[0] = getIndex( inarg, 2, ABC[0] );
00625
00626
00627
00628 double xp( (inarg[1] - ABC[0][1]) / lon[2] );
00629 double xq( (inarg[0] - ABC[0][0]) / lat[2] );
00630
00631
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
00640 inarg = Triple( ABC[0][0], ABC[0][1]+lon[2], ABC[0][2] );
00641 e[1] = getIndex( inarg, 1, ABC[1] );
00642
00643
00644 inarg = Triple( ABC[0][0]+lat[2], ABC[0][1], ABC[0][2] );
00645 e[2] = getIndex( inarg, 1, ABC[2] );
00646
00647
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
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 }
00669
00670
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 }
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 }
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 }
00730
00731
00732
00733 }