General Purpose Geodetic Library
log2ant.cpp
Go to the documentation of this file.
1 /*
2  * This file is a part of log2ant utility. The utility is a part of
3  * nuSolve distribution and is designed to extract meteo parameters,
4  * cable calibrations, tsys data from stations log files and store them
5  * in ANTCAL or/and ANTAB format.
6  * Copyright (C) 2020 Sergei Bolotin.
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program. If not, see <http://www.gnu.org/licenses/>.
20  *
21  */
22 
23 
24 #include <argp.h>
25 //#include <getopt.h>
26 //#include <signal.h>
27 //#include <stdlib.h>
28 //#include <unistd.h>
29 
30 
31 #include <QtCore/QCoreApplication>
32 #include <QtCore/QDir>
33 #include <QtCore/QFileInfo>
34 #include <QtCore/QList>
35 #include <QtCore/QSettings>
36 #include <QtCore/QString>
37 #include <QtCore/QStringList>
38 
39 
40 #include <SgIdentities.h>
41 #include <SgLogger.h>
42 #include <SgStnLogCollector.h>
43 #include <SgVex.h>
44 
45 
46 
47 #include "log2ant.h"
48 
49 
50 // HOPS's whims:
51 char progname[80];
52 int msglev = 2;
53 
54 
55 const char *argp_program_bug_address = "Sergei Bolotin <sergei.bolotin@nasa.gov>";
56 
57 
58 struct l2aOptions
59 {
60  QString inputFileName;
61  QString outputFileName;
63  QString stationKey;
64  QString compressExt;
65  QString logFileName;
68  QMap<QString, AntcalOutputData>
72  int logLevel;
74 };
75 
76 
77 
78 
79 
80 static int parse_opt(int key, char *arg, struct argp_state *state)
81 {
82  int n;
83  bool isOk;
84  QString str("");
85  struct l2aOptions *options=(struct l2aOptions*)state->input;
86 
87  switch (key)
88  {
89  case '3':
90  options->dbbc3InputFileName = QString(arg);
91  break;
92  case 'a':
93  options->reportAllTsysData = true;
94  break;
95  case 'b':
96  str = QString(arg);
97  options->tBegin.fromString(SgMJD::F_FS_LOG, str);
98  if (options->tBegin == tZero)
99  {
100  // try another format:
102  if (options->tBegin == tZero)
104  ": parse_opt(): option \"-b\": cannot convert \"" + str + "\" to epoch");
105  };
106  break;
107  case 'c':
108  options->compressExt = QString(arg);
109  break;
110  case 'e':
111  str = QString(arg);
112  options->tEnd.fromString(SgMJD::F_FS_LOG, str);
113  if (options->tEnd == tZero)
114  {
115  // try another format:
116  options->tEnd.fromString(SgMJD::F_SOLVE_SPLFL_V3, str);
117  if (options->tEnd == tZero)
118  {
120  ": option \"-e\": cannot convert \"" + str + "\" to epoch");
121  options->tEnd = tInf;
122  };
123  };
124  break;
125  case 'l':
126  options->logFileName = QString(arg);
127  break;
128  case 'o':
129  options->outputFileName = QString(arg);
130  break;
131  case 's':
132  options->stationKey = QString(arg).leftJustified(8, ' ');
133  break;
134  case 't':
135  str = QString(arg);
136  if (options->knownWishedData.contains(str))
137  {
138  options->antcalOutputData |= options->knownWishedData.value(str);
140  ": option \"-t\": added type of data: \"" + str + "\"");
141  }
142  else
143  {
145  ": option \"-t\": unknown type of data: \"" + str + "\"");
146  argp_usage(state);
147  return 0;
148  };
149  break;
150  case 'u':
151  options->supressNonUsedSensors = true;
152  break;
153  case 'v':
154  str = QString(arg);
155  n = str.toInt(&isOk);
156  if (isOk)
157  {
158  if (-1 < n && n < 4)
159  {
160  options->logLevel = n;
161  if (2 < options->logLevel)
163  ": option \"-v\": the log level is set to " + QString("").setNum(options->logLevel));
164  }
165  else
167  ": option \"-v\": the level " + str + " is out of range [0..3]");
168  }
169  else
171  ": option \"-v\": cannot convert \"" + str + "\" to int");
172  break;
173  case 'V':
174  std::cout << qPrintable(log2antVersion.name(SgVersion::NF_Petrov)) << "\n";
175  exit(0);
176  break;
177 
178  //
179  //
180  case ARGP_KEY_ARG:
181  if (state->arg_num >= 2)
182  {
183  argp_usage(state);
184  };
185  options->inputFileName = QString(arg);
186  break;
187  case ARGP_KEY_END:
188  if (state->arg_num < 1)
189  argp_usage (state);
190  break;
191  default:
192  return ARGP_ERR_UNKNOWN;
193  break;
194  };
195  return 0;
196 };
197 
198 
199 
200 
201 
202 
203 
204 
205 /***===================================================================================================*/
211 int main(int argc, char** argv)
212 {
213  struct l2aOptions options;
214 
215  QString userCommand("");
216  QString logDirName("./");
217  QString str("");
218  SgIdentities identities;
219 
220  options.inputFileName = QString("");
221  options.stationKey = QString("");
222  options.dbbc3InputFileName = QString("");
223  options.outputFileName = QString("");
224  options.compressExt = QString("");
225  options.logFileName = QString("log2ant.log");
226  options.tBegin = tZero;
227  options.tEnd = tInf;
228  options.reportAllTsysData = false;
229  options.logLevel = 1;
230  options.supressNonUsedSensors = false;
231 
232  options.knownWishedData.insert("dat", AOD_DATA_ON);
233  options.knownWishedData.insert("cbl", AOD_CABLE_CAL);
234  options.knownWishedData.insert("met", AOD_METEO);
235  options.knownWishedData.insert("tsys",AOD_TSYS);
236  options.knownWishedData.insert("tpi", AOD_TPI);
237  options.knownWishedData.insert("phc", AOD_PCAL);
238  options.knownWishedData.insert("fmt", AOD_FMTGPS);
239  options.knownWishedData.insert("tpc", AOD_DBBC3TP);
240  options.knownWishedData.insert("sefd",AOD_SEFD);
241  options.antcalOutputData = 0;
242 
243 
244  for (int i=0; i<argc; i++)
245  userCommand += QString(argv[i]) + " ";
246  userCommand.chop(1);
247  //
248  // ARGP setup:
249  //
250  struct argp_option argp_options[] =
251  {
252  {0, 0, 0, 0, "General options:", 10},
253  {"station-name", 's', "STRING", 0,
254  "Set a name of a station to STRING"},
255 
256  {0, 0, 0, 0, "Input control:", 11},
257  {"DBBC3-dump", '3', "STRING", 0,
258  "Use a file STRING as a DBBC3 dump file"},
259 
260  {0, 0, 0, 0, "Output control:", 12},
261  {"compress", 'c', "STRING", 0,
262  "Use a compressor STRING (gzip or bzip2) to squeeze output ANTCAL file"},
263  {"output", 'o', "STRING", 0,
264  "Set a name of output ANTCAL file to STRING"},
265 
266  {0, 0, 0, 0, "Time interval options:", 13},
267  {"t-begin", 'b', "STRING", 0,
268  "Set an epoch of the first observation to STRING, data before STRING will be ignored"},
269  {"t-end", 'e', "STRING", 0,
270  "Set an epoch of the last observation to STRING, data after STRING will be ignored"},
271 
272  {0, 0, 0, 0, "Data filter:", 14},
273  {"all", 'a', 0, 0,
274  "Report all collected data (i.e., both data_valid on and off intervals)"},
275  {"data-type", 't', "STRING", 0,
276  "Extract only the specified type of data. STRING can be: "
277  "cbl (cable calibration), "
278  "dat (data=on/off), "
279  "fmt (fmt2gps), "
280  "met (meteorological parameters), "
281  "phc (phase calibration), "
282  "sefd (SEFD evaluation), "
283  "tpc (DBBC3 TPC), "
284  "tpi (TPI), "
285  "tsys (TSYS). "
286  "There can be more than one \"-t\" option, e.g.: -t dat -t tsys"},
287  {"strip-unused-sensors",'u', 0, 0,
288  "Do not output sensors that are not in a log file for some particular time"},
289 
290  {0, 0, 0, 0, "Debug output:", -2},
291  {"verbose", 'v', "NUM", 0,
292  "Set a level of log output to NUM: 0 (errors only), 1 (+warnings), 2 (+info) and "
293  "3 (+debug output). Default is 1"},
294  {"log-file", 'l', "STRING", 0,
295  "Store log2ant's output in a file STRING, the default is \"log2ant.log\""},
296 
297  {0, 0, 0, 0, "Operation modes:", -1},
298  {"version", 'V', 0, 0,
299  "Print program version"},
300  //
301  {0}
302  };
303  QString salute("A program that extracts various data from a Field System "
304  "log file, FS_LOG_FILE, and stores them in an ANTCAL file.\v");
305 
306  salute += "The current version is:\n\t" + log2antVersion.name() + " released on " +
308  "\n\t" + libraryVersion.name() + " released on " +
310  salute +=
311  QString("\n\nThe utility log2ant is a part of nuSolve package. See datails in "
312  "\"log2ant User Guide\", a part of nuSolve distribution. You can get the latest version of "
313  "nuSolve at\n\t\thttps://sourceforge.net/projects/nusolve");
314 
315  struct argp argp={argp_options, parse_opt, "FS_LOG_FILE", salute.toLatin1()};
316 
317  argp_parse (&argp, argc, argv, 0, 0, &options);
318 
319 
320  SgMJD startEpoch=SgMJD::currentMJD();
321  //
322  //
323  if (options.antcalOutputData == 0)
324  options.antcalOutputData = AOD_ALL;
325 
327  ": starting");
328  //
329  // check log file name:
330  if (options.logFileName.contains('/'))
331  {
332  QFileInfo fi(options.logFileName);
333  options.logFileName = fi.fileName();
334  logDirName = fi.absolutePath();
335  };
336  //
337  // setup logger:
338  logger->setFileName(options.logFileName);
339  logger->setDirName(logDirName);
340  logger->setIsStoreInFile(true);
341  logger->setIsNeedTimeMark(true);
342  logger->setLogFacility(SgLogger::ERR, options.logLevel>=0?0xFFFFFFFF:0);
343  logger->setLogFacility(SgLogger::WRN, options.logLevel>=1?0xFFFFFFFF:0);
344  logger->setLogFacility(SgLogger::INF, options.logLevel>=2?0xFFFFFFFF:0);
345  logger->setLogFacility(SgLogger::DBG, options.logLevel==3?0xFFFFFFFF:0);
346  // reset the file:
347  logger->rmLogFile();
348 
349  // put fingerprints:
351  ": Library ID: " + libraryVersion.name() + " released on " +
354  ": Driver ID: " + log2antVersion.name() + " released on " +
357  ": Host ID: " + identities.getMachineNodeName() +
358  " (Hw: " + identities.getMachineMachineName() +
359  "; Sw: " + identities.getMachineRelease() +
360  " version of " + identities.getMachineSysName() + ")");
362  ": User ID: " + identities.getUserName() +
363  " <" + identities.getUserEmailAddress() + ">");
365  ": User command: \"" + userCommand + "\"");
366 
367  if (tZero < options.tBegin)
369  ": expected start of a session is set to " + options.tBegin.toString(SgMJD::F_YYYYMMDDHHMMSSSS));
370  if (options.tEnd < tInf)
372  ": expected end of a session is set to " + options.tEnd.toString(SgMJD::F_YYYYMMDDHHMMSSSS));
373 
374 
375  SgStnLogCollector logCollector;
376  bool have2owrt=true;
377 
378  //
379  logCollector.setUserCommand(userCommand);
380  logCollector.setAntcalOutputData(options.antcalOutputData);
381  //
382  if (logCollector.readLogFile(options.inputFileName, options.stationKey, options.tBegin, options.tEnd, ""))
383  {
384  if (!options.stationKey.size())
385  options.stationKey = logCollector.getStationName();
386 
387  if (options.dbbc3InputFileName.size())
388  {
389  if (logCollector.readDbbc3DumpFile(options.dbbc3InputFileName))
391  ": the DBBC3 dump file \"" + options.dbbc3InputFileName + "\" have been read");
392  else
394  ": reading of the DBBC3 dump file \"" + options.dbbc3InputFileName + "\" failed for station");
395  };
396  //
397  // export data:
398  if (logCollector.createAntCalFile(options.stationKey, options.outputFileName, have2owrt, options.reportAllTsysData,
399  options.supressNonUsedSensors, options.compressExt))
401  ": extracted data were successfully stored in an ANTCAL file for station \"" +
402  options.stationKey + "\"");
403  else
405  ": data storing in an ANTCAL file has failed for station \"" + options.stationKey + "\"");
406  }
407  else
409  ": extracting data from the log file \"" + options.inputFileName + "\" failed");
410  //
411  SgMJD finisEpoch=SgMJD::currentMJD();
413  ": the elapsed time to process log file \"" + options.inputFileName + "\" is: " +
414  interval2Str(finisEpoch - startEpoch) + " (" +
415  QString("").sprintf("%.2f", (finisEpoch - startEpoch)*86400.0) + " sec)", true);
416  //
417  return 0;
418 };
419 
420 /*=====================================================================================================*/
421 
422 
423 
424 
425 
426 /*=====================================================================================================*/
SgLogger * logger
Definition: SgLogger.cpp:231
QString interval2Str(double days)
Definition: SgMJD.cpp:1370
const SgMJD tZero(1957, 10, 4)
const SgMJD tInf(2100, 1, 1)
@ AOD_CABLE_CAL
@ AOD_PCAL
@ AOD_DBBC3TP
@ AOD_FMTGPS
@ AOD_ALL
@ AOD_TPI
@ AOD_METEO
@ AOD_DATA_ON
@ AOD_SEFD
@ AOD_TSYS
SgVersion libraryVersion("SgLib", 0, 7, 5, "Tuscarora (rc1)", SgMJD(2022, 2, 18, 17, 34))
const QString & getUserEmailAddress() const
Definition: SgIdentities.h:223
const QString & getMachineMachineName() const
Definition: SgIdentities.h:303
const QString & getMachineRelease() const
Definition: SgIdentities.h:319
const QString & getMachineNodeName() const
Definition: SgIdentities.h:295
const QString & getUserName() const
Definition: SgIdentities.h:215
const QString & getMachineSysName() const
Definition: SgIdentities.h:311
virtual void write(LogLevel, quint32, const QString &, bool=false)
Definition: SgLogger.cpp:88
void setIsStoreInFile(bool isStoreInFile)
Definition: SgLogger.h:127
void setLogFacility(LogLevel lvl, quint32 f)
Definition: SgLogger.h:131
void setFileName(const QString &fileName)
Definition: SgLogger.h:125
void setIsNeedTimeMark(bool isNeedTimeMark)
Definition: SgLogger.h:129
void setDirName(const QString &dirName)
Definition: SgLogger.h:124
@ PREPROC
Definition: SgLogger.h:98
@ SESSION
Definition: SgLogger.h:77
void rmLogFile()
Definition: SgLogger.cpp:148
Definition: SgMJD.h:59
bool fromString(Format format, const QString &str, bool isReset=true)
Definition: SgMJD.cpp:267
@ F_FS_LOG
Another version from spoolfile format: 2012.01.20-09:32:00.960.
Definition: SgMJD.h:79
@ F_YYYYMMDDHHMMSSSS
Long verbose: Fri, the 2nd of Apr, 2010; 17hr 02min 43.6400sec.
Definition: SgMJD.h:67
@ F_DDMonYYYY
Date: 2010 Apr 02.
Definition: SgMJD.h:87
@ F_SOLVE_SPLFL_V3
Another spoolfile represenation of epoch: 2012.01.20-09:14:28.0.
Definition: SgMJD.h:76
QString toString(Format format=F_Verbose) const
Definition: SgMJD.cpp:1007
static SgMJD currentMJD()
Definition: SgMJD.cpp:118
void setAntcalOutputData(int outputData)
const QString & getStationName() const
bool readDbbc3DumpFile(const QString &fileName)
bool createAntCalFile(const QString &stnKey, const QString &outputFileName, bool have2owrt, bool reportAllReadings, bool supressNonUsedSensors, const QString &ext4compress)
bool readLogFile(const QString &fileName, const QString &stnName, const SgMJD &tFirst, const SgMJD &tLast, const QString &orderOfMeteo)
void setUserCommand(const QString &str)
@ NF_Petrov
Definition: SgVersion.h:59
const SgMJD & getReleaseEpoch() const
Definition: SgVersion.h:294
QString name(NameFormat fmt=NF_Human) const
Definition: SgVersion.cpp:54
SgVersion log2antVersion("log2ant", 0, 0, 3, "Reichs Ford", SgMJD(2022, 2, 27, 16, 28))
char progname[80]
Definition: log2ant.cpp:51
int main(int argc, char **argv)
Definition: log2ant.cpp:211
const char * argp_program_bug_address
Definition: log2ant.cpp:55
static int parse_opt(int key, char *arg, struct argp_state *state)
Definition: log2ant.cpp:80
int msglev
Definition: log2ant.cpp:52
QString inputFileName
Definition: log2ant.cpp:60
int reportAllTsysData
Definition: log2ant.cpp:71
bool supressNonUsedSensors
Definition: log2ant.cpp:73
int logLevel
Definition: log2ant.cpp:72
QString stationKey
Definition: log2ant.cpp:63
int antcalOutputData
Definition: log2ant.cpp:70
SgMJD tEnd
Definition: log2ant.cpp:67
QString outputFileName
Definition: log2ant.cpp:61
QString compressExt
Definition: log2ant.cpp:64
SgMJD tBegin
Definition: log2ant.cpp:66
QString dbbc3InputFileName
Definition: log2ant.cpp:62
QMap< QString, AntcalOutputData > knownWishedData
Definition: log2ant.cpp:69
QString logFileName
Definition: log2ant.cpp:65