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 /// This utility assumes that epochs are in ascending time order
40 
41 #include "FileFilterFrameWithHeader.hpp"
42 #include "Rinex3ObsStream.hpp"
43 #include "Rinex3ObsFilterOperators.hpp"
44 
45 #include "DiffFrame.hpp"
46 
47 #include "YDSTime.hpp"
48 
49 using namespace std;
50 using namespace gpstk;
51 
52 class ROWDiff : public DiffFrame
53 {
54 public:
55       /// Input file does not exist exit code
56    static const int EXIST_ERROR = 2;
57       /// Differences found in input files
58    static const int DIFFS_CODE = 1;
ROWDiff(char * arg0)59    ROWDiff(char* arg0) : DiffFrame(arg0, std::string("RINEX Obs")),
60    precisionOption('p',"precision","Limit data comparison to n decimal places. "
61                                    "Default = 5")
62    {}
63    virtual bool initialize(int argc, char* argv[]) throw();
64 
65 protected:
66    virtual void process();
67    gpstk::CommandOptionWithAnyArg precisionOption;
68 
69 private:
70    int precision;
71    static const int DEFAULT_PRECISION = 5;
72 };
73 
initialize(int argc,char * argv[])74 bool ROWDiff::initialize(int argc, char* argv[]) throw()
75 {
76    if (!DiffFrame::initialize(argc, argv))
77    {
78       return false;
79    }
80    if (precisionOption.getCount())
81    {
82       precision = atoi(precisionOption.getValue()[0].c_str());
83    }
84    else
85    {
86       precision = DEFAULT_PRECISION;
87    }
88    return true;
89 }
90 
process()91 void ROWDiff::process()
92 {
93    gpstk::FileFilterFrameWithHeader<Rinex3ObsStream, Rinex3ObsData, Rinex3ObsHeader>
94       ff1(inputFileOption.getValue()[0]), ff2(inputFileOption.getValue()[1]);
95 
96    // no data?  FIX make this program faster.. if one file
97    // doesn't exist, there's little point in reading any.
98    if (ff1.emptyHeader())
99       cerr << "No header information for " << inputFileOption.getValue()[0]
100            << endl;
101    if (ff2.emptyHeader())
102       cerr << "No header information for " << inputFileOption.getValue()[1]
103            << endl;
104    if (ff1.emptyHeader() || ff2.emptyHeader())
105    {
106       cerr << "Check that files exist." << endl;
107       cerr << "diff failed." << endl;
108       exitCode = EXIST_ERROR;
109       return;
110    }
111 
112    // determine whether the two input files have the same observation types
113 
114    Rinex3ObsHeader header1, header2;
115    Rinex3ObsStream ros1(inputFileOption.getValue()[0]), ros2(inputFileOption.getValue()[1]);
116 
117    ros1 >> header1;
118    ros2 >> header2;
119 
120    // find the obs data intersection
121 
122    if(header1.version != header2.version)
123    {
124       cout << "File 1 and file 2 are not the same RINEX version" << endl;
125       // Reading a R2 file in translates/guesses its obsTypes into R3-style obsIDs,
126       // but translating the R3 obsIDs to R2 is more likely to match.
127       // So map R3 -> R2 then change the R2 header to match.
128       if (header1.version < 3 && header2.version >= 3)
129       {
130          header2.prepareVer2Write();
131          Rinex3ObsHeader::RinexObsVec r3ov;
132          Rinex3ObsHeader::StringVec::iterator r2it = header1.R2ObsTypes.begin();
133          while (r2it != header1.R2ObsTypes.end())
134          {
135             r3ov.push_back(header2.mapSysR2toR3ObsID["G"][*r2it]);
136             r2it++;
137          }
138          header1.mapObsTypes["G"] = r3ov;
139          ff1.frontHeader().mapObsTypes["G"] = r3ov;
140       }
141       else if (header2.version < 3 && header1.version >= 3)
142       {
143          header1.prepareVer2Write();
144          Rinex3ObsHeader::RinexObsVec r3ov;
145          Rinex3ObsHeader::StringVec::iterator r2it = header2.R2ObsTypes.begin();
146          while (r2it != header2.R2ObsTypes.end())
147          {
148             r3ov.push_back(header1.mapSysR2toR3ObsID["G"][*r2it]);
149             r2it++;
150          }
151          header2.mapObsTypes["G"] = r3ov;
152          ff2.frontHeader().mapObsTypes["G"] = r3ov;
153       }
154    }
155 
156    // Find out what obs IDs header 1 has that header 2 does/ doesn't have
157    // add those to intersectionRom/ diffRom respectively.
158    cout << "Comparing the following fields:" << endl;
159    Rinex3ObsHeader::RinexObsMap diffRom;
160    Rinex3ObsHeader::RinexObsMap intersectRom;
161    for (Rinex3ObsHeader::RinexObsMap::iterator mit = header1.mapObsTypes.begin();
162         mit != header1.mapObsTypes.end();
163         mit++)
164    {
165       string sysChar = mit->first;
166       cout << sysChar << ": ";
167       for(Rinex3ObsHeader::RinexObsVec::iterator ID1 = mit->second.begin();
168          ID1 != mit->second.end();
169          ID1++)
170       {
171          try
172          {
173             header2.getObsIndex(sysChar, *ID1);
174             intersectRom[sysChar].push_back(*ID1);
175             cout << " " << ID1->asString();
176          }
177          catch(...)
178          {
179             diffRom[sysChar].push_back(*ID1);
180          }
181       }
182       cout << endl;
183    }
184 
185    // Find out what obs IDs header 2 has that header 1 doesn't
186    // and add them to diffRom
187    for (Rinex3ObsHeader::RinexObsMap::iterator mit = header2.mapObsTypes.begin();
188         mit != header2.mapObsTypes.end();
189         mit++)
190    {
191       string sysChar = mit->first;
192       for(Rinex3ObsHeader::RinexObsVec::iterator ID2 = mit->second.begin();
193           ID2 != mit->second.end();
194           ID2++)
195       {
196          try
197          {
198             header1.getObsIndex(sysChar, *ID2);
199          }
200          catch(...)
201          {
202             diffRom[sysChar].push_back(*ID2);
203          }
204       }
205    }
206 
207    // Print out the differences between the obs IDs in header1 and header2
208    if(!diffRom.empty())
209    {
210       cout << "Ignoring unshared obs:" << endl;
211       for (Rinex3ObsHeader::RinexObsMap::iterator mit = diffRom.begin();
212            mit != diffRom.end();
213            mit++)
214       {
215          string sysChar = mit->first;
216          cout << sysChar << ": ";
217          for (Rinex3ObsHeader::RinexObsVec::iterator ID = mit->second.begin();
218               ID != mit->second.end();
219               ID++)
220          {
221             cout << ID->asString() << " ";
222          }
223          cout << endl;
224       }
225    }
226 
227    std::list<Rinex3ObsData> a =
228       ff1.halfDiff(ff2,Rinex3ObsDataOperatorLessThanFull(intersectRom), precision);
229    std::list<Rinex3ObsData> b =
230       ff2.halfDiff(ff1, Rinex3ObsDataOperatorLessThanFull(intersectRom), precision);
231 
232    pair< list<Rinex3ObsData>, list<Rinex3ObsData> > difflist =
233       pair< list<Rinex3ObsData>, list<Rinex3ObsData> >( a, b);
234 
235    if (difflist.first.empty() && difflist.second.empty())
236    {
237       //Indicate to the user, before exiting, that rowdiff
238       //performed properly and no differences were found.
239       cout << "For the observation types that were compared, "
240            << "no differences were found." << endl;
241       exitCode = 0;
242       return;
243    }
244 
245       // differences found
246    exitCode = DIFFS_CODE;
247 
248    list<Rinex3ObsData>::iterator firstDiffItr = difflist.first.begin();
249    list<Rinex3ObsData>::iterator secondDiffItr = difflist.second.begin();
250    while(firstDiffItr != difflist.first.end() || secondDiffItr != difflist.second.end())
251    {
252          //Epoch in both files
253       if(firstDiffItr->time == secondDiffItr->time)
254       {
255          Rinex3ObsData::DataMap::iterator firstObsItr = firstDiffItr->obs.begin();
256          Rinex3ObsData::DataMap::iterator secondObsItr = secondDiffItr->obs.begin();
257             // For each satellite
258          while(firstObsItr != firstDiffItr->obs.end() || secondObsItr != secondDiffItr->obs.end())
259          {
260                // Both files have data for that satellite
261             if(firstObsItr->first == secondObsItr->first)
262             {
263                string sysString = string(1,firstObsItr->first.systemChar());
264                cout << "-" << setw(3) << (static_cast<YDSTime>(firstDiffItr->time))
265                     << ' ' << setw(2) << firstObsItr->first << ' ';
266                Rinex3ObsHeader::RinexObsVec::iterator romIt;
267                for (romIt = intersectRom[sysString].begin();
268                     romIt != intersectRom[sysString].end();
269                     romIt++)
270                {
271                   size_t idx1 = header1.getObsIndex(sysString,*romIt);
272                   size_t idx2 = header2.getObsIndex(sysString,*romIt);
273                   cout << setw(15) << setprecision(3) << fixed
274                        << firstObsItr->second[idx1].data - secondObsItr->second[idx2].data
275                        << ' ' << romIt->asString() << ' ';
276                }
277                firstObsItr++;
278                secondObsItr++;
279             }
280                // Only file 1 has data for that satellite
281             else if (
282                (firstObsItr != firstDiffItr->obs.end()) &&
283                (
284                   (secondObsItr == secondDiffItr->obs.end()) ||
285                   (firstObsItr->first.id < secondObsItr->first.id))
286                )
287             {
288                string sysString = string(1,firstObsItr->first.systemChar());
289                cout << "<" << setw(3) << (static_cast<YDSTime>(firstDiffItr->time))
290                     << ' ' << setw(2) << firstObsItr->first << ' ';
291                Rinex3ObsHeader::RinexObsVec::iterator romIt;
292                for (romIt = intersectRom[sysString].begin();
293                     romIt != intersectRom[sysString].end();
294                     romIt++)
295                {
296                   size_t idx = header1.getObsIndex(sysString,*romIt);
297                   cout << setw(15) << setprecision(3) << fixed
298                        << firstObsItr->second[idx].data << ' ' << romIt->asString() << ' ';
299                }
300                firstObsItr++;
301             }
302                // Only file 2 has data for that satellite
303             else if (secondObsItr != secondDiffItr->obs.end())
304             {
305                string sysString = string(1,secondObsItr->first.systemChar());
306                cout << ">" << setw(3) << (static_cast<YDSTime>(secondDiffItr->time))
307                     << ' ' << setw(2) << secondObsItr->first << ' ';
308                Rinex3ObsHeader::RinexObsVec::iterator romIt;
309                for (romIt = intersectRom[sysString].begin();
310                     romIt != intersectRom[sysString].end();
311                     romIt++)
312                {
313                   size_t idx = header2.getObsIndex(sysString,*romIt);
314                   cout << setw(15) << setprecision(3) << fixed
315                        << secondObsItr->second[idx].data << ' ' << romIt->asString() << ' ';
316                }
317                secondObsItr++;
318             }
319             cout << endl;
320          }
321 
322          firstDiffItr++;
323          secondDiffItr++;
324       }
325          //Epoch only in first file
326       else if((firstDiffItr != difflist.first.end()) && firstDiffItr->time < secondDiffItr->time)
327       {
328          Rinex3ObsData::DataMap::iterator firstObsItr = firstDiffItr->obs.begin();
329          for (;firstObsItr != firstDiffItr->obs.end(); firstObsItr++)
330          {
331             cout << "<" << setw(3) << (static_cast<YDSTime>(firstDiffItr->time))
332                  << ' ' << setw(2) << firstObsItr->first << ' ';
333             string sysString = string(1,firstObsItr->first.systemChar());
334             Rinex3ObsHeader::RinexObsVec::iterator romIt;
335             for (romIt = intersectRom[sysString].begin();
336                  romIt != intersectRom[sysString].end();
337                  romIt++)
338             {
339                size_t idx = header1.getObsIndex(sysString,*romIt);
340                cout << setw(15) << setprecision(3) << fixed
341                     << firstObsItr->second[idx].data << ' ' << romIt->asString() << ' ';
342             }
343             cout << endl;
344          }
345          firstDiffItr++;
346       }
347          //Epoch only in second file
348       else if (secondDiffItr != difflist.second.end()) // && (secondDiffItr->time < firstDiffItr->time)
349       {
350          Rinex3ObsData::DataMap::iterator secondObsItr = secondDiffItr->obs.begin();
351          for (;secondObsItr != secondDiffItr->obs.end(); secondObsItr++)
352          {
353             cout << ">" << setw(3) << (static_cast<YDSTime>(secondDiffItr->time))
354                  << ' ' << setw(2) << secondObsItr->first << ' ';
355             string sysString = string(1,secondObsItr->first.systemChar());
356             Rinex3ObsHeader::RinexObsVec::iterator romIt;
357             for (romIt = intersectRom[sysString].begin();
358                  romIt != intersectRom[sysString].end();
359                  romIt++)
360             {
361                size_t idx = header2.getObsIndex(sysString,*romIt);
362                cout << setw(15) << setprecision(3) << fixed
363                     << secondObsItr->second[idx].data << ' ' << romIt->asString() << ' ';
364             }
365             cout << endl;
366          }
367          secondDiffItr++;
368       }
369    }
370 }
371 
main(int argc,char * argv[])372 int main(int argc, char* argv[])
373 {
374    try
375    {
376       ROWDiff m(argv[0]);
377       if (!m.initialize(argc, argv))
378          return m.exitCode;
379       if (!m.run())
380          return m.exitCode;
381 
382       return m.exitCode;
383    }
384    catch(Exception& e)
385    {
386       cout << e << endl;
387    }
388    catch(std::exception& e)
389    {
390       cout << e.what() << endl;
391    }
392    catch(...)
393    {
394       cout << "unknown error" << endl;
395    }
396       // only reach this point if an exception was caught
397    return BasicFramework::EXCEPTION_ERROR;
398 }
399