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