Improve reading of Compass DAT CORRECTIONS2
[survex.git] / src / datain.c
blob6ef5264b6bca29079dde7a6fb9e7aafda6ca8191
1 /* datain.c
2 * Reads in survey files, dealing with special characters, keywords & data
3 * Copyright (C) 1991-2024 Olly Betts
4 * Copyright (C) 2004 Simeon Warner
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
25 #include <limits.h>
26 #include <stdarg.h>
28 #include "debug.h"
29 #include "cavern.h"
30 #include "date.h"
31 #include "img.h"
32 #include "filename.h"
33 #include "message.h"
34 #include "filelist.h"
35 #include "netbits.h"
36 #include "netskel.h"
37 #include "readval.h"
38 #include "datain.h"
39 #include "commands.h"
40 #include "out.h"
41 #include "str.h"
42 #include "thgeomag.h"
43 #if PROJ_VERSION_MAJOR < 8 || \
44 (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR < 2)
45 /* Needed for proj_factors workaround */
46 # include <proj_experimental.h>
47 #endif
49 #define EPSILON (REAL_EPSILON * 1000)
51 #define var(I) (pcs->Var[(I)])
53 /* true if x is not-a-number value in Compass (999.0 or -999.0) */
54 /* Compass uses -999.0 but understands Karst data which used 999.0
55 * (information from Larry Fish via Simeon Warner). */
56 #define is_compass_NaN(x) ( fabs(fabs(x)-999.0) < EPSILON )
58 int ch;
60 typedef enum {
61 CTYPE_OMIT, CTYPE_READING, CTYPE_PLUMB, CTYPE_INFERPLUMB, CTYPE_HORIZ
62 } clino_type;
64 /* Don't explicitly initialise as we can't set the jmp_buf - this has
65 * static scope so will be initialised like this anyway */
66 parse file /* = { NULL, NULL, 0, false, NULL } */ ;
68 bool f_export_ok;
70 static real value[Fr - 1];
71 #define VAL(N) value[(N)-1]
72 static real variance[Fr - 1];
73 #define VAR(N) variance[(N)-1]
74 static long location[Fr - 1];
75 #define LOC(N) location[(N)-1]
76 static int location_width[Fr - 1];
77 #define WID(N) location_width[(N)-1]
79 /* style functions */
80 static void data_normal(void);
81 static void data_cartesian(void);
82 static void data_passage(void);
83 static void data_nosurvey(void);
84 static void data_ignore(void);
86 void
87 get_pos(filepos *fp)
89 fp->ch = ch;
90 fp->offset = ftell(file.fh);
91 if (fp->offset == -1)
92 fatalerror_in_file(file.filename, 0, /*Error reading file*/18);
95 void
96 set_pos(const filepos *fp)
98 ch = fp->ch;
99 if (fseek(file.fh, fp->offset, SEEK_SET) == -1)
100 fatalerror_in_file(file.filename, 0, /*Error reading file*/18);
103 static void
104 report_parent(parse * p) {
105 if (p->parent)
106 report_parent(p->parent);
107 /* Force re-report of include tree for further errors in
108 * parent files */
109 p->reported_where = false;
110 /* TRANSLATORS: %s is replaced by the filename of the parent file, and %u
111 * by the line number in that file. Your translation should also contain
112 * %s:%u so that automatic parsing of error messages to determine the file
113 * and line number still works. */
114 fprintf(STDERR, msg(/*In file included from %s:%u:\n*/5), p->filename, p->line);
117 static void
118 error_list_parent_files(void)
120 if (!file.reported_where && file.parent) {
121 report_parent(file.parent);
122 /* Suppress reporting of full include tree for further errors
123 * in this file */
124 file.reported_where = true;
128 static void
129 show_line(int col, int width)
131 /* Rewind to beginning of line. */
132 long cur_pos = ftell(file.fh);
133 int tabs = 0;
134 if (cur_pos < 0 || fseek(file.fh, file.lpos, SEEK_SET) == -1)
135 fatalerror_in_file(file.filename, 0, /*Error reading file*/18);
137 /* Read the whole line and write it out. */
138 PUTC(' ', STDERR);
139 while (1) {
140 int c = GETC(file.fh);
141 /* Note: isEol() is true for EOF */
142 if (isEol(c)) break;
143 if (c == '\t') ++tabs;
144 PUTC(c, STDERR);
146 fputnl(STDERR);
148 /* If we have a location in the line for the error, indicate it. */
149 if (col) {
150 PUTC(' ', STDERR);
151 if (tabs == 0) {
152 while (--col) PUTC(' ', STDERR);
153 } else {
154 /* Copy tabs from line, replacing other characters with spaces - this
155 * means that the caret should line up correctly. */
156 if (fseek(file.fh, file.lpos, SEEK_SET) == -1)
157 fatalerror_in_file(file.filename, 0, /*Error reading file*/18);
158 while (--col) {
159 int c = GETC(file.fh);
160 if (c != '\t') c = ' ';
161 PUTC(c, STDERR);
164 PUTC('^', STDERR);
165 while (width > 1) {
166 PUTC('~', STDERR);
167 --width;
169 fputnl(STDERR);
172 /* Revert to where we were. */
173 if (fseek(file.fh, cur_pos, SEEK_SET) == -1)
174 fatalerror_in_file(file.filename, 0, /*Error reading file*/18);
177 char*
178 grab_line(void)
180 /* Rewind to beginning of line. */
181 long cur_pos = ftell(file.fh);
182 char *p = NULL;
183 int len = 0;
184 if (cur_pos < 0 || fseek(file.fh, file.lpos, SEEK_SET) == -1)
185 fatalerror_in_file(file.filename, 0, /*Error reading file*/18);
187 /* Read the whole line into a string. */
188 while (1) {
189 int c = GETC(file.fh);
190 /* Note: isEol() is true for EOF */
191 if (isEol(c)) break;
192 s_catchar(&p, &len, (char)c);
195 /* Revert to where we were. */
196 if (fseek(file.fh, cur_pos, SEEK_SET) == -1) {
197 free(p);
198 fatalerror_in_file(file.filename, 0, /*Error reading file*/18);
201 return p;
204 static int caret_width = 0;
206 static void
207 compile_v_report_fpos(int severity, long fpos, int en, va_list ap)
209 int col = 0;
210 error_list_parent_files();
211 if (fpos >= file.lpos)
212 col = fpos - file.lpos - caret_width;
213 v_report(severity, file.filename, file.line, col, en, ap);
214 if (file.fh) show_line(col, caret_width);
217 static void
218 compile_v_report(int diag_flags, int en, va_list ap)
220 int severity = (diag_flags & DIAG_SEVERITY_MASK);
221 if (diag_flags & (DIAG_COL|DIAG_BUF)) {
222 if (file.fh) {
223 if (diag_flags & DIAG_BUF) caret_width = strlen(buffer);
224 compile_v_report_fpos(severity, ftell(file.fh), en, ap);
225 if (diag_flags & DIAG_BUF) caret_width = 0;
226 if (diag_flags & DIAG_SKIP) skipline();
227 return;
230 error_list_parent_files();
231 v_report(severity, file.filename, file.line, 0, en, ap);
232 if (file.fh) {
233 if (diag_flags & DIAG_BUF) {
234 show_line(0, strlen(buffer));
235 } else {
236 show_line(0, caret_width);
239 if (diag_flags & DIAG_SKIP) skipline();
242 void
243 compile_diagnostic(int diag_flags, int en, ...)
245 va_list ap;
246 va_start(ap, en);
247 if (diag_flags & (DIAG_TOKEN|DIAG_UINT|DIAG_DATE|DIAG_NUM)) {
248 char *p = NULL;
249 int len = 0;
250 skipblanks();
251 if (diag_flags & DIAG_TOKEN) {
252 while (!isBlank(ch) && !isEol(ch)) {
253 s_catchar(&p, &len, (char)ch);
254 nextch();
256 } else if (diag_flags & DIAG_UINT) {
257 while (isdigit(ch)) {
258 s_catchar(&p, &len, (char)ch);
259 nextch();
261 } else if (diag_flags & DIAG_DATE) {
262 while (isdigit(ch) || ch == '.') {
263 s_catchar(&p, &len, (char)ch);
264 nextch();
266 } else {
267 if (isMinus(ch) || isPlus(ch)) {
268 s_catchar(&p, &len, (char)ch);
269 nextch();
271 while (isdigit(ch)) {
272 s_catchar(&p, &len, (char)ch);
273 nextch();
275 if (isDecimal(ch)) {
276 s_catchar(&p, &len, (char)ch);
277 nextch();
279 while (isdigit(ch)) {
280 s_catchar(&p, &len, (char)ch);
281 nextch();
284 if (p) {
285 caret_width = strlen(p);
286 osfree(p);
288 compile_v_report(diag_flags|DIAG_COL, en, ap);
289 caret_width = 0;
290 } else if (diag_flags & DIAG_STRING) {
291 char *p = NULL;
292 int alloced = 0;
293 skipblanks();
294 caret_width = ftell(file.fh);
295 read_string(&p, &alloced);
296 osfree(p);
297 /* We want to include any quotes, so can't use strlen(p). */
298 caret_width = ftell(file.fh) - caret_width;
299 compile_v_report(diag_flags|DIAG_COL, en, ap);
300 caret_width = 0;
301 } else {
302 compile_v_report(diag_flags, en, ap);
304 va_end(ap);
307 static void
308 compile_diagnostic_reading(int diag_flags, reading r, int en, ...)
310 va_list ap;
311 int severity = (diag_flags & DIAG_SEVERITY_MASK);
312 va_start(ap, en);
313 caret_width = WID(r);
314 compile_v_report_fpos(severity, LOC(r) + caret_width, en, ap);
315 caret_width = 0;
316 va_end(ap);
319 static void
320 compile_error_reading_skip(reading r, int en, ...)
322 va_list ap;
323 va_start(ap, en);
324 caret_width = WID(r);
325 compile_v_report_fpos(DIAG_ERR, LOC(r) + caret_width, en, ap);
326 caret_width = 0;
327 va_end(ap);
328 skipline();
331 void
332 compile_diagnostic_at(int diag_flags, const char * filename, unsigned line, int en, ...)
334 va_list ap;
335 int severity = (diag_flags & DIAG_SEVERITY_MASK);
336 va_start(ap, en);
337 v_report(severity, filename, line, 0, en, ap);
338 va_end(ap);
341 void
342 compile_diagnostic_pfx(int diag_flags, const prefix * pfx, int en, ...)
344 va_list ap;
345 int severity = (diag_flags & DIAG_SEVERITY_MASK);
346 va_start(ap, en);
347 v_report(severity, pfx->filename, pfx->line, 0, en, ap);
348 va_end(ap);
351 void
352 compile_diagnostic_token_show(int diag_flags, int en)
354 char *p = NULL;
355 int len = 0;
356 skipblanks();
357 while (!isBlank(ch) && !isEol(ch)) {
358 s_catchar(&p, &len, (char)ch);
359 nextch();
361 if (p) {
362 caret_width = strlen(p);
363 compile_diagnostic(diag_flags|DIAG_COL, en, p);
364 caret_width = 0;
365 osfree(p);
366 } else {
367 compile_diagnostic(DIAG_ERR|DIAG_COL, en, "");
371 static void
372 compile_error_string(const char * s, int en, ...)
374 va_list ap;
375 va_start(ap, en);
376 caret_width = strlen(s);
377 compile_v_report(DIAG_ERR|DIAG_COL, en, ap);
378 va_end(ap);
379 caret_width = 0;
382 /* This function makes a note where to put output files */
383 static void
384 using_data_file(const char *fnm)
386 if (!fnm_output_base) {
387 /* was: fnm_output_base = base_from_fnm(fnm); */
388 fnm_output_base = baseleaf_from_fnm(fnm);
389 } else if (fnm_output_base_is_dir) {
390 /* --output pointed to directory so use the leaf basename in that dir */
391 char *lf, *p;
392 lf = baseleaf_from_fnm(fnm);
393 p = use_path(fnm_output_base, lf);
394 osfree(lf);
395 osfree(fnm_output_base);
396 fnm_output_base = p;
397 fnm_output_base_is_dir = 0;
401 static void
402 skipword(void)
404 while (!isBlank(ch) && !isEol(ch)) nextch();
407 extern void
408 skipblanks(void)
410 while (isBlank(ch)) nextch();
413 extern void
414 skipline(void)
416 while (!isEol(ch)) nextch();
419 static void
420 process_eol(void)
422 int eolchar;
424 skipblanks();
426 if (!isEol(ch)) {
427 if (!isComm(ch))
428 compile_diagnostic(DIAG_ERR|DIAG_COL, /*End of line not blank*/15);
429 skipline();
432 eolchar = ch;
433 file.line++;
434 /* skip any different eol characters so we get line counts correct on
435 * DOS text files and similar, but don't count several adjacent blank
436 * lines as one */
437 while (ch != EOF) {
438 nextch();
439 if (ch == eolchar || !isEol(ch)) {
440 break;
442 if (ch == '\n') eolchar = ch;
444 file.lpos = ftell(file.fh) - 1;
447 static bool
448 process_non_data_line(void)
450 skipblanks();
452 if (isData(ch)) return false;
454 if (isKeywd(ch)) {
455 nextch();
456 handle_command();
459 process_eol();
461 return true;
464 static void
465 read_reading(reading r, bool f_optional)
467 int n_readings;
468 q_quantity q;
469 switch (r) {
470 case Tape: q = Q_LENGTH; break;
471 case BackTape: q = Q_BACKLENGTH; break;
472 case Comp: q = Q_BEARING; break;
473 case BackComp: q = Q_BACKBEARING; break;
474 case Clino: q = Q_GRADIENT; break;
475 case BackClino: q = Q_BACKGRADIENT; break;
476 case FrDepth: case ToDepth: q = Q_DEPTH; break;
477 case Dx: q = Q_DX; break;
478 case Dy: q = Q_DY; break;
479 case Dz: q = Q_DZ; break;
480 case FrCount: case ToCount: q = Q_COUNT; break;
481 case Left: q = Q_LEFT; break;
482 case Right: q = Q_RIGHT; break;
483 case Up: q = Q_UP; break;
484 case Down: q = Q_DOWN; break;
485 default:
486 q = Q_NULL; /* Suppress compiler warning */;
487 BUG("Unexpected case");
489 LOC(r) = ftell(file.fh);
490 /* since we don't handle bearings in read_readings, it's never quadrant */
491 VAL(r) = read_numeric_multi(f_optional, false, &n_readings);
492 WID(r) = ftell(file.fh) - LOC(r);
493 VAR(r) = var(q);
494 if (n_readings > 1) VAR(r) /= sqrt(n_readings);
497 static void
498 read_bearing_or_omit(reading r)
500 int n_readings;
501 bool quadrants = false;
502 q_quantity q = Q_NULL;
503 switch (r) {
504 case Comp:
505 q = Q_BEARING;
506 if (pcs->f_bearing_quadrants)
507 quadrants = true;
508 break;
509 case BackComp:
510 q = Q_BACKBEARING;
511 if (pcs->f_backbearing_quadrants)
512 quadrants = true;
513 break;
514 default:
515 q = Q_NULL; /* Suppress compiler warning */;
516 BUG("Unexpected case");
518 LOC(r) = ftell(file.fh);
519 VAL(r) = read_bearing_multi_or_omit(quadrants, &n_readings);
520 WID(r) = ftell(file.fh) - LOC(r);
521 VAR(r) = var(q);
522 if (n_readings > 1) VAR(r) /= sqrt(n_readings);
525 /* For reading Compass MAK files which have a freeform syntax */
526 static void
527 nextch_handling_eol(void)
529 nextch();
530 while (ch != EOF && isEol(ch)) {
531 process_eol();
535 #define LITLEN(S) (sizeof(S"") - 1)
536 #define has_ext(F,L,E) ((L) > LITLEN(E) + 1 &&\
537 (F)[(L) - LITLEN(E) - 1] == FNM_SEP_EXT &&\
538 strcasecmp((F) + (L) - LITLEN(E), E) == 0)
539 extern void
540 data_file(const char *pth, const char *fnm)
542 int begin_lineno_store;
543 parse file_store;
544 volatile enum {FMT_SVX, FMT_DAT, FMT_MAK} fmt = FMT_SVX;
547 char *filename;
548 FILE *fh;
549 size_t len;
551 if (!pth) {
552 /* file specified on command line - don't do special translation */
553 fh = fopenWithPthAndExt(pth, fnm, EXT_SVX_DATA, "rb", &filename);
554 } else {
555 fh = fopen_portable(pth, fnm, EXT_SVX_DATA, "rb", &filename);
558 if (fh == NULL) {
559 compile_error_string(fnm, /*Couldnā€™t open file ā€œ%sā€*/24, fnm);
560 return;
563 len = strlen(filename);
564 if (has_ext(filename, len, "dat")) {
565 fmt = FMT_DAT;
566 } else if (has_ext(filename, len, "mak")) {
567 fmt = FMT_MAK;
570 file_store = file;
571 if (file.fh) file.parent = &file_store;
572 file.fh = fh;
573 file.filename = filename;
574 file.line = 1;
575 file.lpos = 0;
576 file.reported_where = false;
577 nextch();
578 if (fmt == FMT_SVX && ch == 0xef) {
579 /* Maybe a UTF-8 "BOM" - skip if so. */
580 if (nextch() == 0xbb && nextch() == 0xbf) {
581 nextch();
582 file.lpos = 3;
583 } else {
584 rewind(fh);
585 ch = 0xef;
590 using_data_file(file.filename);
592 begin_lineno_store = pcs->begin_lineno;
593 pcs->begin_lineno = 0;
595 if (fmt == FMT_DAT) {
596 short *t;
597 int i;
598 settings *pcsNew;
600 pcsNew = osnew(settings);
601 *pcsNew = *pcs; /* copy contents */
602 pcsNew->begin_lineno = 0;
603 pcsNew->next = pcs;
604 pcs = pcsNew;
605 default_units(pcs);
606 default_calib(pcs);
607 pcs->z[Q_DECLINATION] = HUGE_REAL;
609 pcs->recorded_style = pcs->style = STYLE_NORMAL;
610 pcs->units[Q_LENGTH] = METRES_PER_FOOT;
611 t = ((short*)osmalloc(ossizeof(short) * 257)) + 1;
613 t[EOF] = SPECIAL_EOL;
614 memset(t, 0, sizeof(short) * 33);
615 for (i = 33; i < 127; i++) t[i] = SPECIAL_NAMES;
616 t[127] = 0;
617 for (i = 128; i < 256; i++) t[i] = SPECIAL_NAMES;
618 t['\t'] |= SPECIAL_BLANK;
619 t[' '] |= SPECIAL_BLANK;
620 t['\032'] |= SPECIAL_EOL; /* Ctrl-Z, so olde DOS text files are handled ok */
621 t['\n'] |= SPECIAL_EOL;
622 t['\r'] |= SPECIAL_EOL;
623 t['.'] |= SPECIAL_DECIMAL;
624 t['-'] |= SPECIAL_MINUS;
625 t['+'] |= SPECIAL_PLUS;
626 pcs->Translate = t;
627 pcs->Case = OFF;
628 pcs->Truncate = INT_MAX;
629 pcs->infer = BIT(INFER_EQUATES)|
630 BIT(INFER_EQUATES_SELF_OK)|
631 BIT(INFER_EXPORTS)|
632 BIT(INFER_PLUMBS);
633 } else if (fmt == FMT_MAK) {
634 short *t;
635 int i;
636 settings *pcsNew;
638 pcsNew = osnew(settings);
639 *pcsNew = *pcs; /* copy contents */
640 pcsNew->begin_lineno = 0;
641 pcsNew->next = pcs;
642 pcs = pcsNew;
644 t = ((short*)osmalloc(ossizeof(short) * 257)) + 1;
646 t[EOF] = SPECIAL_EOL;
647 memset(t, 0, sizeof(short) * 33);
648 for (i = 33; i < 127; i++) t[i] = SPECIAL_NAMES;
649 t[127] = 0;
650 for (i = 128; i < 256; i++) t[i] = SPECIAL_NAMES;
651 t['['] = t[','] = t[';'] = 0;
652 t['\t'] |= SPECIAL_BLANK;
653 t[' '] |= SPECIAL_BLANK;
654 t['\032'] |= SPECIAL_EOL; /* Ctrl-Z, so olde DOS text files are handled ok */
655 t['\n'] |= SPECIAL_EOL;
656 t['\r'] |= SPECIAL_EOL;
657 t['.'] |= SPECIAL_DECIMAL;
658 t['-'] |= SPECIAL_MINUS;
659 t['+'] |= SPECIAL_PLUS;
660 pcs->Translate = t;
661 pcs->Case = OFF;
662 pcs->Truncate = INT_MAX;
665 #ifdef HAVE_SETJMP_H
666 /* errors in nested functions can longjmp here */
667 if (setjmp(file.jbSkipLine)) {
668 skipline();
669 process_eol();
671 #endif
673 if (fmt == FMT_DAT) {
674 while (ch != EOF && !ferror(file.fh)) {
675 static const reading compass_order[] = {
676 Fr, To, Tape, CompassDATComp, CompassDATClino,
677 CompassDATLeft, CompassDATUp, CompassDATDown, CompassDATRight,
678 CompassDATFlags, IgnoreAll
680 static const reading compass_order_backsights[] = {
681 Fr, To, Tape, CompassDATComp, CompassDATClino,
682 CompassDATLeft, CompassDATUp, CompassDATDown, CompassDATRight,
683 CompassDATBackComp, CompassDATBackClino,
684 CompassDATFlags, IgnoreAll
686 /* <Cave name> */
687 skipline();
688 process_eol();
689 /* SURVEY NAME: <Short name> */
690 get_token();
691 get_token();
692 /* if (ch != ':') ... */
693 nextch();
694 get_token();
695 skipline();
696 process_eol();
697 /* SURVEY DATE: 7 10 79 COMMENT:<Long name> */
698 get_token();
699 get_token();
700 copy_on_write_meta(pcs);
701 if (ch == ':') {
702 int year, month, day;
704 nextch();
706 /* NB order is *month* *day* year */
707 month = read_uint();
708 day = read_uint();
709 year = read_uint();
710 /* Note: Larry says a 2 digit year is always 19XX */
711 if (year < 100) year += 1900;
713 /* Compass uses 1901-01-01 when no date was specified. */
714 if (year == 1901 && day == 1 && month == 1) goto compass_dat_no_date;
715 pcs->meta->days1 = pcs->meta->days2 = days_since_1900(year, month, day);
716 } else {
717 compass_dat_no_date:
718 pcs->meta->days1 = pcs->meta->days2 = -1;
720 pcs->declination = HUGE_REAL;
721 skipline();
722 process_eol();
723 /* SURVEY TEAM: */
724 get_token();
725 get_token();
726 skipline();
727 process_eol();
728 /* <Survey team> */
729 skipline();
730 process_eol();
731 /* DECLINATION: 1.00 FORMAT: DDDDLUDRADLN CORRECTIONS: 2.00 3.00 4.00 */
732 get_token();
733 nextch(); /* : */
734 skipblanks();
735 if (pcs->dec_filename == NULL) {
736 pcs->z[Q_DECLINATION] = -read_numeric(false);
737 pcs->z[Q_DECLINATION] *= pcs->units[Q_DECLINATION];
738 } else {
739 (void)read_numeric(false);
741 get_token();
742 pcs->ordering = compass_order;
743 if (strcmp(buffer, "FORMAT") == 0) {
744 /* This documents the format in the original survey notebook - we
745 * don't need to fully parse it to be able to parse the survey data
746 * in the file, which gets converted to a fixed order and units.
748 size_t buffer_len;
749 nextch(); /* : */
750 get_token();
751 buffer_len = strlen(buffer);
752 if (buffer_len >= 4 && buffer[3] == 'W') {
753 /* Original "Inclination Units" were "Depth Gauge". */
754 pcs->recorded_style = STYLE_DIVING;
756 if (buffer_len >= 12 && buffer[buffer_len >= 15 ? 13 : 11] == 'B') {
757 /* We have backsights for compass and clino */
758 pcs->ordering = compass_order_backsights;
760 get_token();
763 if (strcmp(buffer, "CORRECTIONS") == 0 && ch == ':') {
764 nextch(); /* : */
765 pcs->z[Q_BEARING] = -rad(read_numeric(false));
766 pcs->z[Q_GRADIENT] = -rad(read_numeric(false));
767 pcs->z[Q_LENGTH] = -METRES_PER_FOOT * read_numeric(false);
768 get_token();
771 /* get_token() only reads alphas so we must check for '2' here. */
772 if (strcmp(buffer, "CORRECTIONS") == 0 && ch == '2') {
773 nextch(); /* 2 */
774 nextch(); /* : */
775 pcs->z[Q_BACKBEARING] = -rad(read_numeric(false));
776 pcs->z[Q_BACKGRADIENT] = -rad(read_numeric(false));
777 get_token();
779 skipline();
780 process_eol();
781 /* BLANK LINE */
782 skipline();
783 process_eol();
784 /* heading line */
785 skipline();
786 process_eol();
787 /* BLANK LINE */
788 skipline();
789 process_eol();
790 while (ch != EOF) {
791 if (ch == '\x0c') {
792 nextch();
793 process_eol();
794 break;
796 data_normal();
798 clear_last_leg();
800 pcs->ordering = NULL; /* Avoid free() of static array. */
801 pop_settings();
802 } else if (fmt == FMT_MAK) {
803 int datum = 0;
804 int utm_zone = 0;
805 real base_x = 0.0, base_y = 0.0, base_z = 0.0;
806 int base_utm_zone = 0;
807 unsigned int base_line = 0;
808 long base_lpos = 0;
809 char *path = path_from_fnm(file.filename);
810 int path_len = strlen(path);
811 struct mak_folder {
812 struct mak_folder *next;
813 int len;
814 } *folder_stack = NULL;
816 while (ch != EOF && !ferror(file.fh)) {
817 switch (ch) {
818 case '#': {
819 /* include a file */
820 int ch_store;
821 char *dat_fnm = NULL;
822 int dat_fnm_len;
823 nextch_handling_eol();
824 while (ch != ',' && ch != ';' && ch != EOF) {
825 while (isEol(ch)) process_eol();
826 s_catchar(&dat_fnm, &dat_fnm_len, (char)ch);
827 nextch_handling_eol();
829 if (dat_fnm) {
830 if (base_utm_zone) {
831 // Process the previous @ command using the datum from &.
832 char *proj_str = img_compass_utm_proj_str(datum, base_utm_zone);
833 if (proj_str) {
834 // Temporarily reset line and lpos so dec_context and
835 // dec_line refer to the @ command.
836 unsigned saved_line = file.line;
837 file.line = base_line;
838 long saved_lpos = file.lpos;
839 file.lpos = base_lpos;
840 set_declination_location(base_x, base_y, base_z,
841 proj_str);
842 file.line = saved_line;
843 file.lpos = saved_lpos;
844 if (!pcs->proj_str) {
845 pcs->proj_str = proj_str;
846 if (!proj_str_out) {
847 proj_str_out = osstrdup(proj_str);
849 } else {
850 osfree(proj_str);
854 ch_store = ch;
855 data_file(path, dat_fnm);
856 ch = ch_store;
857 osfree(dat_fnm);
859 while (ch != ';' && ch != EOF) {
860 prefix *name;
861 nextch_handling_eol();
862 name = read_prefix(PFX_STATION|PFX_OPT);
863 if (name) {
864 skipblanks();
865 if (ch == '[') {
866 /* fixed pt */
867 node *stn;
868 real x, y, z;
869 bool in_feet = false;
870 // Compass treats these fixed points as entrances
871 // ("distance from entrance" in a .DAT file counts from
872 // 0.0 at these points) so we do too.
873 name->sflags |= BIT(SFLAGS_FIXED) | BIT(SFLAGS_ENTRANCE);
874 nextch_handling_eol();
875 if (ch == 'F' || ch == 'f') {
876 in_feet = true;
877 nextch_handling_eol();
878 } else if (ch == 'M' || ch == 'm') {
879 nextch_handling_eol();
880 } else {
881 compile_diagnostic(DIAG_ERR, /*Expecting ā€œFā€ or ā€œMā€*/103);
883 while (!isdigit(ch) && ch != '+' && ch != '-' &&
884 ch != '.' && ch != ']' && ch != EOF) {
885 nextch_handling_eol();
887 x = read_numeric(false);
888 while (!isdigit(ch) && ch != '+' && ch != '-' &&
889 ch != '.' && ch != ']' && ch != EOF) {
890 nextch_handling_eol();
892 y = read_numeric(false);
893 while (!isdigit(ch) && ch != '+' && ch != '-' &&
894 ch != '.' && ch != ']' && ch != EOF) {
895 nextch_handling_eol();
897 z = read_numeric(false);
898 if (in_feet) {
899 x *= METRES_PER_FOOT;
900 y *= METRES_PER_FOOT;
901 z *= METRES_PER_FOOT;
903 stn = StnFromPfx(name);
904 if (!fixed(stn)) {
905 POS(stn, 0) = x;
906 POS(stn, 1) = y;
907 POS(stn, 2) = z;
908 fix(stn);
909 } else {
910 if (x != POS(stn, 0) || y != POS(stn, 1) ||
911 z != POS(stn, 2)) {
912 compile_diagnostic(DIAG_ERR, /*Station already fixed or equated to a fixed point*/46);
913 } else {
914 compile_diagnostic(DIAG_WARN, /*Station already fixed at the same coordinates*/55);
917 while (ch != ']' && ch != EOF) nextch_handling_eol();
918 if (ch == ']') {
919 nextch_handling_eol();
920 skipblanks();
922 } else {
923 /* FIXME: link station - ignore for now */
924 /* FIXME: perhaps issue warning? Other station names can be "reused", which is problematic... */
926 while (ch != ',' && ch != ';' && ch != EOF)
927 nextch_handling_eol();
930 break;
932 case '$':
933 /* UTM zone */
934 nextch();
935 skipblanks();
936 utm_zone = read_int(-60, 60);
937 skipblanks();
938 if (ch == ';') nextch_handling_eol();
940 update_proj_str:
941 if (!pcs->next || pcs->proj_str != pcs->next->proj_str)
942 osfree(pcs->proj_str);
943 pcs->proj_str = NULL;
944 if (datum && utm_zone && abs(utm_zone) <= 60) {
945 /* Set up coordinate system. */
946 char *proj_str = img_compass_utm_proj_str(datum, utm_zone);
947 if (proj_str) {
948 pcs->proj_str = proj_str;
949 if (!proj_str_out) {
950 proj_str_out = osstrdup(proj_str);
954 invalidate_pj_cached();
955 break;
956 case '&': {
957 /* Datum */
958 char *p = NULL;
959 int len = 0;
960 int datum_len = 0;
961 int c = 0;
962 nextch();
963 skipblanks();
964 while (ch != ';' && !isEol(ch)) {
965 s_catchar(&p, &len, (char)ch);
966 ++c;
967 /* Ignore trailing blanks. */
968 if (!isBlank(ch)) datum_len = c;
969 nextch();
971 if (ch == ';') nextch_handling_eol();
972 datum = img_parse_compass_datum_string(p, datum_len);
973 osfree(p);
974 goto update_proj_str;
976 case '[': {
977 // Enter subdirectory.
978 struct mak_folder *p = folder_stack;
979 folder_stack = osnew(struct mak_folder);
980 folder_stack->next = p;
981 folder_stack->len = strlen(path);
982 if (path[0])
983 s_catchar(&path, &path_len, FNM_SEP_LEV);
984 nextch();
985 while (ch != ';' && !isEol(ch)) {
986 if (ch == '\\') {
987 ch = FNM_SEP_LEV;
989 s_catchar(&path, &path_len, (char)ch);
990 nextch();
992 if (ch == ';') nextch_handling_eol();
993 break;
995 case ']': {
996 // Leave subdirectory.
997 struct mak_folder *p = folder_stack;
998 if (folder_stack == NULL) {
999 // FIXME: Report?
1000 break;
1002 path[folder_stack->len] = '\0';
1003 folder_stack = folder_stack->next;
1004 osfree(p);
1005 nextch();
1006 skipblanks();
1007 if (ch == ';') nextch_handling_eol();
1008 break;
1010 case '@': {
1011 /* "Base Location" to calculate magnetic declination at:
1012 * UTM East, UTM North, Elevation, UTM Zone, Convergence Angle
1013 * The first three are in metres.
1015 nextch();
1016 real easting = read_numeric(false);
1017 skipblanks();
1018 if (ch != ',') break;
1019 nextch();
1020 real northing = read_numeric(false);
1021 skipblanks();
1022 if (ch != ',') break;
1023 nextch();
1024 real elevation = read_numeric(false);
1025 skipblanks();
1026 if (ch != ',') break;
1027 nextch();
1028 int zone = read_int(-60, 60);
1029 skipblanks();
1030 if (ch != ',') break;
1031 nextch();
1032 real convergence_angle = read_numeric(false);
1033 /* We've now read them all successfully so store them. The Compass
1034 * documentation gives an example which specifies the datum *AFTER*
1035 * the base location, so we need to convert lazily.
1037 base_x = easting;
1038 base_y = northing;
1039 base_z = elevation;
1040 base_utm_zone = zone;
1041 base_line = file.line;
1042 base_lpos = file.lpos;
1043 // We ignore the stored UTM grid convergence angle since we get
1044 // this from PROJ.
1045 (void)convergence_angle;
1046 if (ch == ';') nextch_handling_eol();
1047 break;
1049 default:
1050 nextch_handling_eol();
1051 break;
1054 pop_settings();
1055 osfree(path);
1056 } else {
1057 while (ch != EOF && !ferror(file.fh)) {
1058 if (!process_non_data_line()) {
1059 f_export_ok = false;
1060 switch (pcs->style) {
1061 case STYLE_NORMAL:
1062 case STYLE_DIVING:
1063 case STYLE_CYLPOLAR:
1064 data_normal();
1065 break;
1066 case STYLE_CARTESIAN:
1067 data_cartesian();
1068 break;
1069 case STYLE_PASSAGE:
1070 data_passage();
1071 break;
1072 case STYLE_NOSURVEY:
1073 data_nosurvey();
1074 break;
1075 case STYLE_IGNORE:
1076 data_ignore();
1077 break;
1078 default:
1079 BUG("bad style");
1083 clear_last_leg();
1086 /* don't allow *BEGIN at the end of a file, then *EXPORT in the
1087 * including file */
1088 f_export_ok = false;
1090 if (pcs->begin_lineno) {
1091 error_in_file(file.filename, pcs->begin_lineno,
1092 /*BEGIN with no matching END in this file*/23);
1093 /* Implicitly close any unclosed BEGINs from this file */
1094 do {
1095 pop_settings();
1096 } while (pcs->begin_lineno);
1099 pcs->begin_lineno = begin_lineno_store;
1101 if (ferror(file.fh))
1102 fatalerror_in_file(file.filename, 0, /*Error reading file*/18);
1104 (void)fclose(file.fh);
1106 file = file_store;
1108 /* don't free this - it may be pointed to by prefix.file */
1109 /* osfree(file.filename); */
1112 static real
1113 mod2pi(real a)
1115 return a - floor(a / (2 * M_PI)) * (2 * M_PI);
1118 static real
1119 handle_plumb(clino_type *p_ctype)
1121 typedef enum {
1122 CLINO_NULL=-1, CLINO_UP, CLINO_DOWN, CLINO_LEVEL
1123 } clino_tok;
1124 static const sztok clino_tab[] = {
1125 {"D", CLINO_DOWN},
1126 {"DOWN", CLINO_DOWN},
1127 {"H", CLINO_LEVEL},
1128 {"LEVEL", CLINO_LEVEL},
1129 {"U", CLINO_UP},
1130 {"UP", CLINO_UP},
1131 {NULL, CLINO_NULL}
1133 static const real clinos[] = {(real)M_PI_2, (real)(-M_PI_2), (real)0.0};
1134 clino_tok tok;
1136 skipblanks();
1137 if (isalpha(ch)) {
1138 filepos fp;
1139 get_pos(&fp);
1140 get_token();
1141 tok = match_tok(clino_tab, TABSIZE(clino_tab));
1142 if (tok != CLINO_NULL) {
1143 *p_ctype = (tok == CLINO_LEVEL ? CTYPE_HORIZ : CTYPE_PLUMB);
1144 return clinos[tok];
1146 set_pos(&fp);
1147 } else if (isSign(ch)) {
1148 int chOld = ch;
1149 nextch();
1150 if (toupper(ch) == 'V') {
1151 nextch();
1152 *p_ctype = CTYPE_PLUMB;
1153 return (!isMinus(chOld) ? M_PI_2 : -M_PI_2);
1156 if (isOmit(chOld)) {
1157 *p_ctype = CTYPE_OMIT;
1158 /* no clino reading, so assume 0 with large sd */
1159 return (real)0.0;
1161 } else if (isOmit(ch)) {
1162 /* OMIT char may not be a SIGN char too so we need to check here as
1163 * well as above... */
1164 nextch();
1165 *p_ctype = CTYPE_OMIT;
1166 /* no clino reading, so assume 0 with large sd */
1167 return (real)0.0;
1169 return HUGE_REAL;
1172 static void
1173 warn_readings_differ(int msgno, real diff, int units)
1175 char buf[64];
1176 char *p;
1177 diff /= get_units_factor(units);
1178 sprintf(buf, "%.2f", fabs(diff));
1179 for (p = buf; *p; ++p) {
1180 if (*p == '.') {
1181 char *z = p;
1182 while (*++p) {
1183 if (*p != '0') z = p + 1;
1185 p = z;
1186 break;
1189 strcpy(p, get_units_string(units));
1190 compile_diagnostic(DIAG_WARN, msgno, buf);
1193 static bool
1194 handle_comp_units(void)
1196 bool fNoComp = true;
1197 if (VAL(Comp) != HUGE_REAL) {
1198 fNoComp = false;
1199 VAL(Comp) *= pcs->units[Q_BEARING];
1200 if (VAL(Comp) < (real)0.0 || VAL(Comp) - M_PI * 2.0 > EPSILON) {
1201 /* TRANSLATORS: Suspicious means something like 410 degrees or -20
1202 * degrees */
1203 compile_diagnostic_reading(DIAG_WARN, Comp, /*Suspicious compass reading*/59);
1204 VAL(Comp) = mod2pi(VAL(Comp));
1207 if (VAL(BackComp) != HUGE_REAL) {
1208 fNoComp = false;
1209 VAL(BackComp) *= pcs->units[Q_BACKBEARING];
1210 if (VAL(BackComp) < (real)0.0 || VAL(BackComp) - M_PI * 2.0 > EPSILON) {
1211 /* FIXME: different message for BackComp? */
1212 compile_diagnostic_reading(DIAG_WARN, BackComp, /*Suspicious compass reading*/59);
1213 VAL(BackComp) = mod2pi(VAL(BackComp));
1216 return fNoComp;
1219 static real compute_convergence(real lon, real lat) {
1220 // PROJ < 8.1.0 dereferences the context without a NULL check inside
1221 // proj_create_ellipsoidal_2D_cs() but PJ_DEFAULT_CTX is really just
1222 // NULL so for affected PROJ versions we create a context temporarily to
1223 // avoid a segmentation fault.
1224 PJ_CONTEXT * ctx = PJ_DEFAULT_CTX;
1225 #if PROJ_VERSION_MAJOR < 8 || \
1226 (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR < 1)
1227 ctx = proj_context_create();
1228 #endif
1230 if (!proj_str_out) {
1231 compile_diagnostic(DIAG_ERR, /*Output coordinate system not set*/488);
1232 return 0.0;
1234 PJ * pj = proj_create(ctx, proj_str_out);
1235 PJ_COORD lp;
1236 lp.lp.lam = lon;
1237 lp.lp.phi = lat;
1238 #if PROJ_VERSION_MAJOR < 8 || \
1239 (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR < 2)
1240 /* Code adapted from fix in PROJ 8.2.0 to make proj_factors() work in
1241 * cases we need (e.g. a CRS specified as "EPSG:<number>").
1243 switch (proj_get_type(pj)) {
1244 case PJ_TYPE_PROJECTED_CRS: {
1245 /* If it is a projected CRS, then compute the factors on the conversion
1246 * associated to it. We need to start from a temporary geographic CRS
1247 * using the same datum as the one of the projected CRS, and with
1248 * input coordinates being in longitude, latitude order in radian,
1249 * to be consistent with the expectations of the lp input parameter.
1252 PJ * geodetic_crs = proj_get_source_crs(ctx, pj);
1253 if (!geodetic_crs)
1254 break;
1255 PJ * datum = proj_crs_get_datum(ctx, geodetic_crs);
1256 #if PROJ_VERSION_MAJOR == 8 || \
1257 (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
1258 /* PROJ 7.2.0 upgraded to EPSG 10.x which added the concept
1259 * of a datum ensemble, and this version of PROJ also added
1260 * an API to deal with these.
1262 * If we're using PROJ < 7.2.0 then its EPSG database won't
1263 * have datum ensembles, so we don't need any code to handle
1264 * them.
1266 if (!datum) {
1267 datum = proj_crs_get_datum_ensemble(ctx, geodetic_crs);
1269 #endif
1270 PJ * cs = proj_create_ellipsoidal_2D_cs(
1271 ctx, PJ_ELLPS2D_LONGITUDE_LATITUDE, "Radian", 1.0);
1272 PJ * temp = proj_create_geographic_crs_from_datum(
1273 ctx, "unnamed crs", datum, cs);
1274 proj_destroy(datum);
1275 proj_destroy(cs);
1276 proj_destroy(geodetic_crs);
1277 PJ * newOp = proj_create_crs_to_crs_from_pj(ctx, temp, pj, NULL, NULL);
1278 proj_destroy(temp);
1279 if (newOp) {
1280 proj_destroy(pj);
1281 pj = newOp;
1283 break;
1285 default:
1286 break;
1288 #endif
1289 #if PROJ_VERSION_MAJOR < 9 || \
1290 (PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR < 3)
1291 if (pj) {
1292 /* In PROJ < 9.3.0 proj_factors() returns a grid convergence which is
1293 * off by 90Ā° for a projected coordinate system with northing/easting
1294 * axis order. We can't copy over the fix for this in PROJ 9.3.0's
1295 * proj_factors() since it uses non-public PROJ functions, but
1296 * normalising the output order here works too.
1298 PJ* pj_norm = proj_normalize_for_visualization(PJ_DEFAULT_CTX, pj);
1299 proj_destroy(pj);
1300 pj = pj_norm;
1302 #endif
1303 PJ_FACTORS factors = proj_factors(pj, lp);
1304 proj_destroy(pj);
1305 #if PROJ_VERSION_MAJOR < 8 || \
1306 (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR < 1)
1307 proj_context_destroy(ctx);
1308 #endif
1309 return factors.meridian_convergence;
1312 static real
1313 handle_compass(real *p_var)
1315 real compvar = VAR(Comp);
1316 real comp = VAL(Comp);
1317 real backcomp = VAL(BackComp);
1318 real declination;
1319 if (pcs->z[Q_DECLINATION] != HUGE_REAL) {
1320 declination = -pcs->z[Q_DECLINATION];
1321 } else if (pcs->declination != HUGE_REAL) {
1322 /* Cached value calculated for a previous compass reading taken on the
1323 * same date (by the 'else' just below).
1325 declination = pcs->declination;
1326 } else {
1327 if (!pcs->meta || pcs->meta->days1 == -1) {
1328 compile_diagnostic(DIAG_WARN, /*No survey date specified - using 0 for magnetic declination*/304);
1329 declination = 0;
1330 } else {
1331 int avg_days = (pcs->meta->days1 + pcs->meta->days2) / 2;
1332 double dat = julian_date_from_days_since_1900(avg_days);
1333 /* thgeomag() takes (lat, lon, h, dat) - i.e. (y, x, z, date). */
1334 declination = thgeomag(pcs->dec_lat, pcs->dec_lon, pcs->dec_alt, dat);
1335 if (declination < pcs->min_declination) {
1336 pcs->min_declination = declination;
1337 pcs->min_declination_days = avg_days;
1339 if (declination > pcs->max_declination) {
1340 pcs->max_declination = declination;
1341 pcs->max_declination_days = avg_days;
1344 if (pcs->convergence == HUGE_REAL) {
1345 /* Compute the convergence lazily. It only depends on the output
1346 * coordinate system so we can cache it for reuse to apply to
1347 * a declination value for a different date.
1349 pcs->convergence = compute_convergence(pcs->dec_lon, pcs->dec_lat);
1351 declination -= pcs->convergence;
1352 /* We cache the calculated declination as the calculation is relatively
1353 * expensive. We also cache an "assumed 0" answer so that we only
1354 * warn once per such survey rather than for every line with a compass
1355 * reading. */
1356 pcs->declination = declination;
1358 if (comp != HUGE_REAL) {
1359 comp = (comp - pcs->z[Q_BEARING]) * pcs->sc[Q_BEARING];
1360 comp += declination;
1362 if (backcomp != HUGE_REAL) {
1363 backcomp = (backcomp - pcs->z[Q_BACKBEARING])
1364 * pcs->sc[Q_BACKBEARING];
1365 backcomp += declination;
1366 backcomp -= M_PI;
1367 if (comp != HUGE_REAL) {
1368 real diff = comp - backcomp;
1369 real adj = fabs(diff) > M_PI ? M_PI : 0;
1370 diff -= floor((diff + M_PI) / (2 * M_PI)) * 2 * M_PI;
1371 if (sqrd(diff / 3.0) > compvar + VAR(BackComp)) {
1372 /* fore and back readings differ by more than 3 sds */
1373 /* TRANSLATORS: %s is replaced by the amount the readings disagree
1374 * by, e.g. "2.5Ā°" or "3įµ". */
1375 warn_readings_differ(/*COMPASS reading and BACKCOMPASS reading disagree by %s*/98,
1376 diff, get_angle_units(Q_BEARING));
1378 comp = (comp / compvar + backcomp / VAR(BackComp));
1379 compvar = (compvar + VAR(BackComp)) / 4;
1380 comp *= compvar;
1381 comp += adj;
1382 } else {
1383 comp = backcomp;
1384 compvar = VAR(BackComp);
1387 *p_var = compvar;
1388 return comp;
1391 static real
1392 handle_clino(q_quantity q, reading r, real val, bool percent, clino_type *p_ctype)
1394 bool range_0_180;
1395 real z;
1396 real diff_from_abs90;
1397 val *= pcs->units[q];
1398 /* percentage scale */
1399 if (percent) val = atan(val);
1400 /* We want to warn if there's a reading which it would be impossible
1401 * to have read from the instrument (e.g. on a -90 to 90 degree scale
1402 * you can't read "96" (it's probably a typo for "69"). However, the
1403 * gradient reading from a topofil is typically in the range 0 to 180,
1404 * with 90 being horizontal.
1406 * Really we should allow the valid range to be specified, but for now
1407 * we infer it from the zero error - if this is within 45 degrees of
1408 * 90 then we assume the range is 0 to 180.
1410 z = pcs->z[q];
1411 range_0_180 = (z > M_PI_4 && z < 3*M_PI_4);
1412 diff_from_abs90 = fabs(val) - M_PI_2;
1413 if (diff_from_abs90 > EPSILON) {
1414 if (!range_0_180) {
1415 int clino_units = get_angle_units(q);
1416 const char * units = get_units_string(clino_units);
1417 real right_angle = M_PI_2 / get_units_factor(clino_units);
1418 /* FIXME: different message for BackClino? */
1419 /* TRANSLATORS: %.f%s will be replaced with a right angle in the
1420 * units currently in use, e.g. "90Ā°" or "100įµ". And "absolute
1421 * value" means the reading ignoring the sign (so it might be
1422 * < -90Ā° or > 90Ā°. */
1423 compile_diagnostic_reading(DIAG_WARN, r, /*Clino reading over %.f%s (absolute value)*/51,
1424 right_angle, units);
1426 } else if (TSTBIT(pcs->infer, INFER_PLUMBS) &&
1427 diff_from_abs90 >= -EPSILON) {
1428 *p_ctype = CTYPE_INFERPLUMB;
1430 if (range_0_180 && *p_ctype != CTYPE_INFERPLUMB) {
1431 /* FIXME: Warning message not ideal... */
1432 if (val < 0.0 || val - M_PI > EPSILON) {
1433 int clino_units = get_angle_units(q);
1434 const char * units = get_units_string(clino_units);
1435 real right_angle = M_PI_2 / get_units_factor(clino_units);
1436 compile_diagnostic_reading(DIAG_WARN, r, /*Clino reading over %.f%s (absolute value)*/51,
1437 right_angle, units);
1440 return val;
1443 static int
1444 process_normal(prefix *fr, prefix *to, bool fToFirst,
1445 clino_type ctype, clino_type backctype)
1447 real tape = VAL(Tape);
1448 real clin = VAL(Clino);
1449 real backclin = VAL(BackClino);
1451 real dx, dy, dz;
1452 real vx, vy, vz;
1453 #ifndef NO_COVARIANCES
1454 real cxy, cyz, czx;
1455 #endif
1457 bool fNoComp;
1459 /* adjusted tape is negative -- probably the calibration is wrong */
1460 if (tape < (real)0.0) {
1461 /* TRANSLATE different message for topofil? */
1462 compile_diagnostic_reading(DIAG_WARN, Tape, /*Negative adjusted tape reading*/79);
1465 fNoComp = handle_comp_units();
1467 if (ctype == CTYPE_READING) {
1468 clin = handle_clino(Q_GRADIENT, Clino, clin,
1469 pcs->f_clino_percent, &ctype);
1472 if (backctype == CTYPE_READING) {
1473 backclin = handle_clino(Q_BACKGRADIENT, BackClino, backclin,
1474 pcs->f_backclino_percent, &backctype);
1477 /* un-infer the plumb if the backsight was just a reading */
1478 if (ctype == CTYPE_INFERPLUMB && backctype == CTYPE_READING) {
1479 ctype = CTYPE_READING;
1482 if (ctype != CTYPE_OMIT && backctype != CTYPE_OMIT && ctype != backctype) {
1483 /* TRANSLATORS: In data with backsights, the user has tried to give a
1484 * plumb for the foresight and a clino reading for the backsight, or
1485 * something similar. */
1486 compile_error_reading_skip(Clino, /*CLINO and BACKCLINO readings must be of the same type*/84);
1487 return 0;
1490 if (ctype == CTYPE_PLUMB || ctype == CTYPE_INFERPLUMB ||
1491 backctype == CTYPE_PLUMB || backctype == CTYPE_INFERPLUMB) {
1492 /* plumbed */
1493 if (!fNoComp) {
1494 if (ctype == CTYPE_PLUMB ||
1495 (ctype == CTYPE_INFERPLUMB && VAL(Comp) != 0.0) ||
1496 backctype == CTYPE_PLUMB ||
1497 (backctype == CTYPE_INFERPLUMB && VAL(BackComp) != 0.0)) {
1498 /* FIXME: Different message for BackComp? */
1499 /* TRANSLATORS: A "plumbed leg" is one measured using a plumbline
1500 * (a weight on a string). So the problem here is that the leg is
1501 * vertical, so a compass reading has no meaning! */
1502 compile_diagnostic(DIAG_WARN, /*Compass reading given on plumbed leg*/21);
1506 dx = dy = (real)0.0;
1507 if (ctype != CTYPE_OMIT) {
1508 if (backctype != CTYPE_OMIT && (clin > 0) == (backclin > 0)) {
1509 /* TRANSLATORS: We've been told the foresight and backsight are
1510 * both "UP", or that they're both "DOWN". */
1511 compile_error_reading_skip(Clino, /*Plumbed CLINO and BACKCLINO readings can't be in the same direction*/92);
1512 return 0;
1514 dz = (clin > (real)0.0) ? tape : -tape;
1515 } else {
1516 dz = (backclin < (real)0.0) ? tape : -tape;
1518 vx = vy = var(Q_POS) / 3.0 + dz * dz * var(Q_PLUMB);
1519 vz = var(Q_POS) / 3.0 + VAR(Tape);
1520 #ifndef NO_COVARIANCES
1521 /* Correct values - no covariances in this case! */
1522 cxy = cyz = czx = (real)0.0;
1523 #endif
1524 } else {
1525 /* Each of ctype and backctype are either CTYPE_READING/CTYPE_HORIZ
1526 * or CTYPE_OMIT */
1527 /* clino */
1528 real L2, cosG, LcosG, cosG2, sinB, cosB, dx2, dy2, dz2, v, V;
1529 if (fNoComp) {
1530 /* TRANSLATORS: Here "legs" are survey legs, i.e. measurements between
1531 * survey stations. */
1532 compile_error_reading_skip(Comp, /*Compass reading may not be omitted except on plumbed legs*/14);
1533 return 0;
1535 if (tape == (real)0.0) {
1536 dx = dy = dz = (real)0.0;
1537 vx = vy = vz = (real)(var(Q_POS) / 3.0); /* Position error only */
1538 #ifndef NO_COVARIANCES
1539 cxy = cyz = czx = (real)0.0;
1540 #endif
1541 #if DEBUG_DATAIN_1
1542 printf("Zero length leg: vx = %f, vy = %f, vz = %f\n", vx, vy, vz);
1543 #endif
1544 } else {
1545 real sinGcosG;
1546 /* take into account variance in LEVEL case */
1547 real var_clin = var(Q_LEVEL);
1548 real var_comp;
1549 real comp = handle_compass(&var_comp);
1550 /* ctype != CTYPE_READING is LEVEL case */
1551 if (ctype == CTYPE_READING) {
1552 clin = (clin - pcs->z[Q_GRADIENT]) * pcs->sc[Q_GRADIENT];
1553 var_clin = VAR(Clino);
1555 if (backctype == CTYPE_READING) {
1556 backclin = (backclin - pcs->z[Q_BACKGRADIENT])
1557 * pcs->sc[Q_BACKGRADIENT];
1558 if (ctype == CTYPE_READING) {
1559 if (sqrd((clin + backclin) / 3.0) > var_clin + VAR(BackClino)) {
1560 /* fore and back readings differ by more than 3 sds */
1561 /* TRANSLATORS: %s is replaced by the amount the readings disagree
1562 * by, e.g. "2.5Ā°" or "3įµ". */
1563 warn_readings_differ(/*CLINO reading and BACKCLINO reading disagree by %s*/99,
1564 clin + backclin, get_angle_units(Q_GRADIENT));
1566 clin = (clin / var_clin - backclin / VAR(BackClino));
1567 var_clin = (var_clin + VAR(BackClino)) / 4;
1568 clin *= var_clin;
1569 } else {
1570 clin = -backclin;
1571 var_clin = VAR(BackClino);
1575 #if DEBUG_DATAIN
1576 printf(" %4.2f %4.2f %4.2f\n", tape, comp, clin);
1577 #endif
1578 cosG = cos(clin);
1579 LcosG = tape * cosG;
1580 sinB = sin(comp);
1581 cosB = cos(comp);
1582 #if DEBUG_DATAIN_1
1583 printf("sinB = %f, cosG = %f, LcosG = %f\n", sinB, cosG, LcosG);
1584 #endif
1585 dx = LcosG * sinB;
1586 dy = LcosG * cosB;
1587 dz = tape * sin(clin);
1588 /* printf("%.2f\n",clin); */
1589 #if DEBUG_DATAIN_1
1590 printf("dx = %f\ndy = %f\ndz = %f\n", dx, dy, dz);
1591 #endif
1592 dx2 = dx * dx;
1593 L2 = tape * tape;
1594 V = VAR(Tape) / L2;
1595 dy2 = dy * dy;
1596 cosG2 = cosG * cosG;
1597 sinGcosG = sin(clin) * cosG;
1598 dz2 = dz * dz;
1599 v = dz2 * var_clin;
1600 #ifdef NO_COVARIANCES
1601 vx = (var(Q_POS) / 3.0 + dx2 * V + dy2 * var_comp +
1602 (.5 + sinB * sinB * cosG2) * v);
1603 vy = (var(Q_POS) / 3.0 + dy2 * V + dx2 * var_comp +
1604 (.5 + cosB * cosB * cosG2) * v);
1605 if (ctype == CTYPE_OMIT && backctype == CTYPE_OMIT) {
1606 /* if no clino, assume sd=tape/sqrt(10) so 3sds = .95*tape */
1607 vz = var(Q_POS) / 3.0 + L2 * (real)0.1;
1608 } else {
1609 vz = var(Q_POS) / 3.0 + dz2 * V + L2 * cosG2 * var_clin;
1611 /* for Surveyor87 errors: vx=vy=vz=var(Q_POS)/3.0; */
1612 #else
1613 vx = var(Q_POS) / 3.0 + dx2 * V + dy2 * var_comp +
1614 (sinB * sinB * v);
1615 vy = var(Q_POS) / 3.0 + dy2 * V + dx2 * var_comp +
1616 (cosB * cosB * v);
1617 if (ctype == CTYPE_OMIT && backctype == CTYPE_OMIT) {
1618 /* if no clino, assume sd=tape/sqrt(10) so 3sds = .95*tape */
1619 vz = var(Q_POS) / 3.0 + L2 * (real)0.1;
1620 } else {
1621 vz = var(Q_POS) / 3.0 + dz2 * V + L2 * cosG2 * var_clin;
1623 /* usual covariance formulae are fine in no clino case since
1624 * dz = 0 so value of var_clin is ignored */
1625 cxy = sinB * cosB * (VAR(Tape) * cosG2 + var_clin * dz2)
1626 - var_comp * dx * dy;
1627 czx = VAR(Tape) * sinB * sinGcosG - var_clin * dx * dz;
1628 cyz = VAR(Tape) * cosB * sinGcosG - var_clin * dy * dz;
1629 #if 0
1630 printf("vx = %6.3f, vy = %6.3f, vz = %6.3f\n", vx, vy, vz);
1631 printf("cxy = %6.3f, cyz = %6.3f, czx = %6.3f\n", cxy, cyz, czx);
1632 #endif
1633 #endif
1634 #if DEBUG_DATAIN_1
1635 printf("In DATAIN.C, vx = %f, vy = %f, vz = %f\n", vx, vy, vz);
1636 #endif
1639 #if DEBUG_DATAIN_1
1640 printf("Just before addleg, vx = %f\n", vx);
1641 #endif
1642 /*printf("dx,dy,dz = %.2f %.2f %.2f\n\n", dx, dy, dz);*/
1643 addlegbyname(fr, to, fToFirst, dx, dy, dz, vx, vy, vz
1644 #ifndef NO_COVARIANCES
1645 , cyz, czx, cxy
1646 #endif
1648 return 1;
1651 static int
1652 process_diving(prefix *fr, prefix *to, bool fToFirst, bool fDepthChange)
1654 real tape = VAL(Tape);
1656 real dx, dy, dz;
1657 real vx, vy, vz;
1658 #ifndef NO_COVARIANCES
1659 real cxy = 0, cyz = 0, czx = 0;
1660 #endif
1662 handle_comp_units();
1664 /* depth gauge readings increase upwards with default calibration */
1665 if (fDepthChange) {
1666 SVX_ASSERT(VAL(FrDepth) == 0.0);
1667 dz = VAL(ToDepth) * pcs->units[Q_DEPTH] - pcs->z[Q_DEPTH];
1668 dz *= pcs->sc[Q_DEPTH];
1669 } else {
1670 dz = VAL(ToDepth) - VAL(FrDepth);
1671 dz *= pcs->units[Q_DEPTH] * pcs->sc[Q_DEPTH];
1674 /* adjusted tape is negative -- probably the calibration is wrong */
1675 if (tape < (real)0.0) {
1676 compile_diagnostic(DIAG_WARN, /*Negative adjusted tape reading*/79);
1679 /* check if tape is less than depth change */
1680 if (tape < fabs(dz)) {
1681 /* FIXME: allow margin of error based on variances? */
1682 /* TRANSLATORS: This means that the data fed in said this.
1684 * It could be a gross error (e.g. the decimal point is missing from the
1685 * depth gauge reading) or it could just be due to random error on a near
1686 * vertical leg */
1687 compile_diagnostic(DIAG_WARN, /*Tape reading is less than change in depth*/62);
1690 if (tape == (real)0.0 && dz == 0.0) {
1691 dx = dy = dz = (real)0.0;
1692 vx = vy = vz = (real)(var(Q_POS) / 3.0); /* Position error only */
1693 } else if (VAL(Comp) == HUGE_REAL &&
1694 VAL(BackComp) == HUGE_REAL) {
1695 /* plumb */
1696 dx = dy = (real)0.0;
1697 if (dz < 0) tape = -tape;
1698 /* FIXME: Should use FrDepth sometimes... */
1699 dz = (dz * VAR(Tape) + tape * 2 * VAR(ToDepth))
1700 / (VAR(Tape) * 2 * VAR(ToDepth));
1701 vx = vy = var(Q_POS) / 3.0 + dz * dz * var(Q_PLUMB);
1702 /* FIXME: Should use FrDepth sometimes... */
1703 vz = var(Q_POS) / 3.0 + VAR(Tape) * 2 * VAR(ToDepth)
1704 / (VAR(Tape) + VAR(ToDepth));
1705 } else {
1706 real L2, sinB, cosB, dz2, D2;
1707 real var_comp;
1708 real comp = handle_compass(&var_comp);
1709 sinB = sin(comp);
1710 cosB = cos(comp);
1711 L2 = tape * tape;
1712 dz2 = dz * dz;
1713 D2 = L2 - dz2;
1714 if (D2 <= (real)0.0) {
1715 /* FIXME: Should use FrDepth sometimes... */
1716 real vsum = VAR(Tape) + 2 * VAR(ToDepth);
1717 dx = dy = (real)0.0;
1718 vx = vy = var(Q_POS) / 3.0;
1719 /* FIXME: Should use FrDepth sometimes... */
1720 vz = var(Q_POS) / 3.0 + VAR(Tape) * 2 * VAR(ToDepth) / vsum;
1721 if (dz > 0) {
1722 /* FIXME: Should use FrDepth sometimes... */
1723 dz = (dz * VAR(Tape) + tape * 2 * VAR(ToDepth)) / vsum;
1724 } else {
1725 dz = (dz * VAR(Tape) - tape * 2 * VAR(ToDepth)) / vsum;
1727 } else {
1728 real D = sqrt(D2);
1729 /* FIXME: Should use FrDepth sometimes... */
1730 real F = VAR(Tape) * L2 + 2 * VAR(ToDepth) * D2;
1731 dx = D * sinB;
1732 dy = D * cosB;
1734 vx = var(Q_POS) / 3.0 +
1735 sinB * sinB * F / D2 + var_comp * dy * dy;
1736 vy = var(Q_POS) / 3.0 +
1737 cosB * cosB * F / D2 + var_comp * dx * dx;
1738 /* FIXME: Should use FrDepth sometimes... */
1739 vz = var(Q_POS) / 3.0 + 2 * VAR(ToDepth);
1741 #ifndef NO_COVARIANCES
1742 cxy = sinB * cosB * (F / D2 + var_comp * D2);
1743 /* FIXME: Should use FrDepth sometimes... */
1744 cyz = -2 * VAR(ToDepth) * dy / D;
1745 czx = -2 * VAR(ToDepth) * dx / D;
1746 #endif
1748 /* FIXME: If there's a clino reading, check it against the depth reading,
1749 * and average.
1750 * if (VAL(Clino) != HUGE_REAL || VAL(BackClino) != HUGE_REAL) { ... }
1753 addlegbyname(fr, to, fToFirst, dx, dy, dz, vx, vy, vz
1754 #ifndef NO_COVARIANCES
1755 , cxy, cyz, czx
1756 #endif
1758 return 1;
1761 static int
1762 process_cartesian(prefix *fr, prefix *to, bool fToFirst)
1764 real dx = (VAL(Dx) * pcs->units[Q_DX] - pcs->z[Q_DX]) * pcs->sc[Q_DX];
1765 real dy = (VAL(Dy) * pcs->units[Q_DY] - pcs->z[Q_DY]) * pcs->sc[Q_DY];
1766 real dz = (VAL(Dz) * pcs->units[Q_DZ] - pcs->z[Q_DZ]) * pcs->sc[Q_DZ];
1768 addlegbyname(fr, to, fToFirst, dx, dy, dz, VAR(Dx), VAR(Dy), VAR(Dz)
1769 #ifndef NO_COVARIANCES
1770 , 0, 0, 0
1771 #endif
1773 return 1;
1776 static void
1777 data_cartesian(void)
1779 prefix *fr = NULL, *to = NULL;
1781 bool fMulti = false;
1783 reading first_stn = End;
1785 const reading *ordering;
1787 again:
1789 for (ordering = pcs->ordering ; ; ordering++) {
1790 skipblanks();
1791 switch (*ordering) {
1792 case Fr:
1793 fr = read_prefix(PFX_STATION|PFX_ALLOW_ROOT);
1794 if (first_stn == End) first_stn = Fr;
1795 break;
1796 case To:
1797 to = read_prefix(PFX_STATION|PFX_ALLOW_ROOT);
1798 if (first_stn == End) first_stn = To;
1799 break;
1800 case Station:
1801 fr = to;
1802 to = read_prefix(PFX_STATION);
1803 first_stn = To;
1804 break;
1805 case Dx: case Dy: case Dz:
1806 read_reading(*ordering, false);
1807 break;
1808 case Ignore:
1809 skipword(); break;
1810 case IgnoreAllAndNewLine:
1811 skipline();
1812 /* fall through */
1813 case Newline:
1814 if (fr != NULL) {
1815 if (!process_cartesian(fr, to, first_stn == To))
1816 skipline();
1818 fMulti = true;
1819 while (1) {
1820 process_eol();
1821 skipblanks();
1822 if (isData(ch)) break;
1823 if (!isComm(ch)) {
1824 return;
1827 break;
1828 case IgnoreAll:
1829 skipline();
1830 /* fall through */
1831 case End:
1832 if (!fMulti) {
1833 process_cartesian(fr, to, first_stn == To);
1834 process_eol();
1835 return;
1837 do {
1838 process_eol();
1839 skipblanks();
1840 } while (isComm(ch));
1841 goto again;
1842 default: BUG("Unknown reading in ordering");
1847 static int
1848 process_cylpolar(prefix *fr, prefix *to, bool fToFirst, bool fDepthChange)
1850 real tape = VAL(Tape);
1852 real dx, dy, dz;
1853 real vx, vy, vz;
1854 #ifndef NO_COVARIANCES
1855 real cxy = 0;
1856 #endif
1858 handle_comp_units();
1860 /* depth gauge readings increase upwards with default calibration */
1861 if (fDepthChange) {
1862 SVX_ASSERT(VAL(FrDepth) == 0.0);
1863 dz = VAL(ToDepth) * pcs->units[Q_DEPTH] - pcs->z[Q_DEPTH];
1864 dz *= pcs->sc[Q_DEPTH];
1865 } else {
1866 dz = VAL(ToDepth) - VAL(FrDepth);
1867 dz *= pcs->units[Q_DEPTH] * pcs->sc[Q_DEPTH];
1870 /* adjusted tape is negative -- probably the calibration is wrong */
1871 if (tape < (real)0.0) {
1872 compile_diagnostic(DIAG_WARN, /*Negative adjusted tape reading*/79);
1875 if (VAL(Comp) == HUGE_REAL && VAL(BackComp) == HUGE_REAL) {
1876 /* plumb */
1877 dx = dy = (real)0.0;
1878 vx = vy = var(Q_POS) / 3.0 + dz * dz * var(Q_PLUMB);
1879 /* FIXME: Should use FrDepth sometimes... */
1880 vz = var(Q_POS) / 3.0 + 2 * VAR(ToDepth);
1881 } else {
1882 real sinB, cosB;
1883 real var_comp;
1884 real comp = handle_compass(&var_comp);
1885 sinB = sin(comp);
1886 cosB = cos(comp);
1888 dx = tape * sinB;
1889 dy = tape * cosB;
1891 vx = var(Q_POS) / 3.0 +
1892 VAR(Tape) * sinB * sinB + var_comp * dy * dy;
1893 vy = var(Q_POS) / 3.0 +
1894 VAR(Tape) * cosB * cosB + var_comp * dx * dx;
1895 /* FIXME: Should use FrDepth sometimes... */
1896 vz = var(Q_POS) / 3.0 + 2 * VAR(ToDepth);
1898 #ifndef NO_COVARIANCES
1899 cxy = (VAR(Tape) - var_comp * tape * tape) * sinB * cosB;
1900 #endif
1902 addlegbyname(fr, to, fToFirst, dx, dy, dz, vx, vy, vz
1903 #ifndef NO_COVARIANCES
1904 , cxy, 0, 0
1905 #endif
1907 return 1;
1910 /* Process tape/compass/clino, diving, and cylpolar styles of survey data
1911 * Also handles topofil (fromcount/tocount or count) in place of tape */
1912 static void
1913 data_normal(void)
1915 prefix *fr = NULL, *to = NULL;
1916 reading first_stn = End;
1918 bool fTopofil = false, fMulti = false;
1919 bool fRev;
1920 clino_type ctype, backctype;
1921 bool fDepthChange;
1922 unsigned long compass_dat_flags = 0;
1924 const reading *ordering;
1926 VAL(Tape) = VAL(BackTape) = HUGE_REAL;
1927 VAL(Comp) = VAL(BackComp) = HUGE_REAL;
1928 VAL(FrCount) = VAL(ToCount) = 0;
1929 VAL(FrDepth) = VAL(ToDepth) = 0;
1930 VAL(Left) = VAL(Right) = VAL(Up) = VAL(Down) = HUGE_REAL;
1932 fRev = false;
1933 ctype = backctype = CTYPE_OMIT;
1934 fDepthChange = false;
1936 /* ordering may omit clino reading, so set up default here */
1937 /* this is also used if clino reading is the omit character */
1938 VAL(Clino) = VAL(BackClino) = 0;
1940 again:
1942 /* We clear these flags in the normal course of events, but if there's an
1943 * error in a reading, we might not, so make sure it has been cleared here.
1945 pcs->flags &= ~(BIT(FLAGS_ANON_ONE_END) | BIT(FLAGS_IMPLICIT_SPLAY));
1946 for (ordering = pcs->ordering; ; ordering++) {
1947 skipblanks();
1948 switch (*ordering) {
1949 case Fr:
1950 fr = read_prefix(PFX_STATION|PFX_ALLOW_ROOT|PFX_ANON);
1951 if (first_stn == End) first_stn = Fr;
1952 break;
1953 case To:
1954 to = read_prefix(PFX_STATION|PFX_ALLOW_ROOT|PFX_ANON);
1955 if (first_stn == End) first_stn = To;
1956 break;
1957 case Station:
1958 fr = to;
1959 to = read_prefix(PFX_STATION);
1960 first_stn = To;
1961 break;
1962 case Dir: {
1963 typedef enum {
1964 DIR_NULL=-1, DIR_FORE, DIR_BACK
1965 } dir_tok;
1966 static const sztok dir_tab[] = {
1967 {"B", DIR_BACK},
1968 {"F", DIR_FORE},
1970 dir_tok tok;
1971 get_token();
1972 tok = match_tok(dir_tab, TABSIZE(dir_tab));
1973 switch (tok) {
1974 case DIR_FORE:
1975 break;
1976 case DIR_BACK:
1977 fRev = true;
1978 break;
1979 default:
1980 compile_diagnostic(DIAG_ERR|DIAG_BUF|DIAG_SKIP, /*Found ā€œ%sā€, expecting ā€œFā€ or ā€œBā€*/131, buffer);
1981 process_eol();
1982 return;
1984 break;
1986 case Tape: case BackTape: {
1987 reading r = *ordering;
1988 read_reading(r, true);
1989 if (VAL(r) == HUGE_REAL) {
1990 if (!isOmit(ch)) {
1991 compile_diagnostic_token_show(DIAG_ERR, /*Expecting numeric field, found ā€œ%sā€*/9);
1992 /* Avoid also warning about omitted tape reading. */
1993 VAL(r) = 0;
1994 } else {
1995 nextch();
1997 } else if (VAL(r) < (real)0.0) {
1998 compile_diagnostic_reading(DIAG_WARN, r, /*Negative tape reading*/60);
2000 break;
2002 case Count:
2003 VAL(FrCount) = VAL(ToCount);
2004 LOC(FrCount) = LOC(ToCount);
2005 WID(FrCount) = WID(ToCount);
2006 read_reading(ToCount, false);
2007 fTopofil = true;
2008 break;
2009 case FrCount:
2010 read_reading(FrCount, false);
2011 break;
2012 case ToCount:
2013 read_reading(ToCount, false);
2014 fTopofil = true;
2015 break;
2016 case Comp: case BackComp:
2017 read_bearing_or_omit(*ordering);
2018 break;
2019 case Clino: case BackClino: {
2020 reading r = *ordering;
2021 clino_type * p_ctype = (r == Clino ? &ctype : &backctype);
2022 read_reading(r, true);
2023 if (VAL(r) == HUGE_REAL) {
2024 VAL(r) = handle_plumb(p_ctype);
2025 if (VAL(r) != HUGE_REAL) break;
2026 compile_diagnostic_token_show(DIAG_ERR, /*Expecting numeric field, found ā€œ%sā€*/9);
2027 skipline();
2028 process_eol();
2029 return;
2031 *p_ctype = CTYPE_READING;
2032 break;
2034 case FrDepth: case ToDepth:
2035 read_reading(*ordering, false);
2036 break;
2037 case Depth:
2038 VAL(FrDepth) = VAL(ToDepth);
2039 LOC(FrDepth) = LOC(ToDepth);
2040 WID(FrDepth) = WID(ToDepth);
2041 read_reading(ToDepth, false);
2042 break;
2043 case DepthChange:
2044 fDepthChange = true;
2045 VAL(FrDepth) = 0;
2046 read_reading(ToDepth, false);
2047 break;
2048 case CompassDATComp:
2049 read_bearing_or_omit(Comp);
2050 if (is_compass_NaN(VAL(Comp))) VAL(Comp) = HUGE_REAL;
2051 break;
2052 case CompassDATBackComp:
2053 read_bearing_or_omit(BackComp);
2054 if (is_compass_NaN(VAL(BackComp))) VAL(BackComp) = HUGE_REAL;
2055 break;
2056 case CompassDATClino: case CompassDATBackClino: {
2057 reading r;
2058 clino_type * p_ctype;
2059 if (*ordering == CompassDATClino) {
2060 r = Clino;
2061 p_ctype = &ctype;
2062 } else {
2063 r = BackClino;
2064 p_ctype = &backctype;
2066 read_reading(r, false);
2067 if (is_compass_NaN(VAL(r))) {
2068 VAL(r) = HUGE_REAL;
2069 *p_ctype = CTYPE_OMIT;
2070 } else {
2071 *p_ctype = CTYPE_READING;
2073 break;
2075 case CompassDATLeft: case CompassDATRight:
2076 case CompassDATUp: case CompassDATDown: {
2077 /* FIXME: need to actually make use of these entries! */
2078 reading actual = Left + (*ordering - CompassDATLeft);
2079 read_reading(actual, false);
2080 if (VAL(actual) < 0) VAL(actual) = HUGE_REAL;
2081 break;
2083 case CompassDATFlags:
2084 if (ch == '#') {
2085 filepos fp;
2086 get_pos(&fp);
2087 nextch();
2088 if (ch == '|') {
2089 nextch();
2090 while (ch >= 'A' && ch <= 'Z') {
2091 compass_dat_flags |= BIT(ch - 'A');
2092 /* Flags we handle:
2093 * L (exclude from length)
2094 * S (splay)
2095 * P (no plot) (mapped to FLAG_SURFACE)
2096 * X (exclude data)
2097 * FIXME: Defined flags we currently ignore:
2098 * C (no adjustment) (set all (co)variances to 0? Then
2099 * we need to handle a loop of such legs or a traverse
2100 * of such legs between two fixed points...)
2102 nextch();
2104 if (ch == '#') {
2105 nextch();
2106 } else {
2107 compass_dat_flags = 0;
2108 set_pos(&fp);
2110 } else {
2111 set_pos(&fp);
2114 break;
2115 case Ignore:
2116 skipword(); break;
2117 case IgnoreAllAndNewLine:
2118 skipline();
2119 /* fall through */
2120 case Newline:
2121 if (fr != NULL) {
2122 int r;
2123 int save_flags;
2124 int implicit_splay;
2125 if (fTopofil) {
2126 VAL(Tape) = VAL(ToCount) - VAL(FrCount);
2127 LOC(Tape) = LOC(ToCount);
2128 WID(Tape) = WID(ToCount);
2130 /* Note: frdepth == todepth test works regardless of fDepthChange
2131 * (frdepth always zero, todepth is change of depth) and also
2132 * works for STYLE_NORMAL (both remain 0) */
2133 if (TSTBIT(pcs->infer, INFER_EQUATES) &&
2134 (VAL(Tape) == (real)0.0 || VAL(Tape) == HUGE_REAL) &&
2135 (VAL(BackTape) == (real)0.0 || VAL(BackTape) == HUGE_REAL) &&
2136 VAL(FrDepth) == VAL(ToDepth)) {
2137 if (!TSTBIT(pcs->infer, INFER_EQUATES_SELF_OK) || fr != to)
2138 process_equate(fr, to);
2139 goto inferred_equate;
2141 if (fRev) {
2142 prefix *t = fr;
2143 fr = to;
2144 to = t;
2146 if (fTopofil) {
2147 VAL(Tape) *= pcs->units[Q_COUNT] * pcs->sc[Q_COUNT];
2148 } else if (VAL(Tape) != HUGE_REAL) {
2149 VAL(Tape) *= pcs->units[Q_LENGTH];
2150 VAL(Tape) -= pcs->z[Q_LENGTH];
2151 VAL(Tape) *= pcs->sc[Q_LENGTH];
2153 if (VAL(BackTape) != HUGE_REAL) {
2154 VAL(BackTape) *= pcs->units[Q_BACKLENGTH];
2155 VAL(BackTape) -= pcs->z[Q_BACKLENGTH];
2156 VAL(BackTape) *= pcs->sc[Q_BACKLENGTH];
2157 if (VAL(Tape) != HUGE_REAL) {
2158 real diff = VAL(Tape) - VAL(BackTape);
2159 if (sqrd(diff / 3.0) > VAR(Tape) + VAR(BackTape)) {
2160 /* fore and back readings differ by more than 3 sds */
2161 /* TRANSLATORS: %s is replaced by the amount the readings disagree
2162 * by, e.g. "0.12m" or "0.2ft". */
2163 warn_readings_differ(/*TAPE reading and BACKTAPE reading disagree by %s*/97,
2164 diff, get_length_units(Q_LENGTH));
2166 VAL(Tape) = VAL(Tape) / VAR(Tape) + VAL(BackTape) / VAR(BackTape);
2167 VAR(Tape) = (VAR(Tape) + VAR(BackTape)) / 4;
2168 VAL(Tape) *= VAR(Tape);
2169 } else {
2170 VAL(Tape) = VAL(BackTape);
2171 VAR(Tape) = VAR(BackTape);
2173 } else if (VAL(Tape) == HUGE_REAL) {
2174 compile_diagnostic_reading(DIAG_ERR, Tape, /*Tape reading may not be omitted*/94);
2175 goto inferred_equate;
2177 implicit_splay = TSTBIT(pcs->flags, FLAGS_IMPLICIT_SPLAY);
2178 pcs->flags &= ~(BIT(FLAGS_ANON_ONE_END) | BIT(FLAGS_IMPLICIT_SPLAY));
2179 save_flags = pcs->flags;
2180 if (implicit_splay) {
2181 pcs->flags |= BIT(FLAGS_SPLAY);
2183 switch (pcs->style) {
2184 case STYLE_NORMAL:
2185 r = process_normal(fr, to, (first_stn == To) ^ fRev,
2186 ctype, backctype);
2187 break;
2188 case STYLE_DIVING:
2189 /* FIXME: Handle any clino readings */
2190 r = process_diving(fr, to, (first_stn == To) ^ fRev,
2191 fDepthChange);
2192 break;
2193 case STYLE_CYLPOLAR:
2194 r = process_cylpolar(fr, to, (first_stn == To) ^ fRev,
2195 fDepthChange);
2196 break;
2197 default:
2198 r = 0; /* avoid warning */
2199 BUG("bad style");
2201 pcs->flags = save_flags;
2202 if (!r) skipline();
2204 /* Swap fr and to back to how they were for next line */
2205 if (fRev) {
2206 prefix *t = fr;
2207 fr = to;
2208 to = t;
2212 fRev = false;
2213 ctype = backctype = CTYPE_OMIT;
2214 fDepthChange = false;
2216 /* ordering may omit clino reading, so set up default here */
2217 /* this is also used if clino reading is the omit character */
2218 VAL(Clino) = VAL(BackClino) = 0;
2219 LOC(Clino) = LOC(BackClino) = -1;
2220 WID(Clino) = WID(BackClino) = 0;
2222 inferred_equate:
2224 fMulti = true;
2225 while (1) {
2226 process_eol();
2227 skipblanks();
2228 if (isData(ch)) break;
2229 if (!isComm(ch)) {
2230 return;
2233 break;
2234 case IgnoreAll:
2235 skipline();
2236 /* fall through */
2237 case End:
2238 if (!fMulti) {
2239 int save_flags;
2240 int implicit_splay;
2241 /* Compass ignore flag is 'X' */
2242 if ((compass_dat_flags & BIT('X' - 'A'))) {
2243 process_eol();
2244 return;
2246 if (fRev) {
2247 prefix *t = fr;
2248 fr = to;
2249 to = t;
2251 if (fTopofil) {
2252 VAL(Tape) = VAL(ToCount) - VAL(FrCount);
2253 LOC(Tape) = LOC(ToCount);
2254 WID(Tape) = WID(ToCount);
2256 /* Note: frdepth == todepth test works regardless of fDepthChange
2257 * (frdepth always zero, todepth is change of depth) and also
2258 * works for STYLE_NORMAL (both remain 0) */
2259 if (TSTBIT(pcs->infer, INFER_EQUATES) &&
2260 (VAL(Tape) == (real)0.0 || VAL(Tape) == HUGE_REAL) &&
2261 (VAL(BackTape) == (real)0.0 || VAL(BackTape) == HUGE_REAL) &&
2262 VAL(FrDepth) == VAL(ToDepth)) {
2263 if (!TSTBIT(pcs->infer, INFER_EQUATES_SELF_OK) || fr != to)
2264 process_equate(fr, to);
2265 process_eol();
2266 return;
2268 if (fTopofil) {
2269 VAL(Tape) *= pcs->units[Q_COUNT] * pcs->sc[Q_COUNT];
2270 } else if (VAL(Tape) != HUGE_REAL) {
2271 VAL(Tape) *= pcs->units[Q_LENGTH];
2272 VAL(Tape) -= pcs->z[Q_LENGTH];
2273 VAL(Tape) *= pcs->sc[Q_LENGTH];
2275 if (VAL(BackTape) != HUGE_REAL) {
2276 VAL(BackTape) *= pcs->units[Q_BACKLENGTH];
2277 VAL(BackTape) -= pcs->z[Q_BACKLENGTH];
2278 VAL(BackTape) *= pcs->sc[Q_BACKLENGTH];
2279 if (VAL(Tape) != HUGE_REAL) {
2280 real diff = VAL(Tape) - VAL(BackTape);
2281 if (sqrd(diff / 3.0) > VAR(Tape) + VAR(BackTape)) {
2282 /* fore and back readings differ by more than 3 sds */
2283 /* TRANSLATORS: %s is replaced by the amount the readings disagree
2284 * by, e.g. "0.12m" or "0.2ft". */
2285 warn_readings_differ(/*TAPE reading and BACKTAPE reading disagree by %s*/97,
2286 diff, get_length_units(Q_LENGTH));
2288 VAL(Tape) = VAL(Tape) / VAR(Tape) + VAL(BackTape) / VAR(BackTape);
2289 VAR(Tape) = (VAR(Tape) + VAR(BackTape)) / 4;
2290 VAL(Tape) *= VAR(Tape);
2291 } else {
2292 VAL(Tape) = VAL(BackTape);
2293 VAR(Tape) = VAR(BackTape);
2295 } else if (VAL(Tape) == HUGE_REAL) {
2296 compile_diagnostic_reading(DIAG_ERR, Tape, /*Tape reading may not be omitted*/94);
2297 process_eol();
2298 return;
2300 implicit_splay = TSTBIT(pcs->flags, FLAGS_IMPLICIT_SPLAY);
2301 pcs->flags &= ~(BIT(FLAGS_ANON_ONE_END) | BIT(FLAGS_IMPLICIT_SPLAY));
2302 save_flags = pcs->flags;
2303 if (implicit_splay) {
2304 pcs->flags |= BIT(FLAGS_SPLAY);
2306 if ((compass_dat_flags & BIT('S' - 'A'))) {
2307 /* 'S' means "splay". It's currently only documented as part
2308 * of the PLT file format, but the flags are the same and so
2309 * it seems it must be supported in DAT files too.
2311 pcs->flags |= BIT(FLAGS_SPLAY);
2313 if ((compass_dat_flags & BIT('P' - 'A'))) {
2314 /* 'P' means "Exclude this shot from plotting", but the use
2315 * suggested in the Compass docs is for surface data, and legs
2316 * with this flag "[do] not support passage modeling".
2318 * Even if it's actually being used for a different
2319 * purpose, Survex programs don't show surface legs
2320 * by default so FLAGS_SURFACE matches fairly well.
2322 pcs->flags |= BIT(FLAGS_SURFACE);
2324 if ((compass_dat_flags & BIT('L' - 'A'))) {
2325 /* 'L' means "exclude from length" - map this to Survex's
2326 * FLAGS_DUPLICATE. */
2327 pcs->flags |= BIT(FLAGS_DUPLICATE);
2329 switch (pcs->style) {
2330 case STYLE_NORMAL:
2331 process_normal(fr, to, (first_stn == To) ^ fRev,
2332 ctype, backctype);
2333 break;
2334 case STYLE_DIVING:
2335 /* FIXME: Handle any clino readings */
2336 process_diving(fr, to, (first_stn == To) ^ fRev,
2337 fDepthChange);
2338 break;
2339 case STYLE_CYLPOLAR:
2340 process_cylpolar(fr, to, (first_stn == To) ^ fRev,
2341 fDepthChange);
2342 break;
2343 default:
2344 BUG("bad style");
2346 pcs->flags = save_flags;
2348 process_eol();
2349 return;
2351 do {
2352 process_eol();
2353 skipblanks();
2354 } while (isComm(ch));
2355 goto again;
2356 default:
2357 BUG("Unknown reading in ordering");
2362 static int
2363 process_lrud(prefix *stn)
2365 SVX_ASSERT(next_lrud);
2366 lrud * xsect = osnew(lrud);
2367 xsect->stn = stn;
2368 xsect->l = (VAL(Left) * pcs->units[Q_LEFT] - pcs->z[Q_LEFT]) * pcs->sc[Q_LEFT];
2369 xsect->r = (VAL(Right) * pcs->units[Q_RIGHT] - pcs->z[Q_RIGHT]) * pcs->sc[Q_RIGHT];
2370 xsect->u = (VAL(Up) * pcs->units[Q_UP] - pcs->z[Q_UP]) * pcs->sc[Q_UP];
2371 xsect->d = (VAL(Down) * pcs->units[Q_DOWN] - pcs->z[Q_DOWN]) * pcs->sc[Q_DOWN];
2372 xsect->meta = pcs->meta;
2373 if (pcs->meta) ++pcs->meta->ref_count;
2374 xsect->next = NULL;
2375 *next_lrud = xsect;
2376 next_lrud = &(xsect->next);
2378 return 1;
2381 static void
2382 data_passage(void)
2384 prefix *stn = NULL;
2385 const reading *ordering;
2387 for (ordering = pcs->ordering ; ; ordering++) {
2388 skipblanks();
2389 switch (*ordering) {
2390 case Station:
2391 stn = read_prefix(PFX_STATION);
2392 break;
2393 case Left: case Right: case Up: case Down: {
2394 reading r = *ordering;
2395 read_reading(r, true);
2396 if (VAL(r) == HUGE_REAL) {
2397 if (!isOmit(ch)) {
2398 compile_diagnostic_token_show(DIAG_ERR, /*Expecting numeric field, found ā€œ%sā€*/9);
2399 } else {
2400 nextch();
2402 VAL(r) = -1;
2404 break;
2406 case Ignore:
2407 skipword(); break;
2408 case IgnoreAll:
2409 skipline();
2410 /* fall through */
2411 case End: {
2412 process_lrud(stn);
2413 process_eol();
2414 return;
2416 default: BUG("Unknown reading in ordering");
2421 static int
2422 process_nosurvey(prefix *fr, prefix *to, bool fToFirst)
2424 nosurveylink *link;
2426 /* Suppress "unused fixed point" warnings for these stations */
2427 fr->sflags |= BIT(SFLAGS_USED);
2428 to->sflags |= BIT(SFLAGS_USED);
2430 /* add to linked list which is dealt with after network is solved */
2431 link = osnew(nosurveylink);
2432 if (fToFirst) {
2433 link->to = StnFromPfx(to);
2434 link->fr = StnFromPfx(fr);
2435 } else {
2436 link->fr = StnFromPfx(fr);
2437 link->to = StnFromPfx(to);
2439 link->flags = pcs->flags | (STYLE_NOSURVEY << FLAGS_STYLE_BIT0);
2440 link->meta = pcs->meta;
2441 if (pcs->meta) ++pcs->meta->ref_count;
2442 link->next = nosurveyhead;
2443 nosurveyhead = link;
2444 return 1;
2447 static void
2448 data_nosurvey(void)
2450 prefix *fr = NULL, *to = NULL;
2452 bool fMulti = false;
2454 reading first_stn = End;
2456 const reading *ordering;
2458 again:
2460 for (ordering = pcs->ordering ; ; ordering++) {
2461 skipblanks();
2462 switch (*ordering) {
2463 case Fr:
2464 fr = read_prefix(PFX_STATION|PFX_ALLOW_ROOT);
2465 if (first_stn == End) first_stn = Fr;
2466 break;
2467 case To:
2468 to = read_prefix(PFX_STATION|PFX_ALLOW_ROOT);
2469 if (first_stn == End) first_stn = To;
2470 break;
2471 case Station:
2472 fr = to;
2473 to = read_prefix(PFX_STATION);
2474 first_stn = To;
2475 break;
2476 case Ignore:
2477 skipword(); break;
2478 case IgnoreAllAndNewLine:
2479 skipline();
2480 /* fall through */
2481 case Newline:
2482 if (fr != NULL) {
2483 if (!process_nosurvey(fr, to, first_stn == To))
2484 skipline();
2486 if (ordering[1] == End) {
2487 do {
2488 process_eol();
2489 skipblanks();
2490 } while (isComm(ch));
2491 if (!isData(ch)) {
2492 return;
2494 goto again;
2496 fMulti = true;
2497 while (1) {
2498 process_eol();
2499 skipblanks();
2500 if (isData(ch)) break;
2501 if (!isComm(ch)) {
2502 return;
2505 break;
2506 case IgnoreAll:
2507 skipline();
2508 /* fall through */
2509 case End:
2510 if (!fMulti) {
2511 (void)process_nosurvey(fr, to, first_stn == To);
2512 process_eol();
2513 return;
2515 do {
2516 process_eol();
2517 skipblanks();
2518 } while (isComm(ch));
2519 goto again;
2520 default: BUG("Unknown reading in ordering");
2525 /* totally ignore a line of survey data */
2526 static void
2527 data_ignore(void)
2529 skipline();
2530 process_eol();