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 test_tides.cpp
40 /// Compute and dump solid earth tide, pole tide and ocean loading tide.
41
42 // system includes
43 #include <string>
44 #include <vector>
45 #include <iostream>
46 #include <iomanip>
47 #include <fstream>
48 // gpstk
49 #include "Exception.hpp"
50 #include "EphTime.hpp"
51 #include "Position.hpp"
52 // geomatics
53 #include "CommandLine.hpp"
54 #include "logstream.hpp"
55 #include "singleton.hpp"
56 #include "expandtilde.hpp"
57 #include "SolarPosition.hpp"
58 #include "SolarSystem.hpp"
59 #include "SunEarthSatGeometry.hpp"
60 #include "OceanLoadTides.hpp"
61 #include "AtmLoadTides.hpp"
62 #include "logstream.hpp"
63
64 using namespace std;
65 using namespace gpstk;
66 using namespace StringUtils;
67
68 //------------------------------------------------------------------------------------
69 static const string tidesVersion("4.0 12/3/19");
70
71 //------------------------------------------------------------------------------------
72 // data input from command line
73 class InputConfig : public Singleton<InputConfig> {
74 public:
75 // input data
76 bool doSimple,doOcean,doSolid,doPole,doAtm;
77 string logfile, SSEfile, earthfile, oceanfile, atmfile;
78 string fmtGPS,fmtCAL,fmt;
79 ofstream oflog;
80 EphTime beg, end;
81 int begmjd,endmjd;
82 bool help,verbose;
83 int prec, debug, iersyear; // debug output - prints all the data
84 double dt;
85 string refPosstr;
86 vector<string> oceannames,atmnames;
87 Position posset,posotl,posatm;
88 SolarSystem SolSys;
89 OceanLoadTides oceanStore;
90 AtmLoadTides atmStore;
91
92 // for CommandLine::ProcessCommandLine()
93 string cmdlineUsage, cmdlineErrors, cmdlineDump;
94 vector<string> cmdlineUnrecog;
95
96 // ctor with defaults
InputConfig(void)97 InputConfig(void) throw() {
98 fmtGPS = string("%F,%g");
99 fmtCAL = string("%Y,%m,%d,%02H,%M,%f");
100 fmt = string("%4F %10.3g %4Y %2m %2d %2H %2M %6.3f");
101 prec = 5;
102 help = verbose = false;
103 debug = -1;
104 begmjd = 55007.0;
105 endmjd = 55008.0;
106 dt = 900.0; // 15 minutes
107 refPosstr = string("-740289.9049,-5457071.7352,3207245.5544 #ARL.2012.0000");
108 iersyear = 2010;
109 // add options to make it look like three programs
110 doPole = doSolid = doOcean = doAtm = false;
111 // make default NOT to use SSE and earth
112 doSimple = true;
113 //SSEfile = string("/home/btolman/.grits/SolarSystem1960to2020.405.bin");
114 //iersyear = 2010; // or 2003
115 //SSEfile = string("/home/btolman/.grits/SolarSystem1960to2020.405.bin");
116 //earthfile = string("/home/btolman/.grits/finals2000A.data");
117 //oceanfile = string("/home/btolman/.grits/ocean.blq");
118 }
119
120 }; // end class InputConfig
121
122 //------------------------------------------------------------------------------------
123 // prototypes
124 /**
125 * @throw Exception
126 */
127 int GetCommandLine(int argc, char **argv);
128
129 //------------------------------------------------------------------------------------
main(int argc,char ** argv)130 int main(int argc, char **argv)
131 {
132 try {
133 InputConfig& C=InputConfig::Instance();
134 int i, iret;
135
136 // Title and description
137 string Title =
138 "tides, a program to compute solid Earth, ocean loading and pole tides, Ver "
139 + tidesVersion;
140 LOG(INFO) << Title;
141
142 // TEMP, for debugging CommandLine;
143 //LOGlevel = ConfigureLOG::Level("DEBUG");
144
145 iret = GetCommandLine(argc, argv);
146 LOG(DEBUG) << "GetCommandLine returned " << iret;
147 //LOG(INFO) << "LOG level is " << LOGlevel;
148
149 // return with help or errors - print to the screen - LOG is cout here
150 if(iret) {
151 if(iret == 1) { // help, else -1 == error
152 if(!C.cmdlineUsage.empty()) { LOG(INFO) << C.cmdlineUsage; }
153 if(!C.cmdlineDump.empty()) { LOG(INFO) << endl << C.cmdlineDump; }
154 }
155 else
156 LOG(ERROR) << C.cmdlineErrors;
157 return iret;
158 }
159
160 // open output file
161 if(!C.logfile.empty()) {
162 C.oflog.open(C.logfile.c_str(),ios_base::out);
163 if(!C.oflog.is_open()) {
164 LOG(ERROR) << "Failed to open log file " << C.logfile;
165 return -1;
166 }
167 else {
168 C.oflog.exceptions(ios::failbit);
169 LOG(INFO) << "Output directed to file " << C.logfile;
170 pLOGstrm = &C.oflog; // ConfigureLOG::Stream() = &C.oflog;
171 ConfigureLOG::ReportLevels() = false;
172 ConfigureLOG::ReportTimeTags() = false;
173 // debug and verbose handled earlier in GetCommandLine
174 LOG(INFO) << Title;
175 }
176 }
177
178 //// turn off screen if output is cout
179 //if(pLOGstrm == &cout) C.screen = false;
180
181 // dump configuration
182 LOG(VERBOSE) << endl << C.cmdlineDump;
183
184 // -------------------------------------------------------------------------
185 // initialize
186 bool isValid(true);
187
188 if(!C.SSEfile.empty()) {
189 C.doSimple = false;
190
191 // initialize solar system
192 if(C.iersyear == 1996) C.SolSys.setConvention(IERSConvention::IERS1996);
193 if(C.iersyear == 2003) C.SolSys.setConvention(IERSConvention::IERS2003);
194 if(C.iersyear == 2010) C.SolSys.setConvention(IERSConvention::IERS2010);
195
196 // read solar system ephemeris file
197 expand_filename(C.SSEfile);
198 C.SolSys.initializeWithBinaryFile(C.SSEfile);
199 LOG(INFO) << "Solar System Ephemeris is DE"
200 << C.SolSys.EphNumber() << "; timespan "
201 << C.SolSys.startTime() << " to " << C.SolSys.endTime()
202 << " with " << C.SolSys.getConvention();
203
204 // read EarthOP file and fill store
205 expand_filename(C.earthfile);
206 C.SolSys.addFile(C.earthfile);
207
208 // trim the EOP list, as we might be using e.g. finals2000A.data (huge);
209 // trim generously so ephemeris limits the data, not EOP
210 if(C.beg.dMJD()-10 > C.SolSys.getFirstTimeMJD() ||
211 C.end.dMJD()+10 < C.SolSys.getLastTimeMJD()) {
212 //LOG(VERBOSE) << "Trim EOP store based on input time limits.";
213 C.SolSys.edit(C.beg.lMJD()-10, C.end.lMJD()+10);
214 }
215
216 // dump the EOP summary
217 if(C.verbose) C.SolSys.dump(C.debug > -1 ? 1 : 0, LOGstrm);
218 }
219 else {
220 LOG(INFO) << "Solar System Ephemeris is simple Solar Position";
221 }
222
223 // doSimple and doPole inconsistent
224 if(C.doSimple && C.doPole) {
225 LOG(ERROR) << "Error - pole option requires SSEfile and earthfile; abort.";
226 isValid = false;
227 }
228
229 // fill ocean store
230 if(C.doOcean && (C.oceanfile.empty() || C.oceannames.size()==0)) {
231 // no ocean file and doOcean inconsistent
232 LOG(ERROR) << "Error - ocean option requires oceanfile and oceansite; abort.";
233 isValid = false;
234 }
235 else if(C.doOcean) {
236 vector<string> sites(C.oceannames);
237 // add the ocean file(s) and name(s) to the store
238 try {
239 C.oceanStore.initializeSites(sites,C.oceanfile);
240 }
241 catch(Exception& e) {
242 LOG(ERROR) << "Error - failed to open ocean loading file: "
243 << C.oceanfile << " :\n" << e.what();
244 isValid = false;
245 }
246 catch(exception& e) {
247 LOG(ERROR) << "Error - failed to open ocean loading file: "
248 << C.oceanfile << " :\n" << e.what();
249 isValid = false;
250 }
251
252 // get the site
253 for(i=0; i<C.oceannames.size(); i++) {
254 if(C.oceannames[i].empty())
255 isValid = false;
256 else try {
257 Triple pos = C.oceanStore.getPosition(C.oceannames[i]);
258 if(pos[0] == 0.0 && pos[1] == 0.0) {
259 LOG(ERROR) << "Error - Failed to find ocean site name "
260 << C.oceannames[i];
261 isValid = false;
262 }
263 else LOG(VERBOSE) << "Found ocean loading site " << C.oceannames[i]
264 << " at position " << pos[0] << "N, " << pos[1] << "E";
265
266 C.posotl.setGeodetic(pos[0],pos[1],0.0);
267
268 }
269 catch(Exception& e) {
270 LOG(ERROR) << "Error - failed to get ocean loading site: "
271 << C.oceannames[i] << " from ocean loading files" << " :\n"<< e.what();
272 isValid = false;
273 }
274 catch(exception& e) {
275 LOG(ERROR) << "Error - failed to get ocean loading site: "
276 << C.oceannames[i] << " from ocean loading files\n" << e.what();
277 isValid = false;
278 }
279 }
280 }
281
282 // fill atmospheric loading store
283 if(C.doAtm && (C.atmfile.empty() || C.atmnames.size()==0)) {
284 // no atm file and doAtm inconsistent
285 LOG(ERROR) << "Error - atm option requires atmfile and atmsite; abort.";
286 isValid = false;
287 }
288 else if(C.doAtm) {
289 vector<string> sites(C.atmnames);
290 // add the atm file(s) and name(s) to the store
291 try {
292 C.atmStore.initializeSites(sites,C.atmfile);
293 }
294 catch(Exception& e) {
295 LOG(ERROR) << "Error - failed to open atm loading file: "
296 << C.atmfile << " :\n" << e.what();
297 isValid = false;
298 }
299 catch(exception& e) {
300 LOG(ERROR) << "Error - failed to open atm loading file: "
301 << C.atmfile << " :\n" << e.what();
302 isValid = false;
303 }
304
305 // get the site
306 for(i=0; i<C.atmnames.size(); i++) {
307 if(C.atmnames[i].empty())
308 isValid = false;
309 else try {
310 Triple pos = C.atmStore.getPosition(C.atmnames[i]);
311 if(pos[0] == 0.0 && pos[1] == 0.0) {
312 LOG(ERROR) << "Error - Failed to find atm site name "
313 << C.atmnames[i];
314 isValid = false;
315 }
316 else LOG(VERBOSE) << "Found atm loading site " << C.atmnames[i]
317 << " at position " << pos[0] << "N, " << pos[1] << "E";
318
319 C.posatm.setGeodetic(pos[0],pos[1],0.0);
320
321 }
322 catch(Exception& e) {
323 LOG(ERROR) << "Error - failed to get atm loading site: "
324 << C.atmnames[i] << " from atm loading files" << " :\n"<< e.what();
325 isValid = false;
326 }
327 catch(exception& e) {
328 LOG(ERROR) << "Error - failed to get atm loading site: "
329 << C.atmnames[i] << " from atm loading files\n" << e.what();
330 isValid = false;
331 }
332 }
333 }
334
335 // get rotation matrix XYZ->NEU for pos
336 Matrix<double> Rotate,RotOTL,RotATM;
337 Rotate = NorthEastUp(C.posset);
338 RotOTL = NorthEastUp(C.posotl);
339 RotATM = NorthEastUp(C.posatm);
340
341 // -------------------------------------------------------------------------
342 if(!isValid) {
343 if(C.oflog.is_open()) C.oflog.close();
344 return iret;
345 }
346
347 // do it
348 if(C.doSolid) {
349 LOG(INFO) << "SET MJD HH:MM:SS.sss "
350 << "SET_X_cm SET_Y_cm SET_Z_cm SET_N_cm SET_E_cm SET_U_cm";
351 }
352 if(C.doOcean) {
353 LOG(INFO) << "OLT MJD HH:MM:SS.sss "
354 << "OLT_X_cm OLT_Y_cm OLT_Z_cm OLT_N_cm OLT_E_cm OLT_U_cm site";
355 }
356 if(C.doPole) {
357 LOG(INFO) << "POT MJD HH:MM:SS.sss "
358 << "POT_X_cm POT_Y_cm POT_Z_cm POT_N_cm POT_E_cm POT_U_cm";
359 }
360 if(C.doAtm) {
361 LOG(INFO) << "ATL MJD HH:MM:SS.sss "
362 << "ATL_X_cm ATL_Y_cm ATL_Z_cm ATL_N_cm ATL_E_cm ATL_U_cm site";
363 }
364
365 // loop over times
366 int w(C.prec+3);
367 double arad;
368 double mjd;
369 EphTime ttag;
370 Vector<double> XYZ(3),NEU(3);
371 Triple dXYZ,dNEU;
372 for(mjd=static_cast<double>(C.begmjd);
373 mjd<static_cast<double>(C.endmjd); mjd += C.dt/SEC_PER_DAY)
374 {
375 ttag.setMJD(mjd);
376 ttag.setTimeSystem(TimeSystem::UTC);
377 if(C.doSolid) {
378 if(C.doSimple) {
379 Position Sun(SolarPosition(ttag, arad));
380 Position Moon(LunarPosition(ttag, arad));
381 dXYZ = computeSolidEarthTides(C.posset, ttag, Sun, Moon);
382 }
383 else {
384 dXYZ = C.SolSys.computeSolidEarthTides(C.posset, ttag);
385 }
386 for(i=0; i<3; i++) XYZ(i) = dXYZ[i];
387 NEU = Rotate * XYZ;
388
389 LOG(INFO) << "SET " << ttag.asMJDString()
390 << fixed << setprecision(C.prec)
391 << " " << setw(w) << XYZ(0)*100.
392 << " " << setw(w) << XYZ(1)*100.
393 << " " << setw(w) << XYZ(2)*100.
394 << " " << setw(w) << NEU(0)*100.
395 << " " << setw(w) << NEU(1)*100.
396 << " " << setw(w) << NEU(2)*100.;
397 }
398
399 if(C.doOcean) {
400 for(size_t j=0; j<C.oceannames.size(); j++) {
401 dNEU = C.oceanStore.computeDisplacement(C.oceannames[j], ttag);
402 for(i=0; i<3; i++) NEU(i) = dNEU[i];
403 XYZ = transpose(RotOTL) * NEU;
404
405 LOG(INFO) << "OLT " << ttag.asMJDString()
406 << fixed << setprecision(C.prec)
407 << " " << setw(w) << XYZ(0)*100.
408 << " " << setw(w) << XYZ(1)*100.
409 << " " << setw(w) << XYZ(2)*100.
410 << " " << setw(w) << NEU(0)*100.
411 << " " << setw(w) << NEU(1)*100.
412 << " " << setw(w) << NEU(2)*100.
413 << " " << C.oceannames[j];
414 }
415 }
416
417 if(C.doPole) {
418 dXYZ = C.SolSys.computePolarTides(C.posset, ttag);
419 for(i=0; i<3; i++) XYZ(i) = dXYZ[i];
420 NEU = Rotate * XYZ;
421
422 LOG(INFO) << "POT " << ttag.asMJDString()
423 << fixed << setprecision(C.prec)
424 << " " << setw(w) << XYZ(0)*100.
425 << " " << setw(w) << XYZ(1)*100.
426 << " " << setw(w) << XYZ(2)*100.
427 << " " << setw(w) << NEU(0)*100.
428 << " " << setw(w) << NEU(1)*100.
429 << " " << setw(w) << NEU(2)*100.;
430 }
431
432 if(C.doAtm) {
433 for(size_t j=0; j<C.atmnames.size(); j++) {
434 dNEU = C.atmStore.computeDisplacement(C.atmnames[j], ttag);
435 for(i=0; i<3; i++) NEU(i) = dNEU[i];
436 XYZ = transpose(RotATM) * NEU;
437
438 LOG(INFO) << "ATL " << ttag.asMJDString()
439 << fixed << setprecision(C.prec)
440 << " " << setw(w) << XYZ(0)*100.
441 << " " << setw(w) << XYZ(1)*100.
442 << " " << setw(w) << XYZ(2)*100.
443 << " " << setw(w) << NEU(0)*100.
444 << " " << setw(w) << NEU(1)*100.
445 << " " << setw(w) << NEU(2)*100.
446 << " " << C.atmnames[j];
447 }
448 }
449
450 } // end loop over times
451
452 if(C.oflog.is_open()) C.oflog.close();
453
454 return iret;
455 }
456 catch(gpstk::Exception& e) { cerr << "Exception: " << e; }
457 catch (...) { cerr << "Unknown exception. Abort." << endl; }
458 return 1;
459 } // end main()
460
461 //------------------------------------------------------------------------------------
GetCommandLine(int argc,char ** argv)462 int GetCommandLine(int argc, char **argv)
463 {
464 try {
465 InputConfig& C=InputConfig::Instance();
466 int i;
467
468 // ---------------------------------------------------------------
469 // create list of command line options, and fill it
470 CommandLine opts;
471
472 // build the command line == syntax page
473 opts.DefineUsageString("tides [options]");
474 string PrgmDesc =
475 "Prgm tides computes tides (solid earth, ocean loading, pole) for a given\n"
476 " time (UTC) and site, and dumps them to the screen.\n"
477 " NB One or more of options (solid ocean pole atm) must be provided.\n"
478 " NB ocean option requires oceanfile and oceansite.\n"
479 " NB atm option requires atmfile and atmsite.\n"
480 " NB pole requires SSEfile and earthfile.\n"
481 " NB SSEfile and earthfile are optional (unless pole); they are more accurate.\n"
482 " Input is on the command line, or of the same format in a file (see --file);\n"
483 " lines in that file which begin with '#' are ignored.\n"
484 " Options are shown below, with a description and default value, if any, in ().\n"
485 ;
486
487 // opts.Add(char, opt, arg, repeat?, required?, &target, pre-descript, descript.);
488 bool req(false); // obs is not req'd b/c filenames can appear alone (Unrecog)
489 // optional args
490 string dummy(""); // dummy for --file
491 opts.Add(0, "solid", "", false, req, &C.doSolid,
492 "# Computation: Require one or more of the following:",
493 "Output Solid Earth tide");
494 opts.Add(0, "ocean", "", false, req, &C.doOcean, "",
495 "Output Ocean loading [requires oceanfile and oceansite]");
496 opts.Add(0, "pole", "", false, req, &C.doPole, "",
497 "Output Polar tide [requires SSEfile and earthfile]");
498 opts.Add(0, "atm", "", false, req, &C.doAtm, "",
499 "Output Atmospheric loading [requires atmfile and atmsite]");
500 opts.Add('f', "file", "name", true, req, &dummy, "# File I/O:",
501 "Name of file containing more options [#-EOL = comment]");
502 opts.Add('o', "log", "fn", false, req, &C.logfile, "",
503 "Output the summary to a file named <fn>");
504 opts.Add(0, "start", "mjd", false, req, &C.begmjd, "",
505 "Start processing the input data at this MJD");
506 opts.Add(0, "stop", "mjd", false, req, &C.endmjd, "",
507 "Stop processing the input data at this MJD");
508 opts.Add(0, "dt", "sec", false, req, &C.dt, "",
509 "Timestep in seconds");
510 opts.Add(0, "refPos", "X,Y,Z", false, req, &C.refPosstr, "",
511 "Position for SET (ECEF XYZ)");
512 opts.Add(0, "IERS", "year", false, req, &C.iersyear, "",
513 "Year of IERS convention: 1996, 2003 or 2010");
514 opts.Add(0, "SSEfile", "fn", false, req, &C.SSEfile, "",
515 "Solar System ephemeris binary file name [else use simple ephem]");
516 opts.Add(0, "earthfile", "fn", false, req, &C.earthfile, "",
517 "Earth orientation parameter file name [if & only if --SSEfile]");
518 opts.Add(0, "oceanfile", "fn", true, req, &C.oceanfile, "",
519 "Ocean loading file name");
520 opts.Add(0, "oceansite", "name", true, req, &C.oceannames, "",
521 "Site name in ocean loading file");
522 opts.Add(0, "atmfile", "fn", true, req, &C.atmfile, "",
523 "Ocean loading file name");
524 opts.Add(0, "atmsite", "name", true, req, &C.atmnames, "",
525 "Site name in atmospheric loading file");
526 opts.Add(0, "timefmt", "f", false, req, &C.fmt, "# Output",
527 "Output format for time tag");
528 opts.Add('p', "prec", "n", false, req, &C.prec, "",
529 "Output precision for offsets");
530 opts.Add('d', "debug", "", false, req, &C.debug, "",
531 "Print debug output at level 0 [debug<n> for level n=1-7]");
532 opts.Add(0, "verbose", "", false, req, &C.verbose, "",
533 "print extended output information");
534 opts.Add('h', "help", "", false, req, &C.help, "",
535 "Print this syntax page and quit");
536
537 // options to ignore
538 //opts.Add_ignore_off("--Scan");
539 // deprecated args
540 //opts.Add_deprecated("--dummy","--thisisatest");
541 // undocumented args
542 //opts.Add(0, "progress", "", false, req, &C.progress, "", "", false);
543
544 // ---------------------------------------------------------------
545 // declare it and parse it; write all errors to string C.cmdlineErrors
546 int iret = opts.ProcessCommandLine(argc, argv, PrgmDesc,
547 C.cmdlineUsage, C.cmdlineErrors, C.cmdlineUnrecog);
548 if(iret == -2) return -1; // bad alloc
549 if(iret == -3) return -1; // invalid command line
550
551 // ---------------------------------------------------------------
552 // do extra parsing - append errors to C.cmdlineErrors
553 string msg;
554 vector<string> fields;
555 ostringstream oss;
556
557 // unrecognized arguments are an error
558 if(C.cmdlineUnrecog.size() > 0) {
559 oss << " Error - unrecognized arguments:\n";
560 for(i=0; i<C.cmdlineUnrecog.size(); i++)
561 oss << C.cmdlineUnrecog[i] << "\n";
562 oss << " End of unrecognized arguments\n";
563 }
564
565 // start and stop times
566 C.beg.setMJD(C.begmjd);
567 C.end.setMJD(C.endmjd);
568
569 // reference position
570 if(!C.refPosstr.empty()) {
571 fields = StringUtils::split(C.refPosstr,',');
572 if(fields.size() != 3)
573 oss << "Error - invalid field in --refPos input: " << C.refPosstr << endl;
574 else {
575 try { C.posset.setECEF(StringUtils::asDouble(fields[0]),
576 StringUtils::asDouble(fields[1]),
577 StringUtils::asDouble(fields[2]));
578 }
579 catch(Exception& e) {
580 oss << "Error - invalid position in --refPos input: "
581 << C.refPosstr << endl;
582 }
583 }
584 }
585
586 if(C.iersyear != 1996 && C.iersyear != 2003 && C.iersyear != 2010) {
587 oss << "Error - invalid field in --IERS input: " << C.iersyear
588 << " - use 1996, 2003 or 2010." << endl;
589 }
590
591 // append errors onto cmdlineErrors
592 C.cmdlineErrors += oss.str();
593 stripTrailing(C.cmdlineErrors,'\n');
594
595 // ---------------------------------------------------------------
596 // dump a summary of command line configuration
597 if(C.verbose) {
598 oss.str(""); // clear it
599 oss << "------ Summary of tides command line configuration --------" << endl;
600 opts.DumpConfiguration(oss);
601 oss << endl << " Begin time is " << C.beg.asMJDString() << endl;
602 oss << " End time is " << C.end.asMJDString() << endl;
603 oss << " Position is "
604 << C.posset.printf("ECEF %.4x %.4y %.4z meters") << endl;
605 oss << "------ End configuration summary --------" << endl;
606 C.cmdlineDump = oss.str();
607 stripTrailing(C.cmdlineDump,'\n');
608 }
609
610 // ---------------------------------------------------------------
611 // return
612 if(!C.cmdlineErrors.empty()) return -1; // errors
613 if(C.help) return 1; // help
614
615 return 0; // ok
616 }
617 catch(Exception& e) { GPSTK_RETHROW(e); }
618 catch(exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
619 catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
620 return -1;
621 }
622
623 //------------------------------------------------------------------------------------
624 //------------------------------------------------------------------------------------
625