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 RinEdit.cpp
40 /// Read Rinex observation files (version 2 or 3) and edit them, writing the edited
41 /// data to a new RINEX file.
42
43 // system
44 #include <string>
45 #include <vector>
46 #include <map>
47 #include <iostream>
48 #include <fstream>
49 #include <algorithm>
50
51 // GPSTK
52 #include "Exception.hpp"
53 #include "StringUtils.hpp"
54 #include "GNSSconstants.hpp"
55
56 #include "singleton.hpp"
57 #include "expandtilde.hpp"
58 #include "logstream.hpp"
59 #include "CommandLine.hpp"
60
61 #include "CommonTime.hpp"
62 #include "Epoch.hpp"
63 #include "TimeString.hpp"
64
65 #include "RinexSatID.hpp"
66 #include "RinexObsID.hpp"
67 #include "Rinex3ObsStream.hpp"
68 #include "Rinex3ObsHeader.hpp"
69 #include "Rinex3ObsData.hpp"
70
71 //------------------------------------------------------------------------------
72 using namespace std;
73 using namespace gpstk;
74 using namespace StringUtils;
75
76 //------------------------------------------------------------------------------
77 string version(string("2.4 9/23/15 rev"));
78 // TD
79 // if reading a R2 file, allow obs types in cmds to be R2 versions (C1,etc)
80 // option to replace input with output?
81 // include optional fmt input for t in edit cmds - is this feasible?
82 // if given a 4-char OT and SV, check their consistency
83 // OK - test it. implement DO - how? copy and edit, or clear and copy?
84 // OK - test it. edit header when DS (alone) or DO appear ... how?
85 // how to handle aux header data if its first - OF not yet opened
86 // END TD
87
88 //------------------------------------------------------------------------------
89 //------------------------------------------------------------------------------
90 // class to encapsulate editing commands
91 class EditCmd
92 {
93 public:
94 enum CmdType
95 {
96 invalidCT=0,
97 ofCT,
98 daCT,
99 doCT,
100 dsCT,
101 ddCT,
102 sdCT,
103 ssCT,
104 slCT,
105 bdCT,
106 bsCT,
107 blCT,
108 bzCT,
109 countCT
110 };
111 CmdType type; // the type of this command
112 RinexSatID sat; // satellite
113 RinexObsID obs; // observation type
114 CommonTime ttag; // associated time tag
115 int sign; // sign +1,0,-1 meaning start, one-time, stop
116 int idata; // integer e.g. SSI or LLI
117 double data; // data e.g. bias value
118 string field; // OF file name
119
EditCmd(void)120 EditCmd(void) : type(invalidCT) {} // default constructor
~EditCmd(void)121 ~EditCmd(void) {} // destructor
122
123 /** constructor from strings, i.e. parser e.g. "DA+","t" or
124 * "BDp","SV,OT,t,s"
125 * @throw Exception */
126 EditCmd(const string typestr, const string arg);
127 /// parse time from string
128 bool parseTime(const string arg, CommonTime& ttag) throw();
129
130 /// is it valid?
isValid(void)131 inline bool isValid(void) throw()
132 { return (type != invalidCT); }
133
134 /** dump, with optional message at front
135 * @throw Exception */
136 string asString(string msg=string());
137
138 }; // end class EditCmd
139
140
141 // used for sorting
142 class EditCmdLessThan
143 {
144 public:
operator ()(const EditCmd & ec1,const EditCmd & ec2)145 bool operator()(const EditCmd& ec1, const EditCmd& ec2)
146 { return (ec1.ttag < ec2.ttag); }
147 };
148
149 //------------------------------------------------------------------------------
150 //------------------------------------------------------------------------------
151 // Object for command line input and global data
152 class Configuration : public Singleton<Configuration>
153 {
154 public:
155
156 // Default and only constructor
Configuration()157 Configuration() throw() { setDefaults(); }
158
159 // Create, parse and process command line options and user input
160 int processUserInput(int argc, char **argv) throw();
161
162 // Design the command line
163 string buildCommandLine(void) throw();
164
165 // Open the output file, and parse the strings used on the command line
166 // return -4 if log file could not be opened
167 int extraProcessing(string& errors, string& extras) throw();
168
169 // Parse one of the vector<string> of edit cmd options
170 void parseEditCmds(vector<string>& v, const string l, ostringstream& os) throw();
171
172 private:
173
174 // Define default values
setDefaults(void)175 void setDefaults(void) throw()
176 {
177 defaultstartStr = string("[Beginning of dataset]");
178 defaultstopStr = string("[End of dataset]");
179 beginTime = CivilTime(1980,1,6,0,0,0.0,TimeSystem::GPS).convertToCommonTime();
180 endTime = CommonTime::END_OF_TIME;
181 decimate = 0.0;
182
183 help = verbose = outver2 = false;
184 debug = -1;
185
186 messHDdc = messHDda = false;
187 } // end Configuration::SetDefaults()
188
189 public:
190
191 // member data
192 CommandLine opts; // command line options and syntax page
193 static const string prgmName; // program name
194 string Title; // id line printed to screen and log
195
196 // start command line input
197 bool help, verbose, outver2;
198 int debug;
199 string cfgfile;
200
201 vector<string> messIF, messOF; // RINEX obs file names - IF and OF args
202 string InObspath,OutObspath; // obs file path
203
204 // times derived from --start and --stop
205 string defaultstartStr,startStr;
206 string defaultstopStr,stopStr;
207 CommonTime beginTime,endTime,decTime;
208
209 double decimate,timetol; // decimate input data
210 string logfile; // output log file
211
212 // added "mess" to each of these variables because they were
213 // conflicting with precompiler macro definitions and enum names
214 // and I couldn't be bothered to do a more intelligent renaming.
215
216 // editing commands
217 bool messHDdc,messHDda,messBZ;
218 string messHDp,messHDr,messHDo,messHDa,messHDx,messHDm,messHDn,messHDt,messHDh,messHDj,messHDk,messHDl,messHDs;
219 vector<string> messHDc,messDA,messDAm,messDAp,messDO,messDS,messDSp,messDSm,messDD,messDDp,messDDm;
220 vector<string> messSD,messSS,messSL,messSLp,messSLm,messBD,messBDp,messBDm,messBS,messBL;
221
222 // end of command line input
223
224 string msg;
225 ofstream logstrm;
226 static const string calfmt,gpsfmt,longfmt;
227
228 // handle commands
229 vector<EditCmd> vecCmds, currCmds;
230 Rinex3ObsStream ostrm; // RINEX output
231
232 }; // end class Configuration
233
234 //------------------------------------------------------------------------------
235 // const members of Configuration
236 const string Configuration::prgmName = string("RinEdit");
237 const string Configuration::calfmt = string("%04Y/%02m/%02d %02H:%02M:%02S");
238 const string Configuration::gpsfmt = string("%4F %10.3g");
239 const string Configuration::longfmt = calfmt + " = " + gpsfmt + " %P";
240
241 //------------------------------------------------------------------------------
242 // prototypes
243 /**
244 * @throw Exception */
245 int initialize(string& errors);
246 void fixEditCmdList(void) throw();
247 /**
248 * @throw Exception */
249 int processFiles(void);
250 /**
251 * @throw Exception */
252 int processOneEpoch(Rinex3ObsHeader& Rhead, Rinex3ObsHeader& RHout,
253 Rinex3ObsData& Rdata, Rinex3ObsData& RDout);
254 /**
255 * @throw Exception */
256 int executeEditCmd(const vector<EditCmd>::iterator& it, Rinex3ObsHeader& Rhead,
257 Rinex3ObsData& Rdata);
258
259 //------------------------------------------------------------------------------
260 //------------------------------------------------------------------------------
main(int argc,char ** argv)261 int main(int argc, char **argv)
262 {
263 // get the (single) instance of the configuration
264 Configuration& C(Configuration::Instance());
265
266 try {
267 int iret;
268 clock_t totaltime(clock());
269 Epoch wallclkbeg;
270 wallclkbeg.setLocalTime();
271
272 // build title = first line of output
273 C.Title = "# " + C.prgmName + ", part of the GPS Toolkit, Ver " + version
274 + ", Run " + printTime(wallclkbeg,C.calfmt);
275
276 for(;;) {
277 // get information from the command line
278 // iret -2 -3 -4
279 if((iret = C.processUserInput(argc, argv)) != 0) break;
280
281 // read stores, check input etc
282 string errs;
283 if((iret = initialize(errs)) != 0) {
284 LOG(ERROR) << "------- Input is not valid: ----------\n" << errs
285 << "\n------- end errors -----------";
286 break;
287 }
288 if(!errs.empty()) LOG(INFO) << errs; // Warnings are here too
289
290 processFiles();
291 // iret = 0 = success
292 iret = 0;
293
294 break; // mandatory
295 }
296
297 if(iret == 0) {
298 // print elapsed time
299 totaltime = clock()-totaltime;
300 Epoch wallclkend;
301 wallclkend.setLocalTime();
302 ostringstream oss;
303 oss << C.prgmName << " timing: processing " << fixed << setprecision(3)
304 << double(totaltime)/double(CLOCKS_PER_SEC) << " sec, wallclock: "
305 << setprecision(0) << (wallclkend-wallclkbeg) << " sec.";
306 LOG(INFO) << oss.str();
307 }
308
309 return iret;
310 }
311 catch(FFStreamError& e) { cerr << "FFStreamError: " << e.what(); }
312 catch(Exception& e) { cerr << "Exception: " << e.what(); }
313 catch (...) { cerr << "Unknown exception. Abort." << endl; }
314 return 1;
315
316 } // end main()
317
318 //------------------------------------------------------------------------------
319 // return -5 if input is not valid
initialize(string & errors)320 int initialize(string& errors)
321 {
322 try
323 {
324 bool isValid(true);
325 int j;
326 size_t i;
327 Configuration& C(Configuration::Instance());
328 errors = string("");
329 ostringstream ossE;
330
331 // must have an input file and an output file
332 if(C.messIF.size() == 0) {
333 ossE << "Error : No valid input files have been specified.";
334 isValid = false;
335 }
336 if(C.messOF.size() == 0) {
337 ossE << "Error : No valid output files have been specified.";
338 isValid = false;
339 }
340
341 // add path to filenames, and expand tilde (~)
342 include_path(C.InObspath, C.messIF);
343
344 // add path to all OF
345 // also if first OF command has a timetag, remove it and make that the start time
346 for(j=0, i=0; i<C.vecCmds.size(); ++i) {
347 if(C.vecCmds[i].type == EditCmd::ofCT) {
348 if(j == 0 && C.vecCmds[i].ttag != CommonTime::BEGINNING_OF_TIME) {
349 if(C.beginTime < C.vecCmds[i].ttag)
350 C.beginTime = C.vecCmds[i].ttag;
351 C.vecCmds[i].ttag = CommonTime::BEGINNING_OF_TIME;
352 }
353 j++;
354 include_path(C.OutObspath, C.vecCmds[i].field);
355 //LOG(VERBOSE) << "Full output file name is " << C.vecCmds[i].field;
356 }
357 }
358
359 // ------ compute and save a reference time for decimation
360 if(C.decimate > 0.0) {
361 // TD what if beginTime == BEGINNING_OF_TIME ?
362 C.decTime = C.beginTime;
363 double s,sow(static_cast<GPSWeekSecond>(C.decTime).sow);
364 s = int(C.decimate * int(sow/C.decimate));
365 if(::fabs(s-sow) > 1.0) LOG(WARNING) << "Warning : decimation reference time "
366 << "(--start) is not an even GPS-seconds-of-week mark.";
367 C.decTime = static_cast<CommonTime>(
368 GPSWeekSecond(static_cast<GPSWeekSecond>(C.decTime).week,0.0));
369 LOG(DEBUG) << "Decimate, with final decimate ref time "
370 << printTime(C.decTime,C.longfmt) << " and step " << C.decimate;
371 }
372
373 // -------- save errors and output
374 errors = ossE.str();
375 stripTrailing(errors,'\n');
376 replaceAll(errors,"\n","\n# ");
377
378 if(!isValid) return -5;
379 return 0;
380 }
381 catch(Exception& e) { GPSTK_RETHROW(e); }
382 } // end initialize()
383
384
385 //------------------------------------------------------------------------------
386 // Return 0 ok, >0 number of files successfully read, <0 fatal error
processFiles(void)387 int processFiles(void)
388 {
389 try
390 {
391 Configuration& C(Configuration::Instance());
392 C.beginTime.setTimeSystem(TimeSystem::GPS);
393 C.endTime.setTimeSystem(TimeSystem::GPS);
394 int iret,nfiles;
395 size_t i,nfile;
396 string tag;
397 RinexSatID sat;
398 ostringstream oss;
399
400 if (C.debug > -1)
401 Rinex3ObsHeader::debug = 1;
402
403 for(nfiles=0,nfile=0; nfile<C.messIF.size(); nfile++)
404 {
405 Rinex3ObsStream istrm;
406 Rinex3ObsHeader Rhead,RHout; // use one header for input and output
407 Rinex3ObsData Rdata,RDout;
408 string filename(C.messIF[nfile]);
409
410 // iret is set to 0 ok, or could not: 1 open file, 2 read header, 3 read data
411 iret = 0;
412
413 // open the file ------------------------------------------------
414 istrm.open(filename.c_str(),ios::in);
415 if(!istrm.is_open())
416 {
417 LOG(WARNING) << "Warning : could not open file " << filename;
418 iret = 1;
419 continue;
420 }
421 else
422 LOG(DEBUG) << "Opened input file " << filename;
423 istrm.exceptions(ios::failbit);
424
425 // read the header ----------------------------------------------
426 LOG(INFO) << "Reading header...";
427 try
428 {
429 istrm >> Rhead;
430 }
431 catch(Exception& e)
432 {
433 LOG(WARNING) << "Warning : Failed to read header: " << e.what()
434 << "\n Header dump follows.";
435 Rhead.dump(LOGstrm);
436 istrm.close();
437 iret = 2;
438 continue;
439 }
440 if(C.debug > -1)
441 {
442 LOG(DEBUG) << "Input header for RINEX file " << filename;
443 Rhead.dump(LOGstrm);
444 }
445 // dump the obs types
446 map<string,vector<RinexObsID> >::const_iterator kt;
447 for (kt = Rhead.mapObsTypes.begin(); kt != Rhead.mapObsTypes.end(); kt++)
448 {
449 sat.fromString(kt->first);
450 oss.str("");
451 oss << "# Header ObsIDs " << sat.systemString3()
452 << " (" << kt->second.size() << "):";
453 for(i=0; i<kt->second.size(); i++)
454 oss << " " << kt->second[i].asString();
455 LOG(INFO) << oss.str();
456 }
457 // we have to set the time system of all the timetags using ttag from file
458 vector<EditCmd>::iterator jt;
459 for (jt=C.vecCmds.begin(); jt != C.vecCmds.end(); ++jt)
460 jt->ttag.setTimeSystem(Rhead.firstObs.getTimeSystem());
461 // -----------------------------------------------------------------
462 // generate output header from input header and DO,DS commands
463 bool mungeData(false);
464 map<string, map<int,int> > mapSysObsIDTranslate;
465
466 RHout = Rhead;
467 vector<EditCmd>::iterator it;
468 for (it = C.vecCmds.begin(); it != C.vecCmds.end(); it++)
469 {
470 LOG(DEBUG) << "Killing " << it->asString() << " " << it->sat;
471
472 // DO delete obs without sign
473 if (it->type == EditCmd::doCT)
474 {
475 // if the system is defined, delete only for that system
476 string sys(asString(it->sat.systemChar()));
477
478 // loop over systems (short-circuit if sys is defined)
479 Rinex3ObsHeader::RinexObsMap::iterator jt;
480 for (jt=RHout.mapObsTypes.begin(); jt != RHout.mapObsTypes.end(); ++jt)
481 {
482 if (sys != string("?") && sys != jt->first)
483 continue;
484 RinexObsID obsid(jt->first + it->obs.asString(),
485 Rhead.version);
486
487 // find the OT in the output header map, and delete it
488 Rinex3ObsHeader::RinexObsVec::iterator kt;
489 kt = find(jt->second.begin(), jt->second.end(), obsid);
490 if(kt == jt->second.end())
491 continue;
492 jt->second.erase(kt);
493 // flag the obs types have changed so the translations need to as well
494 mungeData = true;
495 }
496 }
497
498 // DS delete sat without sign and without time
499 else if (it->type == EditCmd::dsCT && it->sign >= 0
500 && it->ttag == CommonTime::BEGINNING_OF_TIME)
501 {
502 if (it->sat.id == -1)
503 {
504 // Delete all satellites with this system
505 Rinex3ObsHeader::PRNNumObsMap::iterator i,j;
506 for (i = RHout.numObsForSat.begin(); i != RHout.numObsForSat.end();)
507 {
508 j = i++;
509 if (j->first.system == it->sat.system)
510 RHout.numObsForSat.erase(j);
511 }
512 if (it->sat.system == SatelliteSystem::Glonass)
513 RHout.glonassFreqNo.clear();
514
515 // Remove obs types for that system
516 string sys(asString(it->sat.systemChar()));
517 RHout.mapObsTypes.erase(sys);
518 }
519 else
520 {
521 // Just delete a single satellite if its there
522 Rinex3ObsHeader::PRNNumObsMap::iterator jt = RHout.numObsForSat.find(it->sat);
523 if (jt != RHout.numObsForSat.end())
524 RHout.numObsForSat.erase(jt);
525
526 Rinex3ObsHeader::GLOFreqNumMap::iterator kt = RHout.glonassFreqNo.find(it->sat);
527 if (kt != RHout.glonassFreqNo.end())
528 RHout.glonassFreqNo.erase(kt);
529 }
530 }
531 } // end loop over edit commands
532
533 // if mapObsTypes has changed, must make a map of indexes for translation
534 // mapSysObsIDTranslate[sys][input index] = output index
535 if (mungeData)
536 {
537 map<string, vector<RinexObsID> >::iterator jt;
538 for(jt = Rhead.mapObsTypes.begin(); jt != Rhead.mapObsTypes.end(); ++jt)
539 {
540 string sys(jt->first);
541 // TD what if entire sys is deleted? RHout[sys] does not exist
542 vector<RinexObsID>::iterator kt;
543 for(i=0; i < jt->second.size(); i++)
544 {
545 kt = find(RHout.mapObsTypes[sys].begin(),
546 RHout.mapObsTypes[sys].end(), jt->second[i]);
547 mapSysObsIDTranslate[sys][i]
548 = (kt == RHout.mapObsTypes[sys].end()
549 ? -1 // not found
550 : (kt - RHout.mapObsTypes[sys].begin())); // output index
551 }
552 }
553
554 // dump it
555 if(C.debug > -1)
556 {
557 for(jt = Rhead.mapObsTypes.begin(); jt != Rhead.mapObsTypes.end(); ++jt)
558 {
559 string sys(jt->first);
560 oss.str("");
561 oss << "Translation map for sys " << sys;
562 for(i=0; i < jt->second.size(); i++)
563 oss << " " << i << ":" << mapSysObsIDTranslate[sys][i];
564 LOG(DEBUG) << oss.str();
565 }
566 }
567 }
568
569
570 if (C.outver2)
571 {
572 if (C.debug > -1)
573 {
574 LOG(DEBUG) << "Header pre prepareVer2Write";
575 RHout.dump(LOGstrm);
576 }
577 RHout.prepareVer2Write();
578 }
579
580 // NB. header will be written by executeEditCmd
581 // -----------------------------------------------------------------
582
583 if (C.debug > -1)
584 {
585 LOG(DEBUG) << "Output header";
586 RHout.dump(LOGstrm);
587 }
588
589 // loop over epochs ---------------------------------------------
590 LOG(INFO) << "Reading observations...";
591 while(1)
592 {
593 try { istrm >> Rdata; }
594 catch(Exception& e)
595 {
596 LOG(WARNING) << " Warning : Failed to read obs data (Exception "
597 << e.getText(0) << "); dump follows.";
598 Rdata.dump(LOGstrm,Rhead);
599 istrm.close();
600 iret = 3;
601 break;
602 }
603 catch(std::exception& e) {
604 Exception ge(string("Std excep: ") + e.what());
605 GPSTK_THROW(ge);
606 }
607 catch(...) {
608 Exception ue("Unknown exception while reading RINEX data.");
609 GPSTK_THROW(ue);
610 }
611
612 // normal EOF
613 if(!istrm.good() || istrm.eof()) { iret = 0; break; }
614
615 LOG(DEBUG) << "";
616 LOG(DEBUG) << " Read RINEX data: flag " << Rdata.epochFlag
617 << ", timetag " << printTime(Rdata.time,C.longfmt);
618
619 // stay within time limits
620 if(Rdata.time < C.beginTime) {
621 LOG(DEBUG) << " RINEX data timetag " << printTime(C.beginTime,C.longfmt)
622 << " is before begin time.";
623 continue;
624 }
625 if(Rdata.time > C.endTime) {
626 LOG(DEBUG) << " RINEX data timetag " << printTime(C.endTime,C.longfmt)
627 << " is after end time.";
628 break;
629 }
630
631 // decimate
632 if (C.decimate > 0.0)
633 {
634 double dt(::fabs(Rdata.time - C.decTime));
635 dt -= C.decimate * long(0.5 + dt/C.decimate);
636 LOG(DEBUG) << "Decimate? dt = " << fixed << setprecision(2) << dt;
637 if (::fabs(dt) > 0.25)
638 {
639 LOG(DEBUG) << " Decimation rejects RINEX data timetag "
640 << printTime(Rdata.time,C.longfmt);
641 continue;
642 }
643 }
644
645 // copy data to output
646 RDout = Rdata;
647 if (mungeData)
648 {
649 // must edit RDout.obs
650 RDout.obs.clear();
651 // loop over satellites -----------------------------
652 Rinex3ObsData::DataMap::const_iterator kt;
653 for (kt=Rdata.obs.begin(); kt!=Rdata.obs.end(); ++kt)
654 {
655 sat = kt->first;
656 string sys(string(1,sat.systemChar()));
657 for (i=0; i<Rdata.obs[sat].size(); i++)
658 if (mapSysObsIDTranslate[sys][i] > -1)
659 RDout.obs[sat].push_back(Rdata.obs[sat][i]);
660 } // end loop over sats
661 }
662
663 // apply editing commands, including open files, write out headers
664 iret = processOneEpoch(Rhead, RHout, Rdata, RDout);
665 if(iret < 0) break;
666 if(iret > 0) continue;
667
668 // write data out
669 try { C.ostrm << RDout; }
670 catch(Exception& e) { GPSTK_RETHROW(e); }
671
672 // debug: dump the RINEX data objects input and output
673 if (C.debug > -1)
674 {
675 LOG(DEBUG) << "INPUT data ---------------";
676 Rdata.dump(LOGstrm,Rhead);
677 LOG(DEBUG) << "OUTPUT data ---------------";
678 RDout.dump(LOGstrm,Rhead);
679 }
680
681 } // end while loop over epochs
682
683 // clean up
684 istrm.close();
685
686 // failure due to critical error
687 if(iret < 0) break;
688
689 if(iret >= 0) nfiles++;
690
691 } // end loop over files
692
693 // final clean up
694 LOG(INFO) << " Close output file.";
695 C.ostrm.close();
696
697 if(iret < 0) return iret;
698
699 return nfiles;
700 }
701 catch(Exception& e) { GPSTK_RETHROW(e); }
702 } // end processFiles()
703
704 //------------------------------------------------------------------------------
705 // return <0 fatal; >0 skip this epoch
processOneEpoch(Rinex3ObsHeader & Rhead,Rinex3ObsHeader & RHout,Rinex3ObsData & Rdata,Rinex3ObsData & RDout)706 int processOneEpoch(Rinex3ObsHeader& Rhead, Rinex3ObsHeader& RHout,
707 Rinex3ObsData& Rdata, Rinex3ObsData& RDout)
708 {
709 try
710 {
711 Configuration& C(Configuration::Instance());
712 int iret(0);
713 RinexSatID sat;
714 CommonTime now(Rdata.time); // TD what if its aux data w/o an epoch?
715
716 // if aux header data, either output or skip
717 if (RDout.epochFlag > 1)
718 { // aux header data
719 if (C.messHDda)
720 return 1;
721 return 0;
722 }
723
724 else
725 { // regular data
726 vector<EditCmd>::iterator it, jt;
727 vector<EditCmd> toCurr;
728
729 // for cmds with ttag <= now either execute and delete, or move to current
730 it = C.vecCmds.begin();
731 while(it != C.vecCmds.end())
732 {
733 if (it->ttag <= now || ::fabs(it->ttag - now) < C.timetol)
734 {
735 LOG(DEBUG) << "Execute vec cmd " << it->asString();
736 // delete one-time cmds, move others to curr and delete
737 iret = executeEditCmd(it, RHout, RDout);
738 if(iret < 0) return iret; // fatal error
739
740 // keep this command on the current list
741 if (iret > 0) toCurr.push_back(*it); // C.currCmds.push_back(*it);
742
743 // if this is a '-' cmd to be deleted, find matching '+' and delete
744 // note fixEditCmdList() forced every - to have a corresponding +
745 if (iret == 0 && it->sign == -1)
746 {
747 for (jt = C.currCmds.begin(); jt != C.currCmds.end(); ++jt)
748 if (jt->type==it->type && jt->sat==it->sat && jt->obs==it->obs)
749 break;
750 if (jt == C.currCmds.end())
751 {
752 Exception e(string("Execute failed to find + cmd matching ")+it->asString());
753 GPSTK_THROW(e);
754 }
755 C.currCmds.erase(jt);
756 }
757
758 // remove from vecCmds
759 it = C.vecCmds.erase(it); // erase vector element
760 }
761 else
762 ++it;
763 }
764
765 // apply current commands, deleting obsolete ones
766 it = C.currCmds.begin();
767 while(it != C.currCmds.end())
768 {
769 LOG(DEBUG) << "Execute current cmd " << it->asString();
770 // execute command; delete obsolete commands
771 iret = executeEditCmd(it, RHout, RDout);
772 if(iret < 0)
773 return iret;
774
775 if(iret == 0)
776 it = C.currCmds.erase(it); // erase vector element
777 else
778 ++it;
779 }
780
781 for (it = toCurr.begin(); it != toCurr.end(); ++it)
782 C.currCmds.push_back(*it);
783 }
784
785 return 0;
786 }
787 catch(Exception& e) { GPSTK_RETHROW(e); }
788 } // end processOneEpoch()
789
790
791 //------------------------------------------------------------------------------
792 // return >0 to put/keep the command on the 'current' queue
793 // return <0 for fatal error
executeEditCmd(const vector<EditCmd>::iterator & it,Rinex3ObsHeader & Rhead,Rinex3ObsData & Rdata)794 int executeEditCmd(const vector<EditCmd>::iterator& it, Rinex3ObsHeader& Rhead,
795 Rinex3ObsData& Rdata)
796 {
797 Configuration& C(Configuration::Instance());
798 size_t i,j;
799 string sys;
800 vector<string> flds;
801 Rinex3ObsData::DataMap::const_iterator kt;
802 vector<RinexObsID>::iterator jt;
803
804 try
805 {
806 if(it->type == EditCmd::invalidCT)
807 {
808 LOG(DEBUG) << " Invalid command " << it->asString();
809 return 0;
810 }
811
812 // OF output file --------------------------------------------------------
813 else if(it->type == EditCmd::ofCT)
814 {
815 // close the old file
816 if(C.ostrm.is_open()) { C.ostrm.close(); C.ostrm.clear(); }
817
818 // open the new file
819 C.ostrm.open(it->field.c_str(),ios::out);
820 if(!C.ostrm.is_open())
821 {
822 LOG(ERROR) << "Error : could not open output file " << it->field;
823 return -1;
824 }
825 C.ostrm.exceptions(ios::failbit);
826
827 LOG(INFO) << " Opened output file " << it->field << " at time "
828 << printTime(Rdata.time,C.longfmt);
829
830 // if this is the first file, apply the header commands
831 if(it->ttag == CommonTime::BEGINNING_OF_TIME)
832 {
833 Rhead.fileProgram = C.prgmName;
834 if(!C.messHDp.empty()) Rhead.fileProgram = C.messHDp;
835 if(!C.messHDr.empty()) Rhead.fileAgency = C.messHDr;
836 if(!C.messHDo.empty()) Rhead.observer = C.messHDo;
837 if(!C.messHDa.empty()) Rhead.agency = C.messHDa;
838 if(!C.messHDj.empty()) Rhead.recNo = C.messHDj;
839 if(!C.messHDk.empty()) Rhead.recType = C.messHDk;
840 if(!C.messHDl.empty()) Rhead.recVers = C.messHDl;
841 if(!C.messHDs.empty()) Rhead.antNo = C.messHDs;
842 if(!C.messHDx.empty())
843 {
844 flds = split(C.messHDx,',');
845 // TD check n==3,doubles in initialize
846 for(i=0; i<3; i++) Rhead.antennaPosition[i] = asDouble(flds[i]);
847 }
848 if(!C.messHDm.empty()) Rhead.markerName = C.messHDm;
849 if(!C.messHDn.empty())
850 {
851 Rhead.markerNumber = C.messHDn;
852 Rhead.valid |= Rinex3ObsHeader::validMarkerNumber;
853 }
854 if(!C.messHDt.empty())
855 Rhead.antType = C.messHDt;
856 if(!C.messHDh.empty())
857 {
858 flds = split(C.messHDh,','); // TD check n==3,doubles in initialize
859 for(i=0; i<3; i++) Rhead.antennaDeltaHEN[i] = asDouble(flds[i]);
860 }
861 if(C.messHDdc)
862 {
863 Rhead.commentList.clear();
864 Rhead.valid ^= Rinex3ObsHeader::validComment;
865 }
866 if(C.messHDc.size() > 0)
867 {
868 for(i=0; i<C.messHDc.size(); i++)
869 Rhead.commentList.push_back(C.messHDc[i]);
870 Rhead.valid |= Rinex3ObsHeader::validComment;
871 }
872 if (C.decimate > 0.0)
873 {
874 Rhead.interval = C.decimate;
875 Rhead.valid |= Rinex3ObsHeader::validInterval;
876 }
877 }
878
879 Rhead.firstObs = Rdata.time;
880 Rhead.valid.clear(Rinex3ObsHeader::validLastTime); // turn off
881
882 // write the header
883 C.ostrm << Rhead;
884 return 0;
885 }
886
887 // DA delete all ---------------------------------------------------------------
888 else if(it->type == EditCmd::daCT)
889 {
890 switch(it->sign)
891 {
892 case 1: case 0:
893 Rdata.numSVs = 0; // clear this data, keep the cmd
894 Rdata.obs.clear();
895 if(it->sign == 0) return 0;
896 break;
897 case -1:
898 return 0; // delete the (-) command
899 break;
900 }
901 }
902
903 // DO delete obs type ----------------------------------------------------------
904 // This is handled above where output header is created w/o the deleted
905 // obs type in it. The data isn't actually deleted from the obs data objects
906 // but just doesn't get written out
907 else if (it->type == EditCmd::doCT)
908 return 0;
909
910 // DS delete satellite ---------------------------------------------------------
911 else if(it->type == EditCmd::dsCT)
912 {
913 vector<RinexSatID> sats;
914 if (it->sign == -1)
915 return 0; // delete the (-) command
916
917 LOG(DEBUG) << " Delete sat " << it->asString();
918 if (it->sat.id > 0)
919 {
920 // Find a specific satellite
921 kt = Rdata.obs.find(it->sat);
922 if (kt != Rdata.obs.end()) // found the SV
923 sats.push_back(kt->first);
924 else
925 LOG(DEBUG) << " Execute: sat " << it->sat << " not found in data";
926 }
927 else
928 {
929 // Delete all with the specified system
930 for (kt=Rdata.obs.begin(); kt!=Rdata.obs.end(); ++kt)
931 if (kt->first.system == it->sat.system)
932 sats.push_back(kt->first);
933 }
934
935 LOG(DEBUG) << " sats.size() " << sats.size() << " Rdata.obs.size() " << Rdata.obs.size();
936
937 for (j=0; j<sats.size(); j++)
938 Rdata.obs.erase(sats[j]);
939
940 Rdata.numSVs = Rdata.obs.size();
941
942 if (it->sign == 0)
943 return 0; // delete the one-time command
944 }
945
946 // -----------------------------------------------------------------------------
947 // the rest require that we find satellite and obsid in Rdata.obs
948 else
949 {
950 vector<RinexSatID> sats;
951
952 if(it->sign == -1) return 0; // delete the (-) command
953
954 sys = asString(it->sat.systemChar()); // find the system
955
956 // find the OT in the header map, and get index into vector
957 jt = find(Rhead.mapObsTypes[sys].begin(),
958 Rhead.mapObsTypes[sys].end(), it->obs);
959 if (jt == Rhead.mapObsTypes[sys].end()) { // ObsID not found
960 // TD message? user error: ask to delete one that's not there
961 LOG(DEBUG) << " Execute: obstype " << it->obs << " not found in header";
962 return 0; // delete the cmd
963 }
964
965 i = (jt - Rhead.mapObsTypes[sys].begin()); // index into vector
966
967 // find the sat
968 if(it->sat.id > 0)
969 {
970 if(Rdata.obs.find(it->sat)==Rdata.obs.end())
971 { // sat not found
972 LOG(DEBUG) << " Execute: sat " << it->sat << " not found in data";
973 }
974 else
975 sats.push_back(it->sat);
976 }
977 else
978 {
979 for(kt=Rdata.obs.begin(); kt!=Rdata.obs.end(); ++kt)
980 {
981 if(kt->first.system == it->sat.system)
982 sats.push_back(kt->first);
983 }
984 }
985
986 for(j=0; j<sats.size(); j++)
987 {
988 switch(it->type)
989 {
990 // DD delete data -----------------------------------------------------
991 case EditCmd::ddCT:
992 Rdata.obs[sats[j]][i].data = 0.0;
993 Rdata.obs[sats[j]][i].ssi = 0;
994 Rdata.obs[sats[j]][i].lli = 0;
995 break;
996 // SD set data --------------------------------------------------------
997 case EditCmd::sdCT:
998 Rdata.obs[sats[j]][i].data = it->data;
999 break;
1000 // SS set SSI ---------------------------------------------------------
1001 case EditCmd::ssCT:
1002 Rdata.obs[sats[j]][i].ssi = it->idata;
1003 break;
1004 // SL set LLI ---------------------------------------------------------
1005 case EditCmd::slCT:
1006 Rdata.obs[sats[j]][i].lli = it->idata;
1007 break;
1008 // BD bias data -------------------------------------------------------
1009 case EditCmd::bdCT: // do not bias
1010 if(Rdata.obs[sats[j]][i].data != 0.0 || C.messBZ)
1011 Rdata.obs[sats[j]][i].data += it->data;
1012 break;
1013 // BS bias SSI --------------------------------------------------------
1014 case EditCmd::bsCT:
1015 Rdata.obs[sats[j]][i].ssi += it->idata;
1016 break;
1017 // BL bias LLI --------------------------------------------------------
1018 case EditCmd::blCT:
1019 Rdata.obs[sats[j]][i].lli += it->idata;
1020 break;
1021 // never reached ------------------------------------------------------
1022 default:
1023 // message?
1024 break;
1025 }
1026 }
1027
1028 if(it->sign == 0)
1029 return 0; // delete the one-time command
1030 }
1031
1032 return 1;
1033 }
1034 catch(Exception& e) { GPSTK_RETHROW(e); }
1035 } // end executeEditCmd()
1036
1037
1038 //------------------------------------------------------------------------------
processUserInput(int argc,char ** argv)1039 int Configuration::processUserInput(int argc, char **argv) throw()
1040 {
1041 string PrgmDesc,cmdlineUsage, cmdlineErrors, cmdlineExtras;
1042 vector<string> cmdlineUnrecognized;
1043
1044 // build the command line
1045 opts.DefineUsageString(prgmName + " [options]");
1046 PrgmDesc = buildCommandLine();
1047
1048 // let CommandLine parse options; write all errors, etc to the passed strings
1049 int iret = opts.ProcessCommandLine(argc, argv, PrgmDesc,
1050 cmdlineUsage, cmdlineErrors, cmdlineUnrecognized);
1051
1052 // handle return values
1053 if(iret == -2) return iret; // bad alloc
1054 if(iret == -3) return iret; // invalid command line
1055
1056 // help: print syntax page and quit
1057 if(opts.hasHelp()) {
1058 LOG(INFO) << cmdlineUsage;
1059 return 1;
1060 }
1061
1062 // extra parsing (perhaps add to cmdlineErrors, cmdlineExtras)
1063 iret = extraProcessing(cmdlineErrors, cmdlineExtras);
1064 if(iret == -4) return iret; // log file could not be opened
1065
1066 // output warning / error messages
1067 if(cmdlineUnrecognized.size() > 0) {
1068 LOG(WARNING) << "Warning - unrecognized arguments:";
1069 for(size_t i=0; i<cmdlineUnrecognized.size(); i++)
1070 LOG(WARNING) << " " << cmdlineUnrecognized[i];
1071 LOG(WARNING) << "End of unrecognized arguments";
1072 }
1073
1074 if(!cmdlineExtras.empty()) {
1075 stripTrailing(cmdlineExtras,'\n');
1076 LOG(INFO) << cmdlineExtras;
1077 }
1078
1079 // fatal errors
1080 if(!cmdlineErrors.empty()) {
1081 stripTrailing(cmdlineErrors,'\n');
1082 replaceAll(cmdlineErrors,"\n","\n ");
1083 LOG(ERROR) << "Errors found on command line:\n " << cmdlineErrors
1084 << "\nEnd of command line errors.";
1085 return 1;
1086 }
1087
1088 // success: dump configuration summary
1089 if(debug > -1) {
1090 ostringstream oss;
1091 oss << "------ Summary of " << prgmName
1092 << " command line configuration ------\n";
1093 opts.DumpConfiguration(oss);
1094 if(!cmdlineExtras.empty()) oss << "# Extra Processing:\n" << cmdlineExtras;
1095 oss << "\n------ End configuration summary ------";
1096 LOG(DEBUG) << oss.str();
1097 }
1098
1099 return 0;
1100
1101 } // end Configuration::CommandLine()
1102
1103 //------------------------------------------------------------------------------
buildCommandLine(void)1104 string Configuration::buildCommandLine(void) throw()
1105 {
1106 // Program description will appear at the top of the syntax page
1107 string PrgmDesc = " Program " + prgmName +
1108 " will open and read RINEX observation files(s), apply editing\n"
1109 " commands, and write out the modified RINEX data to RINEX file(s).\n"
1110 " Input is on the command line, or of the same form in a file (--file).\n"
1111 " NB. Minimum required input is one input file (--IF) and one output file (--OF).\n"
1112 " Usage: " + prgmName + " [options] [editing commands]\n"
1113 " Options:";
1114
1115 // options to appear on the syntax page, and to be accepted on command line
1116 //opts.Add(char, opt, arg, repeat?, required?, &target, pre-desc, desc);
1117 // NB cfgfile is a dummy, but it must exist when cmdline is processed.
1118 opts.Add(0, "IF", "fn", true, false, &messIF,
1119 "# RINEX input and output files",
1120 "Input RINEX observation file name");
1121 opts.Add(0, "ID", "p", false, false, &InObspath, "",
1122 "Path of input RINEX observation file(s)");
1123 opts.Add(0, "OF", "fn", true, false, &messOF, "",
1124 "Output RINEX obs files [also see --OF <f,t> below]");
1125 opts.Add(0, "OD", "p", false, false, &OutObspath, "",
1126 "Path of output RINEX observation file(s)");
1127
1128 opts.Add('f', "file", "fn", true, false, &cfgfile, "# Other file I/O",
1129 "Name of file containing more options [#->EOL = comment]");
1130 opts.Add('l', "log", "fn", false, false, &logfile, "",
1131 "Output log file name");
1132 opts.Add(0, "ver2", "", false, false, &outver2, "",
1133 "Write out RINEX version 2");
1134
1135 opts.Add(0, "verbose", "", false, false, &verbose, "# Help",
1136 "Print extra output information");
1137 opts.Add(0, "debug", "", false, false, &debug, "",
1138 "Print debug output at level 0 [debug<n> for level n=1-7]");
1139 opts.Add(0, "help", "", false, false, &help, "",
1140 "Print this syntax page, and quit");
1141
1142 opts.Add(0, "HDp", "p", false, false, &messHDp, "# ------ Editing commands ------\n"
1143 "# RINEX header modifications (arguments with whitespace must be quoted)",
1144 "Set header 'PROGRAM' field to <p>");
1145 opts.Add(0, "HDr", "rb", false, false, &messHDr, "",
1146 "Set header 'RUN BY' field to <rb>");
1147 opts.Add(0, "HDo", "obs", false, false, &messHDo, "",
1148 "Set header 'OBSERVER' field to <obs>");
1149 opts.Add(0, "HDa", "a", false, false, &messHDa, "",
1150 "Set header 'AGENCY' field to <a>");
1151 opts.Add(0, "HDx", "x,y,z", false, false, &messHDx, "",
1152 "Set header 'POSITION' field to <x,y,z> (ECEF, m)");
1153 opts.Add(0, "HDm", "m", false, false, &messHDm, "",
1154 "Set header 'MARKER NAME' field to <m>");
1155 opts.Add(0, "HDn", "n", false, false, &messHDn, "",
1156 "Set header 'MARKER NUMBER' field to <n>");
1157 opts.Add(0, "HDj", "n", false, false, &messHDj, "",
1158 "Set header 'REC #' field to <n>");
1159 opts.Add(0, "HDk", "t", false, false, &messHDk, "",
1160 "Set header 'REC TYPE' field to <t>");
1161 opts.Add(0, "HDl", "v", false, false, &messHDl, "",
1162 "Set header 'REC VERS' field to <v>");
1163 opts.Add(0, "HDs", "n", false, false, &messHDs, "",
1164 "Set header 'ANT #' field to <n>");
1165 opts.Add(0, "HDt", "t", false, false, &messHDt, "",
1166 "Set header 'ANT TYPE' field to <t>");
1167 opts.Add(0, "HDh", "h,e,n", false, false, &messHDh, "",
1168 "Set header 'ANTENNA OFFSET' field to <h,e,n> (Ht,East,North)");
1169 opts.Add(0, "HDc", "c", true, false, &messHDc, "",
1170 "Add 'COMMENT' <c> to the output header");
1171 opts.Add(0, "HDdc", "", false, false, &messHDdc, "",
1172 "Delete all comments [not --HDc] from input header");
1173 opts.Add(0, "HDda", "", false, false, &messHDda, "",
1174 "Delete all auxiliary header data");
1175
1176 startStr = defaultstartStr;
1177 stopStr = defaultstopStr;
1178 opts.Add(0, "TB", "t[:f]", false, false, &startStr,
1179 "# Time related [t,f are strings, time t conforms to format f;"
1180 " cf. gpstk::Epoch.]\n# Default t(f) is 'week,sec-of-week'(%F,%g)"
1181 " OR 'y,m,d,h,m,s'(%Y,%m,%d,%H,%M,%S)\n"
1182 " --OF <f,t> At RINEX time <t>, close output file and open "
1183 "another named <f> ()",
1184 "Start time: Reject data before this time");
1185 opts.Add(0, "TE", "t[:f]", false, false, &stopStr, "",
1186 "Stop time: Reject data after this time");
1187 opts.Add(0, "TT", "dt", false, false, &timetol, "",
1188 "Tolerance in comparing times, in seconds");
1189 opts.Add(0, "TN", "dt", false, false, &decimate, "",
1190 "If dt>0, decimate data to times = TB + N*dt [sec, w/in tol]");
1191
1192 opts.Add(0, "DA", "t", true, false, &messDA,
1193 "# In the following <SV> is a RINEX satellite identifier, "
1194 "e.g. G17 R7 E22 R etc.\n"
1195 "# and <OT> is a 3- or 4-char RINEX observation code "
1196 "e.g. C1C GL2X S2N\n"
1197 "# Delete cmds; for start(stop) cmds. stop(start) time defaults "
1198 "to end(begin) of data\n"
1199 "# and 'deleting' data for a single OT means it is set to zero "
1200 "- as RINEX requires.",
1201 "Delete all data at a single time <t>");
1202 opts.Add(0, "DA+", "t", true, false, &messDAp, "",
1203 "Delete all data beginning at time <t>");
1204 opts.Add(0, "DA-", "t", true, false, &messDAm, "",
1205 "Stop deleting at time <t>");
1206
1207 opts.Add(0, "DO", "OT", true, false, &messDO, "",
1208 "Delete RINEX obs type <OT> entirely (incl. header)");
1209
1210 opts.Add(0, "DS", "SV,t", true, false, &messDS,
1211 " --DS <SV> Delete all data for satellite <SV> [SV may be char]",
1212 "Delete all data for satellite <SV> at single time <t>");
1213 opts.Add(0, "DS+", "SV,t", true, false, &messDSp, "",
1214 "Delete data for satellite <SV> beginning at time <t>");
1215 opts.Add(0, "DS-", "SV,t", true, false, &messDSm, "",
1216 "Stop deleting data for sat <SV> beginning at time <t>");
1217
1218 opts.Add(0, "DD", "SV,OT,t", true, false, &messDD, "",
1219 "Delete a single RINEX datum(SV,OT) at time <t>");
1220 opts.Add(0, "DD+", "SV,OT,t", true, false, &messDDp, "",
1221 "Delete all RINEX data(SV,OT) starting at time <t>");
1222 opts.Add(0, "DD-", "SV,OT,t", true, false, &messDDm, "",
1223 "Stop deleting RINEX data(SV,OT) at time <t>");
1224
1225 opts.Add(0, "SD", "SV,OT,t,d", true, false, &messSD, "",
1226 "Set data(SV,OT) to value <d> at single time <t>");
1227 opts.Add(0, "SS", "SV,OT,t,s", true, false, &messSS, "",
1228 "Set SSI(SV,OT) to value <s> at single time <t>");
1229 opts.Add(0, "SL", "SV,OT,t,l", true, false, &messSL, "",
1230 "Set LLI(SV,OT) to value <l> at single time <t>");
1231 opts.Add(0, "SL+", "SV,OT,t,l", true, false, &messSLp, "",
1232 "Set all LLI(SV,OT) to value <l> starting at time <t>");
1233 opts.Add(0, "SL-", "SV,OT,t,l", true, false, &messSLm, "",
1234 "Stop setting LLI(SV,OT) to value <l> at time <t>");
1235
1236 opts.Add(0, "BZ", "", false, false, &messBZ,
1237 "# Bias cmds: (BD cmds apply only when data is non-zero, unless --BZ)",
1238 "Apply BD command even when data is zero (i.e. 'missing')");
1239 opts.Add(0, "BS", "SV,OT,t,s", true, false, &messBS, "",
1240 "Add the value <s> to SSI(SV,OT) at single time <t>");
1241 opts.Add(0, "BL", "SV,OT,t,l", true, false, &messBL, "",
1242 "Add the value <l> to LLI(SV,OT) at single time <t>");
1243 opts.Add(0, "BD", "SV,OT,t,d", true, false, &messBD, "",
1244 "Add the value <d> to data(SV,OT) at single time <t>");
1245 opts.Add(0, "BD+", "SV,OT,t,d", true, false, &messBDp, "",
1246 "Add the value <d> to data(SV,OT) beginning at time <t>");
1247 opts.Add(0, "BD-", "SV,OT,t,d", true, false, &messBDm, "",
1248 "Stop adding the value <d> to data(SV,OT) at time <t>");
1249
1250 // turn off argument expansion for the editing commands
1251 opts.noExpansion("HDc");
1252 opts.noExpansion("OF");
1253 opts.noExpansion("DA");
1254 opts.noExpansion("DA-");
1255 opts.noExpansion("DA+");
1256 opts.noExpansion("DO");
1257 opts.noExpansion("DS");
1258 opts.noExpansion("DS+");
1259 opts.noExpansion("DS-");
1260 opts.noExpansion("DD");
1261 opts.noExpansion("DD+");
1262 opts.noExpansion("DD-");
1263 opts.noExpansion("SD");
1264 opts.noExpansion("SS");
1265 opts.noExpansion("SL");
1266 opts.noExpansion("SL+");
1267 opts.noExpansion("SL-");
1268 opts.noExpansion("BD");
1269 opts.noExpansion("BD+");
1270 opts.noExpansion("BD-");
1271 opts.noExpansion("BS");
1272 opts.noExpansion("BL");
1273
1274 // deprecated (old,new)
1275 //opts.Add_deprecated("--SP3","--eph");
1276
1277 return PrgmDesc;
1278
1279 } // end Configuration::buildCommandLine()
1280
1281 //------------------------------------------------------------------------------
extraProcessing(string & errors,string & extras)1282 int Configuration::extraProcessing(string& errors, string& extras) throw()
1283 {
1284 int n;
1285 size_t i;
1286 vector<string> fld;
1287 ostringstream oss,ossx; // oss for Errors, ossx for Warnings and info
1288
1289 // start and stop times
1290 for(i=0; i<2; i++) {
1291 static const string fmtGPS("%F,%g"),fmtCAL("%Y,%m,%d,%H,%M,%S");
1292 msg = (i==0 ? startStr : stopStr);
1293 if(msg == (i==0 ? defaultstartStr : defaultstopStr)) continue;
1294
1295 bool ok(true);
1296 bool hasfmt(msg.find('%') != string::npos);
1297 n = numWords(msg,',');
1298 if(hasfmt) {
1299 fld = split(msg,':');
1300 if(fld.size() != 2) { ok = false; }
1301 else try {
1302 Epoch ep;
1303 stripLeading(fld[0]," \t");
1304 stripLeading(fld[1]," \t");
1305 ep.scanf(fld[0],fld[1]);
1306 (i==0 ? beginTime : endTime) = static_cast<CommonTime>(ep);
1307 }
1308 catch(Exception& e) { ok = false; LOG(INFO) << "excep " << e.what(); }
1309 }
1310 else if(n == 2 || n == 6) { // try the defaults
1311 try {
1312 Epoch ep;
1313 ep.scanf(msg,(n==2 ? fmtGPS : fmtCAL));
1314 (i==0 ? beginTime : endTime) = static_cast<CommonTime>(ep);
1315 }
1316 catch(Exception& e) { ok = false; LOG(INFO) << "excep " << e.what(); }
1317 }
1318
1319 if(ok) {
1320 msg = printTime((i==0 ? beginTime : endTime),fmtGPS+" = "+fmtCAL);
1321 if(msg.find("Error") != string::npos) ok = false;
1322 }
1323
1324 if(!ok)
1325 oss << "Error : invalid time or format in --" << (i==0 ? "start" : "stop")
1326 << " " << (i==0 ? startStr : stopStr) << endl;
1327 else
1328 ossx << (i==0 ? " Begin time --begin" : " End time --end") << " is "
1329 << printTime((i==0 ? beginTime : endTime), fmtGPS+" = "+fmtCAL+"\n");
1330 }
1331
1332 // parse the editing commands
1333 parseEditCmds(messOF, "OF", oss);
1334 parseEditCmds(messDA, "DA", oss);
1335 parseEditCmds(messDAp, "DA+", oss);
1336 parseEditCmds(messDAm, "DA-", oss);
1337 parseEditCmds(messDO, "DO", oss);
1338 parseEditCmds(messDS, "DS", oss);
1339 parseEditCmds(messDSp, "DS+", oss);
1340 parseEditCmds(messDSm, "DS-", oss);
1341 parseEditCmds(messDD, "DD", oss);
1342 parseEditCmds(messDDp, "DD+", oss);
1343 parseEditCmds(messDDm, "DD-", oss);
1344 parseEditCmds(messSD, "SD", oss);
1345 parseEditCmds(messSS, "SS", oss);
1346 parseEditCmds(messSL, "SL", oss);
1347 parseEditCmds(messSLp, "SL+", oss);
1348 parseEditCmds(messSLm, "SL-", oss);
1349 parseEditCmds(messBD, "BD", oss);
1350 parseEditCmds(messBDp, "BD+", oss);
1351 parseEditCmds(messBDm, "BD-", oss);
1352 parseEditCmds(messBS, "BS", oss);
1353 parseEditCmds(messBL, "BL", oss);
1354
1355 // dump commands for debug
1356 //TEMP if(debug > -1)
1357 //for(i=0; i<vecCmds.size(); i++)
1358 //LOG(INFO) << vecCmds[i].asString(" Input Edit cmd:");
1359
1360 // 'fix up' list of edit cmds: sort, add -(+) for unmatched +(-), find + > -
1361 fixEditCmdList();
1362
1363 // dump final list of commands
1364 if(verbose)
1365 for(i=0; i<vecCmds.size(); i++)
1366 ossx << vecCmds[i].asString(" Edit cmd:") << endl;
1367
1368 // open the log file (so warnings, configuration summary, etc can go there) -----
1369 if(!logfile.empty()) {
1370 logstrm.open(logfile.c_str(), ios::out);
1371 if(!logstrm.is_open()) {
1372 LOG(ERROR) << "Error : Failed to open log file " << logfile;
1373 return -4;
1374 }
1375 LOG(INFO) << "Output redirected to log file " << logfile;
1376 pLOGstrm = &logstrm;
1377 }
1378 LOG(INFO) << Title;
1379
1380 // add new errors to the list
1381 msg = oss.str();
1382 if(!msg.empty()) errors += msg;
1383 msg = ossx.str();
1384 if(!msg.empty()) extras += msg;
1385
1386 return 0;
1387
1388 } // end Configuration::extraProcessing(string& errors) throw()
1389
1390 //------------------------------------------------------------------------------
1391 // little helper routine for extraProcessing
parseEditCmds(vector<string> & vec,const string lab,ostringstream & os)1392 void Configuration::parseEditCmds(vector<string>& vec, const string lab,
1393 ostringstream& os) throw()
1394 {
1395 for(size_t i=0; i<vec.size(); i++) {
1396 EditCmd ec(lab,vec[i]);
1397 if(ec.isValid()) vecCmds.push_back(ec);
1398 else os << "Error: invalid argument in " << lab << " cmd: >" << vec[i] << "<\n";
1399 }
1400 }
1401
1402 //------------------------------------------------------------------------------
1403 //------------------------------------------------------------------------------
1404 // constructor from strings, i.e. parser e.g. "DA+","t" or "BDp","SV,OT,t,s"
EditCmd(const string intypestr,const string inarg)1405 EditCmd::EditCmd(const string intypestr, const string inarg)
1406 {
1407 try {
1408 string tag(upperCase(intypestr)), arg(inarg);
1409 vector<string> flds;
1410
1411 type = invalidCT; // defaults
1412 ttag = CommonTime::BEGINNING_OF_TIME;
1413 sign = idata = 0;
1414 data = 0.0;
1415
1416 if(tag.size() == 2) sign = 0; // pull off sign
1417 else if(tag[2] == '+') sign = 1;
1418 else if(tag[2] == '-') sign = -1;
1419 else return;
1420 tag = tag.substr(0,2);
1421
1422 flds = split(arg,','); // split arg
1423 const size_t n(flds.size()); // number of args
1424
1425 if(tag == "OF") {
1426 if(n != 1 && n != 3 && n != 7) return;
1427 field = flds[0];
1428 if(n != 1) {
1429 stripLeading(arg,field+",");
1430 if(!parseTime(arg,ttag)) return;
1431 }
1432 type = ofCT;
1433 }
1434 else if(tag == "DA") {
1435 if(!parseTime(arg,ttag)) return;
1436 type = daCT;
1437 }
1438 else if(tag == "DO") {
1439 if(sign != 0) return; // no DO+ or DO-
1440
1441 if(arg.size() == 4) // get sys
1442 sat.fromString(string(1,arg[0]));
1443 // else sat sys is unknown
1444 if(isValidRinexObsID(arg))
1445 {
1446 /** @bug This is kind of ugly. If someone tries to
1447 * specify 3.02 codes, they'll be interpreted
1448 * incorrectly. */
1449 obs = RinexObsID(arg, Rinex3ObsBase::currentVersion);
1450 }
1451 else
1452 return;
1453 type = doCT;
1454 }
1455 else if(tag == "DS") {
1456 if(n != 1 && n != 3 && n != 7) return; // DS DS,w,sow and DS,y,m,d,h,m,s
1457 try { sat.fromString(flds[0]); } catch(Exception) { return; }
1458 if(n != 1) { // time for DS is BeginTime
1459 stripLeading(arg,flds[0]+",");
1460 if(!parseTime(arg,ttag)) return;
1461 }
1462 if(sign == 0 && n == 1) sign = 1;
1463 type = dsCT;
1464 }
1465 else {
1466 // args are SV,OT,t[,d or s or l]
1467 if(n < 4) return; // at minimum SV,OT,week,sow
1468
1469 stripLeading(arg,flds[0]+","+flds[1]+","); // remove 'SV,OT,' from arg
1470
1471 string dat;
1472 if(tag != "DD") { // strip and save last arg (dsl)
1473 dat = flds[flds.size()-1];
1474 stripTrailing(arg,string(",")+dat);
1475 }
1476 if(!parseTime(arg,ttag)) return; // get the time
1477
1478 // parse satellite
1479 try { sat.fromString(flds[0]); } catch(Exception) { return; }
1480
1481 // add system char to obs string
1482 if(flds[1].size() == 3 && sat.systemChar() != '?')
1483 flds[1] = string(1,sat.systemChar()) + flds[1];
1484 // parse obs type
1485 if(isValidRinexObsID(flds[1]))
1486 {
1487 /** @bug This is kind of ugly. If someone tries to
1488 * specify 3.02 codes, they'll be interpreted
1489 * incorrectly. */
1490 obs = RinexObsID(flds[1], Rinex3ObsBase::currentVersion);
1491 }
1492 else
1493 return;
1494
1495 if(tag == "DD") { type = ddCT; return; } // DD is done
1496
1497 if(n != 5 && n != 9) return; // rest have SV,OT,t,d = 5 or 9 args
1498
1499 if(tag == "SD" || tag == "BD") { // double data
1500 if(isScientificString(dat)) data = asDouble(dat); else return;
1501 }
1502 else { // rest have int data
1503 if(isDigitString(dat)) idata = asInt(dat); else return;
1504 }
1505
1506 // now just set type
1507 if(tag == "SD") type = sdCT;
1508 else if(tag == "SS") type = ssCT;
1509 else if(tag == "SL") type = slCT;
1510 else if(tag == "BS") type = bsCT;
1511 else if(tag == "BL") type = blCT;
1512 else if(tag == "BD") type = bdCT;
1513 }
1514 }
1515 catch(Exception& e) { GPSTK_RETHROW(e); }
1516 }
1517
1518 //------------------------------------------------------------------------------
parseTime(const string arg,CommonTime & ttag)1519 bool EditCmd::parseTime(const string arg, CommonTime& ttag) throw()
1520 {
1521 static const string fmtGPS("%F,%g"),fmtCAL("%Y,%m,%d,%H,%M,%S");
1522 stripLeading(arg," \t");
1523 int n(numWords(arg,','));
1524 if(n == 2 || n == 6) {
1525 Epoch ep;
1526 try { ep.scanf(arg, (n==2 ? fmtGPS : fmtCAL)); }
1527 catch(StringException) { return false; }
1528 ttag = static_cast<CommonTime>(ep);
1529 }
1530 else return false;
1531
1532 return true;
1533 }
1534
1535 //------------------------------------------------------------------------------
1536 // dump, with optional message
asString(string msg)1537 string EditCmd::asString(string msg)
1538 {
1539 try {
1540 Configuration& C(Configuration::Instance());
1541 static map<CmdType,string> typeLabel;
1542 if(typeLabel.size() == 0) {
1543 typeLabel[invalidCT] = string("--invalidCT-- ");
1544 typeLabel[ofCT] = string("OF_Output_File");
1545 typeLabel[daCT] = string("DA_Delete_All ");
1546 typeLabel[doCT] = string("DO_Delete_Obs ");
1547 typeLabel[dsCT] = string("DS_Delete_Sat ");
1548 typeLabel[ddCT] = string("DD_Delete_Data");
1549 typeLabel[sdCT] = string("SD_Set_Data ");
1550 typeLabel[ssCT] = string("SS_Set_SSI ");
1551 typeLabel[slCT] = string("SL_Set_LLI ");
1552 typeLabel[bdCT] = string("BD_Bias_Data ");
1553 typeLabel[bsCT] = string("BS_Bias_SSI ");
1554 typeLabel[blCT] = string("BL_Bias_LLI ");
1555 }
1556
1557 ostringstream os;
1558
1559 if(msg.size()) os << msg;
1560 os << " " << typeLabel[type]
1561 << " " << (sign==0 ? "0" : (sign<0 ? "-":"+"))
1562 << " SV:" << sat.toString()
1563 << " OT:" << obs.asString()
1564 << " d:" << fixed << setprecision(4) << data
1565 << " i:" << idata
1566 << " t:" << (ttag == CommonTime::BEGINNING_OF_TIME
1567 ? "BeginTime" : printTime(ttag,C.longfmt))
1568 << " >" << field << "<";
1569
1570 return os.str();
1571 }
1572 catch(Exception& e) { GPSTK_RETHROW(e); }
1573 catch(std::exception& e) {
1574 Exception E(string("std::except: ") + e.what());
1575 GPSTK_THROW(E);
1576 }
1577 }
1578
1579 //------------------------------------------------------------------------------
fixEditCmdList(void)1580 void fixEditCmdList(void) throw()
1581 {
1582 Configuration& C(Configuration::Instance());
1583 vector<EditCmd>::iterator it, jt;
1584 vector<EditCmd> newCmds;
1585
1586 // sort on time
1587 sort(C.vecCmds.begin(), C.vecCmds.end(), EditCmdLessThan());
1588
1589 // ensure each - command has a corresponding + command
1590 // (note that + cmds do not need a - cmd: they will just never be turned off)
1591 for(it = C.vecCmds.begin(); it != C.vecCmds.end(); ++it ) {
1592 if(it->sign == -1 && it->type != EditCmd::invalidCT) {
1593 bool havePair(false);
1594 if(it != C.vecCmds.begin()) {
1595 jt = it; --jt; // --(jt = it);
1596 while(1) { // search backwards for match
1597 if(jt->type == it->type &&
1598 jt->sat == it->sat &&
1599 jt->obs == it->obs)
1600 {
1601 if(jt->sign == 1) havePair=true; // its a match
1602 else if(jt->sign == -1) { // this is an error
1603 LOG(ERROR) << it->asString("Error: repeat '-'");
1604 //LOG(ERROR) << jt->asString("Error: ref here ");
1605 it->type = EditCmd::invalidCT;
1606 }
1607 break;
1608 }
1609 if(jt == C.vecCmds.begin()) break;
1610 --jt;
1611 }
1612 }
1613 if(!havePair && it->type != EditCmd::invalidCT) {
1614 EditCmd ec(*it);
1615 ec.sign = 1;
1616 ec.ttag = CommonTime::BEGINNING_OF_TIME;
1617 newCmds.push_back(ec);
1618 LOG(VERBOSE) << ec.asString(" Add cmd:");
1619 }
1620 }
1621 }
1622
1623 if(newCmds.size() > 0) {
1624 for(it = newCmds.begin(); it != newCmds.end(); ++it )
1625 C.vecCmds.push_back(*it);
1626 newCmds.clear();
1627 sort(C.vecCmds.begin(), C.vecCmds.end(), EditCmdLessThan());
1628 }
1629
1630 // remove invalidCT commands
1631 it = C.vecCmds.begin();
1632 while(it != C.vecCmds.end()) {
1633 if(it->type == EditCmd::invalidCT)
1634 it = C.vecCmds.erase(it); // erase vector element
1635 else
1636 ++it;
1637 }
1638 }
1639
1640 //------------------------------------------------------------------------------
1641 //------------------------------------------------------------------------------
1642 //------------------------------------------------------------------------------
1643