source: git/src/cavern.c @ b0aabdd

debug-cidebug-ci-sanitisersfaster-cavernloglog-selectstereo-2025walls-datawalls-data-hanging-as-warningwarn-only-for-hanging-survey
Last change on this file since b0aabdd was 37d6b84, checked in by Olly Betts <olly@…>, 3 years ago

cavern: Report results of each *declination auto

For each *declination auto command cavern now reports an "info"
message showing the range of calculated declination values and
the dates at which the ends of the range were obtained, and also
the grid convergence (which doesn't vary with time).

Fixes #92, reported by Rob Eavis.

  • Property mode set to 100644
File size: 14.2 KB
Line 
1/* cavern.c
2 * SURVEX Cave surveying software: data reduction main and related functions
3 * Copyright (C) 1991-2022 Olly Betts
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program 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 General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include <limits.h>
25#include <stdlib.h>
26#include <time.h>
27
28#include "cavern.h"
29#include "cmdline.h"
30#include "commands.h"
31#include "date.h"
32#include "datain.h"
33#include "debug.h"
34#include "message.h"
35#include "filename.h"
36#include "filelist.h"
37#include "img_hosted.h"
38#include "listpos.h"
39#include "netbits.h"
40#include "netskel.h"
41#include "osdepend.h"
42#include "out.h"
43#include "str.h"
44#include "validate.h"
45#include "whichos.h"
46
47#if OS_WIN32
48# include <conio.h> /* for _kbhit() and _getch() */
49#endif
50
51/* For funcs which want to be immune from messing around with different
52 * calling conventions */
53#ifndef CDECL
54# define CDECL
55#endif
56
57/* Globals */
58node *stnlist = NULL;
59settings *pcs;
60prefix *root;
61prefix *anon_list = NULL;
62long cLegs, cStns;
63long cComponents;
64bool fExportUsed = fFalse;
65char * proj_str_out = NULL;
66PJ * pj_cached = NULL;
67
68FILE *fhErrStat = NULL;
69img *pimg = NULL;
70bool fQuiet = fFalse; /* just show brief summary + errors */
71bool fMute = fFalse; /* just show errors */
72bool fSuppress = fFalse; /* only output 3d file */
73static bool fLog = fFalse; /* stdout to .log file */
74static bool f_warnings_are_errors = fFalse; /* turn warnings into errors */
75
76nosurveylink *nosurveyhead;
77
78real totadj, total, totplan, totvert;
79real min[6], max[6];
80prefix *pfxHi[6], *pfxLo[6];
81
82char *survey_title = NULL;
83int survey_title_len;
84
85bool fExplicitTitle = fFalse;
86
87char *fnm_output_base = NULL;
88int fnm_output_base_is_dir = 0;
89
90lrudlist * model = NULL;
91lrud ** next_lrud = NULL;
92
93static void do_stats(void);
94
95static const struct option long_opts[] = {
96   /* const char *name; int has_arg (0 no_argument, 1 required_*, 2 optional_*); int *flag; int val; */
97   {"percentage", no_argument, 0, 'p'},
98   /* Ignore for compatibility with older versions. */
99   {"no-percentage", no_argument, 0, 0},
100   {"output", required_argument, 0, 'o'},
101   {"quiet", no_argument, 0, 'q'},
102   {"no-auxiliary-files", no_argument, 0, 's'},
103   {"warnings-are-errors", no_argument, 0, 'w'},
104   {"log", no_argument, 0, 1},
105   {"3d-version", required_argument, 0, 'v'},
106#if OS_WIN32
107   {"pause", no_argument, 0, 2},
108#endif
109   {"help", no_argument, 0, HLP_HELP},
110   {"version", no_argument, 0, HLP_VERSION},
111   {0, 0, 0, 0}
112};
113
114#define short_opts "pao:qsv:wz:"
115
116static struct help_msg help[] = {
117/*                              <-- */
118   /* TRANSLATORS: --help output for cavern --output option */
119   {HLP_ENCODELONG(2),        /*set location for output files*/162, 0},
120   /* TRANSLATORS: --help output for cavern --quiet option */
121   {HLP_ENCODELONG(3),        /*only show brief summary (-qq for errors only)*/163, 0},
122   /* TRANSLATORS: --help output for cavern --no-auxiliary-files option */
123   {HLP_ENCODELONG(4),        /*do not create .err file*/164, 0},
124   /* TRANSLATORS: --help output for cavern --warnings-are-errors option */
125   {HLP_ENCODELONG(5),        /*turn warnings into errors*/165, 0},
126   /* TRANSLATORS: --help output for cavern --log option */
127   {HLP_ENCODELONG(6),        /*log output to .log file*/170, 0},
128   /* TRANSLATORS: --help output for cavern --3d-version option */
129   {HLP_ENCODELONG(7),        /*specify the 3d file format version to output*/171, 0},
130 /*{'z',                        "set optimizations for network reduction"},*/
131   {0, 0, 0}
132};
133
134/* atexit functions */
135static void
136delete_output_on_error(void)
137{
138   if (msg_errors || (f_warnings_are_errors && msg_warnings))
139      filename_delete_output();
140}
141
142#if OS_WIN32
143static void
144pause_on_exit(void)
145{
146   while (_kbhit()) _getch();
147   _getch();
148}
149#endif
150
151int current_days_since_1900;
152
153static void discarding_proj_logger(void *ctx, int level, const char *message) {
154    (void)ctx;
155    (void)level;
156    (void)message;
157}
158
159extern CDECL int
160main(int argc, char **argv)
161{
162   int d;
163   time_t tmUserStart = time(NULL);
164   clock_t tmCPUStart = clock();
165   {
166       /* FIXME: localtime? */
167       struct tm * t = localtime(&tmUserStart);
168       int y = t->tm_year + 1900;
169       current_days_since_1900 = days_since_1900(y, t->tm_mon + 1, t->tm_mday);
170   }
171
172   /* Always buffer by line for aven's benefit. */
173   setvbuf(stdout, NULL, _IOLBF, 0);
174
175   /* Prevent stderr spew from PROJ. */
176   proj_log_func(PJ_DEFAULT_CTX, NULL, discarding_proj_logger);
177
178   msg_init(argv);
179
180   pcs = osnew(settings);
181   pcs->next = NULL;
182   pcs->Translate = ((short*) osmalloc(ossizeof(short) * 257)) + 1;
183   pcs->meta = NULL;
184   pcs->proj_str = NULL;
185   pcs->declination = HUGE_REAL;
186   pcs->convergence = 0.0;
187   pcs->dec_filename = NULL;
188   pcs->dec_line = 0;
189   pcs->dec_context = NULL;
190   pcs->dec_lat = HUGE_VAL;
191   pcs->dec_lon = HUGE_VAL;
192   pcs->dec_alt = HUGE_VAL;
193   pcs->min_declination = HUGE_VAL;
194   pcs->max_declination = -HUGE_VAL;
195
196   /* Set up root of prefix hierarchy */
197   root = osnew(prefix);
198   root->up = root->right = root->down = NULL;
199   root->stn = NULL;
200   root->pos = NULL;
201   root->ident = NULL;
202   root->min_export = root->max_export = 0;
203   root->sflags = BIT(SFLAGS_SURVEY);
204   root->filename = NULL;
205
206   nosurveyhead = NULL;
207
208   stnlist = NULL;
209   cLegs = cStns = cComponents = 0;
210   totadj = total = totplan = totvert = 0.0;
211
212   for (d = 0; d < 6; d++) {
213      min[d] = HUGE_REAL;
214      max[d] = -HUGE_REAL;
215      pfxHi[d] = pfxLo[d] = NULL;
216   }
217
218   /* at least one argument must be given */
219   cmdline_init(argc, argv, short_opts, long_opts, NULL, help, 1, -1);
220   while (1) {
221      int opt = cmdline_getopt();
222      if (opt == EOF) break;
223      switch (opt) {
224       case 'p':
225         /* Ignore for compatibility with older versions. */
226         break;
227       case 'o': {
228         osfree(fnm_output_base); /* in case of multiple -o options */
229         /* can be a directory (in which case use basename of leaf input)
230          * or a file (in which case just trim the extension off) */
231         if (fDirectory(optarg)) {
232            /* this is a little tricky - we need to note the path here,
233             * and then add the leaf later on (in datain.c) */
234            fnm_output_base = base_from_fnm(optarg);
235            fnm_output_base_is_dir = 1;
236         } else {
237            fnm_output_base = base_from_fnm(optarg);
238         }
239         break;
240       }
241       case 'q':
242         if (fQuiet) fMute = 1;
243         fQuiet = 1;
244         break;
245       case 's':
246         fSuppress = 1;
247         break;
248       case 'v': {
249         int v = atoi(optarg);
250         if (v < IMG_VERSION_MIN || v > IMG_VERSION_MAX)
251            fatalerror(/*3d file format versions %d to %d supported*/88,
252                       IMG_VERSION_MIN, IMG_VERSION_MAX);
253         img_output_version = v;
254         break;
255       }
256       case 'w':
257         f_warnings_are_errors = 1;
258         break;
259       case 'z': {
260         /* Control which network optimisations are used (development tool) */
261         static int first_opt_z = 1;
262         char c;
263         if (first_opt_z) {
264            optimize = 0;
265            first_opt_z = 0;
266         }
267         /* Lollipops, Parallel legs, Iterate mx, Delta* */
268         while ((c = *optarg++) != '\0')
269            if (islower((unsigned char)c)) optimize |= BITA(c);
270         break;
271       case 1:
272         fLog = fTrue;
273         break;
274#if OS_WIN32
275       case 2:
276         atexit(pause_on_exit);
277         break;
278#endif
279       }
280      }
281   }
282
283   if (fLog) {
284      char *fnm;
285      if (!fnm_output_base) {
286         char *p;
287         p = baseleaf_from_fnm(argv[optind]);
288         fnm = add_ext(p, EXT_LOG);
289         osfree(p);
290      } else if (fnm_output_base_is_dir) {
291         char *p;
292         fnm = baseleaf_from_fnm(argv[optind]);
293         p = use_path(fnm_output_base, fnm);
294         osfree(fnm);
295         fnm = add_ext(p, EXT_LOG);
296         osfree(p);
297      } else {
298         fnm = add_ext(fnm_output_base, EXT_LOG);
299      }
300
301      if (!freopen(fnm, "w", stdout))
302         fatalerror(/*Failed to open output file “%s”*/47, fnm);
303
304      osfree(fnm);
305   }
306
307   if (!fMute) {
308      const char *p = COPYRIGHT_MSG;
309      puts(PRETTYPACKAGE" "VERSION);
310      while (1) {
311          const char *q = p;
312          p = strstr(p, "(C)");
313          if (p == NULL) {
314              puts(q);
315              break;
316          }
317          fwrite(q, 1, p - q, stdout);
318          fputs(msg(/*©*/0), stdout);
319          p += 3;
320      }
321   }
322
323   atexit(delete_output_on_error);
324
325   /* end of options, now process data files */
326   while (argv[optind]) {
327      const char *fnm = argv[optind];
328
329      if (!fExplicitTitle) {
330         char *lf;
331         lf = baseleaf_from_fnm(fnm);
332         if (survey_title) s_catchar(&survey_title, &survey_title_len, ' ');
333         s_cat(&survey_title, &survey_title_len, lf);
334         osfree(lf);
335      }
336
337      /* Select defaults settings */
338      default_all(pcs);
339      data_file(NULL, fnm); /* first argument is current path */
340
341      optind++;
342   }
343
344   validate();
345
346   report_declination(pcs);
347
348   solve_network(/*stnlist*/); /* Find coordinates of all points */
349   validate();
350
351   /* close .3d file */
352   if (!img_close(pimg)) {
353      char *fnm = add_ext(fnm_output_base, EXT_SVX_3D);
354      fatalerror(img_error2msg(img_error()), fnm);
355   }
356   if (fhErrStat) safe_fclose(fhErrStat);
357
358   out_current_action(msg(/*Calculating statistics*/120));
359   if (!fMute) do_stats();
360   if (!fQuiet) {
361      /* clock() typically wraps after 72 minutes, but there doesn't seem
362       * to be a better way.  Still 72 minutes means some cave!
363       * We detect if clock() could have wrapped and suppress CPU time
364       * printing in this case.
365       */
366      double tmUser = difftime(time(NULL), tmUserStart);
367      double tmCPU;
368      clock_t now = clock();
369#define CLOCK_T_WRAP \
370        (sizeof(clock_t)<sizeof(long)?(1ul << (CHAR_BIT * sizeof(clock_t))):0)
371      tmCPU = (now - (unsigned long)tmCPUStart)
372         / (double)CLOCKS_PER_SEC;
373      if (now < tmCPUStart)
374         tmCPU += CLOCK_T_WRAP / (double)CLOCKS_PER_SEC;
375      if (tmUser >= tmCPU + CLOCK_T_WRAP / (double)CLOCKS_PER_SEC)
376         tmCPU = 0;
377
378      /* tmUser is integer, tmCPU not - equivalent to (ceil(tmCPU) >= tmUser) */
379      if (tmCPU + 1 > tmUser) {
380         printf(msg(/*CPU time used %5.2fs*/140), tmCPU);
381      } else if (tmCPU == 0) {
382         if (tmUser != 0.0) {
383            printf(msg(/*Time used %5.2fs*/141), tmUser);
384         } else {
385            fputs(msg(/*Time used unavailable*/142), stdout);
386         }
387      } else {
388         printf(msg(/*Time used %5.2fs (%5.2fs CPU time)*/143), tmUser, tmCPU);
389      }
390      putnl();
391   }
392   if (msg_warnings || msg_errors) {
393      if (msg_errors || (f_warnings_are_errors && msg_warnings)) {
394         printf(msg(/*There were %d warning(s) and %d error(s) - no output files produced.*/113),
395                msg_warnings, msg_errors);
396         putnl();
397         return EXIT_FAILURE;
398      }
399      printf(msg(/*There were %d warning(s).*/16), msg_warnings);
400      putnl();
401   }
402   return EXIT_SUCCESS;
403}
404
405static void
406do_range(int d, int msgno, real length_factor, const char * units)
407{
408   if (d < 3) {
409      /* If the bound including anonymous stations is at an anonymous station
410       * but the bound only considering named stations is the same, use the
411       * named station for the anonymous bound too.
412       */
413      if (TSTBIT(pfxHi[d]->sflags, SFLAGS_ANON) && max[d] == max[d + 3]) {
414         pfxHi[d] = pfxHi[d + 3];
415      }
416      if (TSTBIT(pfxLo[d]->sflags, SFLAGS_ANON) && min[d] == min[d + 3]) {
417         pfxLo[d] = pfxLo[d + 3];
418      }
419   }
420
421   /* sprint_prefix uses a single buffer, so to report two stations in one
422    * message we need to make a temporary copy of the string for one of them.
423    */
424   char * pfx_hi = osstrdup(sprint_prefix(pfxHi[d]));
425   char * pfx_lo = sprint_prefix(pfxLo[d]);
426   real hi = max[d] * length_factor;
427   real lo = min[d] * length_factor;
428   printf(msg(msgno), hi - lo, units, pfx_hi, hi, units, pfx_lo, lo, units);
429   osfree(pfx_hi);
430   putnl();
431
432   /* Range without anonymous stations at offset 3. */
433   if (d < 3 && (pfxHi[d] != pfxHi[d + 3] || pfxLo[d] != pfxLo[d + 3])) {
434      do_range(d + 3, msgno, length_factor, units);
435   }
436}
437
438static void
439do_stats(void)
440{
441   long cLoops = cComponents + cLegs - cStns;
442   int length_units = get_length_units(Q_LENGTH);
443   const char * units = get_units_string(length_units);
444   real length_factor = 1.0 / get_units_factor(length_units);
445
446   putnl();
447
448   if (cStns == 1) {
449      fputs(msg(/*Survey contains 1 survey station,*/172), stdout);
450   } else {
451      printf(msg(/*Survey contains %ld survey stations,*/173), cStns);
452   }
453
454   if (cLegs == 1) {
455      fputs(msg(/* joined by 1 leg.*/174), stdout);
456   } else {
457      printf(msg(/* joined by %ld legs.*/175), cLegs);
458   }
459
460   putnl();
461
462   if (cLoops == 1) {
463      fputs(msg(/*There is 1 loop.*/138), stdout);
464   } else {
465      printf(msg(/*There are %ld loops.*/139), cLoops);
466   }
467
468   putnl();
469
470   if (cComponents != 1) {
471      /* TRANSLATORS: "Connected component" in the graph theory sense - it
472       * means there are %ld bits of survey with no connections between them.
473       * This message is only used if there are more than 1. */
474      printf(msg(/*Survey has %ld connected components.*/178), cComponents);
475      putnl();
476   }
477
478   printf(msg(/*Total length of survey legs = %7.2f%s (%7.2f%s adjusted)*/132),
479          total * length_factor, units, totadj * length_factor, units);
480   putnl();
481   printf(msg(/*Total plan length of survey legs = %7.2f%s*/133),
482          totplan * length_factor, units);
483   putnl();
484   printf(msg(/*Total vertical length of survey legs = %7.2f%s*/134),
485          totvert * length_factor, units);
486   putnl();
487
488   /* If there's no underground survey, we've no ranges */
489   if (pfxHi[0]) {
490      /* TRANSLATORS: numbers are altitudes of highest and lowest stations */
491      do_range(2, /*Vertical range = %4.2f%s (from %s at %4.2f%s to %s at %4.2f%s)*/135,
492               length_factor, units);
493      /* TRANSLATORS: c.f. previous message */
494      do_range(1, /*North-South range = %4.2f%s (from %s at %4.2f%s to %s at %4.2f%s)*/136,
495               length_factor, units);
496      /* TRANSLATORS: c.f. previous two messages */
497      do_range(0, /*East-West range = %4.2f%s (from %s at %4.2f%s to %s at %4.2f%s)*/137,
498               length_factor, units);
499   }
500
501   print_node_stats();
502   /* Also, could give:
503    *  # nodes stations (ie have other than two references or are fixed)
504    *  # fixed stations (list of?)
505    */
506}
Note: See TracBrowser for help on using the repository browser.