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 // DiscFix.cpp Read a RINEX observation file containing dual frequency
41 // pseudorange and phase, separate the data into satellite passes, and then
42 // find and estimate discontinuities in the phase (using the GPSTk Discontinuity
43 // Corrector (GDC) in DiscCorr.hpp).
44 // The corrected data can be written out to another RINEX file, plus there is the
45 // option to smooth the pseudorange and/or debias the phase (SatPass::smooth()).
46 //------------------------------------------------------------------------------------
47
48 /// @file DiscFix.cpp
49 /// Correct phase discontinuities (cycle slips) in dual frequency data in a RINEX
50 /// observation file, plus optionally smooth the pseudoranges and/or debias the phases
51
52 // system
53 #include <ctime>
54 #include <cstring>
55 #include <string>
56 #include <vector>
57 #include <iostream>
58 #include <fstream>
59 #include <algorithm>
60 // gpstk
61 #include "MathBase.hpp"
62 #include "RinexSatID.hpp"
63 #include "RinexObsBase.hpp"
64 #include "RinexObsData.hpp"
65 #include "RinexObsHeader.hpp"
66 #include "RinexObsStream.hpp"
67 #include "CommonTime.hpp"
68 #include "CivilTime.hpp"
69 #include "GPSWeekSecond.hpp"
70 #include "Epoch.hpp"
71 #include "TimeString.hpp"
72 #include "StringUtils.hpp"
73 // geomatics
74 #include "logstream.hpp"
75 #include "stl_helpers.hpp"
76 #include "expandtilde.hpp"
77 #include "CommandLine.hpp"
78 #include "SatPass.hpp"
79 #include "SatPassUtilities.hpp"
80 #include "DiscCorr.hpp"
81
82 using namespace std;
83 using namespace gpstk;
84 using namespace StringUtils;
85
86 //------------------------------------------------------------------------------------
87 //------------------------------------------------------------------------------------
88 // prgm data
89 static const string DiscFixVersion = string("6.3 2/4/16");
90 static const string PrgmName("DiscFix");
91 // a convenience
92 static const string L1("L1"),L2("L2"),P1("P1"),P2("P2"),C1("C1"),C2("C2");
93
94 // all input and global data
95 typedef struct configuration {
96 // input
97 string inputPath;
98 vector<string> obsfiles;
99 // data flow
100 double decimate;
101 Epoch begTime, endTime;
102 double MaxGap;
103 //int MinPts;
104 // processing
105 double dt0,dt; // data interval in input file, and final (decimated) dt
106 bool noCA1,noCA2,useCA1,forceCA1,useCA2,forceCA2,doGLO;
107 vector<RinexSatID> exSat;
108 RinexSatID SVonly;
109 // output files
110 string LogFile,OutFile;
111 ofstream oflog,ofout;
112 string format;
113 int round;
114 // output
115 string OutRinexObs;
116 string HDPrgm; // header of output RINEX file
117 string HDRunby;
118 string HDObs;
119 string HDAgency;
120 string HDMarker;
121 string HDNumber;
122 int NrecOut;
123 Epoch FirstEpoch,LastEpoch;
124 bool smoothPR,smoothPH,smooth;
125 int debug;
126 bool verbose,DChelp;
127 vector<string> DCcmds; // all the --DC... on the cmd line
128 // estimate dt from data
129 double estdt[9];
130 int ndt[9];
131 // input and/or compute GLONASS frequency channel for each GLO satellite
132 map<RinexSatID,int> GLOfreqChannel;
133
134 // summary of cmd line input
135 string cmdlineSum;
136
137 string Title,Date;
138 Epoch PrgmEpoch;
139 RinexObsStream irfstr; //, orfstr; // input and output RINEX files
140 RinexObsHeader rhead;
141 int inP1,inP2,inL1,inL2; // indexes in rhead of C1/P1, P2, L1 and L2
142 string P1C1,P2C2; // either P1 or C1
143
144 // Data for an entire pass is stored in SatPass object
145 // This vector contains all the SatPass's defined so far
146 // The parallel vector holds an iterator for use in writing out the data
147 vector<SatPass> SPList;
148
149 // list of observation types to be included in each SatPass
150 vector<string> obstypes;
151
152 // this is a map relating a satellite to the index in SVPList of the current pass
153 vector<unsigned int> SPIndexList;
154 map<RinexSatID,int> SatToCurrentIndexMap;
155
156 GDCconfiguration GDConfig; // the discontinuity corrector configuration
157 } DFConfig;
158
159 // declare (one only) global configuration object
160 DFConfig cfg;
161
162 //------------------------------------------------------------------------------------
163 //------------------------------------------------------------------------------------
164 // prototypes
165 /**
166 * @throw Exception
167 */
168 int GetCommandLine(int argc, char **argv);
169 /**
170 * @throw Exception
171 */
172 void DumpConfiguration(void);
173 /**
174 * @throw Exception
175 */
176 int Initialize(void);
177 /**
178 * @throw Exception
179 */
180 int ShallowCheck(void); // called by Initialize()
181 /**
182 * @throw Exception
183 */
184 int WriteToRINEX(void);
185 void PrintSPList(ostream&, string, vector<SatPass>&);
186
187 //------------------------------------------------------------------------------------
188 //------------------------------------------------------------------------------------
main(int argc,char ** argv)189 int main(int argc, char **argv)
190 {
191 try {
192 clock_t totaltime = clock();
193 int i,nread,npass,iret;
194 Epoch ttag;
195 string msg;
196 vector<string> EditCmds;
197
198 // Title and description
199 cfg.Title = PrgmName+", part of the GPS ToolKit, Ver "+DiscFixVersion+", Run ";
200 cfg.PrgmEpoch.setLocalTime();
201 cfg.Date = printTime(cfg.PrgmEpoch,"%04Y/%02m/%02d %02H:%02M:%02S");
202 cfg.Title += cfg.Date;
203 cout << cfg.Title << endl;
204
205 for(;;) { // a convenience
206 // -------------------------------- get command line
207 iret = GetCommandLine(argc, argv);
208 if(iret) break;
209
210 // -------------------------------- initialize
211 iret = Initialize();
212 if(iret) break;
213
214 // -------------------------------- read in the data
215 try {
216 nread = SatPassFromRinexFiles(cfg.obsfiles, cfg.obstypes, cfg.dt0,
217 cfg.SPList, cfg.exSat, true, cfg.begTime, cfg.endTime);
218 LOG(VERBOSE) << "Successfully read " << nread << " RINEX obs files.";
219 }
220 catch(Exception &e) { // time tags out of order or a read error
221 string what(e.what());
222 string::size_type pos(what.find("Time tags out of order", 0));
223 if(pos != string::npos) {
224 pos = what.find("\n");
225 if(pos != string::npos) what.erase(pos);
226 LOG(ERROR) << "Error - " << what;
227 }
228 else { GPSTK_RETHROW(e); }
229 }
230
231 if(nread != cfg.obsfiles.size()) {
232 iret = -7;
233 break;
234 }
235 if(cfg.SPList.size() <= 0) {
236 LOG(ERROR) << "Error - no data found.";
237 iret = -8;
238 break;
239 }
240
241 // -------------------------------- exclude satellites
242 for(npass=0; npass<cfg.SPList.size(); npass++) {
243 RinexSatID sat(cfg.SPList[npass].getSat());
244
245 if(cfg.SVonly.id != -1 && sat != cfg.SVonly) {
246 cfg.SPList[npass].status() = -1;
247 LOG(VERBOSE) << "Exclude pass #" << setw(2) << npass+1 << " (" << sat
248 << ") as only one satellite is to be processed.";
249 }
250 // done in SatPassFromRinex()
251 //else if(vectorindex(cfg.exSat,sat) != -1) {
252 // cfg.SPList[npass].status() = -1;
253 // LOG(VERBOSE) << "Exclude pass #" << setw(2) << npass+1 << " (" << sat
254 // << ") as satellite is excluded explicitly.";
255 //}
256 //else if((!cfg.doGLO && sat.system != SatelliteSystem::GPS) ||
257 // ( cfg.doGLO && sat.system != SatelliteSystem::GPS
258 // && sat.system != SatelliteSystem::Glonass))
259 //{
260 // cfg.SPList[npass].status() = -1;
261 // LOG(VERBOSE) << "Exclude pass #" << setw(2) << npass+1 << " (" << sat
262 // << ") as satellite system is excluded.";
263 //}
264 else if(cfg.SPList[npass].size()==0 || cfg.SPList[npass].getNgood()==0) {
265 cfg.SPList[npass].status() = -1;
266 LOG(VERBOSE) << "Exclude pass #" << setw(2) << npass+1 << " (" << sat
267 << ") as it is empty.";
268 }
269 //else if(cfg.SPList[npass].getNgood() < minpass) {
270 // cfg.SPList[npass].status() = -1;
271 // LOG(VERBOSE) << "Exclude pass #" << setw(2) << npass+1 << " (" << sat
272 // << ") as it is too small (" << cfg.SPList[npass].getNgood()
273 // << " < " << minpass << ").";
274 //}
275
276 } // end for() over all passes
277
278 // remove the invalid ones
279 vector<SatPass>::iterator it(cfg.SPList.begin());
280 while(it != cfg.SPList.end()) {
281 if(it->status() == -1)
282 it = cfg.SPList.erase(it); // remove it; it points to next pass
283 else
284 ++it; // go to next pass
285 }
286
287 // is there anything left?
288 if(cfg.SPList.size() <= 0) {
289 LOG(ERROR) << "Error - no data found.";
290 iret = -9;
291 break;
292 }
293
294 // -------------------------------- decimate
295 // set the data interval, and decimate if the user input is N*raw interval
296 if(cfg.decimate < 0.0) {
297 LOG(INFO) << PrgmName << ": decimation timestep must be positive";
298 iret = -2;
299 break;
300 }
301 else if(cfg.decimate == 0.0) {
302 cfg.dt = cfg.dt0; // just go with raw interval
303 }
304 else if(fmod(cfg.decimate,cfg.dt0) < 0.01) { // decimate
305 int N(0.5+cfg.decimate/cfg.dt0);
306 ttag = cfg.SPList[0].getFirstTime();
307 int n(ttag.GPSsow()/cfg.decimate);
308 //ttag.setGPSfullweek(ttag.GPSfullweek(), n*cfg.decimate);
309 GPSWeekSecond gpst(ttag.GPSweek(), n*cfg.decimate);
310 ttag = static_cast<Epoch>(gpst);
311 for(npass=0; npass<cfg.SPList.size(); npass++)
312 cfg.SPList[npass].decimate(N, ttag);
313 cfg.dt = cfg.decimate;
314 }
315 else { // can't decimate
316 LOG(ERROR) << "Error - cannot decimate; input time step ("
317 << asString(cfg.decimate,2)
318 << ") is not an even multiple of the data rate ("
319 << asString(cfg.dt0,2) << ")";
320 iret = -10;
321 break;
322 }
323
324 cfg.GDConfig.setParameter(string("DT:")+asString(cfg.dt,2));
325 cfg.GDConfig.setParameter(string("MaxGap:")+asString(cfg.MaxGap,2));
326 LOG(INFO) << "\nHere is the current GPSTk DC configuration:";
327 cfg.GDConfig.DisplayParameterUsage(LOGstrm,(cfg.DChelp && cfg.verbose));
328 LOG(INFO) << "";
329
330 // -------------------------------- call the GDC, output results and smooth
331 for(npass=0; npass<cfg.SPList.size(); npass++) {
332
333 LOG(INFO) << "Proc " << setw(2) << npass+1 << " " << cfg.SPList[npass];
334 //cfg.SPList[npass].dump(*pLOGstrm,"RAW"); // temp
335
336 msg = "";
337 iret=DiscontinuityCorrector(cfg.SPList[npass],cfg.GDConfig,EditCmds,msg);
338 if(iret != 0) {
339 cfg.SPList[npass].status() = -1; // failed
340 LOG(ERROR) << "GDC failed (" << iret << " "
341 << (iret==-1 ? "Singularity":
342 (iret==-3 ? "DT not set, or memory":
343 (iret==-4 ? "No data":"Bad input")))
344 << ") for pass "
345 << npass+1 << " :\n" << msg;
346 continue;
347 }
348 //if(cfg.verbose && LOGlevel < ConfigureLOG::Level("VERBOSE"))
349 LOG(INFO) << msg;
350
351 ttag = cfg.SPList[npass].getFirstGoodTime();
352 if(ttag < cfg.FirstEpoch) cfg.FirstEpoch = ttag;
353 ttag = cfg.SPList[npass].getLastTime();
354 if(ttag > cfg.LastEpoch) cfg.LastEpoch = ttag;
355
356 // output editing commands
357 for(i=0; i<EditCmds.size(); i++)
358 cfg.ofout << EditCmds[i] << " # pass " << npass+1 << endl;
359 EditCmds.clear();
360
361 // smooth pseudorange and debias phase
362 if(cfg.smooth) {
363 cfg.SPList[npass].smooth(cfg.smoothPR, cfg.smoothPH, msg);
364 LOG(INFO) << msg;
365 }
366
367 } // end for() loop over passes
368
369 // -------------------------------- write to RINEX
370 iret = WriteToRINEX();
371 if(iret) break;
372
373 // -------------------------------- print a summary
374 PrintSPList(LOGstrm,"Fine",cfg.SPList);
375
376 break;
377 }
378
379 // timing
380 totaltime = clock()-totaltime;
381 LOG(INFO) << PrgmName << " timing: " << fixed << setprecision(3)
382 << double(totaltime)/double(CLOCKS_PER_SEC) << " seconds.\n";
383 cout << PrgmName << " timing: " << fixed << setprecision(3)
384 << double(totaltime)/double(CLOCKS_PER_SEC) << " seconds.\n";
385
386 // clean up
387 cfg.ofout.close();
388 cfg.oflog.close();
389
390 return iret;
391 }
392 catch(Exception& e) {
393 cfg.oflog << e.what();
394 cout << e.what();
395 }
396 catch (...) {
397 cfg.oflog << PrgmName << ": Unknown error. Abort." << endl;
398 cout << PrgmName << ": Unknown error. Abort." << endl;
399 }
400
401 return -1;
402
403 } // end main()
404
405 //------------------------------------------------------------------------------------
Initialize(void)406 int Initialize(void)
407 {
408 try {
409 int i;
410
411 // open the log file
412 cfg.oflog.open(cfg.LogFile.c_str(),ios::out);
413 if(!cfg.oflog.is_open()) {
414 cerr << PrgmName << " failed to open log file " << cfg.LogFile << ".\n";
415 return -3;
416 }
417
418 // last write to screen
419 LOG(INFO) << PrgmName << " is writing to log file " << cfg.LogFile;
420
421 // attach LOG to the log file
422 pLOGstrm = &cfg.oflog;
423 ConfigureLOG::ReportLevels() = false;
424 ConfigureLOG::ReportTimeTags() = false;
425
426 // set the DC commands now (setParameter may write to log file)
427 for(i=0; i<cfg.DCcmds.size(); i++) {
428 cfg.GDConfig.setParameter(cfg.DCcmds[i]);
429 if(cfg.DCcmds[i].substr(0,5) == string("Debug")) {
430 string msg("DEBUG");
431 msg += asString(cfg.DCcmds[i].substr(6));
432 LOGlevel = ConfigureLOG::Level(msg);
433 }
434 else if(cfg.DCcmds[i].substr(0,4) == string("--DC DT=<dt>")) {
435 LOG(WARNING) << "Warning - Input of the timestep with --DCDT is ignored.";
436 }
437 }
438
439 if(cfg.verbose && LOGlevel < ConfigureLOG::Level("VERBOSE"))
440 LOGlevel = ConfigureLOG::Level("VERBOSE");
441
442 LOG(INFO) << cfg.Title;
443 //LOG(INFO) << "LOG level is at " << ConfigureLOG::ToString(LOGlevel);
444
445 // open input obs files, read header and some of the data
446 i = ShallowCheck();
447 if(i) return i;
448
449 // allow GDC to output to log file
450 cfg.GDConfig.setDebugStream(cfg.oflog);
451 if(cfg.P1C1 == C1) cfg.GDConfig.setParameter("useCA1:1"); // Shallow sets P1C1
452 if(cfg.P2C2 == C2) cfg.GDConfig.setParameter("useCA2:1"); // Shallow sets P2C2
453
454 cfg.SVonly.setfill('0'); // set fill char in RinexSatID
455
456 // catch input trap
457 if(!cfg.doGLO && cfg.SVonly.system == SatelliteSystem::Glonass) {
458 LOG(VERBOSE) << "SVonly is GLONASS - turn on processing of GLONASS";
459 cfg.doGLO = true;
460 }
461
462 // write to log file
463 DumpConfiguration();
464
465 cfg.FirstEpoch = CommonTime::END_OF_TIME;
466 cfg.LastEpoch = CommonTime::BEGINNING_OF_TIME;
467 // configure SatPass
468 {
469 cfg.obstypes.push_back(L1); // DiscFix requires these 4 observables only
470 cfg.obstypes.push_back(L2);
471 cfg.obstypes.push_back(cfg.P1C1);
472 cfg.obstypes.push_back(P2);
473
474 SatPass dummy(cfg.SVonly,cfg.dt0);
475 dummy.setMaxGap(cfg.MaxGap);
476 dummy.setOutputFormat(cfg.format,cfg.round);
477 }
478
479 // open output file
480 // output for editing commands - write to this in ProcessSatPass()
481 cfg.ofout.open(cfg.OutFile.c_str());
482 if(!cfg.oflog.is_open()) {
483 cfg.oflog << "Error: " << PrgmName << " failed to open output file "
484 << cfg.OutFile << endl;
485 return -5;
486 }
487 else
488 LOG(INFO) << PrgmName << " is writing to output file " << cfg.OutFile;
489
490 return 0;
491 }
492 catch(Exception& e) { GPSTK_RETHROW(e); }
493 }
494
495 //------------------------------------------------------------------------------------
496 // open the input files, read the headers and some of the data. Determine dt0 & C1/P1.
ShallowCheck(void)497 int ShallowCheck(void)
498 {
499 try {
500 bool inputValid(true);
501 int i,j;
502 string msg;
503 vector<bool> fileValid;
504 vector<int> fileHasP1C1,fileHasP2C2;
505 vector<long> filesize;
506 vector<double> fileDT;
507 vector<Epoch> fileFirst;
508
509 // open obs files and read few epochs; test validity and determine content
510 for(i=0; i<cfg.obsfiles.size(); i++) {
511 fileValid.push_back(false);
512 fileHasP1C1.push_back(0); // 0: don't know, 1 C1, 2 P1, 3 both
513 fileHasP2C2.push_back(0); // 0: don't know, 1 C2, 2 P2, 3 both
514 filesize.push_back(long(0));
515 fileDT.push_back(-1.0);
516 fileFirst.push_back(CommonTime::BEGINNING_OF_TIME);
517
518 // open obs files
519 RinexObsStream rstrm;
520 rstrm.open(cfg.obsfiles[i].c_str(),ios_base::in);
521 if(!rstrm.is_open()) {
522 LOG(ERROR) << " Error - Observation file " << cfg.obsfiles[i]
523 << " could not be opened.";
524 inputValid = false;
525 }
526 else {
527 LOG(DEBUG) << "Opened file " << cfg.obsfiles[i] << flush;
528 rstrm.exceptions(ios_base::failbit);
529
530 // get file size
531 long begin = rstrm.tellg();
532 rstrm.seekg(0,ios::end);
533 long end = rstrm.tellg();
534 rstrm.seekg(0,ios::beg);
535 filesize[i] = end-begin;
536
537 // read header
538 bool rinexok=true;
539 //RinexObsHeader head;
540 try { rstrm >> cfg.rhead; }
541 catch(Exception& e) { rinexok = false; }
542 catch(exception& e) { rinexok = false; }
543
544 // is header valid?
545 LOG(DEBUG) << "Read header for " << cfg.obsfiles[i];
546 if(rinexok && !cfg.rhead.isValid()) { rinexok = false; }
547 if(!rinexok) {
548 LOG(ERROR) << " Error - Observation file " << cfg.obsfiles[i]
549 << " does not contain valid RINEX observations.";
550 inputValid = false;
551 }
552 // look for L1,L2,C1/P1,P2 observations, and antenna height
553 else {
554 unsigned int found=0;
555 for(j=0; j<cfg.rhead.obsTypeList.size(); j++) {
556 if(cfg.rhead.obsTypeList[j] == RinexObsHeader::L1) found +=32;
557 if(cfg.rhead.obsTypeList[j] == RinexObsHeader::L2) found +=16; // 48
558 if(cfg.rhead.obsTypeList[j] == RinexObsHeader::P1) found += 8; // 56
559 if(cfg.rhead.obsTypeList[j] == RinexObsHeader::P2) found += 4; // 60
560 if(cfg.rhead.obsTypeList[j] == RinexObsHeader::C1) found += 2; // 62
561 if(cfg.rhead.obsTypeList[j] == RinexObsHeader::C2) found += 1; // 63
562 }
563 if(found & 8) fileHasP1C1[i] += 2; // has P1
564 if(found & 2) fileHasP1C1[i] += 1; // has C1
565 if(found & 4) fileHasP2C2[i] += 2; // has P2
566 if(found & 1) fileHasP2C2[i] += 1; // has C2
567 if(!(found & 32)) LOG(ERROR) << " Error - Observation file "
568 << cfg.obsfiles[i] << " has no L1 data.";
569 if(!(found & 16)) LOG(ERROR) << " Error - Observation file "
570 << cfg.obsfiles[i] << " has no L2 data.";
571 if(fileHasP1C1[i] == 0) LOG(ERROR) << " Error - Observation file "
572 << cfg.obsfiles[i] << " has no P1 or C1 data.";
573 if(fileHasP2C2[i] == 0) LOG(ERROR) << " Error - Observation file "
574 << cfg.obsfiles[i] << " has no P2 or C2 data.";
575
576 if(!(found & 48) || fileHasP1C1[i]*fileHasP2C2[i] == 0) {
577 inputValid = false;
578 }
579 else {
580 fileFirst[i] = cfg.rhead.firstObs;
581
582 // read a few obs to determine data interval
583 const int N=10; // how many epochs to read?
584 begin = rstrm.tellg();
585 int jj,kk,nleast,nepochs=0,ndt[9]={-1,-1,-1, -1,-1,-1, -1,-1,-1};
586 double dt,bestdt[9];
587 Epoch first=CommonTime::END_OF_TIME,prev=CommonTime::END_OF_TIME;
588 RinexObsData robs;
589 while(1) {
590 try { rstrm >> robs; }
591 catch(Exception& e) { break; } // simply quit if meet failure
592 if(!rstrm) break; // or EOF
593 dt = robs.time - prev;
594 if(dt > 0.0) {
595 for(j=0; j<9; j++) {
596 if(ndt[j] <= 0) { bestdt[j]=dt; ndt[j]=1; break; }
597 if(fabs(dt-bestdt[j]) < 0.002) { ndt[j]++; break; }
598 if(j == 8) {
599 kk=0; nleast=ndt[kk];
600 for(jj=1; jj<9; jj++) if(ndt[jj] <= nleast) {
601 kk=jj; nleast=ndt[jj];
602 }
603 ndt[kk]=1; bestdt[kk]=dt;
604 }
605 }
606 }
607 if(++nepochs >= N) break;
608 prev = robs.time;
609 if(first == CommonTime::END_OF_TIME) first = robs.time;
610 }
611
612 // save the results
613 for(jj=1,kk=0; jj<9; jj++) if(ndt[jj]>ndt[kk]) kk=jj;
614 // round to nearest 0.1 second
615 fileDT[i] = double(0.1*int(0.5+bestdt[kk]/0.1));
616 fileValid[i] = true;
617
618 // dump the results
619 LOG(VERBOSE) << " RINEX observation file " << cfg.obsfiles[i]
620 << " starts at "
621 << printTime(first,"%04Y/%02m/%02d %02H:%02M:%02S = %F %10.3g");
622 LOG(VERBOSE) << " RINEX observation file " << cfg.obsfiles[i]
623 << " has data interval " << asString(fileDT[i],2) << " sec,"
624 << " size " << filesize[i] << " bytes, and types"
625 << (found & 16 ? " L1":"")
626 << (found & 8 ? " L2":"")
627 << (found & 2 ? " P1":"")
628 << (found & 4 ? " P2":"")
629 << (found & 1 ? " C1":"");
630 }
631 }
632 rstrm.clear();
633 rstrm.close();
634 } // end reading file
635
636 LOG(DEBUG) << "End reading file " << cfg.obsfiles[i] << flush;
637
638 } // end loop over cfg.obsfiles
639 cfg.dt0 = fileDT[0];
640
641 LOG(VERBOSE)
642 << "The data interval in input file is " << fixed << setprecision(2) << cfg.dt0;
643
644 // test that obs files agree on data interval, and look for C1/P1
645 bool P1missing(false), C1missing(false);
646 bool P2missing(false), C2missing(false);
647 for(j=0; j<cfg.obsfiles.size(); j++) {
648 if(fabs(cfg.dt0 - fileDT[j]) > 0.001) {
649 msg = string(" Error - RINEX Obs files data intervals differ: ")
650 + StringUtils::asString(fileDT[j],2) + string(" != ")
651 + StringUtils::asString(cfg.dt0,2);
652 LOG(ERROR) << msg;
653 inputValid = false;
654 }
655 if(!(fileHasP1C1[j] & 1)) C1missing = true;
656 if(!(fileHasP1C1[j] & 2)) P1missing = true;
657 if(!(fileHasP2C2[j] & 1)) C2missing = true;
658 if(!(fileHasP2C2[j] & 2)) P2missing = true;
659 }
660
661 // handle C1 vs P1
662 if(C1missing && cfg.forceCA1) {
663 msg = string(" Error - Found '--forceCA1', but these files have no C1 data:");
664 for(j=0; j<cfg.obsfiles.size(); j++)
665 if(!(fileHasP1C1[j] & 1)) msg += string("\n ") + cfg.obsfiles[j];
666 LOG(ERROR) << msg;
667 inputValid = false;
668 }
669 // NB 'else' and order of if()'s matter in the next stmt
670 else if(P1missing && (!cfg.useCA1 || C1missing)) {
671 if(C1missing) {
672 msg = string(" Error - Not all obs files have either P1 or C1 data.");
673 }
674 else {
675 msg = string(" Error - '--useCA1' not found, ")
676 + string("yet these obs files have no P1 data:");
677 for(j=0; j<cfg.obsfiles.size(); j++)
678 if(!(fileHasP1C1[j] & 2)) msg += string("\n ") + cfg.obsfiles[j];
679 }
680 LOG(ERROR) << msg;
681 inputValid = false;
682 }
683 else {
684 if(P1missing || cfg.forceCA1) cfg.P1C1 = C1;
685 else cfg.P1C1 = P1;
686 //LOG(VERBOSE) << "Choose to use " << cfg.P1C1 << " for L1 pseudorange";
687 }
688 // handle C2 vs P2
689 if(C2missing && cfg.forceCA2) {
690 msg = string(" Error - Found '--forceCA2', but these files have no C2 data:");
691 for(j=0; j<cfg.obsfiles.size(); j++)
692 if(!(fileHasP2C2[j] & 1)) msg += string("\n ") + cfg.obsfiles[j];
693 LOG(ERROR) << msg;
694 inputValid = false;
695 }
696 // NB 'else' and order of if()'s matter in the next stmt
697 else if(P2missing && (!cfg.useCA2 || C2missing)) {
698 if(C2missing) {
699 msg = string(" Error - Not all obs files have either P2 or C2 data.");
700 }
701 else {
702 msg = string(" Error - '--useCA2' not found, ")
703 + string("yet these obs files have no P2 data:");
704 for(j=0; j<cfg.obsfiles.size(); j++)
705 if(!(fileHasP2C2[j] & 2)) msg += string("\n ") + cfg.obsfiles[j];
706 }
707 LOG(ERROR) << msg;
708 inputValid = false;
709 }
710 else {
711 if(P2missing || cfg.forceCA2) cfg.P2C2 = C2;
712 else cfg.P2C2 = P2;
713 //LOG(VERBOSE) << "Choose to use " << cfg.P2C2 << " for L2 pseudorange";
714 }
715
716 if(inputValid) return 0;
717 return -6;
718 }
719 catch(Exception& e) { GPSTK_RETHROW(e); }
720 }
721
722 //------------------------------------------------------------------------------------
WriteToRINEX(void)723 int WriteToRINEX(void)
724 {
725 try {
726 if(cfg.OutRinexObs.empty()) return 0;
727 LOG(VERBOSE) << "Write the output RINEX file " << cfg.OutRinexObs;
728
729 // copy user input into the last input header
730 RinexObsHeader rheadout(cfg.rhead);
731
732 // change the obs type list to include only P1(C1) P2 L1 L2
733 rheadout.obsTypeList.clear();
734
735 rheadout.obsTypeList.push_back(RinexObsHeader::L1);
736 rheadout.obsTypeList.push_back(RinexObsHeader::L2);
737 if(cfg.P1C1 == C1)
738 rheadout.obsTypeList.push_back(RinexObsHeader::C1);
739 else
740 rheadout.obsTypeList.push_back(RinexObsHeader::P1);
741 rheadout.obsTypeList.push_back(RinexObsHeader::P2);
742
743 // fill records in output header
744 rheadout.fileProgram = PrgmName + string(" v.") + DiscFixVersion.substr(0,4)
745 + string(",") + cfg.GDConfig.Version().substr(0,4);
746 if(!cfg.HDRunby.empty()) rheadout.fileAgency = cfg.HDRunby;
747 if(!cfg.HDObs.empty()) rheadout.observer = cfg.HDObs;
748 if(!cfg.HDAgency.empty()) rheadout.agency = cfg.HDAgency;
749 if(!cfg.HDMarker.empty()) rheadout.markerName = cfg.HDMarker;
750 if(!cfg.HDNumber.empty()) rheadout.markerNumber = cfg.HDNumber;
751 rheadout.version = 2.1;
752 rheadout.valid |= RinexObsHeader::versionValid;
753 rheadout.firstObs = cfg.FirstEpoch;
754 rheadout.valid |= RinexObsHeader::firstTimeValid;
755 rheadout.interval = cfg.dt;
756 rheadout.valid |= RinexObsHeader::intervalValid;
757 rheadout.lastObs = cfg.LastEpoch;
758 rheadout.valid |= RinexObsHeader::lastTimeValid;
759 if(cfg.smoothPR)
760 rheadout.commentList.push_back(string("Ranges smoothed by ") + PrgmName
761 + string(" v.") + DiscFixVersion.substr(0,4) + string(" ") + cfg.Date);
762 if(cfg.smoothPH)
763 rheadout.commentList.push_back(string("Phases debiased by ") + PrgmName
764 + string(" v.") + DiscFixVersion.substr(0,4) + string(" ") + cfg.Date);
765 if(cfg.smoothPR || cfg.smoothPH)
766 rheadout.valid |= RinexObsHeader::commentValid;
767 // invalidate the table
768 if(rheadout.valid & RinexObsHeader::numSatsValid)
769 rheadout.valid ^= RinexObsHeader::numSatsValid;
770 if(rheadout.valid & RinexObsHeader::prnObsValid)
771 rheadout.valid ^= RinexObsHeader::prnObsValid;
772
773 int iret = SatPassToRinex2File(cfg.OutRinexObs,rheadout,cfg.SPList);
774 if(iret) return -4;
775
776 return 0;
777 }
778 catch(Exception& e) { GPSTK_RETHROW(e); }
779 }
780
781 //------------------------------------------------------------------------------------
PrintSPList(ostream & os,string msg,vector<SatPass> & v)782 void PrintSPList(ostream& os, string msg, vector<SatPass>& v)
783 {
784 int i,j,gap;
785 RinexSatID sat;
786 map<RinexSatID,int> lastSP;
787 map<RinexSatID,int>::const_iterator kt;
788
789 os << "#" << leftJustify(msg,4)
790 << " N gap tot sat ok s start time end time dt"
791 << " observation types\n";
792
793 for(i=0; i<v.size(); i++) {
794 os << msg;
795 sat = v[i].getSat();
796 kt = lastSP.find(sat);
797 if(kt == lastSP.end())
798 gap = 0;
799 else {
800 j = kt->second;
801 gap = int((v[i].getFirstTime() - v[j].getLastTime()) / v[i].getDT() + 0.5);
802 lastSP.erase(sat);
803 }
804 lastSP[sat] = i;
805 // n,gap,sat,length,ngood,firstTime,lastTime
806 os << " " << setw(2) << i+1 << " " << setw(4) << gap << " " << v[i];
807 os << endl;
808 }
809 }
810
811 //------------------------------------------------------------------------------------
812 //------------------------------------------------------------------------------------
GetCommandLine(int argc,char ** argv)813 int GetCommandLine(int argc, char **argv)
814 {
815 try {
816 size_t i;
817 // defaults
818 cfg.DChelp = false;
819 cfg.verbose = false;
820 cfg.decimate = 0.0;
821 cfg.begTime = Epoch(CommonTime::BEGINNING_OF_TIME);
822 cfg.endTime = Epoch(CommonTime::END_OF_TIME);
823 cfg.MaxGap = 600.0;
824
825 cfg.LogFile = string("df.log");
826 cfg.OutFile = string("df.out");
827 cfg.format = string("%4F %10.3g");
828 cfg.round = 3;
829
830 cfg.noCA1 = false; // if false, use CA code on L1 (C1) if P1 absent
831 cfg.useCA1 = true;
832 cfg.forceCA1 = false; // if true, use CA code on L1 even if P1 is present
833 cfg.noCA2 = false; // if false, use CA code on L2 (C2) if P2 absent
834 cfg.useCA2 = true;
835 cfg.forceCA2 = false; // if true, use CA code on L2 even if P2 is present
836 cfg.doGLO = false; // if true, process GLONASS sats
837
838 cfg.dt = -1.0;
839
840 cfg.HDPrgm = PrgmName + string(" v.") + DiscFixVersion.substr(0,4);
841 cfg.HDRunby = string("ARL:UT/SGL/GPSTk");
842
843 cfg.smoothPR = false;
844 cfg.smoothPH = false;
845 cfg.smooth = false;
846
847 for(i=0; i<9; i++) cfg.ndt[i]=-1;
848
849 cfg.inputPath = string(".");
850
851 // -------------------------------------------------------
852 // create list of command line options, and fill it
853 // put required options first - they will get listed first anyway
854 CommandLine opts;
855 string cmdlineUsage, cmdlineErrors;
856 vector<string> cmdlineUnrecognized;
857
858 // build the options list == syntax page
859 string PrgmDesc = "Prgm " + PrgmName +
860 " reads a RINEX observation data file containing GPS or GLO dual frequency\n"
861 " pseudorange and carrier phase measurements, divides the data into\n"
862 " 'satellite passes', and finds and fixes discontinuities in the phases for\n"
863 " each pass. Output is a list of editing commands for use with RinexEdit.\n"
864 " " + PrgmName
865 + " will (optionally) write the corrected pseudorange and phase data\n"
866 " to a new RINEX observation file. Other options will also smooth the\n"
867 " pseudorange and/or debias the corrected phase.\n\n"
868 " " + PrgmName + " calls the GPSTk Discontinuity Corrector (GDC vers "
869 + cfg.GDConfig.Version() + ").\n" +
870 " GDC options (--DC below, and see --DChelp) are passed to GDC,\n"
871 " except --DCDT is ignored; it is computed from the data.";
872
873 // temp variables for input
874 bool help=false;
875 const string defaultstartStr("[Beginning of dataset]");
876 const string defaultstopStr("[End of dataset]");
877 string startStr(defaultstartStr);
878 string stopStr(defaultstopStr);
879 vector<string> GLOfreqStrs;
880
881 // required
882 opts.Add(0,"obs","file", true, true, &cfg.obsfiles,"\n# File I/O:",
883 "Input RINEX obs file - may be repeated");
884
885 // optional
886 // opts.Add(char, opt, arg, repeat?, required?, &target, pre-descript, descript.);
887 string dummy(""); // dummy for --file
888 opts.Add('f', "file", "name", true, false, &dummy, "",
889 "Name of file containing more options [#-EOL = comment]");
890 opts.Add(0, "obspath", "path", false, false, &cfg.inputPath, "",
891 "Path for input RINEX obs file(s)");
892
893 opts.Add(0, "start", "time", false, false, &startStr,
894 "\n# Times (time = \"GPSweek,SOW\" OR \"YYYY,Mon,D,H,Min,S)\":",
895 "Start processing the input data at this time");
896 opts.Add(0, "stop", "time", false, false, &stopStr, "",
897 "Stop processing the input data at this time");
898
899 opts.Add(0, "decimate", "dt", false, false, &cfg.decimate, "# Data config:",
900 "Decimate data to time interval (sec) dt");
901 opts.Add(0, "gap", "t", false, false, &cfg.MaxGap, "",
902 "Minimum gap (sec) between passes [same as --DCMaxGap] ("
903 + asString(int(cfg.MaxGap)) + ")");
904 opts.Add(0, "noCA1", "", false, false, &cfg.noCA1, "",
905 "Fail if L1 P-code is missing, even if L1 CA-code is present");
906 opts.Add(0, "noCA2", "", false, false, &cfg.noCA2, "",
907 "Fail if L2 P-code is missing, even if L2 CA-code is present");
908 opts.Add(0, "forceCA1", "", false, false, &cfg.forceCA1, "",
909 "Use C/A L1 range, even if L1 P-code is present");
910 opts.Add(0, "forceCA2", "", false, false, &cfg.forceCA2, "",
911 "Use C/A L2 range, even if L2 P-code is present");
912 opts.Add(0, "onlySat", "sat", false, false, &cfg.SVonly, "",
913 "Process only satellite <sat> (a SatID, e.g. G21 or R17)");
914 opts.Add(0, "exSat", "sat", true, false, &cfg.exSat, "",
915 "Exclude satellite(s) [e.g. --exSat G22,R]");
916 opts.Add(0, "doGLO", "", false, false, &cfg.doGLO, "",
917 "Process GLONASS satellites as well as GPS");
918 opts.Add(0, "GLOfreq", "sat:n", true, false, &GLOfreqStrs, "",
919 "GLO channel #s for each sat [e.g. R17:-4]");
920
921 opts.Add(0, "smoothPR", "", false, false, &cfg.smoothPR,
922 "# Smoothing: [NB smoothed pseudorange and debiased phase are not identical.]",
923 "Smooth pseudorange and output in place of raw pseudorange");
924 opts.Add(0, "smoothPH", "", false, false, &cfg.smoothPH, "",
925 "Debias phase and output in place of raw phase");
926 opts.Add(0, "smooth", "", false, false, &cfg.smooth, "",
927 "Same as (--smoothPR AND --smoothPH)");
928
929 opts.Add(0, "DC", "param=value", true, false, &cfg.DCcmds,
930 "# Discontinuity Corrector (DC) - cycle slip fixer - configuration:",
931 "Set DC parameter <param> to <value>");
932 opts.Add(0, "DChelp", "", false, false, &cfg.DChelp, "",
933 "Print list of DC parameters (all if -v) and their defaults, then quit");
934
935 opts.Add(0, "log", "file", false, false, &cfg.LogFile, "# Output:",
936 "Output log file name (" + cfg.LogFile + ")");
937 opts.Add(0, "cmd", "file", false, false, &cfg.OutFile, "",
938 "Output file name (for editing commands) (" + cfg.OutFile + ")");
939 opts.Add(0, "format", "fmt", false, false, &cfg.format, "",
940 "Output time format (cf. gpstk::" "Epoch) (" + cfg.format + ")");
941 opts.Add(0, "round", "n", false, false, &cfg.round, "",
942 "Round output time format (--format) to n digits");
943
944 opts.Add(0, "RinexFile", "file", false, false, &cfg.OutRinexObs, "# RINEX output:",
945 "RINEX (obs) file name for output of corrected data");
946 opts.Add(0, "Prgm", "str", false, false, &cfg.HDPrgm, "",
947 "RINEX header 'PROGRAM' string for output");
948 opts.Add(0, "RunBy", "str", false, false, &cfg.HDRunby, "",
949 "RINEX header 'RUNBY' string for output");
950 opts.Add(0, "Observer", "str", false, false, &cfg.HDObs, "",
951 "RINEX header 'OBSERVER' string for output");
952 opts.Add(0, "Agency", "str", false, false, &cfg.HDAgency, "",
953 "RINEX header 'AGENCY' string for output");
954 opts.Add(0, "Marker", "str", false, false, &cfg.HDMarker, "",
955 "RINEX header 'MARKER' string for output");
956 opts.Add(0, "Number", "str", false, false, &cfg.HDNumber, "",
957 "RINEX header 'NUMBER' string for output");
958
959 opts.Add(0, "verbose", "", false, false, &cfg.verbose, "# Help:",
960 "print extended output information");
961 //opts.Add(0, "debug", "", false, false, &cfg.debug, "",
962 // "print debug output at level 0 [debug<n> for level n=1-7]");
963 opts.Add(0, "help", "", false, false, &help, "",
964 "print this and quit");
965
966 // declare it and parse it; write all errors to string GD.cmdlineErrors
967 int iret = opts.ProcessCommandLine(argc, argv, PrgmDesc,
968 cmdlineUsage, cmdlineErrors, cmdlineUnrecognized);
969 if(iret == -2) {
970 LOG(ERROR) << " Error - command line failed (memory)";
971 return iret;
972 }
973
974 // ---------------------------------------------------
975 // do extra parsing -- append errors to GD.cmdlineErrors
976 RinexSatID sat;
977 string msg;
978 vector<string> fields;
979 ostringstream oss;
980
981 // unrecognized arguments are an error
982 if(cmdlineUnrecognized.size() > 0) {
983 oss << "Error - unrecognized arguments:\n";
984 for(i=0; i<cmdlineUnrecognized.size(); i++)
985 oss << cmdlineUnrecognized[i] << "\n";
986 oss << "End of unrecognized arguments\n";
987 }
988
989 // if no GLO, add to exSat
990 if(!cfg.doGLO && cfg.SVonly.system != SatelliteSystem::Glonass) {
991 sat.fromString("R");
992 if(vectorindex(cfg.exSat,sat) == -1) cfg.exSat.push_back(sat);
993 }
994
995 // parse GLO freq
996 for(i=0; i<GLOfreqStrs.size(); i++) {
997 fields = StringUtils::split(GLOfreqStrs[i],':');
998 if(fields.size() != 2) {
999 oss << "Error - invalid GLO sat:chan pair in --GLOfreq input: "
1000 << GLOfreqStrs[i] << endl;
1001 }
1002 else {
1003 sat.fromString(fields[0]);
1004 cfg.GLOfreqChannel.insert(
1005 map<RinexSatID,int>::value_type(sat,asInt(fields[1])));
1006 }
1007 }
1008
1009 // start and stop times
1010 for(i=0; i<2; i++) {
1011 string msg = (i==0 ? startStr : stopStr);
1012 if(msg == (i==0 ? defaultstartStr : defaultstopStr)) continue;
1013
1014 int n(StringUtils::numWords(msg,','));
1015 if(n != 2 && n != 6) {
1016 oss << "Error - invalid argument in --" << (i==0 ? "start" : "stop")
1017 << " " << (i==0 ? startStr : stopStr) << endl;
1018 continue;
1019 }
1020
1021 string fmtGPS("%F,%g"),fmtCAL("%Y,%m,%d,%H,%M,%S");
1022 try {
1023 (i==0 ? cfg.begTime:cfg.endTime).scanf(msg,(n==2 ? fmtGPS:fmtCAL));
1024 }
1025 catch(Exception& e) {
1026 oss << "Error - invalid time in --" << (i==0 ? "start" : "stop")
1027 << " " << (i==0 ? startStr : stopStr) << endl;
1028 }
1029 }
1030
1031 if(cfg.noCA1) cfg.useCA1 = false;
1032 if(cfg.noCA2) cfg.useCA2 = false;
1033
1034 // append errors
1035 cmdlineErrors += oss.str();
1036 stripTrailing(cmdlineErrors,'\n');
1037
1038 // --------------------------------------------------------------------
1039 // dump a summary of the command line configuration
1040 oss.str(""); // clear it
1041 oss << "------ Summary of " << PrgmName
1042 << " command line configuration --------" << endl;
1043 opts.DumpConfiguration(oss);
1044 // perhaps dump the 'extra parsing' things
1045 oss << "------ End configuration summary --------" << endl;
1046 cfg.cmdlineSum = oss.str();
1047
1048 // --------------------------------------------------------------------
1049 // return
1050 if(opts.hasHelp() || cfg.DChelp) {
1051 stripTrailing(cmdlineUsage,'\n');
1052 LOG(INFO) << cmdlineUsage;
1053 if(cfg.DChelp) cfg.GDConfig.DisplayParameterUsage(LOGstrm,cfg.verbose);
1054 return 1;
1055 }
1056 if(opts.hasErrors()) {
1057 LOG(ERROR) << cmdlineErrors << endl;
1058 return -1;
1059 }
1060 if(!cmdlineErrors.empty()) { // unrecognized or extra parsing produced an error
1061 LOG(ERROR) << cmdlineErrors;
1062 return -2;
1063 }
1064 return 0;
1065
1066 } // end try
1067 catch(Exception& e) { GPSTK_RETHROW(e); }
1068 catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
1069 catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
1070 }
1071
1072 //------------------------------------------------------------------------------------
DumpConfiguration(void)1073 void DumpConfiguration(void)
1074 {
1075 try {
1076 int i,j;
1077 // print config to log, first DF
1078 LOG(INFO) << "\nHere is the " << PrgmName << " configuration:";
1079 LOG(INFO) << " Input RINEX obs files are:";
1080 for(i=0; i<cfg.obsfiles.size(); i++) {
1081 LOG(INFO) << " " << cfg.obsfiles[i];
1082 }
1083 LOG(INFO) << " Input path for obs files is " << cfg.inputPath;
1084 if(cfg.decimate > 0.0)
1085 LOG(INFO) << " Decimate to time interval " << cfg.decimate;
1086 if(cfg.begTime > Epoch(CommonTime::BEGINNING_OF_TIME))
1087 LOG(INFO) << " Begin time is "
1088 << printTime(cfg.begTime,"%04Y/%02m/%02d %02H:%02M:%.3f")
1089 << " = " << printTime(cfg.begTime,"%04F/%10.3g");
1090 if(cfg.endTime < Epoch(CommonTime::END_OF_TIME))
1091 LOG(INFO) << " End time is "
1092 << printTime(cfg.endTime,"%04Y/%02m/%02d %02H:%02M:%.3f")
1093 << " = " << printTime(cfg.endTime,"%04F/%10.3g");
1094 if(cfg.useCA1)
1095 {
1096 LOG(INFO) << " Use the L1 C/A pseudorange if P-code is not found";
1097 }
1098 else
1099 {
1100 LOG(INFO) << " Do not use L1 C/A code range (C1)";
1101 }
1102 if(cfg.useCA2)
1103 {
1104 LOG(INFO) << " Use the L2 C/A pseudorange if P-code is not found";
1105 }
1106 else
1107 {
1108 LOG(INFO) << " Do not use L2 C/A code range (C2)";
1109 }
1110 if(cfg.forceCA1) LOG(INFO) <<" Use the L1 C/A pseudorange even if P-code is found";
1111 if(cfg.forceCA2) LOG(INFO) <<" Use the L2 C/A pseudorange even if P-code is found";
1112 if(cfg.dt0 > 0) LOG(INFO) << " dt is input as " << cfg.dt0 << " seconds.";
1113 LOG(INFO) << " Max gap is " << cfg.MaxGap << " seconds";
1114 if(cfg.exSat.size()) {
1115 LOGstrm << " Exclude satellites";
1116 for(i=0; i<cfg.exSat.size(); i++) {
1117 if(cfg.exSat[i].id == -1)
1118 LOGstrm << " (all " << cfg.exSat[i].systemString() << ")";
1119 else
1120 LOGstrm << " " << cfg.exSat[i];
1121 }
1122 LOGstrm << endl;
1123 }
1124 if(cfg.SVonly.id > 0)
1125 LOG(INFO) << " Process only satellite : " << cfg.SVonly;
1126 LOG(INFO) << (cfg.doGLO ? " P":" Do not p") << "rocess GLONASS satellites";
1127 if(cfg.GLOfreqChannel.size() > 0) {
1128 j = 0;
1129 LOGstrm << " GLO frequency channels:";
1130 map<RinexSatID,int>::const_iterator it(cfg.GLOfreqChannel.begin());
1131 while(it != cfg.GLOfreqChannel.end()) {
1132 LOGstrm << (j==0 ? " ":",") << it->first << ":" << it->second;
1133 ++j; ++it;
1134 if((j % 9)==0) { j=0; LOGstrm << endl << " "; }
1135 }
1136 LOGstrm << endl;
1137 }
1138 LOG(INFO) << " Log file is " << cfg.LogFile;
1139 LOG(INFO) << " Out file is " << cfg.OutFile;
1140 LOG(INFO) << " Output times in this format '" << cfg.format << "', rounding to "
1141 << cfg.round << " digits.";
1142 if(!cfg.OutRinexObs.empty())
1143 LOG(INFO) << " Output RINEX file name is " << cfg.OutRinexObs;
1144 if(!cfg.HDRunby.empty())
1145 LOG(INFO) << " Output RINEX 'RUN BY' is " << cfg.HDRunby;
1146 if(!cfg.HDObs.empty())
1147 LOG(INFO) << " Output RINEX 'OBSERVER' is " << cfg.HDObs;
1148 if(!cfg.HDAgency.empty())
1149 LOG(INFO) << " Output RINEX 'AGENCY' is " << cfg.HDAgency;
1150 if(!cfg.HDMarker.empty())
1151 LOG(INFO) << " Output RINEX 'MARKER' is " << cfg.HDMarker;
1152 if(!cfg.HDNumber.empty())
1153 LOG(INFO) << " Output RINEX 'NUMBER' is " << cfg.HDNumber;
1154 if(cfg.smoothPR) LOG(INFO) << " 'Smoothed range' option is on\n";
1155 if(cfg.smoothPH) LOG(INFO) << " 'Smoothed phase' option is on\n";
1156 if(!cfg.smooth) LOG(INFO) << " No smoothing.\n";
1157
1158 } // end try
1159 catch(Exception& e) { GPSTK_RETHROW(e); }
1160 catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
1161 catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
1162 }
1163
1164 //------------------------------------------------------------------------------------
1165 //------------------------------------------------------------------------------------
1166