1 //============================================================================== 2 // 3 // This file is part of GPSTk, the GPS Toolkit. 4 // 5 // The GPSTk is free software; you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published 7 // by the Free Software Foundation; either version 3.0 of the License, or 8 // any later version. 9 // 10 // The GPSTk is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public 16 // License along with GPSTk; if not, write to the Free Software Foundation, 17 // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA 18 // 19 // This software was developed by Applied Research Laboratories at the 20 // University of Texas at Austin. 21 // Copyright 2004-2020, The Board of Regents of The University of Texas System 22 // 23 //============================================================================== 24 25 //============================================================================== 26 // 27 // This software was developed by Applied Research Laboratories at the 28 // University of Texas at Austin, under contract to an agency or agencies 29 // within the U.S. Department of Defense. The U.S. Government retains all 30 // rights to use, duplicate, distribute, disclose, or release this software. 31 // 32 // Pursuant to DoD Directive 523024 33 // 34 // DISTRIBUTION STATEMENT A: This software has been approved for public 35 // release, distribution is unlimited. 36 // 37 //============================================================================== 38 39 /** 40 * @file RinexObsID.cpp 41 * gpstk::RinexObsID - Identifies types of observations 42 */ 43 44 #include "RinexObsID.hpp" 45 #include "RinexSatID.hpp" 46 #include "StringUtils.hpp" 47 #include "Rinex3ObsHeader.hpp" 48 49 namespace gpstk 50 { 51 52 // string containing the system characters for all valid RINEX systems. 53 std::string RinexObsID::validRinexSystems("GRESCJI"); 54 55 // maps between 1-char and 3-char system id 56 std::map<std::string, std::string> RinexObsID::map1to3sys { 57 { "G", "GPS" }, 58 { "R", "GLO" }, 59 { "E", "GAL" }, 60 { "S", "GEO" }, 61 { "C", "BDS" }, 62 { "J", "QZS" }, 63 { "I", "IRN" } 64 }; 65 66 std::map<std::string, std::string> RinexObsID::map3to1sys { 67 { "GPS", "G" }, 68 { "GLO", "R" }, 69 { "GAL", "E" }, 70 { "GEO", "S" }, 71 { "BDS", "C" }, 72 { "QZS", "J" }, 73 { "IRN", "I" } 74 }; 75 76 // string containing the frequency digits for all valid RINEX systems. 77 std::string RinexObsID::validRinexFrequencies("123456789"); 78 79 /// Construct this object from the string specifier 80 RinexObsID(const RinexObsType & rot)81 RinexObsID::RinexObsID(const RinexObsType& rot) 82 : ObsID() 83 { 84 // Note that the choice of tracking code for L1, L2, S1, S2 85 // are arbitrary since they are ambiguous in the rinex 2 86 // specifications 87 // L1 -> L1P; P1 -> C1P; C1 -> C1C; S1 -> S1P; D1 -> D1P 88 if (rot == RinexObsHeader::L1) 89 { 90 type=ObservationType::Phase; 91 band=CarrierBand::L1; 92 code=TrackingCode::P; 93 } 94 else if (rot == RinexObsHeader::P1) 95 { 96 type=ObservationType::Range; 97 band=CarrierBand::L1; 98 code=TrackingCode::P; 99 } 100 else if (rot == RinexObsHeader::C1) 101 { 102 type=ObservationType::Range; 103 band=CarrierBand::L1; 104 code=TrackingCode::CA; 105 } 106 else if (rot == RinexObsHeader::S1) 107 { 108 type=ObservationType::SNR; 109 band=CarrierBand::L1; 110 code=TrackingCode::P; 111 } 112 else if (rot == RinexObsHeader::D1) 113 { 114 type=ObservationType::Doppler; 115 band=CarrierBand::L1; 116 code=TrackingCode::P; 117 } 118 // L2 -> L2P; P2 -> C2P; C2 -> C2X; S2 -> S2P; D2 -> D2P 119 else if (rot == RinexObsHeader::L2) 120 { 121 type=ObservationType::Phase; 122 band=CarrierBand::L2; 123 code=TrackingCode::P; 124 } 125 else if (rot == RinexObsHeader::P2) 126 { 127 type=ObservationType::Range; 128 band=CarrierBand::L2; 129 code=TrackingCode::P; 130 } 131 else if (rot == RinexObsHeader::C2) 132 { 133 type=ObservationType::Range; 134 band=CarrierBand::L2; 135 code=TrackingCode::L2CML; 136 } 137 else if (rot == RinexObsHeader::S2) 138 { 139 type=ObservationType::SNR; 140 band=CarrierBand::L2; 141 code=TrackingCode::P; 142 } 143 else if (rot == RinexObsHeader::D2) 144 { 145 type=ObservationType::Doppler; 146 band=CarrierBand::L2; 147 code=TrackingCode::P; 148 } 149 } 150 151 // Represent this object using the Rinex3 notation asString(double version) const152 std::string RinexObsID::asString(double version) const 153 { 154 char buff[4]; 155 156 buff[0] = ot2char[type]; 157 buff[1] = cb2char[band]; 158 buff[2] = tc2char[code]; 159 if ((fabs(version - 3.02) < 0.005) && (band == CarrierBand::B1) && 160 ((code == TrackingCode::B1I) || (code == TrackingCode::B1Q) || 161 (code == TrackingCode::B1IQ))) 162 { 163 // kludge for RINEX 3.02 BDS codes 164 buff[1] = '1'; 165 } 166 // special cases. 167 if (type == ObservationType::Iono) 168 { 169 buff[2] = ' '; 170 } 171 else if (type == ObservationType::Channel) 172 { 173 buff[1] = '1'; 174 buff[2] = ' '; 175 } 176 buff[3] = 0; 177 return std::string(buff); 178 } 179 180 namespace StringUtils 181 { 182 // convert this object to a string representation asString(const RinexObsID & p)183 std::string asString(const RinexObsID& p) 184 { 185 return p.asString(); 186 } 187 } 188 189 // RINEX 3.03 document, section 5.1 190 // GPS 191 // L1 C,S,L,X,P,W,Y,M,N(but not C1N) G 1 CSLXPWYMN (but not C1N) 192 // L2 C,D,S,L,X,P,W,Y,M,N(but not C2N) G 2 CDSLXPWYMN (but not C1N) 193 // L5 I,Q,X G 5 IQX 194 // GLO 195 // G1 C,P R 1 CP 196 // G2 C,P R 2 CP 197 // GAL 198 // E1 A,B,C,X,Z E 1 ABCXZ 199 // E5a I,Q,X E 5 IQX 200 // E5b I,Q,X E 7 IQX 201 // E5ab I,Q,X E 8 IQX 202 // E6 A,B,C,X,Z E 6 ABCXZ 203 // SBAS 204 // L1 C S 1 C 205 // L5 I,Q,X S 5 IQX 206 // BDS 207 // B1 I,Q,X C 1 IQX 208 // B2 I,Q,X C 7 IQX 209 // B3 I,Q,X C 6 IQX 210 // QZSS 211 // L1 C,S,L,X,Z J 1 CSLXZ 212 // L2 S,L,X J 2 SLX 213 // L5 I,Q,X J 5 IQX 214 // L6 S,L,X J 6 SLX 215 // IRNSS 216 // L5 A,B,C,X I 5 ABCX 217 // L9 A,B,C,X I 9 ABCX 218 219 // Determine if the given ObsID is valid. If the input string is 3 220 // characters long, the system is assumed to be GPS. If this string is 4 221 // characters long, the first character is the system designator as 222 // described in the Rinex 3 specification. isValidRinexObsID(const std::string & strID)223 bool isValidRinexObsID(const std::string& strID) 224 { 225 int i(static_cast<int>(strID.length())-3); 226 if(i < 0 || i > 1) 227 { 228 return false; 229 } 230 231 char sys; 232 std::string id; 233 234 if(i == 1) 235 { 236 sys = strID[0]; 237 id = strID.substr(1); 238 return isValidRinexObsID(id,sys); 239 } 240 241 // test all RINEX systems 242 std::string syss(RinexObsID::validRinexSystems); 243 for(size_t j=0; j<syss.size(); j++) 244 { 245 if(isValidRinexObsID(strID,syss[j])) 246 { 247 return true; 248 } 249 } 250 251 return false; 252 } 253 254 // Determine if the given ObsID is valid, for the given system isValidRinexObsID(const std::string & strID,const char sys)255 bool isValidRinexObsID(const std::string& strID, const char sys) 256 { 257 if(strID.length() != 3) 258 { 259 return false; 260 } 261 char ot(strID[0]); 262 char cb(strID[1]); 263 char tc(strID[2]); 264 const std::string &codes(RinexObsID::validRinexTrackingCodes[sys][cb]); 265 if(ot == ' ' || ot == '-') 266 { 267 return false; 268 } 269 if (RinexObsID::char2ot.find(ot) == RinexObsID::char2ot.end()) 270 { 271 return false; 272 } 273 if(codes.find(std::string(1,tc)) == std::string::npos) 274 { 275 return false; 276 } 277 // special cases for iono and channel num 278 if (ot == 'I' && ((tc != ' ') || (cb < '1') || (cb > '9'))) 279 { 280 return false; 281 } 282 if (ot == 'X' && ((tc != ' ') || (cb != '1'))) 283 { 284 return false; 285 } 286 if (((codes == "* ") || (codes == " *")) && (ot == 'I')) 287 { 288 // channel num must always be "band" 1, but if the system 289 // doesn't have any actual data on "1" band, we don't want 290 // to accidentally say that we can get iono delay data for 291 // a band that isn't valid for the system. 292 return false; 293 } 294 if(sys == 'G' && ot == 'C' && tc == 'N') // the one exception 295 { 296 return false; 297 } 298 299 return true; 300 } 301 dumpCheck(std::ostream & s)302 std::ostream& RinexObsID::dumpCheck(std::ostream& s) 303 { 304 try { 305 const std::string types("CLDS"); 306 std::map<char,std::string>::const_iterator it; 307 308 for(size_t i=0; i<RinexObsID::validRinexSystems.size(); i++) { 309 char csys = RinexObsID::validRinexSystems[i]; 310 std::string sys = RinexObsID::validRinexSystems.substr(i,1); 311 RinexSatID sat(sys); 312 std::string system(sat.systemString()); 313 314 s << "System " << sys << " = " << system << ", frequencies "; 315 for(it = RinexObsID::validRinexTrackingCodes[sys[0]].begin(); 316 it != RinexObsID::validRinexTrackingCodes[sys[0]].end(); ++it) 317 s << it->first; 318 s << std::endl; 319 320 for(it = RinexObsID::validRinexTrackingCodes[sys[0]].begin(); 321 it != RinexObsID::validRinexTrackingCodes[sys[0]].end(); ++it) 322 { 323 s << " " << system << "(" << sys << "), freq " << it->first 324 << ", codes '" << it->second << "'" << std::endl; 325 std::string codes(it->second), str; 326 for(size_t j=0; j<codes.size(); ++j) { 327 std::ostringstream oss1; 328 for(size_t k=0; k<types.size(); ++k) { 329 str = std::string(1,types[k]) + std::string(1,it->first) 330 + std::string(1,codes[j]); 331 std::ostringstream oss; 332 if(!isValidRinexObsID(str,csys)) 333 oss << str << " " << "-INVALID-"; 334 else 335 { 336 RinexObsID robsid(sys+str, 337 Rinex3ObsBase::currentVersion); 338 oss << str << " " << robsid; 339 } 340 oss1 << " " << StringUtils::leftJustify(oss.str(),34); 341 } 342 s << StringUtils::stripTrailing(oss1.str()) << std::endl; 343 } 344 } 345 } 346 } 347 catch(Exception& e) { 348 s << "Exception: " << e.what() << std::endl; 349 GPSTK_RETHROW(e); 350 } 351 352 return s; 353 } 354 355 bool RinexObsID :: equalIndex(const RinexObsID & right) const356 equalIndex(const RinexObsID& right) 357 const 358 { 359 if (type != right.type) 360 return false; 361 if (type == ObservationType::Iono) 362 { 363 // only check band for ionospheric delay. 364 return band == right.band; 365 } 366 if (type == ObservationType::Channel) 367 { 368 // There's only one channel type pseudo-observable 369 return true; 370 } 371 // use the default for everything else 372 return operator==(right); 373 } 374 375 // This is used to register a new RinexObsID & Rinex 3 376 // identifier. The syntax for the Rinex 3 identifier is the 377 // same as for the RinexObsID constructor. If there are spaces 378 // in the provided identifier, they are ignored 379 RinexObsID RinexObsID :: newID(const std::string & strID,const std::string & desc)380 newID(const std::string& strID, const std::string& desc) 381 { 382 if (char2ot.count(strID[0]) && 383 char2cb.count(strID[1]) && 384 char2tc.count(strID[2])) 385 { 386 GPSTK_THROW(InvalidParameter("Identifier " + strID + 387 " already defined.")); 388 } 389 390 return idCreator(strID, desc); 391 } 392 393 394 RinexObsID RinexObsID :: idCreator(const std::string & strID,const std::string & desc)395 idCreator(const std::string& strID, const std::string& desc) 396 { 397 char ot = strID[0]; 398 ObservationType type; 399 if (!char2ot.count(ot)) 400 { 401 type = (ObservationType)otDesc.size(); 402 otDesc[type] = desc; 403 char2ot[ot] = type; 404 ot2char[type] = ot; 405 } 406 else 407 type = char2ot[ot]; 408 409 char cb = strID[1]; 410 CarrierBand band; 411 if (!char2cb.count(cb)) 412 { 413 band = (CarrierBand)cbDesc.size(); 414 cbDesc[band] = desc; 415 char2cb[cb] = band; 416 cb2char[band] = cb; 417 } 418 else 419 band = char2cb[cb]; 420 421 char tc = strID[2]; 422 TrackingCode code; 423 if (!char2tc.count(tc)) 424 { 425 code = (TrackingCode) tcDesc.size(); 426 tcDesc[code] = desc; 427 char2tc[tc] = code; 428 tc2char[code] = tc; 429 } 430 else 431 code = char2tc[tc]; 432 433 return RinexObsID(type, band, code); 434 } 435 } // end namespace 436