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 #ifdef OLD_HOPS
52 char progname[80];
53 int msglev = 2;
54 #endif
55 
56 const char *argp_program_bug_address = "Sergei Bolotin <sergei.bolotin@nasa.gov>";
57 
58 
59 struct l2aOptions
60 {
61  QString inputFileName;
62  QString outputFileName;
64  QString stationKey;
65  QString compressExt;
66  QString logFileName;
69  QMap<QString, AntcalOutputData>
73  int logLevel;
75 };
76 
77 
78 
79 
80 
81 static int parse_opt(int key, char *arg, struct argp_state *state)
82 {
83  int n;
84  bool isOk;
85  QString str("");
86  struct l2aOptions *options=(struct l2aOptions*)state->input;
87 
88  switch (key)
89  {
90  case '3':
91  options->dbbc3InputFileName = QString(arg);
92  break;
93  case 'a':
94  options->reportAllTsysData = true;
95  break;
96  case 'b':
97  str = QString(arg);
98  options->tBegin.fromString(SgMJD::F_FS_LOG, str);
99  if (options->tBegin == tZero)
100  {
101  // try another format:
103  if (options->tBegin == tZero)
105  ": parse_opt(): option \"-b\": cannot convert \"" + str + "\" to epoch");
106  };
107  break;
108  case 'c':
109  options->compressExt = QString(arg);
110  break;
111  case 'e':
112  str = QString(arg);
113  options->tEnd.fromString(SgMJD::F_FS_LOG, str);
114  if (options->tEnd == tZero)
115  {
116  // try another format:
117  options->tEnd.fromString(SgMJD::F_SOLVE_SPLFL_V3, str);
118  if (options->tEnd == tZero)
119  {
121  ": option \"-e\": cannot convert \"" + str + "\" to epoch");
122  options->tEnd = tInf;
123  };
124  };
125  break;
126  case 'l':
127  options->logFileName = QString(arg);
128  break;
129  case 'o':
130  options->outputFileName = QString(arg);
131  break;
132  case 's':
133  options->stationKey = QString(arg).leftJustified(8, ' ');
134  break;
135  case 't':
136  str = QString(arg);
137  if (options->knownWishedData.contains(str))
138  {
139  options->antcalOutputData |= options->knownWishedData.value(str);
141  ": option \"-t\": added type of data: \"" + str + "\"");
142  }
143  else
144  {
146  ": option \"-t\": unknown type of data: \"" + str + "\"");
147  argp_usage(state);
148  return 0;
149  };
150  break;
151  case 'u':
152  options->supressNonUsedSensors = true;
153  break;
154  case 'v':
155  str = QString(arg);
156  n = str.toInt(&isOk);
157  if (isOk)
158  {
159  if (-1 < n && n < 4)
160  {
161  options->logLevel = n;
162  if (2 < options->logLevel)
164  ": option \"-v\": the log level is set to " + QString("").setNum(options->logLevel));
165  }
166  else
168  ": option \"-v\": the level " + str + " is out of range [0..3]");
169  }
170  else
172  ": option \"-v\": cannot convert \"" + str + "\" to int");
173  break;
174  case 'V':
175  std::cout << qPrintable(log2antVersion.name(SgVersion::NF_Petrov)) << "\n";
176  exit(0);
177  break;
178 
179  //
180  //
181  case ARGP_KEY_ARG:
182  if (state->arg_num >= 2)
183  {
184  argp_usage(state);
185  };
186  options->inputFileName = QString(arg);
187  break;
188  case ARGP_KEY_END:
189  if (state->arg_num < 1)
190  argp_usage (state);
191  break;
192  default:
193  return ARGP_ERR_UNKNOWN;
194  break;
195  };
196  return 0;
197 };
198 
199 
200 
201 
202 
203 
204 
205 
206 /***===================================================================================================*/
212 int main(int argc, char** argv)
213 {
214  struct l2aOptions options;
215  int rc;
216 
217  QString userCommand("");
218  QString logDirName("./");
219  QString str("");
220  SgIdentities identities;
221 
222  options.inputFileName = QString("");
223  options.stationKey = QString("");
224  options.dbbc3InputFileName = QString("");
225  options.outputFileName = QString("");
226  options.compressExt = QString("");
227  options.logFileName = QString("log2ant.log");
228  options.tBegin = tZero;
229  options.tEnd = tInf;
230  options.reportAllTsysData = false;
231  options.logLevel = 1;
232  options.supressNonUsedSensors = false;
233 
234  options.knownWishedData.insert("dat", AOD_DATA_ON);
235  options.knownWishedData.insert("cbl", AOD_CABLE_CAL);
236  options.knownWishedData.insert("met", AOD_METEO);
237  options.knownWishedData.insert("tsys",AOD_TSYS);
238  options.knownWishedData.insert("tpi", AOD_TPI);
239  options.knownWishedData.insert("phc", AOD_PCAL);
240  options.knownWishedData.insert("fmt", AOD_FMTGPS);
241  options.knownWishedData.insert("tpc", AOD_DBBC3TP);
242  options.knownWishedData.insert("sefd",AOD_SEFD);
243  options.antcalOutputData = 0;
244 
245  rc = 0;
246 
247 
248  for (int i=0; i<argc; i++)
249  userCommand += QString(argv[i]) + " ";
250  userCommand.chop(1);
251  //
252  // ARGP setup:
253  //
254  struct argp_option argp_options[] =
255  {
256  {0, 0, 0, 0, "General options:", 10},
257  {"station-name", 's', "STRING", 0,
258  "Set a name of a station to STRING"},
259 
260  {0, 0, 0, 0, "Input control:", 11},
261  {"DBBC3-dump", '3', "STRING", 0,
262  "Use a file STRING as a DBBC3 dump file"},
263 
264  {0, 0, 0, 0, "Output control:", 12},
265  {"compress", 'c', "STRING", 0,
266  "Use a compressor STRING (gzip or bzip2) to squeeze output ANTCAL file"},
267  {"output", 'o', "STRING", 0,
268  "Set a name of output ANTCAL file to STRING"},
269 
270  {0, 0, 0, 0, "Time interval options:", 13},
271  {"t-begin", 'b', "STRING", 0,
272  "Set an epoch of the first observation to STRING, data before STRING will be ignored"},
273  {"t-end", 'e', "STRING", 0,
274  "Set an epoch of the last observation to STRING, data after STRING will be ignored"},
275 
276  {0, 0, 0, 0, "Data filter:", 14},
277  {"all", 'a', 0, 0,
278  "Report all collected data (i.e., both data_valid on and off intervals)"},
279  {"data-type", 't', "STRING", 0,
280  "Extract only the specified type of data. STRING can be: "
281  "cbl (cable calibration), "
282  "dat (data=on/off), "
283  "fmt (fmt2gps), "
284  "met (meteorological parameters), "
285  "phc (phase calibration), "
286  "sefd (SEFD evaluation), "
287  "tpc (DBBC3 TPC), "
288  "tpi (TPI), "
289  "tsys (TSYS). "
290  "There can be more than one \"-t\" option, e.g.: -t dat -t tsys"},
291  {"strip-unused-sensors",'u', 0, 0,
292  "Do not output sensors that are not in a log file for some particular time"},
293 
294  {0, 0, 0, 0, "Debug output:", -2},
295  {"verbose", 'v', "NUM", 0,
296  "Set a level of log output to NUM: 0 (errors only), 1 (+warnings), 2 (+info) and "
297  "3 (+debug output). Default is 1"},
298  {"log-file", 'l', "STRING", 0,
299  "Store log2ant's output in a file STRING, the default is \"log2ant.log\""},
300 
301  {0, 0, 0, 0, "Operation modes:", -1},
302  {"version", 'V', 0, 0,
303  "Print program version"},
304  //
305  {0}
306  };
307  QString salute("A program that extracts various data from a Field System "
308  "log file, FS_LOG_FILE, and stores them in an ANTCAL file.\v");
309 
310  salute += "The current version is:\n\t" + log2antVersion.name() + " released on " +
312  "\n\t" + libraryVersion.name() + " released on " +
314  salute +=
315  QString("\n\nThe utility log2ant is a part of nuSolve package. See datails in "
316  "\"log2ant User Guide\", a part of nuSolve distribution. You can get the latest version of "
317  "nuSolve at\n\t\thttps://sourceforge.net/projects/nusolve");
318 
319  struct argp argp={argp_options, parse_opt, "FS_LOG_FILE", salute.toLatin1()};
320 
321  argp_parse (&argp, argc, argv, 0, 0, &options);
322 
323 
324  SgMJD startEpoch=SgMJD::currentMJD();
325  //
326  //
327  if (options.antcalOutputData == 0)
328  options.antcalOutputData = AOD_ALL;
329 
331  ": starting");
332  //
333  // check log file name:
334  if (options.logFileName.contains('/'))
335  {
336  QFileInfo fi(options.logFileName);
337  options.logFileName = fi.fileName();
338  logDirName = fi.absolutePath();
339  };
340  //
341  // setup logger:
342  logger->setFileName(options.logFileName);
343  logger->setDirName(logDirName);
344  logger->setIsStoreInFile(true);
345  logger->setIsNeedTimeMark(true);
346  logger->setLogFacility(SgLogger::ERR, options.logLevel>=0?0xFFFFFFFF:0);
347  logger->setLogFacility(SgLogger::WRN, options.logLevel>=1?0xFFFFFFFF:0);
348  logger->setLogFacility(SgLogger::INF, options.logLevel>=2?0xFFFFFFFF:0);
349  logger->setLogFacility(SgLogger::DBG, options.logLevel==3?0xFFFFFFFF:0);
350  // reset the file:
351  logger->rmLogFile();
352 
353  // put fingerprints:
355  ": Library ID: " + libraryVersion.name() + " released on " +
358  ": Driver ID: " + log2antVersion.name() + " released on " +
361  ": Host ID: " + identities.getMachineNodeName() +
362  " (Hw: " + identities.getMachineMachineName() +
363  "; Sw: " + identities.getMachineRelease() +
364  " version of " + identities.getMachineSysName() + ")");
366  ": User ID: " + identities.getUserName() +
367  " <" + identities.getUserEmailAddress() + ">");
369  ": User command: \"" + userCommand + "\"");
370 
371  if (tZero < options.tBegin)
373  ": expected start of a session is set to " + options.tBegin.toString(SgMJD::F_YYYYMMDDHHMMSSSS));
374  if (options.tEnd < tInf)
376  ": expected end of a session is set to " + options.tEnd.toString(SgMJD::F_YYYYMMDDHHMMSSSS));
377 
378 
379  SgStnLogCollector logCollector;
380  bool have2owrt=true;
381 
382  //
383  logCollector.setUserCommand(userCommand);
384  logCollector.setAntcalOutputData(options.antcalOutputData);
385  //
386  if (logCollector.readLogFile(options.inputFileName,
387  options.stationKey, options.tBegin, options.tEnd, ""))
388  {
389  if (!options.stationKey.size())
390  options.stationKey = logCollector.getStationName();
391 
392  if (options.dbbc3InputFileName.size())
393  {
394  if (logCollector.readDbbc3DumpFile(options.dbbc3InputFileName))
396  ": the DBBC3 dump file \"" + options.dbbc3InputFileName + "\" have been read");
397  else
399  ": reading of the DBBC3 dump file \"" + options.dbbc3InputFileName + "\" failed for station");
400  };
401  //
402  // export data:
403  if (logCollector.createAntCalFile(options.stationKey, options.outputFileName, have2owrt,
404  options.reportAllTsysData, options.supressNonUsedSensors, options.compressExt))
406  ": extracted data were successfully stored in an ANTCAL file for station \"" +
407  options.stationKey + "\"");
408  else
409  {
410  rc = 1;
412  ": data storing in an ANTCAL file has failed for station \"" + options.stationKey + "\"");
413  };
414  }
415  else
416  {
417  rc = 1;
419  ": extracting data from the log file \"" + options.inputFileName + "\" failed");
420  };
421  //
422  if (rc == 0)
423  {
424  SgMJD finisEpoch=SgMJD::currentMJD();
426  ": the elapsed time to process log file \"" + options.inputFileName + "\" is: " +
427  interval2Str(finisEpoch - startEpoch) + " (" +
428  QString("").sprintf("%.2f", (finisEpoch - startEpoch)*86400.0) + " sec)", true);
429  };
430  //
431  return rc;
432 };
433 
434 /*=====================================================================================================*/
435 
436 
437 
438 
439 
440 /*=====================================================================================================*/
SgLogger * logger
Definition: SgLogger.cpp:231
QString interval2Str(double days)
Definition: SgMJD.cpp:1371
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, 8, 2, "Compton Peak (rc2)", SgMJD(2023, 4, 3, 10, 59))
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:268
@ 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:1008
static SgMJD currentMJD()
Definition: SgMJD.cpp:119
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, 1, 2, "Lands Run Falls (rc2)", SgMJD(2023, 4, 3, 11, 0))
int main(int argc, char **argv)
Definition: log2ant.cpp:212
const char * argp_program_bug_address
Definition: log2ant.cpp:56
static int parse_opt(int key, char *arg, struct argp_state *state)
Definition: log2ant.cpp:81
QString inputFileName
Definition: log2ant.cpp:61
int reportAllTsysData
Definition: log2ant.cpp:72
bool supressNonUsedSensors
Definition: log2ant.cpp:74
int logLevel
Definition: log2ant.cpp:73
QString stationKey
Definition: log2ant.cpp:64
int antcalOutputData
Definition: log2ant.cpp:71
SgMJD tEnd
Definition: log2ant.cpp:68
QString outputFileName
Definition: log2ant.cpp:62
QString compressExt
Definition: log2ant.cpp:65
SgMJD tBegin
Definition: log2ant.cpp:67
QString dbbc3InputFileName
Definition: log2ant.cpp:63
QMap< QString, AntcalOutputData > knownWishedData
Definition: log2ant.cpp:70
QString logFileName
Definition: log2ant.cpp:66