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 /// @file dfix.cpp Discontinuity detection and correction (cycleslip fixer) using
40 /// class gdc.
41
42 //------------------------------------------------------------------------------------
43 // system includes
44 #include <ctime>
45 #include <iostream>
46 #include <iomanip>
47 #include <fstream>
48 #include <string>
49 #include <vector>
50 #include <map>
51 // GPSTk
52 #include "Exception.hpp"
53 #include "StringUtils.hpp"
54 #include "Epoch.hpp"
55 #include "RinexSatID.hpp"
56 #include "Position.hpp"
57 #include "RinexUtilities.hpp"
58 #include "EphemerisRange.hpp"
59 #include "singleton.hpp"
60 #include "GNSSconstants.hpp"
61 #include "msecHandler.hpp"
62 #include "stl_helpers.hpp"
63 // geomatics
64 #include "expandtilde.hpp"
65 #include "logstream.hpp"
66 #include "SatPass.hpp"
67 #include "SatPassUtilities.hpp"
68 #include "Rinex3ObsFileLoader.hpp"
69 // dfix
70 #include "CommandLine.hpp"
71 #include "gdc.hpp"
72
73 //------------------------------------------------------------------------------------
74 using namespace std;
75 using namespace gpstk;
76
77 //------------------------------------------------------------------------------------
78 //------------------------------------------------------------------------------------
79 // prototypes
80 /// Read and process command line
81 /// @throw Exception
82 int GetCommandLine(int argc, char **argv);
83
84 /// Validate input -- check that files exist, append path, quit if user chose validate
85 /// @throw Exception
86 int ValidateInput(void);
87
88 /// Load ephemeris stores
89 /// @throw Exception
90 int Initialize(void);
91
92 /// Choose R3ObsIDs given user input, read RINEX file(s) into SatPass list
93 /// @return 0 success, <0 error code
94 /// @throw Exception
95 int ReadRinexFiles(void);
96
97 /// Remove milliseconds, mark low elevations
98 /// @throw Exception
99 int PreProcess(void);
100
101 /// Call GDC for each pass and output
102 /// @throw Exception
103 int Process(void);
104
105 //------------------------------------------------------------------------------------
106 //------------------------------------------------------------------------------------
107 /// Class GlobalData (a singleton) encapsulates global static data as well as
108 /// command line definition and processing
109 class GlobalData : public Singleton<GlobalData> {
110 public:
111 //// Default and only constructor, sets defaults.
GlobalData()112 GlobalData() throw() { SetDefaults(); }
113
114 // prgm housekeeping
115 static const string Version; ///< version string - see below
116 string PrgmName; ///< this program name
117 string Title; ///< name, version and runtime
118 string logfile; ///< name of log file
119 ofstream oflog; ///< output log file stream
120
121 // command line input ----------------------------------------------------------
122 bool inputIsValid; ///< if true, quit after Validate()
123
124 /// strings used by GetCommandLine - usage, errors, etc
125 string cmdlineErrors,cmdlineDump,cmdlineUsage,cmdlineExtras;
126 vector<string> cmdlineUnrecognized;
127
128 string logpath,obspath; ///< paths for log and obs files
129 vector<string> obsfiles; ///< input obs files - required
130 string obsout, cmdout; ///< output obs and command files
131
132 vector<string> wantedObsIDs; ///< 4-char obs types wanted in the data store
133 unsigned int def_wanted_in; ///< the number of default wantedObsIDs elements
134 vector<string> syscode_in; ///< systems:codes input
135 unsigned int def_syscode_in; ///< the number of default syscode_in elements
136
137 Epoch startTime, stopTime; ///< start and stop times for data
138 double decdt; ///< decimate data to this timestep (sec)
139 map<RinexSatID,int> GLOfreqCh;///< input freq channel - overrides eph input
140
141 vector<string> DCcmds; ///< GDC editing cmds - written to cmdout
142 vector<RinexSatID> exSat; ///< ignore these sats
143 vector<RinexSatID> onlySat; ///< process only these sats
144 vector<int> exPass; ///< ignore these passes
145 vector<int> onlyPass; ///< process only these passes
146 bool fixMS;
147
148 // ephemeris and refpos input
149 vector<string> SP3files; ///< SP3 ephemeris file names
150 vector<string> RNavfiles; ///< RINEX nav file names
151 string ephpath; ///< path for nav/ephemeris files
152 SP3EphemerisStore SP3EphList; ///< Store of SP3 ephemeris data
153 GPSEphemerisStore BCEphList; ///< Store of NAV ephemeris data
154 XvtStore<SatID> *pEph; ///< Pointer to chosen ephemeris store
155 Position Rx; ///< Receiver position provided by user
156 double elevLimit; ///< Elevation angle lower limit (deg)
157 bool doElev; ///< set true if elevation screening is to be done
158
159 vector<string> outlabels; ///< tell GDC to output labels
160 bool typehelp; ///< print all RINEX3 ObsIDs, then quit
161 bool DChelp; ///< help for the GDC
162 bool DChelpall; ///< help for the GDC with advanced options
163 bool validate; ///< validate the input, then quit
164 string timefmt; ///< output format for GDC
165 // end command line input ------------------------------------------------------
166
167 bool verbose; ///< flag handled by CommandLine
168 int debug; ///< int handled by CommandLine
169
170 double nomdt; ///< nominal timestep of data
171 vector<SatPass> SPList; ///< the SatPass list
172 vector<string> obstypes; ///< list of 4 obstypes e.g. L1 L2 P1 P2
173 /// map<sys,vector<string>> of SatPass obstypes each system parallel to obstypes
174 map<char, vector<string> > SPsysobs;
175 /// map<sys,vector<string>> of R3 obsID each system parallel to SPsysobs & obstypes
176 map<char, vector<string> > R3sysobs;
177
178 // these parallel : they come from --syscode s:codes,s
179 vector<string> Syss; ///< systems, 1-char strings
180 vector<string> Codes; ///< preferred codes, in order of "quality"
181
182 Rinex3ObsHeader header; ///< save for RINEX output
183
184 gdc GDC; ///< the GDC object
185
186 vector<string> EditCmds; ///< editing commands returned by GDC - write cmdout
187 string longfmt; ///< times in loader, error messages, etc.
188
189 private:
190
191 /// Define default values, called by c'tor
SetDefaults(void)192 void SetDefaults(void) throw()
193 {
194 PrgmName = string("dfix");
195 logfile = string();
196
197 // command line input -------------------------------------------
198 // input control
199 startTime = CommonTime::BEGINNING_OF_TIME;
200 stopTime = CommonTime::END_OF_TIME;
201
202 // default obs types - GPS only, ignored if user provides input
203 wantedObsIDs.push_back("GC1*");
204 wantedObsIDs.push_back("GC2*");
205 wantedObsIDs.push_back("GL1*");
206 wantedObsIDs.push_back("GL2*");
207 def_wanted_in = wantedObsIDs.size();
208
209 // default tracking codes, ignored if user provides input
210 // NB since GPS L2C (code X,L) is not constellation-wide, suggest G:PYWCXL
211 syscode_in.push_back("G:PYWCXL");
212 syscode_in.push_back("R:PC");
213 def_syscode_in = syscode_in.size();
214
215 // editing
216 decdt = -1.0;
217 fixMS = doElev = false;
218 elevLimit = 0.0;
219
220 // output
221 DChelp = DChelpall = typehelp = validate = false;
222 timefmt = string("%4F %10.3g");
223 //timefmt = string("%.6Q"); // 1.5/86400 = 1.7e-5
224
225 // end command line input ---------------------------------------
226
227 longfmt = string("%04F %10.3g %04Y/%02m/%02d %02H:%02M:%06.3f %P");
228 }
229
230 }; // end class GlobalData
231
232 //------------------------------------------------------------------------------------
233 /// version string
234 const string GlobalData::Version(string("3.0 6/20/19"));
235
236 //------------------------------------------------------------------------------------
237 //------------------------------------------------------------------------------------
main(int argc,char ** argv)238 int main(int argc, char **argv)
239 {
240 string PrgmName; // for catch only
241 try {
242 // begin counting time
243 clock_t totaltime = clock();
244 Epoch wallbegin,wallend;
245 wallbegin.setLocalTime();
246
247 // get (create) the global data object (a singleton);
248 // since this is the first instance, this will also set default values
249 GlobalData& GD=GlobalData::Instance();
250 PrgmName = GD.PrgmName;
251
252 // Build title
253 Epoch ttag;
254 ttag.setLocalTime();
255 GD.Title = GD.PrgmName + " ver " + GD.Version
256 + ttag.printf(", Run %04Y/%02m/%02d at %02H:%02M:%02S");
257
258 // display title on screen
259 LOG(INFO) << GD.Title;
260
261 // TEMP, for debugging CommandLine;
262 //LOGlevel = ConfigureLOG::Level("DEBUG");
263
264 // process : loop once -----------------------------------------------------
265 int iret;
266 for(bool go=true; go; go=false) {
267
268 // process the command line ------------------------------------
269 // open log file, dump config
270 iret = GetCommandLine(argc,argv);
271 if(iret) break;
272
273 // check input, add paths to filenames
274 iret = ValidateInput();
275 if(iret) break;
276
277 // fill ephemeris store
278 iret = Initialize();
279 if(iret) break;
280
281 // read files into SatPassList
282 iret = ReadRinexFiles();
283 if(iret) break;
284
285 // preprocess the data - millisec fix,
286 iret = PreProcess();
287 if(iret) break;
288
289 // do it
290 iret = Process();
291 if(iret) break;
292
293 } // end loop once
294
295 // error condition ---------------------------------------------------------
296 // return codes: 0 ok, -3,1..6 from CommandLine - see below
297 // 6 decimation invalid
298 // 7 failed to read RINEX file(s)
299 // 8 no good data
300 //LOG(INFO) << "Return code is " << iret;
301 if(iret != 0) {
302 if(iret != 1) {
303 string msg;
304 msg = GD.PrgmName + string(" is terminating with code ")
305 + StringUtils::asString(iret);
306 LOG(ERROR) << msg;
307 }
308
309 if(iret == 1) { // help(s) handled in GetCommand
310 ;
311 }
312 else if(iret == 2) { LOG(INFO) << GD.cmdlineErrors; } // cmd line errors
313 else if(iret == 3) { LOG(INFO) << "The user requested input validation."; }
314 else if(iret == 4) { LOG(INFO) << "The input is invalid."; }
315 else if(iret == 5) { LOG(INFO) << "The log file could not be opened."; }
316 else if(iret == 6) { LOG(INFO) << "Decimation was configured incorrectly."; }
317 else if(iret == 7) { LOG(INFO) << "Failed to read all RINEX files."; }
318 else if(iret == 8) { LOG(INFO) << "No good data was found."; }
319 //else if(iret == 9) { LOG(INFO) << "Input read configuration is invalid."; }
320 else if(iret == -3) { // cmd line definition invalid
321 LOG(INFO) << "The command line definition is invalid.\n" << GD.cmdlineErrors;
322 }
323 else
324 LOG(INFO) << "temp - Some other return code...fix this" << iret;
325 }
326
327 // compute and print run time ----------------------------------------------
328 if(iret != 1) {
329 wallend.setLocalTime();
330 totaltime = clock()-totaltime;
331 ostringstream oss;
332 oss << PrgmName << " timing: " << fixed << setprecision(3)
333 << double(totaltime)/double(CLOCKS_PER_SEC)
334 << " seconds. (" << (wallend - wallbegin) << " sec)";
335 LOG(INFO) << oss.str();
336 if(pLOGstrm != &cout) cout << oss.str() << endl;
337 }
338
339 return (iret == 0 ? 0 : 1);
340 }
341 catch(Exception& e) {
342 cerr << PrgmName << " caught Exception:\n" << e.what() << endl;
343 // don't use LOG here - causes hangup - don't know why
344 }
345 catch (...) {
346 cerr << "Unknown error in " << PrgmName << ". Abort." << endl;
347 }
348 return -1;
349 } // end main()
350
351 //------------------------------------------------------------------------------------
352 //------------------------------------------------------------------------------------
GetCommandLine(int argc,char ** argv)353 int GetCommandLine(int argc, char **argv)
354 {
355 try {
356 unsigned int i,j,k;
357 GlobalData& GD=GlobalData::Instance();
358 vector<string> GLOstrs; // input strings GLOsat:n
359 const string defaultstartStr("[Beginning of dataset]");
360 const string defaultstopStr("[End of dataset]");
361 string startStr(defaultstartStr), stopStr(defaultstopStr);
362 string str,refPosStr;
363
364 // create list of command line options, and fill it
365 // put required options first - they will get listed first anyway
366 CommandLine opts;
367
368 // build the options list == syntax page
369 string PrgmDesc =
370 " Program " + GD.PrgmName + " will read input RINEX obs file(s) and ..."
371 "\n Input is on the command line, or of the same format in a file "
372 "(see --file below);\n lines in that file which begin with '#' are ignored. "
373 "Accepted options are \n shown below, followed by a description, with default "
374 "value, if any, in ().";
375
376 // opts.Add(char, opt, arg, repeat?, required?, &target, pre-descript, descript.);
377 // required options
378 bool req(true);
379 opts.Add('i', "obs", "name", true, req, &GD.obsfiles, "\n# Required input",
380 "Name of input RINEX observation file(s)");
381
382 // optional args
383 req = false;
384 string dummy(""); // dummy for --file
385 opts.Add('f', "file", "name", true, req, &dummy, "\n# File I/O:",
386 "Name of file containing more options [#-EOL = comment]");
387 opts.Add('l', "log", "name", false, req, &GD.logfile, "",
388 "Name of output log file");
389 opts.Add(0, "logpath", "path", false, req, &GD.logpath, "",
390 "Path for output log file");
391 opts.Add(0, "obspath", "path", false, req, &GD.obspath, "",
392 "Path for input RINEX observation file(s)");
393 // Flow control
394 opts.Add(0, "start", "time", false, req, &startStr, "\n# Flow control "
395 "(time = MJD OR GPSweek,SOW OR YYYY,Mon,D,H,Min,S:",
396 "Start processing the input data at this time");
397 opts.Add(0, "stop", "time", false, req, &stopStr, "",
398 "Stop processing the input data at this time");
399 // RINEX input obs IDs
400 opts.Add(0, "obsID", "ot", true, req, &GD.wantedObsIDs,
401 "\n# RINEX3 Data Input (NB ObsIDs for dual-freq PR+phase required;"
402 " defaults erased if obsID input is detected):",
403 "RINEX3 Obs types (4-char) to read from files");
404 opts.Add(0, "syscode", "s[:c]", true, false, &GD.syscode_in, "",
405 "Allowed system:tracking_codes s:c, (c's in order); cf --typehelp");
406 // Data input and config
407 opts.Add(0, "dt", "name", false, req, &GD.decdt, "\n# Data input and config:",
408 "Decimate timestep of the data to this in seconds");
409 opts.Add(0, "DC", "cmd=val", true, req, &GD.DCcmds,"",
410 "Set algorithm configuration parameter (see --DChelp)");
411 opts.Add(0, "exSat", "sat", true, req, &GD.exSat, "\n# Editing:",
412 "Exclude satellite(s) [e.g. G24 or R14 or R]");
413 opts.Add(0, "onlySat", "sat", true, req, &GD.onlySat, "",
414 "Process given satellite(s) only");
415 opts.Add(0, "exPass", "npass", true, req, &GD.exPass, "",
416 "Exclude satellite pass number(s)");
417 opts.Add(0, "onlyPass", "npass", true, req, &GD.onlyPass, "",
418 "Process given satellite pass number(s) only");
419 opts.Add(0, "GLOfreq", "sat:n", true, false, &GLOstrs, "",
420 "GLO channel #s for each sat [e.g. R17:-4]");
421 opts.Add(0, "fixMS", "", false, false, &GD.fixMS, "",
422 "Fix millisecond clock adjusts before processing");
423 // Editing
424 opts.Add(0, "elev", "deg", false, req, &GD.elevLimit,
425 "\n# Exclude low elevation (req's elev>0, ref, and one of eph/nav):",
426 "Minimum elevation angle (deg)");
427 opts.Add(0, "ref", "X,Y,Z", false, req, &refPosStr, "",
428 "Known position (ECEF,m), for computing residuals and ORDs");
429 opts.Add(0, "eph", "fn", true, req, &GD.SP3files, "",
430 "Input Ephemeris+clock (SP3 format) file name");
431 opts.Add(0, "nav", "fn", true, req, &GD.RNavfiles, "",
432 "Input RINEX nav file name(s)");
433 opts.Add(0, "ephpath", "path", false, req, &GD.ephpath, "",
434 "Path for input SP3 or RINEX ephemeris file(s)");
435
436 // Output
437 opts.Add(0, "validate", "", false, req, &GD.validate, "\n# Output:",
438 "Read input and test its validity, then quit");
439 opts.Add('o', "obsout", "name", true, req, &GD.obsout, "",
440 "Name of output (edited) RINEX observation file");
441 opts.Add(0, "cmdout", "name", true, req, &GD.cmdout, "",
442 "Name of output file for RINEX editing commands");
443 opts.Add(0, "dump", "label", true, req, &GD.outlabels, "",
444 "Tell DC to output 'label' data (or 'all') to log - cf. DChelpall");
445 opts.Add(0, "timefmt", "fmt", false, req, &GD.timefmt, "",
446 "Output timetags with this format [cf. class Epoch]");
447 // Help
448 opts.Add(0, "DChelp", "", false, req, &GD.DChelp, "\n# Help",
449 "Print list of DC parameters and their defaults, then quit");
450 opts.Add(0, "DChelpall", "", false, req, &GD.DChelpall, "",
451 "DChelp with advanced options included");
452 opts.Add(0, "typehelp", "", false, false, &GD.typehelp, "",
453 "Print this syntax page and table of all RINEX3 ObsIDs, and quit");
454
455 // add options that are ignored (true if it has an arg)
456 //opts.Add_ignore("--PRSoutput",true);
457
458 // deprecated args
459 //opts.Add_deprecated("--HtOffset","--ht");
460
461 // --------------------------------------------------------------------------
462 // declare it and parse it; write all errors to string GD.cmdlineErrors
463 int iret = opts.ProcessCommandLine(argc,argv,PrgmDesc,
464 GD.cmdlineUsage,GD.cmdlineErrors,GD.cmdlineUnrecognized);
465 if(iret == -2) return iret; // bad alloc
466 if(iret == -3) return iret; // cmd line definition invalid
467
468 // help, debug and verbose handled automatically by CommandLine
469 GD.verbose = (LOGlevel >= VERBOSE);
470 GD.debug = (LOGlevel - DEBUG);
471
472 // --------------------------------------------------------------------------
473 // do extra parsing -- append errors to GD.cmdlineErrors
474 string msg;
475 vector<string> fields;
476 ostringstream oss,ossx;
477
478 // unrecognized arguments are an error
479 if(GD.cmdlineUnrecognized.size() > 0) {
480 oss << " Error - unrecognized arguments:\n";
481 for(i=0; i<GD.cmdlineUnrecognized.size(); i++)
482 oss << GD.cmdlineUnrecognized[i] << "\n";
483 oss << " End of unrecognized arguments\n";
484 }
485
486 // configure the DC
487 if(GD.debug > -1) GD.GDC.setParameter("debug",GD.debug);
488 if(GD.verbose) GD.GDC.setParameter("verbose",1);
489 //GD.GDC.setParameter("DT",GD.dt); // no - dt comes from SatPass
490
491 for(i=0; i<GD.DCcmds.size(); i++) {
492 msg = StringUtils::replaceAll(GD.DCcmds[i],string(" "),string(""));
493 if(GD.GDC.setParameter(msg)) {
494 if(GD.verbose) ossx << "Set GDC parameter with " << msg << endl;
495 }
496 else
497 ossx << " Warning - failed to set GDC parameter " << msg << endl;
498 }
499 for(i=0; i<GD.outlabels.size(); i++) {
500 if(StringUtils::lowerCase(GD.outlabels[i]) == string("all")) {
501 // NB keep this list updated with gdc; 1 means do, 0 means don't
502 GD.GDC.setParameter("RAW=1"); // data (WL,GF) before any processing
503 GD.GDC.setParameter("WL1=1"); // results of 1st diff filter on WL
504 GD.GDC.setParameter("WLG=1"); // WL after fixing gross slips (after fdif)
505 GD.GDC.setParameter("WLW=1"); // results of window filter on WL
506 GD.GDC.setParameter("WLF=1"); // WL after fixing
507 GD.GDC.setParameter("GF1=1"); // results of 1st diff filter on GF
508 GD.GDC.setParameter("GFG=1"); // GF after fixing gross slips (after fdif)
509 GD.GDC.setParameter("GFW=1"); // results of window filter on GF
510 GD.GDC.setParameter("GFF=1"); // GF after fixing
511 LOG(VERBOSE) << "Set GDC output to all data types";
512 }
513 else {
514 msg = GD.outlabels[i]+"=1";
515 if(GD.GDC.setParameter(msg)) {
516 if(GD.verbose) ossx << "Set GDC output to include data type "
517 << GD.outlabels[i] << endl;
518 }
519 else
520 ossx << " Warning - failed to set GDC output to include data type "
521 << GD.outlabels[i] << endl;
522 }
523 }
524
525 // RINEX output requested
526 if(!GD.obsout.empty()) GD.GDC.setParameter("doFix=1");
527 // Editing cmd output requested
528 if(!GD.cmdout.empty()) GD.GDC.setParameter("doCmds=1");
529
530 // start and stop times
531 for(i=0; i<2; i++) {
532 msg = (i==0 ? startStr : stopStr);
533 if(msg == (i==0 ? defaultstartStr : defaultstopStr)) continue;
534
535 int n(StringUtils::numWords(msg,','));
536 if(n != 1 && n != 2 && n != 6) {
537 oss << "Error - invalid argument in --" << (i==0 ? "start" : "stop")
538 << " " << (i==0 ? startStr : stopStr) << endl;
539 continue;
540 }
541
542 string fmtMJD("%Q"),fmtGPS("%F,%g"),fmtCAL("%Y,%m,%d,%H,%M,%S");
543 try {
544 (i==0 ? GD.startTime:GD.stopTime).scanf(msg,
545 (n==1 ? fmtMJD : (n==2 ? fmtGPS : fmtCAL)));
546 }
547 catch(Exception&) {
548 oss << "Error - invalid time in --" << (i==0 ? "start" : "stop")
549 << " " << (i==0 ? startStr : stopStr) << endl;
550 }
551 }
552
553 // GLONASS frequency channels
554 for(i=0; i<GLOstrs.size(); i++) {
555 msg = GLOstrs[i];
556 fields = StringUtils::split(msg,':');
557 if(fields.size() != 2) {
558 oss << "Error - invalid input in --GLOfreq: " << msg << endl;
559 }
560 else {
561 RinexSatID sat(fields[0]);
562 int chan(StringUtils::asInt(fields[1])); // for Solaris
563 GD.GLOfreqCh.insert(make_pair(sat,chan));
564 }
565 }
566
567 // reference position
568 if(!refPosStr.empty()) {
569 msg = refPosStr;
570 fields.clear();
571 while(msg.size() > 0) fields.push_back(StringUtils::stripFirstWord(msg,','));
572 if(fields.size() != 3)
573 oss << "Error - invalid field in --refPos input: " << refPosStr << endl;
574 else {
575 try { GD.Rx.setECEF(StringUtils::asDouble(fields[0]),
576 StringUtils::asDouble(fields[1]),
577 StringUtils::asDouble(fields[2]));
578 }
579 catch(Exception&) {
580 oss << "Error - invalid position in --refPos input: "
581 << refPosStr << endl;
582 }
583 }
584 }
585
586 // Obs types
587 // Rinex3ObsHeader.cpp line ~1820 - mapping R2 -> R3:
588 // C1->C1C C2->C2X C5->C5X
589 // P1->C1W/Y P2->C2X/W/Y P5->P5X
590 // L1->L1C/Y L2->L2X/W/Y L5->L5X
591 // D1->D1C/Y D2->D2X/W/Y D5->D5X
592 // S1->S1C/Y S2->S2X/W/Y S5->S5X
593 // Y if have P2 and P is really Y code - this can't come from RINEX
594 // W if have P2 but P is not Y
595 // X if don't have P2
596 // NB P[12] never -> tracking code P!
597
598 // obs IDs ---------------------------------------------
599 // remove the defaults if user provided inputs
600 if(GD.wantedObsIDs.size() > GD.def_wanted_in) {
601 vector<string> vtemp(GD.wantedObsIDs);
602 GD.wantedObsIDs.clear();
603 for(i=4; i< vtemp.size(); i++)
604 GD.wantedObsIDs.push_back(vtemp[i]);
605 }
606
607 // systems and codes -----------------------------------
608 bool skip;
609 string freqs(RinexObsID::validRinexFrequencies);
610 // loop over input syscode_in, but note it will start with def_syscode_in default
611 // elements, before any user input, and must see if those defaults were overridden
612 for(i=0; i<GD.syscode_in.size(); i++) {
613 fields = StringUtils::split(GD.syscode_in[i],':'); // system:codes
614 string sys(fields[0]); // 1-char sys
615
616 // if defaults are over-ridden by user input, skip them;
617 // def_syscode_in is the number of def.s
618 if(i < GD.def_syscode_in) {
619 skip = false;
620 for(j=i+1; j<GD.syscode_in.size(); j++)
621 if(GD.syscode_in[j][0] == sys[0]) { skip=true; break; } // user input sys
622 if(skip) continue;
623 }
624
625 // if there are no "wanted" ObsIDs for this system, skip it
626 skip = true;
627 for(j=0; j<GD.wantedObsIDs.size(); j++) {
628 if(GD.wantedObsIDs[j][0] == '*' || GD.wantedObsIDs[j][0] == sys[0])
629 { skip = false; break; }
630 }
631 if(skip) {
632 if(i >= GD.def_syscode_in) // don't warn about the defaults
633 ossx << " Warning - system " << sys << " was specified (--syscode)"
634 << " but no ObsIDs for it were specified (--obsID) - skip this system."
635 << endl;
636 continue;
637 }
638
639 // For now, allow only GPS and GLO
640 if(sys != "G" && sys != "R") {
641 ossx << " Warning - only GPS and GLONASS are currently supported." << endl;
642 continue;
643 }
644
645 // ok, save the system and codes
646 GD.Syss.push_back(sys); // keep parallel to Codes
647 if(fields.size() > 1)
648 str = fields[1]; // user-supplied codes
649 else { // default codes - combine all frequencies
650 str = string("");
651 for(j=0; j<freqs.size(); j++) {
652 // default codes for freq j
653 string strf(RinexObsID::validRinexTrackingCodes[sys[0]][freqs[j]]);
654 StringUtils::stripTrailing(strf,"* ");
655 if(str.empty() && !strf.empty()) str=strf;
656 else for(k=0; k<strf.size(); k++) {
657 if(str.find_first_of(strf.substr(k,1),0) == string::npos)
658 str += string(1,strf[k]);
659 }
660 }
661 }
662 GD.Codes.push_back(str); // keep parallel to GD.Syss
663 ossx << " Include system:codes " << sys << ":" << str << endl;
664 }
665
666 // check wantedObsIDs for invalid/no-system (NB ROFL::loadObsID also checks size)
667 for(i=0; i<GD.wantedObsIDs.size(); i++) {
668 if(GD.wantedObsIDs[i].size() != 4) {
669 oss << "Error : invalid RINEX3 obsID (not 4-char): "
670 << GD.wantedObsIDs[i] << endl;
671 continue;
672 }
673 str = GD.wantedObsIDs[i].substr(0,1);
674 if(str != "*" && vectorindex(GD.Syss,str) == -1) {
675 oss << "Error : invalid RINEX3 obsID (system not found): "
676 << GD.wantedObsIDs[i] << endl;
677 continue;
678 }
679 // test using ROFL
680 Rinex3ObsFileLoader rofl;
681 if(!rofl.loadObsID(GD.wantedObsIDs[i])) {
682 oss << "Error : invalid or duplicate RINEX3 (3-char) obsID: "
683 << GD.wantedObsIDs[i] << endl;
684 continue;
685 }
686 ossx << " Request ObsID " << GD.wantedObsIDs[i] << endl;
687 }
688
689 // extra msg for debug<n>
690 ossx << " NB. debug0/1/2/3 <=> --dump WLF,GFF,FIN / +RAW / +WL1,WLG,GF1,GFG"
691 << " / +WLW,GFW" << endl;
692
693 // build list of desired obstypes - C may replace P within the loader
694 GD.obstypes.push_back(string("L1"));
695 GD.obstypes.push_back(string("L2"));
696 GD.obstypes.push_back(string("P1"));
697 GD.obstypes.push_back(string("P2"));
698
699 // --------------------------------------------------------------------------
700 // append errors
701 GD.cmdlineErrors += oss.str();
702 GD.cmdlineExtras += ossx.str();
703
704 LOG(DEBUG) << GD.cmdlineUsage; // this will contain list of args
705
706 // dump it
707 oss.str(""); // clear it
708 oss << "#------ Summary of " << GD.PrgmName
709 << " command line configuration --------" << endl;
710 opts.DumpConfiguration(oss);
711
712 // dump the 'extra parsing' things
713 oss << "\n# Extra Processing:\n" << GD.cmdlineExtras;
714 if(GD.verbose && GD.GLOfreqCh.size() > 0) {
715 oss << "#\n# GLO frequency channels:";
716 map<RinexSatID,int>::const_iterator it(GD.GLOfreqCh.begin());
717 i = 0;
718 for( ; it != GD.GLOfreqCh.end(); ++it) {
719 oss << " " << it->first << ":" << it->second;
720 if((++i % 9)==0) { i=0; oss << endl << "# "; }
721 }
722 oss << endl;
723 }
724 oss << "#------ End configuration summary --------";
725
726 GD.cmdlineDump = oss.str();
727
728 if(opts.hasHelp() || GD.DChelp || GD.DChelpall || GD.typehelp) {
729 LOG(INFO) << GD.cmdlineUsage;
730 // handle DChelp
731 if(GD.DChelpall || GD.DChelp) {
732 LOG(INFO) << "";
733 GD.GDC.DisplayParameterUsage(LOGstrm,"#",GD.DChelpall);
734 }
735 if(GD.typehelp) dumpAllRinex3ObsTypes(LOGstrm);
736 return 1;
737 }
738 if(opts.hasErrors()) return 2;
739 if(!GD.cmdlineErrors.empty()) {
740 //LOG(INFO) << "RETURNING invalid b/c of errors:\n" << GD.cmdlineErrors;
741 return 2;
742 }
743
744 // --------------------------------------------------------------------
745 // Open log file, if it exists
746 if(!GD.logfile.empty()) {
747 GD.oflog.open(GD.logfile.c_str(),ios_base::out);
748 if(!GD.oflog.is_open()) {
749 cerr << "Failed to open log file " << GD.logfile << endl;
750 return 5;
751 }
752 LOG(INFO) << "Output directed to log file " << GD.logfile;
753 pLOGstrm = &GD.oflog; // ConfigureLOG::Stream() = &GD.oflog;
754 LOG(INFO) << GD.Title;
755 }
756
757 // configure log stream
758 ConfigureLOG::ReportLevels() = false;
759 ConfigureLOG::ReportTimeTags() = false;
760 // debug and verbose handled earlier in GetCommandLine/PreProcessArgs
761 if(GD.debug > -1)
762 ; // handled in CommandLine::PreProcessArgs()
763 else if(GD.verbose)
764 LOGlevel = ConfigureLOG::Level("VERBOSE");
765
766 // dump configuration
767 if(GD.debug > -1) {
768 LOG(INFO) << "Found debug switch at level " << GD.debug;
769 LOG(INFO) << "\n" << GD.cmdlineUsage; // this will contain list of args
770 // NB debug turns on verbose
771 }
772
773 LOG(VERBOSE) << GD.cmdlineDump;
774
775 return 0;
776 }
777 catch(Exception& e) { GPSTK_RETHROW(e); }
778 }
779
780 //------------------------------------------------------------------------------------
781 // Check input: read RINEX and generate a complete header, saving the data.
782 // if not correct, set bool GD.inputIsValid
ValidateInput(void)783 int ValidateInput(void)
784 {
785 try {
786 int i;
787 string msg;
788 GlobalData& GD=GlobalData::Instance();
789 GD.inputIsValid = true;
790
791 if(GD.validate) LOG(INFO) << " ---- Validate configuration ----";
792
793 // where else to do this?
794 include_path(GD.logpath,GD.logfile);
795 expand_filename(GD.logfile);
796
797 if(GD.obsfiles.size() == 0) {
798 LOG(ERROR) << "Error - No input file.";
799 GD.inputIsValid = false;
800 }
801 else {
802 for(i=0; i<GD.obsfiles.size(); i++) {
803 include_path(GD.obspath,GD.obsfiles[i]);
804 expand_filename(GD.obsfiles[i]);
805 }
806
807 // sort filenames on time
808 if(GD.obsfiles.size() > 1) {
809 msg = sortRinexObsFiles(GD.obsfiles);
810 if(!msg.empty()) {
811 LOG(ERROR) << msg;
812 GD.inputIsValid = false;
813 }
814 }
815
816 for(i=0; i<GD.SP3files.size(); i++) {
817 include_path(GD.ephpath,GD.SP3files[i]);
818 expand_filename(GD.SP3files[i]);
819 }
820 for(i=0; i<GD.RNavfiles.size(); i++) {
821 include_path(GD.ephpath,GD.RNavfiles[i]);
822 expand_filename(GD.RNavfiles[i]);
823 }
824 }
825
826 if(GD.validate) {
827 LOG(INFO) << " ---- Input is "
828 << (GD.inputIsValid ? "" : "NOT ") << "valid ----";
829 return 3;
830 }
831
832 if(!GD.inputIsValid) return 4;
833
834 return 0;
835 }
836 catch(Exception& e) { GPSTK_RETHROW(e); }
837 }
838
839 //------------------------------------------------------------------------------------
Initialize(void)840 int Initialize(void)
841 {
842 try {
843 int n;
844 GlobalData& GD=GlobalData::Instance();
845
846 // exclude small elevation --------------------------------------
847 // get nav files and build EphemerisStore
848 if(GD.SP3files.size() > 0) {
849 n = FillEphemerisStore(GD.SP3files, GD.SP3EphList, GD.BCEphList);
850 if(GD.verbose) {
851 LOG(VERBOSE) << "Added " << n << " SP3 ephemeris files to store.";
852 }
853 }
854 else if(GD.RNavfiles.size() > 0) {
855 n = FillEphemerisStore(GD.RNavfiles, GD.SP3EphList, GD.BCEphList);
856 if(GD.verbose) {
857 LOG(VERBOSE) << "Added " << n << " nav ephemeris files to store.";
858 }
859 }
860
861 if(GD.SP3files.size() > 0 && GD.RNavfiles.size() > 0)
862 LOG(WARNING) << " Warning - SP3 ephemeris used; RINEX nav ignored.";
863
864 if(GD.SP3EphList.ndata() > 0) {
865 // set gap and interval checking, based on nominal timestep
866 // take default GD.SP3EphList.setPositionInterpOrder(order);
867 int order = GD.SP3EphList.getPositionInterpOrder();
868 GD.SP3EphList.setClockLinearInterp();
869
870 vector<SatID> sats(GD.SP3EphList.getSatList());
871 double dt(GD.SP3EphList.getClockTimeStep(sats[0]));
872 GD.SP3EphList.setClockGapInterval(dt+1);
873 GD.SP3EphList.setClockMaxInterval((order-1)*dt+1);
874 dt = GD.SP3EphList.getPositionTimeStep(sats[0]);
875 GD.SP3EphList.setPosGapInterval(dt+1);
876 GD.SP3EphList.setPosMaxInterval((order-1)*dt+1);
877 if(GD.debug >= 0) GD.SP3EphList.dump(LOGstrm,1);
878 else if(GD.verbose) GD.SP3EphList.dump(LOGstrm,0);
879 GD.pEph = &GD.SP3EphList;
880 }
881 else if(GD.BCEphList.size() > 0) {
882 if(GD.verbose) GD.BCEphList.dump(LOGstrm,1);
883 GD.pEph = &GD.BCEphList;
884 }
885 else if(GD.elevLimit > 0.0) {
886 GD.elevLimit = 0.0;
887 LOG(WARNING) << " Warning - unable to build ephemeris store; ignore elevations";
888 }
889
890 // is it there?
891 if(GD.pEph != NULL) {
892 if(GD.Rx.getCoordinateSystem() != Position::Unknown && GD.elevLimit > 0.0)
893 GD.doElev = true;
894 else if(GD.Rx.getCoordinateSystem() != Position::Unknown) {
895 LOG(WARNING) << " Warning - Excluding low elevation requires --elev";
896 }
897 else {
898 LOG(WARNING) << " Warning - Excluding low elevation requires --ref";
899 }
900 }
901
902 return 0;
903 }
904 catch(Exception& e) { GPSTK_RETHROW(e); }
905 }
906
907 //------------------------------------------------------------------------------------
908 /// Utility for use by ReadRinexFiles()
909 /// find the index in loader obs ids for the given system and obstype, accepting codes
910 /// from the string codes, in order. If totals for this code is zero, try for another.
911 /// return -2 if obstype is not one of L1,L2,P1,P2,
912 /// or -1 if it is, but no R3ObsID was found
findIndex(const vector<string> & allR3ObsID,const char sys,const string & obs,const string & codes,const vector<int> & totals)913 int findIndex(const vector<string>& allR3ObsID, const char sys,
914 const string& obs, const string& codes, const vector<int>& totals)
915 {
916 if(obs[0] != 'P' && obs[0] != 'L') return -2;
917 if(obs[1] != '1' && obs[1] != '2') return -2;
918 char type(obs[0]=='P' ? 'C' : 'L');
919 char freq(obs[1]);
920 //const string tkcodes(RinexObsID::validRinexTrackingCodes[sys][freq]);
921 string test3("123"); test3[0]=sys; test3[1]=type; test3[2]=freq;
922
923 int ind(-1);
924 unsigned int i,j,bestj;
925 for(i=0; i<allR3ObsID.size(); i++) {
926 const string oid(allR3ObsID[i]);
927 if(test3 == oid.substr(0,3)) {
928 for(j=0; j<codes.size(); j++) {
929 if(oid[3] == codes[j]) {
930 if(totals[i] == 0) {
931 LOG(WARNING) << " Warning - no data found for " << sys << " "
932 << obs << " in " << allR3ObsID[i] << " - skip this R3ObsID.";
933 continue;
934 }
935 if(ind == -1 || j < bestj) { ind = i; bestj = j; }
936 break;
937 }
938 }
939 }
940 }
941
942 return ind;
943 }
944
945 //------------------------------------------------------------------------------------
946 // Reads RINEX 2|3 Obs file(s) into SatPass list SPList
ReadRinexFiles(void)947 int ReadRinexFiles(void)
948 {
949 try {
950 int n,iret;
951 unsigned int i,j;
952 string str,msg;
953 GlobalData& GD=GlobalData::Instance();
954
955 LOG(INFO) << "\nLoad the RINEX files using Rinex3ObsFileLoader -------";
956
957 // declare the ROFL, giving it all the filenames
958 // NB could also loop over files here, defining an ROFL for each one...
959 Rinex3ObsFileLoader rofl(GD.obsfiles);
960
961 // configure the loader
962 // NB this already done in ExtraProcessing but leave it here anyway
963 for(i=0; i<GD.wantedObsIDs.size(); i++) { // pass in desired obs IDs
964 if(!rofl.loadObsID(GD.wantedObsIDs[i])) {
965 LOG(WARNING) << " Warning - ignore invalid or duplicate ObsID request: "
966 << GD.wantedObsIDs[i];
967 }
968 }
969
970 // NB do this in Edit() so the pass can be marked as excluded
971 //for(i=0; i<GD.exSats.size(); i++) // excluded sats (not systems)
972 // rofl.excludeSat(GD.exSats[i]);
973 rofl.saveTheData(true); // no sense to have wantedObsIDs without save
974 if(GD.decdt > 0.0) // decimate
975 rofl.setDecimation(GD.decdt);
976 rofl.setStartTime(GD.startTime); // start and stop times
977 rofl.setStopTime(GD.stopTime);
978 rofl.setTimeFormat(GD.longfmt); // time format
979
980 // load the files
981 iret = rofl.loadFiles(str,msg);
982 if(iret < 0 || !str.empty()) {
983 LOG(ERROR) << " Error - Loader failed: returned " << iret
984 << " with message " << str;
985 return -1;
986 }
987 if(!msg.empty()) LOG(INFO) << msg;
988 LOG(INFO) << "Loader read " << iret << " file" << (iret>1?"s":"")
989 << " successfully " << endl;
990
991 // dump a summary of the whole thing, including the sat/obs counts table
992 LOG(INFO) << rofl.asString();
993
994 // dump the headers
995 for(i=0; i<GD.obsfiles.size(); i++) {
996 LOG(INFO) << "\nHeader for file " << GD.obsfiles[i];
997 GD.header = rofl.getFullHeader(i);
998 GD.header.dump(LOGstrm);
999 }
1000
1001 // dump the data - NB setTimeFormat()
1002 // could also get data store and dump each element
1003 //const vector<Rinex3ObsData>& datastore(rofl.getStore());
1004 //if(DUMP(DATA)) rofl.dumpStoreData(LOGstrm);
1005
1006 // now write to SatPass -------------------------------------------
1007 LOG(INFO) << "\nWrite to SatPass -----------------------------";
1008
1009 // ------------------------------------------------
1010 // get vector of ObsIDs actually stored by loader
1011 const vector<string> loadR3ObsIDs(rofl.getWantedObsTypes());
1012 // get vector of total epoch counts for loadR3ObsIDs
1013 const vector<int> totcounts(rofl.getTotalObsCounts());
1014 // indexes[sys][i] = index in loadR3ObsIDs for GD.obstypes[i] in system sys
1015 map<char, vector<int> > indexes;
1016
1017 // loop over all systems, all obstypes: find corresponding R3 ObsID in loader's
1018 // output, and construct a new obstype for SatPass.
1019 for(i=0; i<GD.Syss.size(); i++) {
1020 char sys(GD.Syss[i][0]); // Syss[i] is 1-char string
1021
1022 string codes(GD.Codes[i]); // users prioritized list
1023 vector<int> v;
1024 indexes[sys] = v; // initialize indexes[sys] with empty vector
1025 vector<string> SPot; // SatPass obstype P: P-code PR, C: C/A PR, L: phase
1026 vector<string> R3ot; // R3 obstype from loader for SPot
1027 for(j=0; j<GD.obstypes.size(); j++) {
1028 // find the R3ObsID in loader
1029 n = findIndex(loadR3ObsIDs,sys,GD.obstypes[j],codes,totcounts);
1030 if(n == -1) {
1031 LOG(ERROR) << " Error - loader found no R3ObsID for system " << sys
1032 << " obstype " << GD.obstypes[j] << ". Abort.";
1033 return -2;
1034 }
1035 indexes[sys].push_back(n);
1036
1037 // add to map SPsysobs, use the tracking code to decide on pseudorange P|C
1038 // for purposes of determining when to apply differential code biases.
1039 string ot(GD.obstypes[j]);
1040 if(ot[0] == 'P') { // obstypes above and findIndex() use 'P'
1041 char tc(loadR3ObsIDs[n][3]); // tracking code
1042 if(sys=='G') { // GPS
1043 switch(tc) {
1044 case 'P': case 'Y': case 'W':
1045 case 'I': case 'M': case 'Q': case 'D': // all P-code, right?
1046 ot[0] = 'P'; break;
1047 case 'C': case 'L': case 'X': case 'S': // all C/A
1048 ot[0] = 'C'; break;
1049 default: break;
1050 }
1051 }
1052 if(sys=='R') { // GLO
1053 ot[0] = tc; // P or C are only choices
1054 }
1055 }
1056 SPot.push_back(n >= 0 ? ot : GD.obstypes[j]);
1057 R3ot.push_back(n >= 0 ? loadR3ObsIDs[n] : "-NA-");
1058 }
1059
1060 GD.SPsysobs[sys] = SPot;
1061 GD.R3sysobs[sys] = R3ot;
1062 }
1063
1064 // print obs types assignment SP <=> R3
1065 LOG(INFO) << " Assign RINEX3-ObsIDs to SatPass obstypes for each system :";
1066 ostringstream oss;
1067
1068 map<char, vector<string> >::const_iterator rit = GD.R3sysobs.begin();
1069 while(rit != GD.R3sysobs.end()) {
1070 oss.str("");
1071 oss << " System " << rit->first << " ("
1072 << RinexObsID::map1to3sys[string(1,rit->first)] << "): SatPass obstypes = [";
1073 for(i=0; i<rit->second.size(); i++)
1074 oss << (i==0 ? "":",") << rit->second[i];
1075 LOG(INFO) << oss.str() << "]";
1076 rit++;
1077 }
1078
1079 // define dt
1080 GD.nomdt = rofl.getDT();
1081 LOG(VERBOSE) << fixed << setprecision(2)
1082 << " The input data interval is " << GD.nomdt << " seconds.";
1083
1084 // -------------------------------------------------------------
1085 // NB all passes have the same SP obs types (per sys)
1086 n = rofl.WriteSatPassList(GD.SPsysobs, indexes, GD.SPList);
1087
1088 LOG(INFO) << " WriteSatPassList returned " << n << " passes.";
1089 LOG(INFO) << " Dump the passes:";
1090 for(i=0; i<GD.SPList.size(); i++) {
1091 LOG(INFO) << "SPL " << setw(3) << i+1 << " " << GD.SPList[i];
1092 }
1093
1094 rofl.reset();
1095
1096 return 0;
1097 }
1098 catch(Exception& e) { GPSTK_RETHROW(e); }
1099 } // end ReadRinexFiles()
1100
1101 //------------------------------------------------------------------------------------
PreProcess(void)1102 int PreProcess(void)
1103 {
1104 try {
1105 unsigned int i,j;
1106 string msg;
1107 GlobalData& GD=GlobalData::Instance();
1108
1109 msecHandler msh;
1110 i = FindMilliseconds(GD.SPList, msh);
1111 LOG(INFO) << "\n" << msh.getFindMessage(GD.fixMS); // flag is verbose
1112
1113 if(GD.fixMS && i) {
1114 RemoveMilliseconds(GD.SPList, msh);
1115 LOG(INFO) << msh.getFixMessage(GD.verbose);
1116 }
1117
1118 // mark low elevation data bad
1119 if(GD.doElev) {
1120 CorrectedEphemerisRange CER;
1121 for(i=0; i<GD.SPList.size(); i++) {
1122 if(GD.SPList[i].status() == -1) continue;
1123
1124 RinexSatID sat = GD.SPList[i].getSat();
1125 for(j=0; j<GD.SPList[i].size(); j++) {
1126 Epoch ttag = GD.SPList[i].time(j);
1127 try {
1128 //double ER =
1129 CER.ComputeAtReceiveTime(ttag, GD.Rx, sat, *GD.pEph);
1130 if(CER.elevation >= GD.elevLimit) continue;
1131 }
1132 catch(InvalidRequest&) {
1133 // do not exclude the sat here; PRSolution will...
1134 LOG(DEBUG) << "CER did not find ephemeris for "
1135 << sat << " at time " << ttag.printf(GD.timefmt);
1136 // fall through
1137 }
1138
1139 // mark it bad
1140 GD.SPList[i].setFlag(j,SatPass::BAD);
1141
1142 } // end loop over data in SPList[i]
1143 } // end loop over SatPasses
1144 } // end if elev mask
1145
1146 return 0;
1147 }
1148 catch(Exception& e) { GPSTK_RETHROW(e); }
1149 }
1150
1151 //------------------------------------------------------------------------------------
Process(void)1152 int Process(void)
1153 {
1154 try {
1155 int iret=-666,i=-666,GLOn=-666;
1156 string msg;
1157 ostringstream oss;
1158 map<RinexSatID,int>::const_iterator gloit;
1159 GlobalData& GD=GlobalData::Instance();
1160
1161 // dump the configuration
1162 LOG(INFO) << "\n# GDC configuration:";
1163 GD.GDC.DisplayParameterUsage(LOGstrm, "#", true);
1164 LOG(INFO) << "# End of GDC configuration.\n";
1165
1166 // call the GDC
1167 string retmsg;
1168 for(i=0; i<GD.SPList.size(); i++) {
1169 // configure SatPass SPList[i]
1170 GD.SPList[i].setOutputFormat(GD.timefmt); // nround?
1171
1172 RinexSatID sat(GD.SPList[i].getSat());
1173
1174 // exclude sats
1175 if(vectorindex(GD.exSat,sat) != -1) {
1176 LOG(VERBOSE) << "DFX " << setw(3) << i+1 << " " << sat << " sat excluded.";
1177 continue;
1178 }
1179 if(GD.onlySat.size() > 0 && vectorindex(GD.onlySat,sat) == -1) {
1180 LOG(VERBOSE) << "DFX " << setw(3) << i+1 << " " << sat << " not only sat.";
1181 continue;
1182 }
1183
1184 // exclude passes
1185 if(GD.onlyPass.size() > 0 && vectorindex(GD.onlyPass,i+1) == -1) {
1186 LOG(VERBOSE) << "DFX " << setw(3) << i+1 << " " << sat << " pass excluded.";
1187 continue;
1188 }
1189
1190 // no good data
1191 if(GD.SPList[i].getNgood() == 0) {
1192 LOG(VERBOSE) << "DFX " << setw(3) << i+1 << " " << sat << " no good data.";
1193 continue;
1194 }
1195
1196 // get the GLOn
1197 if(sat.system == SatelliteSystem::Glonass) {
1198 gloit = GD.GLOfreqCh.find(sat);
1199
1200 // if GLONASS frequency channel not given, try to find it
1201 if(gloit != GD.GLOfreqCh.end()) {
1202 GLOn = gloit->second;
1203 }
1204 else {
1205 if(!GD.SPList[i].getGLOchannel(GLOn, msg)) {
1206 LOG(WARNING) << " Warning - unable to compute GLO channel for sat "
1207 << sat << " - skip pass : " + msg;
1208 }
1209 else {
1210 LOG(VERBOSE) << "# GLO frequency channel for " << sat
1211 << " was computed from data, = " << GLOn << "; " << msg;
1212 GD.GLOfreqCh[sat] = GLOn;
1213 }
1214 }
1215 }
1216
1217 // make the unique number == pass number == i+1, always
1218 GD.GDC.ForceUniqueNumber(i); // NB it will be incremented in DC call
1219
1220 // call GDC
1221 iret = GD.GDC.DiscontinuityCorrector(GD.SPList[i], retmsg, GD.EditCmds, GLOn);
1222 // TD is iret<0 handled by retmsg?
1223 int unique(GD.GDC.getUniqueNumber()); // == i+1 here
1224
1225 // save the GLO freq channel
1226 if(sat.system == SatelliteSystem::Glonass &&
1227 GD.GLOfreqCh.find(sat) == GD.GLOfreqCh.end())
1228 GD.GLOfreqCh[sat] = GLOn;
1229
1230 // add tag to lines in the retmsg
1231 oss.str(""); oss << "DFX " << setw(3) << unique << " " << sat;
1232 msg = oss.str();
1233 // add tag == msg to all the lines in retmsg
1234 StringUtils::change(retmsg,"\n","\n"+msg+" ");
1235 retmsg = msg + " " + retmsg;
1236 LOG(INFO) << retmsg;
1237 }
1238
1239 // write editing commands
1240 if(!GD.cmdout.empty()) {
1241 ofstream ofs;
1242 ofs.open(GD.cmdout.c_str(),ios_base::out);
1243 if(!GD.oflog.is_open()) {
1244 LOG(ERROR) << " Error - failed to open file " << GD.cmdout;
1245 GD.cmdout = string();
1246 }
1247 else {
1248 for(i=0; i<GD.EditCmds.size(); i++)
1249 ofs << GD.EditCmds[i] << endl;
1250 ofs.close();
1251 }
1252 }
1253
1254 // write to RINEX
1255 if(!GD.obsout.empty()) {
1256 LOG(INFO) << "Write to RINEX file " << GD.obsout;
1257
1258 // strict RINEX, and we may have computed them
1259 GD.header.glonassFreqNo.clear();
1260 for(gloit = GD.GLOfreqCh.begin(); gloit != GD.GLOfreqCh.end(); ++gloit)
1261 GD.header.glonassFreqNo[gloit->first] = gloit->second;
1262 GD.header.valid |= Rinex3ObsHeader::validGlonassSlotFreqNo;
1263 // strict RINEX
1264 GD.header.valid |= Rinex3ObsHeader::validGlonassCodPhsBias;
1265
1266 GD.header.commentList.push_back(string("Edited by ")+GD.Title);
1267
1268 i = SatPassToRinex3File(GD.obsout, GD.header, GD.R3sysobs, GD.SPList);
1269 LOG(VERBOSE) << "SatPassToRinex3File returned " << i;
1270 }
1271
1272 return 0;
1273 }
1274 catch(Exception& e) { GPSTK_RETHROW(e); }
1275 }
1276
1277 //------------------------------------------------------------------------------------
1278 //------------------------------------------------------------------------------------
1279