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
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>
49 #define EPSILON (REAL_EPSILON * 1000)
51 #define var(I) (pcs->Var[(I)])
53 /* Test for a not-a-number value in Compass data (999.0 or -999.0).
55 * Compass itself uses -999.0 but reportedly understands Karst data which used
56 * 999.0 (information from Larry Fish via Simeon Warner in 2004). However
57 * testing with Compass in early 2024 it seems 999.0 is treated like any other
60 * When "corrected" backsights are specified in FORMAT, Compass seems to write
61 * out -999 with the correction applied to the CLP file.
63 * Valid readings should be 0 to 360 for the compass and -90 to 90 for the
64 * clino, and the correction should have absolute value < 360, so we test for
65 * any reading with an absolute value greater than 999 - 360 = 639, which is
66 * well outside the valid range.
68 #define is_compass_NaN(x) (fabs(x) > (999.0 - 360.0))
71 read_compass_date_as_days_since_1900(void)
73 /* NB order is *month* *day* year */
74 int month
= read_uint();
75 int day
= read_uint();
76 int year
= read_uint();
77 /* Note: Larry says a 2 digit year is always 19XX */
78 if (year
< 100) year
+= 1900;
80 /* Compass uses 1901-01-01 when no date was specified. */
81 if (year
== 1901 && day
== 1 && month
== 1) return -1;
83 return days_since_1900(year
, month
, day
);
88 CTYPE_OMIT
, CTYPE_READING
, CTYPE_PLUMB
, CTYPE_INFERPLUMB
, CTYPE_HORIZ
91 /* Don't explicitly initialise as we can't set the jmp_buf - this has
92 * static scope so will be initialised like this anyway */
93 parse file
/* = { NULL, NULL, 0, false, NULL } */ ;
97 static real value
[Fr
- 1];
98 #define VAL(N) value[(N)-1]
99 static real variance
[Fr
- 1];
100 #define VAR(N) variance[(N)-1]
101 static long location
[Fr
- 1];
102 #define LOC(N) location[(N)-1]
103 static int location_width
[Fr
- 1];
104 #define WID(N) location_width[(N)-1]
106 /* style functions */
107 static void data_normal(void);
108 static void data_cartesian(void);
109 static void data_passage(void);
110 static void data_nosurvey(void);
111 static void data_ignore(void);
117 fp
->offset
= ftell(file
.fh
);
118 if (fp
->offset
== -1)
119 fatalerror_in_file(file
.filename
, 0, /*Error reading file*/18);
123 set_pos(const filepos
*fp
)
126 if (fseek(file
.fh
, fp
->offset
, SEEK_SET
) == -1)
127 fatalerror_in_file(file
.filename
, 0, /*Error reading file*/18);
131 report_parent(parse
* p
) {
133 report_parent(p
->parent
);
134 /* Force re-report of include tree for further errors in
136 p
->reported_where
= false;
137 /* TRANSLATORS: %s is replaced by the filename of the parent file, and %u
138 * by the line number in that file. Your translation should also contain
139 * %s:%u so that automatic parsing of error messages to determine the file
140 * and line number still works. */
141 fprintf(STDERR
, msg(/*In file included from %s:%u:\n*/5), p
->filename
, p
->line
);
145 error_list_parent_files(void)
147 if (!file
.reported_where
&& file
.parent
) {
148 report_parent(file
.parent
);
149 /* Suppress reporting of full include tree for further errors
151 file
.reported_where
= true;
156 show_line(int col
, int width
)
158 /* Rewind to beginning of line. */
159 long cur_pos
= ftell(file
.fh
);
161 if (cur_pos
< 0 || fseek(file
.fh
, file
.lpos
, SEEK_SET
) == -1)
162 fatalerror_in_file(file
.filename
, 0, /*Error reading file*/18);
164 /* Read the whole line and write it out. */
167 int c
= GETC(file
.fh
);
168 /* Note: isEol() is true for EOF */
170 if (c
== '\t') ++tabs
;
175 /* If we have a location in the line for the error, indicate it. */
179 while (--col
) PUTC(' ', STDERR
);
181 /* Copy tabs from line, replacing other characters with spaces - this
182 * means that the caret should line up correctly. */
183 if (fseek(file
.fh
, file
.lpos
, SEEK_SET
) == -1)
184 fatalerror_in_file(file
.filename
, 0, /*Error reading file*/18);
186 int c
= GETC(file
.fh
);
187 if (c
!= '\t') c
= ' ';
199 /* Revert to where we were. */
200 if (fseek(file
.fh
, cur_pos
, SEEK_SET
) == -1)
201 fatalerror_in_file(file
.filename
, 0, /*Error reading file*/18);
207 /* Rewind to beginning of line. */
208 long cur_pos
= ftell(file
.fh
);
210 if (cur_pos
< 0 || fseek(file
.fh
, file
.lpos
, SEEK_SET
) == -1)
211 fatalerror_in_file(file
.filename
, 0, /*Error reading file*/18);
213 /* Read the whole line into a string. */
215 int c
= GETC(file
.fh
);
216 /* Note: isEol() is true for EOF */
221 /* Revert to where we were. */
222 if (fseek(file
.fh
, cur_pos
, SEEK_SET
) == -1) {
224 fatalerror_in_file(file
.filename
, 0, /*Error reading file*/18);
230 static int caret_width
= 0;
233 compile_v_report_fpos(int severity
, long fpos
, int en
, va_list ap
)
236 error_list_parent_files();
237 if (fpos
>= file
.lpos
)
238 col
= fpos
- file
.lpos
- caret_width
;
239 v_report(severity
, file
.filename
, file
.line
, col
, en
, ap
);
240 if (file
.fh
) show_line(col
, caret_width
);
244 compile_v_report(int diag_flags
, int en
, va_list ap
)
246 int severity
= (diag_flags
& DIAG_SEVERITY_MASK
);
247 if (diag_flags
& (DIAG_COL
|DIAG_BUF
)) {
249 if (diag_flags
& DIAG_BUF
) caret_width
= s_len(&token
);
250 compile_v_report_fpos(severity
, ftell(file
.fh
), en
, ap
);
251 if (diag_flags
& DIAG_BUF
) caret_width
= 0;
252 if (diag_flags
& DIAG_SKIP
) skipline();
256 error_list_parent_files();
257 v_report(severity
, file
.filename
, file
.line
, 0, en
, ap
);
259 if (diag_flags
& DIAG_BUF
) {
260 show_line(0, s_len(&token
));
262 show_line(0, caret_width
);
265 if (diag_flags
& DIAG_SKIP
) skipline();
269 compile_diagnostic(int diag_flags
, int en
, ...)
273 if (diag_flags
& (DIAG_TOKEN
|DIAG_UINT
|DIAG_DATE
|DIAG_NUM
)) {
276 if (diag_flags
& DIAG_TOKEN
) {
277 while (!isBlank(ch
) && !isEol(ch
)) {
278 s_catchar(&p
, (char)ch
);
281 } else if (diag_flags
& DIAG_UINT
) {
282 while (isdigit(ch
)) {
283 s_catchar(&p
, (char)ch
);
286 } else if (diag_flags
& DIAG_DATE
) {
287 while (isdigit(ch
) || ch
== '.') {
288 s_catchar(&p
, (char)ch
);
292 if (isMinus(ch
) || isPlus(ch
)) {
293 s_catchar(&p
, (char)ch
);
296 while (isdigit(ch
)) {
297 s_catchar(&p
, (char)ch
);
301 s_catchar(&p
, (char)ch
);
304 while (isdigit(ch
)) {
305 s_catchar(&p
, (char)ch
);
310 caret_width
= s_len(&p
);
313 compile_v_report(diag_flags
|DIAG_COL
, en
, ap
);
315 } else if (diag_flags
& DIAG_STRING
) {
318 caret_width
= ftell(file
.fh
);
321 /* We want to include any quotes, so can't use s_len(&p). */
322 caret_width
= ftell(file
.fh
) - caret_width
;
323 compile_v_report(diag_flags
|DIAG_COL
, en
, ap
);
326 compile_v_report(diag_flags
, en
, ap
);
332 compile_diagnostic_reading(int diag_flags
, reading r
, int en
, ...)
335 int severity
= (diag_flags
& DIAG_SEVERITY_MASK
);
337 caret_width
= WID(r
);
338 compile_v_report_fpos(severity
, LOC(r
) + caret_width
, en
, ap
);
344 compile_error_reading_skip(reading r
, int en
, ...)
348 caret_width
= WID(r
);
349 compile_v_report_fpos(DIAG_ERR
, LOC(r
) + caret_width
, en
, ap
);
356 compile_diagnostic_at(int diag_flags
, const char * filename
, unsigned line
, int en
, ...)
359 int severity
= (diag_flags
& DIAG_SEVERITY_MASK
);
361 v_report(severity
, filename
, line
, 0, en
, ap
);
366 compile_diagnostic_pfx(int diag_flags
, const prefix
* pfx
, int en
, ...)
369 int severity
= (diag_flags
& DIAG_SEVERITY_MASK
);
371 v_report(severity
, pfx
->filename
, pfx
->line
, 0, en
, ap
);
376 compile_diagnostic_token_show(int diag_flags
, int en
)
380 while (!isBlank(ch
) && !isEol(ch
)) {
381 s_catchar(&p
, (char)ch
);
385 caret_width
= s_len(&p
);
386 compile_diagnostic(diag_flags
|DIAG_COL
, en
, p
);
390 compile_diagnostic(DIAG_ERR
|DIAG_COL
, en
, "");
395 compile_error_string(const char * s
, int en
, ...)
399 caret_width
= strlen(s
);
400 compile_v_report(DIAG_ERR
|DIAG_COL
, en
, ap
);
405 /* This function makes a note where to put output files */
407 using_data_file(const char *fnm
)
409 if (!fnm_output_base
) {
410 /* was: fnm_output_base = base_from_fnm(fnm); */
411 fnm_output_base
= baseleaf_from_fnm(fnm
);
412 } else if (fnm_output_base_is_dir
) {
413 /* --output pointed to directory so use the leaf basename in that dir */
415 lf
= baseleaf_from_fnm(fnm
);
416 p
= use_path(fnm_output_base
, lf
);
418 osfree(fnm_output_base
);
420 fnm_output_base_is_dir
= 0;
427 while (!isBlank(ch
) && !isEol(ch
)) nextch();
433 while (isBlank(ch
)) nextch();
439 while (!isEol(ch
)) nextch();
451 compile_diagnostic(DIAG_ERR
|DIAG_COL
, /*End of line not blank*/15);
457 /* skip any different eol characters so we get line counts correct on
458 * DOS text files and similar, but don't count several adjacent blank
462 if (ch
== eolchar
|| !isEol(ch
)) {
465 if (ch
== '\n') eolchar
= ch
;
467 file
.lpos
= ftell(file
.fh
) - 1;
471 process_non_data_line(void)
475 if (isData(ch
)) return false;
488 read_reading(reading r
, bool f_optional
)
493 case Tape
: q
= Q_LENGTH
; break;
494 case BackTape
: q
= Q_BACKLENGTH
; break;
495 case Comp
: q
= Q_BEARING
; break;
496 case BackComp
: q
= Q_BACKBEARING
; break;
497 case Clino
: q
= Q_GRADIENT
; break;
498 case BackClino
: q
= Q_BACKGRADIENT
; break;
499 case FrDepth
: case ToDepth
: q
= Q_DEPTH
; break;
500 case Dx
: q
= Q_DX
; break;
501 case Dy
: q
= Q_DY
; break;
502 case Dz
: q
= Q_DZ
; break;
503 case FrCount
: case ToCount
: q
= Q_COUNT
; break;
504 case Left
: q
= Q_LEFT
; break;
505 case Right
: q
= Q_RIGHT
; break;
506 case Up
: q
= Q_UP
; break;
507 case Down
: q
= Q_DOWN
; break;
509 q
= Q_NULL
; /* Suppress compiler warning */;
510 BUG("Unexpected case");
512 LOC(r
) = ftell(file
.fh
);
513 /* since we don't handle bearings in read_readings, it's never quadrant */
514 VAL(r
) = read_numeric_multi(f_optional
, false, &n_readings
);
515 WID(r
) = ftell(file
.fh
) - LOC(r
);
517 if (n_readings
> 1) VAR(r
) /= sqrt(n_readings
);
521 read_bearing_or_omit(reading r
)
524 bool quadrants
= false;
525 q_quantity q
= Q_NULL
;
529 if (pcs
->f_bearing_quadrants
)
534 if (pcs
->f_backbearing_quadrants
)
538 q
= Q_NULL
; /* Suppress compiler warning */;
539 BUG("Unexpected case");
541 LOC(r
) = ftell(file
.fh
);
542 VAL(r
) = read_bearing_multi_or_omit(quadrants
, &n_readings
);
543 WID(r
) = ftell(file
.fh
) - LOC(r
);
545 if (n_readings
> 1) VAR(r
) /= sqrt(n_readings
);
548 // Set up settings for reading Compass DAT or MAK.
550 initialise_common_compass_settings(void)
552 short *t
= ((short*)osmalloc(ossizeof(short) * 257)) + 1;
554 t
[EOF
] = SPECIAL_EOL
;
555 memset(t
, 0, sizeof(short) * 33);
556 for (i
= 33; i
< 127; i
++) t
[i
] = SPECIAL_NAMES
;
558 for (i
= 128; i
< 256; i
++) t
[i
] = SPECIAL_NAMES
;
559 t
['\t'] |= SPECIAL_BLANK
;
560 t
[' '] |= SPECIAL_BLANK
;
561 t
['\032'] |= SPECIAL_EOL
; /* Ctrl-Z, so olde DOS text files are handled ok */
562 t
['\n'] |= SPECIAL_EOL
;
563 t
['\r'] |= SPECIAL_EOL
;
564 t
['.'] |= SPECIAL_DECIMAL
;
565 t
['-'] |= SPECIAL_MINUS
;
566 t
['+'] |= SPECIAL_PLUS
;
568 settings
*pcsNew
= osnew(settings
);
569 *pcsNew
= *pcs
; /* copy contents */
570 pcsNew
->begin_lineno
= 0;
571 pcsNew
->Translate
= t
;
573 pcsNew
->Truncate
= INT_MAX
;
577 update_output_separator();
580 /* For reading Compass MAK files which have a freeform syntax */
582 nextch_handling_eol(void)
585 while (ch
!= EOF
&& isEol(ch
)) {
591 data_file_compass_dat_or_clp(bool is_clp
)
593 initialise_common_compass_settings();
596 pcs
->z
[Q_DECLINATION
] = HUGE_REAL
;
598 pcs
->recorded_style
= pcs
->style
= STYLE_NORMAL
;
599 pcs
->units
[Q_LENGTH
] = METRES_PER_FOOT
;
600 pcs
->infer
= BIT(INFER_EQUATES
) |
601 BIT(INFER_EQUATES_SELF_OK
) |
604 /* We need to update separator_map so we don't pick a separator character
605 * which occurs in a station name. However Compass DAT allows everything
606 * >= ASCII char 33 except 127 in station names so if we just added all
607 * the valid station name characters we'd always pick space as the
608 * separator for any dataset which included a DAT file, yet in practice
609 * '.' is never used in any of the sample DAT files I've seen. So
610 * instead we scan the characters actually used in station names when we
611 * process CompassDATFr and CompassDATTo fields.
615 /* errors in nested functions can longjmp here */
616 if (setjmp(file
.jbSkipLine
)) {
622 while (ch
!= EOF
&& !ferror(file
.fh
)) {
623 static const reading compass_order
[] = {
624 CompassDATFr
, CompassDATTo
, Tape
, CompassDATComp
, CompassDATClino
,
625 CompassDATLeft
, CompassDATUp
, CompassDATDown
, CompassDATRight
,
626 CompassDATFlags
, IgnoreAll
628 static const reading compass_order_backsights
[] = {
629 CompassDATFr
, CompassDATTo
, Tape
, CompassDATComp
, CompassDATClino
,
630 CompassDATLeft
, CompassDATUp
, CompassDATDown
, CompassDATRight
,
631 CompassDATBackComp
, CompassDATBackClino
,
632 CompassDATFlags
, IgnoreAll
637 /* SURVEY NAME: <Short name> */
640 /* if (ch != ':') ... */
645 /* SURVEY DATE: 7 10 79 COMMENT:<Long name> */
648 copy_on_write_meta(pcs
);
651 int days
= read_compass_date_as_days_since_1900();
652 pcs
->meta
->days1
= pcs
->meta
->days2
= days
;
654 pcs
->meta
->days1
= pcs
->meta
->days2
= -1;
656 pcs
->declination
= HUGE_REAL
;
667 /* DECLINATION: 1.00 FORMAT: DDDDLUDRADLN CORRECTIONS: 2.00 3.00 4.00 */
671 if (pcs
->dec_filename
== NULL
) {
672 pcs
->z
[Q_DECLINATION
] = -read_numeric(false);
673 pcs
->z
[Q_DECLINATION
] *= pcs
->units
[Q_DECLINATION
];
675 (void)read_numeric(false);
678 pcs
->ordering
= compass_order
;
679 if (S_EQ(&token
, "FORMAT")) {
680 /* This documents the format in the original survey notebook - we
681 * don't need to fully parse it to be able to parse the survey data
682 * in the file, which gets converted to a fixed order and units.
686 size_t token_len
= s_len(&token
);
687 if (token_len
>= 4 && s_str(&token
)[3] == 'W') {
688 /* Original "Inclination Units" were "Depth Gauge". */
689 pcs
->recorded_style
= STYLE_DIVING
;
691 if (token_len
>= 12) {
692 char backsight_type
= s_str(&token
)[token_len
>= 15 ? 13 : 11];
693 // B means redundant backsight; C means redundant backsights
694 // but displayed "corrected" (i.e. reversed to make visually
695 // comparing easier).
696 if (backsight_type
== 'B' || backsight_type
== 'C') {
697 /* We have backsights for compass and clino */
698 pcs
->ordering
= compass_order_backsights
;
704 // CORRECTIONS and CORRECTIONS2 have already been applied to data in
707 if (S_EQ(&token
, "CORRECTIONS") && ch
== ':') {
709 pcs
->z
[Q_BACKBEARING
] = pcs
->z
[Q_BEARING
] = -rad(read_numeric(false));
710 pcs
->z
[Q_BACKGRADIENT
] = pcs
->z
[Q_GRADIENT
] = -rad(read_numeric(false));
711 pcs
->z
[Q_LENGTH
] = -METRES_PER_FOOT
* read_numeric(false);
715 /* get_token() only reads alphas so we must check for '2' here. */
716 if (S_EQ(&token
, "CORRECTIONS") && ch
== '2') {
719 pcs
->z
[Q_BACKBEARING
] = -rad(read_numeric(false));
720 pcs
->z
[Q_BACKGRADIENT
] = -rad(read_numeric(false));
726 // FIXME Parse once we handle discovery dates...
727 // NB: Need to skip unread CORRECTIONS* for the `is_clp` case.
728 if (S_EQ(&token
, "DISCOVERY") && ch
== ':') {
729 // Discovery date, e.g. DISCOVERY: 2 28 2024
731 int days
= read_compass_date_as_days_since_1900();
755 pcs
->ordering
= NULL
; /* Avoid free() of static array. */
760 data_file_compass_dat(void)
762 data_file_compass_dat_or_clp(false);
766 data_file_compass_clp(void)
768 data_file_compass_dat_or_clp(true);
772 data_file_compass_mak(void)
774 initialise_common_compass_settings();
775 short *t
= pcs
->Translate
;
776 // In a Compass MAK file a station name can't contain these three
777 // characters due to how the syntax works.
778 t
['['] = t
[','] = t
[';'] = 0;
781 /* errors in nested functions can longjmp here */
782 if (setjmp(file
.jbSkipLine
)) {
790 real base_x
= 0.0, base_y
= 0.0, base_z
= 0.0;
791 int base_utm_zone
= 0;
792 unsigned int base_line
= 0;
794 string path
= S_INIT
;
795 s_donate(&path
, path_from_fnm(file
.filename
));
797 struct mak_folder
*next
;
799 } *folder_stack
= NULL
;
801 while (ch
!= EOF
&& !ferror(file
.fh
)) {
806 string dat_fnm
= S_INIT
;
807 nextch_handling_eol();
808 while (ch
!= ',' && ch
!= ';' && ch
!= EOF
) {
809 while (isEol(ch
)) process_eol();
810 s_catchar(&dat_fnm
, (char)ch
);
811 nextch_handling_eol();
813 if (!s_empty(&dat_fnm
)) {
815 // Process the previous @ command using the datum from &.
816 char *proj_str
= img_compass_utm_proj_str(datum
,
819 // Temporarily reset line and lpos so dec_context and
820 // dec_line refer to the @ command.
821 unsigned saved_line
= file
.line
;
822 file
.line
= base_line
;
823 long saved_lpos
= file
.lpos
;
824 file
.lpos
= base_lpos
;
825 set_declination_location(base_x
, base_y
, base_z
,
827 file
.line
= saved_line
;
828 file
.lpos
= saved_lpos
;
829 if (!pcs
->proj_str
) {
830 pcs
->proj_str
= proj_str
;
832 proj_str_out
= osstrdup(proj_str
);
840 data_file(s_str(&path
), s_str(&dat_fnm
));
844 while (ch
!= ';' && ch
!= EOF
) {
846 nextch_handling_eol();
847 name
= read_prefix(PFX_STATION
|PFX_OPT
);
849 scan_compass_station_name(name
);
855 bool in_feet
= false;
856 // Compass treats these fixed points as entrances
857 // ("distance from entrance" in a .DAT file counts
858 // from 0.0 at these points) so we do too.
859 name
->sflags
|= BIT(SFLAGS_FIXED
) |
860 BIT(SFLAGS_ENTRANCE
);
861 nextch_handling_eol();
862 if (ch
== 'F' || ch
== 'f') {
864 nextch_handling_eol();
865 } else if (ch
== 'M' || ch
== 'm') {
866 nextch_handling_eol();
868 compile_diagnostic(DIAG_ERR
, /*Expecting āFā or āMā*/103);
870 while (!isdigit(ch
) && ch
!= '+' && ch
!= '-' &&
871 ch
!= '.' && ch
!= ']' && ch
!= EOF
) {
872 nextch_handling_eol();
874 x
= read_numeric(false);
875 while (!isdigit(ch
) && ch
!= '+' && ch
!= '-' &&
876 ch
!= '.' && ch
!= ']' && ch
!= EOF
) {
877 nextch_handling_eol();
879 y
= read_numeric(false);
880 while (!isdigit(ch
) && ch
!= '+' && ch
!= '-' &&
881 ch
!= '.' && ch
!= ']' && ch
!= EOF
) {
882 nextch_handling_eol();
884 z
= read_numeric(false);
886 x
*= METRES_PER_FOOT
;
887 y
*= METRES_PER_FOOT
;
888 z
*= METRES_PER_FOOT
;
890 stn
= StnFromPfx(name
);
897 if (x
!= POS(stn
, 0) ||
900 compile_diagnostic(DIAG_ERR
, /*Station already fixed or equated to a fixed point*/46);
902 compile_diagnostic(DIAG_WARN
, /*Station already fixed at the same coordinates*/55);
905 while (ch
!= ']' && ch
!= EOF
) nextch_handling_eol();
907 nextch_handling_eol();
911 /* FIXME: link station - ignore for now */
912 /* FIXME: perhaps issue warning? Other station names
913 * can be "reused", which is problematic... */
915 while (ch
!= ',' && ch
!= ';' && ch
!= EOF
)
916 nextch_handling_eol();
925 utm_zone
= read_int(-60, 60);
927 if (ch
== ';') nextch_handling_eol();
930 if (!pcs
->next
|| pcs
->proj_str
!= pcs
->next
->proj_str
)
931 osfree(pcs
->proj_str
);
932 pcs
->proj_str
= NULL
;
933 if (datum
&& utm_zone
&& abs(utm_zone
) <= 60) {
934 /* Set up coordinate system. */
935 char *proj_str
= img_compass_utm_proj_str(datum
, utm_zone
);
937 pcs
->proj_str
= proj_str
;
939 proj_str_out
= osstrdup(proj_str
);
943 invalidate_pj_cached();
952 while (ch
!= ';' && !isEol(ch
)) {
953 s_catchar(&p
, (char)ch
);
955 /* Ignore trailing blanks. */
956 if (!isBlank(ch
)) datum_len
= c
;
959 if (ch
== ';') nextch_handling_eol();
960 datum
= img_parse_compass_datum_string(s_str(&p
), datum_len
);
962 goto update_proj_str
;
965 // Enter subdirectory.
966 struct mak_folder
*p
= folder_stack
;
967 folder_stack
= osnew(struct mak_folder
);
968 folder_stack
->next
= p
;
969 folder_stack
->len
= s_len(&path
);
971 s_catchar(&path
, FNM_SEP_LEV
);
973 while (ch
!= ';' && !isEol(ch
)) {
977 s_catchar(&path
, (char)ch
);
980 if (ch
== ';') nextch_handling_eol();
984 // Leave subdirectory.
985 struct mak_folder
*p
= folder_stack
;
986 if (folder_stack
== NULL
) {
990 s_truncate(&path
, folder_stack
->len
);
991 folder_stack
= folder_stack
->next
;
995 if (ch
== ';') nextch_handling_eol();
999 /* "Base Location" to calculate magnetic declination at:
1000 * UTM East, UTM North, Elevation, UTM Zone, Convergence Angle
1001 * The first three are in metres.
1004 real easting
= read_numeric(false);
1006 if (ch
!= ',') break;
1008 real northing
= read_numeric(false);
1010 if (ch
!= ',') break;
1012 real elevation
= read_numeric(false);
1014 if (ch
!= ',') break;
1016 int zone
= read_int(-60, 60);
1018 if (ch
!= ',') break;
1020 real convergence_angle
= read_numeric(false);
1021 /* We've now read them all successfully so store them. The
1022 * Compass documentation gives an example which specifies the
1023 * datum *AFTER* the base location, so we need to convert lazily.
1028 base_utm_zone
= zone
;
1029 base_line
= file
.line
;
1030 base_lpos
= file
.lpos
;
1031 // We ignore the stored UTM grid convergence angle since we get
1033 (void)convergence_angle
;
1034 if (ch
== ';') nextch_handling_eol();
1038 nextch_handling_eol();
1047 data_file_survex(void)
1049 int begin_lineno_store
= pcs
->begin_lineno
;
1050 pcs
->begin_lineno
= 0;
1053 /* Maybe a UTF-8 "BOM" - skip if so. */
1054 if (nextch() == 0xbb && nextch() == 0xbf) {
1063 #ifdef HAVE_SETJMP_H
1064 /* errors in nested functions can longjmp here */
1065 if (setjmp(file
.jbSkipLine
)) {
1071 while (ch
!= EOF
&& !ferror(file
.fh
)) {
1072 if (!process_non_data_line()) {
1073 f_export_ok
= false;
1074 switch (pcs
->style
) {
1077 case STYLE_CYLPOLAR
:
1080 case STYLE_CARTESIAN
:
1086 case STYLE_NOSURVEY
:
1099 /* don't allow *BEGIN at the end of a file, then *EXPORT in the
1101 f_export_ok
= false;
1103 if (pcs
->begin_lineno
) {
1104 error_in_file(file
.filename
, pcs
->begin_lineno
,
1105 /*BEGIN with no matching END in this file*/23);
1106 /* Implicitly close any unclosed BEGINs from this file */
1109 } while (pcs
->begin_lineno
);
1112 pcs
->begin_lineno
= begin_lineno_store
;
1115 #define EXT3(C1, C2, C3) (((C3) << 16) | ((C2) << 8) | (C1))
1118 data_file(const char *pth
, const char *fnm
)
1129 /* file specified on command line - don't do special translation */
1130 fh
= fopenWithPthAndExt(pth
, fnm
, EXT_SVX_DATA
, "rb", &filename
);
1132 fh
= fopen_portable(pth
, fnm
, EXT_SVX_DATA
, "rb", &filename
);
1136 compile_error_string(fnm
, /*Couldnāt open file ā%sā*/24, fnm
);
1140 len
= strlen(filename
);
1141 if (len
> 4 && filename
[len
- 4] == FNM_SEP_EXT
) {
1142 /* Read extension and pack into ext. */
1143 for (int i
= 1; i
< 4; ++i
) {
1144 unsigned char ext_ch
= filename
[len
- i
];
1145 ext
= (ext
<< 8) | tolower(ext_ch
);
1150 if (file
.fh
) file
.parent
= &file_store
;
1152 file
.filename
= filename
;
1155 file
.reported_where
= false;
1159 using_data_file(file
.filename
);
1162 case EXT3('d', 'a', 't'):
1163 // Compass survey data.
1164 data_file_compass_dat();
1166 case EXT3('c', 'l', 'p'):
1167 // Compass closed data. The format of .clp is the same as .dat,
1168 // but it contains loop-closed data. This might be useful to
1169 // read if you want to keep existing stations at the same
1170 // adjusted positions, for example to be able to draw extensions
1171 // on an existing drawn-up survey. Or if you managed to lose the
1172 // original .dat but still have the .clp.
1173 data_file_compass_clp();
1175 case EXT3('m', 'a', 'k'):
1176 // Compass project file.
1177 data_file_compass_mak();
1180 // Native Survex data.
1185 if (ferror(file
.fh
))
1186 fatalerror_in_file(file
.filename
, 0, /*Error reading file*/18);
1188 (void)fclose(file
.fh
);
1192 /* don't free this - it may be pointed to by prefix.file */
1193 /* osfree(file.filename); */
1199 return a
- floor(a
/ (2 * M_PI
)) * (2 * M_PI
);
1203 handle_plumb(clino_type
*p_ctype
)
1206 CLINO_NULL
=-1, CLINO_UP
, CLINO_DOWN
, CLINO_LEVEL
1208 static const sztok clino_tab
[] = {
1210 {"DOWN", CLINO_DOWN
},
1212 {"LEVEL", CLINO_LEVEL
},
1217 static const real clinos
[] = {(real
)M_PI_2
, (real
)(-M_PI_2
), (real
)0.0};
1225 tok
= match_tok(clino_tab
, TABSIZE(clino_tab
));
1226 if (tok
!= CLINO_NULL
) {
1227 *p_ctype
= (tok
== CLINO_LEVEL
? CTYPE_HORIZ
: CTYPE_PLUMB
);
1231 } else if (isSign(ch
)) {
1234 if (toupper(ch
) == 'V') {
1236 *p_ctype
= CTYPE_PLUMB
;
1237 return (!isMinus(chOld
) ? M_PI_2
: -M_PI_2
);
1240 if (isOmit(chOld
)) {
1241 *p_ctype
= CTYPE_OMIT
;
1242 /* no clino reading, so assume 0 with large sd */
1245 } else if (isOmit(ch
)) {
1246 /* OMIT char may not be a SIGN char too so we need to check here as
1247 * well as above... */
1249 *p_ctype
= CTYPE_OMIT
;
1250 /* no clino reading, so assume 0 with large sd */
1257 warn_readings_differ(int msgno
, real diff
, int units
)
1261 diff
/= get_units_factor(units
);
1262 snprintf(buf
, sizeof(buf
), "%.2f", fabs(diff
));
1263 for (p
= buf
; *p
; ++p
) {
1267 if (*p
!= '0') z
= p
+ 1;
1273 strcpy(p
, get_units_string(units
));
1274 compile_diagnostic(DIAG_WARN
, msgno
, buf
);
1278 handle_comp_units(void)
1280 bool fNoComp
= true;
1281 if (VAL(Comp
) != HUGE_REAL
) {
1283 VAL(Comp
) *= pcs
->units
[Q_BEARING
];
1284 if (VAL(Comp
) < (real
)0.0 || VAL(Comp
) - M_PI
* 2.0 > EPSILON
) {
1285 /* TRANSLATORS: Suspicious means something like 410 degrees or -20
1287 compile_diagnostic_reading(DIAG_WARN
, Comp
, /*Suspicious compass reading*/59);
1288 VAL(Comp
) = mod2pi(VAL(Comp
));
1291 if (VAL(BackComp
) != HUGE_REAL
) {
1293 VAL(BackComp
) *= pcs
->units
[Q_BACKBEARING
];
1294 if (VAL(BackComp
) < (real
)0.0 || VAL(BackComp
) - M_PI
* 2.0 > EPSILON
) {
1295 /* FIXME: different message for BackComp? */
1296 compile_diagnostic_reading(DIAG_WARN
, BackComp
, /*Suspicious compass reading*/59);
1297 VAL(BackComp
) = mod2pi(VAL(BackComp
));
1303 static real
compute_convergence(real lon
, real lat
) {
1304 // PROJ < 8.1.0 dereferences the context without a NULL check inside
1305 // proj_create_ellipsoidal_2D_cs() but PJ_DEFAULT_CTX is really just
1306 // NULL so for affected PROJ versions we create a context temporarily to
1307 // avoid a segmentation fault.
1308 PJ_CONTEXT
* ctx
= PJ_DEFAULT_CTX
;
1309 #if PROJ_VERSION_MAJOR < 8 || \
1310 (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR < 1)
1311 ctx
= proj_context_create();
1314 if (!proj_str_out
) {
1315 compile_diagnostic(DIAG_ERR
, /*Output coordinate system not set*/488);
1318 PJ
* pj
= proj_create(ctx
, proj_str_out
);
1322 #if PROJ_VERSION_MAJOR < 8 || \
1323 (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR < 2)
1324 /* Code adapted from fix in PROJ 8.2.0 to make proj_factors() work in
1325 * cases we need (e.g. a CRS specified as "EPSG:<number>").
1327 switch (proj_get_type(pj
)) {
1328 case PJ_TYPE_PROJECTED_CRS
: {
1329 /* If it is a projected CRS, then compute the factors on the conversion
1330 * associated to it. We need to start from a temporary geographic CRS
1331 * using the same datum as the one of the projected CRS, and with
1332 * input coordinates being in longitude, latitude order in radian,
1333 * to be consistent with the expectations of the lp input parameter.
1336 PJ
* geodetic_crs
= proj_get_source_crs(ctx
, pj
);
1339 PJ
* datum
= proj_crs_get_datum(ctx
, geodetic_crs
);
1340 #if PROJ_VERSION_MAJOR == 8 || \
1341 (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
1342 /* PROJ 7.2.0 upgraded to EPSG 10.x which added the concept
1343 * of a datum ensemble, and this version of PROJ also added
1344 * an API to deal with these.
1346 * If we're using PROJ < 7.2.0 then its EPSG database won't
1347 * have datum ensembles, so we don't need any code to handle
1351 datum
= proj_crs_get_datum_ensemble(ctx
, geodetic_crs
);
1354 PJ
* cs
= proj_create_ellipsoidal_2D_cs(
1355 ctx
, PJ_ELLPS2D_LONGITUDE_LATITUDE
, "Radian", 1.0);
1356 PJ
* temp
= proj_create_geographic_crs_from_datum(
1357 ctx
, "unnamed crs", datum
, cs
);
1358 proj_destroy(datum
);
1360 proj_destroy(geodetic_crs
);
1361 PJ
* newOp
= proj_create_crs_to_crs_from_pj(ctx
, temp
, pj
, NULL
, NULL
);
1373 #if PROJ_VERSION_MAJOR < 9 || \
1374 (PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR < 3)
1376 /* In PROJ < 9.3.0 proj_factors() returns a grid convergence which is
1377 * off by 90Ā° for a projected coordinate system with northing/easting
1378 * axis order. We can't copy over the fix for this in PROJ 9.3.0's
1379 * proj_factors() since it uses non-public PROJ functions, but
1380 * normalising the output order here works too.
1382 PJ
* pj_norm
= proj_normalize_for_visualization(PJ_DEFAULT_CTX
, pj
);
1387 PJ_FACTORS factors
= proj_factors(pj
, lp
);
1389 #if PROJ_VERSION_MAJOR < 8 || \
1390 (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR < 1)
1391 proj_context_destroy(ctx
);
1393 return factors
.meridian_convergence
;
1397 handle_compass(real
*p_var
)
1399 real compvar
= VAR(Comp
);
1400 real comp
= VAL(Comp
);
1401 real backcomp
= VAL(BackComp
);
1403 if (pcs
->z
[Q_DECLINATION
] != HUGE_REAL
) {
1404 declination
= -pcs
->z
[Q_DECLINATION
];
1405 } else if (pcs
->declination
!= HUGE_REAL
) {
1406 /* Cached value calculated for a previous compass reading taken on the
1407 * same date (by the 'else' just below).
1409 declination
= pcs
->declination
;
1411 if (!pcs
->meta
|| pcs
->meta
->days1
== -1) {
1412 compile_diagnostic(DIAG_WARN
, /*No survey date specified - using 0 for magnetic declination*/304);
1415 int avg_days
= (pcs
->meta
->days1
+ pcs
->meta
->days2
) / 2;
1416 double dat
= julian_date_from_days_since_1900(avg_days
);
1417 /* thgeomag() takes (lat, lon, h, dat) - i.e. (y, x, z, date). */
1418 declination
= thgeomag(pcs
->dec_lat
, pcs
->dec_lon
, pcs
->dec_alt
, dat
);
1419 if (declination
< pcs
->min_declination
) {
1420 pcs
->min_declination
= declination
;
1421 pcs
->min_declination_days
= avg_days
;
1423 if (declination
> pcs
->max_declination
) {
1424 pcs
->max_declination
= declination
;
1425 pcs
->max_declination_days
= avg_days
;
1428 if (pcs
->convergence
== HUGE_REAL
) {
1429 /* Compute the convergence lazily. It only depends on the output
1430 * coordinate system so we can cache it for reuse to apply to
1431 * a declination value for a different date.
1433 pcs
->convergence
= compute_convergence(pcs
->dec_lon
, pcs
->dec_lat
);
1435 declination
-= pcs
->convergence
;
1436 /* We cache the calculated declination as the calculation is relatively
1437 * expensive. We also cache an "assumed 0" answer so that we only
1438 * warn once per such survey rather than for every line with a compass
1440 pcs
->declination
= declination
;
1442 if (comp
!= HUGE_REAL
) {
1443 comp
= (comp
- pcs
->z
[Q_BEARING
]) * pcs
->sc
[Q_BEARING
];
1444 comp
+= declination
;
1446 if (backcomp
!= HUGE_REAL
) {
1447 backcomp
= (backcomp
- pcs
->z
[Q_BACKBEARING
])
1448 * pcs
->sc
[Q_BACKBEARING
];
1449 backcomp
+= declination
;
1451 if (comp
!= HUGE_REAL
) {
1452 real diff
= comp
- backcomp
;
1453 real adj
= fabs(diff
) > M_PI
? M_PI
: 0;
1454 diff
-= floor((diff
+ M_PI
) / (2 * M_PI
)) * 2 * M_PI
;
1455 if (sqrd(diff
/ 3.0) > compvar
+ VAR(BackComp
)) {
1456 /* fore and back readings differ by more than 3 sds */
1457 /* TRANSLATORS: %s is replaced by the amount the readings disagree
1458 * by, e.g. "2.5Ā°" or "3įµ". */
1459 warn_readings_differ(/*COMPASS reading and BACKCOMPASS reading disagree by %s*/98,
1460 diff
, get_angle_units(Q_BEARING
));
1462 comp
= (comp
/ compvar
+ backcomp
/ VAR(BackComp
));
1463 compvar
= (compvar
+ VAR(BackComp
)) / 4;
1468 compvar
= VAR(BackComp
);
1476 handle_clino(q_quantity q
, reading r
, real val
, bool percent
, clino_type
*p_ctype
)
1480 real diff_from_abs90
;
1481 val
*= pcs
->units
[q
];
1482 /* percentage scale */
1483 if (percent
) val
= atan(val
);
1484 /* We want to warn if there's a reading which it would be impossible
1485 * to have read from the instrument (e.g. on a -90 to 90 degree scale
1486 * you can't read "96" (it's probably a typo for "69"). However, the
1487 * gradient reading from a topofil is typically in the range 0 to 180,
1488 * with 90 being horizontal.
1490 * Really we should allow the valid range to be specified, but for now
1491 * we infer it from the zero error - if this is within 45 degrees of
1492 * 90 then we assume the range is 0 to 180.
1495 range_0_180
= (z
> M_PI_4
&& z
< 3*M_PI_4
);
1496 diff_from_abs90
= fabs(val
) - M_PI_2
;
1497 if (diff_from_abs90
> EPSILON
) {
1499 int clino_units
= get_angle_units(q
);
1500 const char * units
= get_units_string(clino_units
);
1501 real right_angle
= M_PI_2
/ get_units_factor(clino_units
);
1502 /* FIXME: different message for BackClino? */
1503 /* TRANSLATORS: %.f%s will be replaced with a right angle in the
1504 * units currently in use, e.g. "90Ā°" or "100įµ". And "absolute
1505 * value" means the reading ignoring the sign (so it might be
1506 * < -90Ā° or > 90Ā°. */
1507 compile_diagnostic_reading(DIAG_WARN
, r
, /*Clino reading over %.f%s (absolute value)*/51,
1508 right_angle
, units
);
1510 } else if (TSTBIT(pcs
->infer
, INFER_PLUMBS
) &&
1511 diff_from_abs90
>= -EPSILON
) {
1512 *p_ctype
= CTYPE_INFERPLUMB
;
1514 if (range_0_180
&& *p_ctype
!= CTYPE_INFERPLUMB
) {
1515 /* FIXME: Warning message not ideal... */
1516 if (val
< 0.0 || val
- M_PI
> EPSILON
) {
1517 int clino_units
= get_angle_units(q
);
1518 const char * units
= get_units_string(clino_units
);
1519 real right_angle
= M_PI_2
/ get_units_factor(clino_units
);
1520 compile_diagnostic_reading(DIAG_WARN
, r
, /*Clino reading over %.f%s (absolute value)*/51,
1521 right_angle
, units
);
1528 process_normal(prefix
*fr
, prefix
*to
, bool fToFirst
,
1529 clino_type ctype
, clino_type backctype
)
1531 real tape
= VAL(Tape
);
1532 real clin
= VAL(Clino
);
1533 real backclin
= VAL(BackClino
);
1537 #ifndef NO_COVARIANCES
1543 /* adjusted tape is negative -- probably the calibration is wrong */
1544 if (tape
< (real
)0.0) {
1545 /* TRANSLATE different message for topofil? */
1546 compile_diagnostic_reading(DIAG_WARN
, Tape
, /*Negative adjusted tape reading*/79);
1549 fNoComp
= handle_comp_units();
1551 if (ctype
== CTYPE_READING
) {
1552 clin
= handle_clino(Q_GRADIENT
, Clino
, clin
,
1553 pcs
->f_clino_percent
, &ctype
);
1556 if (backctype
== CTYPE_READING
) {
1557 backclin
= handle_clino(Q_BACKGRADIENT
, BackClino
, backclin
,
1558 pcs
->f_backclino_percent
, &backctype
);
1561 /* un-infer the plumb if the backsight was just a reading */
1562 if (ctype
== CTYPE_INFERPLUMB
&& backctype
== CTYPE_READING
) {
1563 ctype
= CTYPE_READING
;
1566 if (ctype
!= CTYPE_OMIT
&& backctype
!= CTYPE_OMIT
&& ctype
!= backctype
) {
1567 /* TRANSLATORS: In data with backsights, the user has tried to give a
1568 * plumb for the foresight and a clino reading for the backsight, or
1569 * something similar. */
1570 compile_error_reading_skip(Clino
, /*CLINO and BACKCLINO readings must be of the same type*/84);
1574 if (ctype
== CTYPE_PLUMB
|| ctype
== CTYPE_INFERPLUMB
||
1575 backctype
== CTYPE_PLUMB
|| backctype
== CTYPE_INFERPLUMB
) {
1578 if (ctype
== CTYPE_PLUMB
||
1579 (ctype
== CTYPE_INFERPLUMB
&& VAL(Comp
) != 0.0) ||
1580 backctype
== CTYPE_PLUMB
||
1581 (backctype
== CTYPE_INFERPLUMB
&& VAL(BackComp
) != 0.0)) {
1582 /* FIXME: Different message for BackComp? */
1583 /* TRANSLATORS: A "plumbed leg" is one measured using a plumbline
1584 * (a weight on a string). So the problem here is that the leg is
1585 * vertical, so a compass reading has no meaning! */
1586 compile_diagnostic(DIAG_WARN
, /*Compass reading given on plumbed leg*/21);
1590 dx
= dy
= (real
)0.0;
1591 if (ctype
!= CTYPE_OMIT
) {
1592 if (backctype
!= CTYPE_OMIT
&& (clin
> 0) == (backclin
> 0)) {
1593 /* TRANSLATORS: We've been told the foresight and backsight are
1594 * both "UP", or that they're both "DOWN". */
1595 compile_error_reading_skip(Clino
, /*Plumbed CLINO and BACKCLINO readings can't be in the same direction*/92);
1598 dz
= (clin
> (real
)0.0) ? tape
: -tape
;
1600 dz
= (backclin
< (real
)0.0) ? tape
: -tape
;
1602 vx
= vy
= var(Q_POS
) / 3.0 + dz
* dz
* var(Q_PLUMB
);
1603 vz
= var(Q_POS
) / 3.0 + VAR(Tape
);
1604 #ifndef NO_COVARIANCES
1605 /* Correct values - no covariances in this case! */
1606 cxy
= cyz
= czx
= (real
)0.0;
1609 /* Each of ctype and backctype are either CTYPE_READING/CTYPE_HORIZ
1612 real L2
, cosG
, LcosG
, cosG2
, sinB
, cosB
, dx2
, dy2
, dz2
, v
, V
;
1614 /* TRANSLATORS: Here "legs" are survey legs, i.e. measurements between
1615 * survey stations. */
1616 compile_error_reading_skip(Comp
, /*Compass reading may not be omitted except on plumbed legs*/14);
1619 if (tape
== (real
)0.0) {
1620 dx
= dy
= dz
= (real
)0.0;
1621 vx
= vy
= vz
= (real
)(var(Q_POS
) / 3.0); /* Position error only */
1622 #ifndef NO_COVARIANCES
1623 cxy
= cyz
= czx
= (real
)0.0;
1626 printf("Zero length leg: vx = %f, vy = %f, vz = %f\n", vx
, vy
, vz
);
1630 /* take into account variance in LEVEL case */
1631 real var_clin
= var(Q_LEVEL
);
1633 real comp
= handle_compass(&var_comp
);
1634 /* ctype != CTYPE_READING is LEVEL case */
1635 if (ctype
== CTYPE_READING
) {
1636 clin
= (clin
- pcs
->z
[Q_GRADIENT
]) * pcs
->sc
[Q_GRADIENT
];
1637 var_clin
= VAR(Clino
);
1639 if (backctype
== CTYPE_READING
) {
1640 backclin
= (backclin
- pcs
->z
[Q_BACKGRADIENT
])
1641 * pcs
->sc
[Q_BACKGRADIENT
];
1642 if (ctype
== CTYPE_READING
) {
1643 if (sqrd((clin
+ backclin
) / 3.0) > var_clin
+ VAR(BackClino
)) {
1644 /* fore and back readings differ by more than 3 sds */
1645 /* TRANSLATORS: %s is replaced by the amount the readings disagree
1646 * by, e.g. "2.5Ā°" or "3įµ". */
1647 warn_readings_differ(/*CLINO reading and BACKCLINO reading disagree by %s*/99,
1648 clin
+ backclin
, get_angle_units(Q_GRADIENT
));
1650 clin
= (clin
/ var_clin
- backclin
/ VAR(BackClino
));
1651 var_clin
= (var_clin
+ VAR(BackClino
)) / 4;
1655 var_clin
= VAR(BackClino
);
1660 printf(" %4.2f %4.2f %4.2f\n", tape
, comp
, clin
);
1663 LcosG
= tape
* cosG
;
1667 printf("sinB = %f, cosG = %f, LcosG = %f\n", sinB
, cosG
, LcosG
);
1671 dz
= tape
* sin(clin
);
1672 /* printf("%.2f\n",clin); */
1674 printf("dx = %f\ndy = %f\ndz = %f\n", dx
, dy
, dz
);
1680 cosG2
= cosG
* cosG
;
1681 sinGcosG
= sin(clin
) * cosG
;
1684 #ifdef NO_COVARIANCES
1685 vx
= (var(Q_POS
) / 3.0 + dx2
* V
+ dy2
* var_comp
+
1686 (.5 + sinB
* sinB
* cosG2
) * v
);
1687 vy
= (var(Q_POS
) / 3.0 + dy2
* V
+ dx2
* var_comp
+
1688 (.5 + cosB
* cosB
* cosG2
) * v
);
1689 if (ctype
== CTYPE_OMIT
&& backctype
== CTYPE_OMIT
) {
1690 /* if no clino, assume sd=tape/sqrt(10) so 3sds = .95*tape */
1691 vz
= var(Q_POS
) / 3.0 + L2
* (real
)0.1;
1693 vz
= var(Q_POS
) / 3.0 + dz2
* V
+ L2
* cosG2
* var_clin
;
1695 /* for Surveyor87 errors: vx=vy=vz=var(Q_POS)/3.0; */
1697 vx
= var(Q_POS
) / 3.0 + dx2
* V
+ dy2
* var_comp
+
1699 vy
= var(Q_POS
) / 3.0 + dy2
* V
+ dx2
* var_comp
+
1701 if (ctype
== CTYPE_OMIT
&& backctype
== CTYPE_OMIT
) {
1702 /* if no clino, assume sd=tape/sqrt(10) so 3sds = .95*tape */
1703 vz
= var(Q_POS
) / 3.0 + L2
* (real
)0.1;
1705 vz
= var(Q_POS
) / 3.0 + dz2
* V
+ L2
* cosG2
* var_clin
;
1707 /* usual covariance formulae are fine in no clino case since
1708 * dz = 0 so value of var_clin is ignored */
1709 cxy
= sinB
* cosB
* (VAR(Tape
) * cosG2
+ var_clin
* dz2
)
1710 - var_comp
* dx
* dy
;
1711 czx
= VAR(Tape
) * sinB
* sinGcosG
- var_clin
* dx
* dz
;
1712 cyz
= VAR(Tape
) * cosB
* sinGcosG
- var_clin
* dy
* dz
;
1714 printf("vx = %6.3f, vy = %6.3f, vz = %6.3f\n", vx
, vy
, vz
);
1715 printf("cxy = %6.3f, cyz = %6.3f, czx = %6.3f\n", cxy
, cyz
, czx
);
1719 printf("In DATAIN.C, vx = %f, vy = %f, vz = %f\n", vx
, vy
, vz
);
1724 printf("Just before addleg, vx = %f\n", vx
);
1726 /*printf("dx,dy,dz = %.2f %.2f %.2f\n\n", dx, dy, dz);*/
1727 addlegbyname(fr
, to
, fToFirst
, dx
, dy
, dz
, vx
, vy
, vz
1728 #ifndef NO_COVARIANCES
1736 process_diving(prefix
*fr
, prefix
*to
, bool fToFirst
, bool fDepthChange
)
1738 real tape
= VAL(Tape
);
1742 #ifndef NO_COVARIANCES
1743 real cxy
= 0, cyz
= 0, czx
= 0;
1746 handle_comp_units();
1748 /* depth gauge readings increase upwards with default calibration */
1750 SVX_ASSERT(VAL(FrDepth
) == 0.0);
1751 dz
= VAL(ToDepth
) * pcs
->units
[Q_DEPTH
] - pcs
->z
[Q_DEPTH
];
1752 dz
*= pcs
->sc
[Q_DEPTH
];
1754 dz
= VAL(ToDepth
) - VAL(FrDepth
);
1755 dz
*= pcs
->units
[Q_DEPTH
] * pcs
->sc
[Q_DEPTH
];
1758 /* adjusted tape is negative -- probably the calibration is wrong */
1759 if (tape
< (real
)0.0) {
1760 compile_diagnostic(DIAG_WARN
, /*Negative adjusted tape reading*/79);
1763 /* check if tape is less than depth change */
1764 if (tape
< fabs(dz
)) {
1765 /* FIXME: allow margin of error based on variances? */
1766 /* TRANSLATORS: This means that the data fed in said this.
1768 * It could be a gross error (e.g. the decimal point is missing from the
1769 * depth gauge reading) or it could just be due to random error on a near
1771 compile_diagnostic(DIAG_WARN
, /*Tape reading is less than change in depth*/62);
1774 if (tape
== (real
)0.0 && dz
== 0.0) {
1775 dx
= dy
= dz
= (real
)0.0;
1776 vx
= vy
= vz
= (real
)(var(Q_POS
) / 3.0); /* Position error only */
1777 } else if (VAL(Comp
) == HUGE_REAL
&&
1778 VAL(BackComp
) == HUGE_REAL
) {
1780 dx
= dy
= (real
)0.0;
1781 if (dz
< 0) tape
= -tape
;
1782 /* FIXME: Should use FrDepth sometimes... */
1783 dz
= (dz
* VAR(Tape
) + tape
* 2 * VAR(ToDepth
))
1784 / (VAR(Tape
) * 2 * VAR(ToDepth
));
1785 vx
= vy
= var(Q_POS
) / 3.0 + dz
* dz
* var(Q_PLUMB
);
1786 /* FIXME: Should use FrDepth sometimes... */
1787 vz
= var(Q_POS
) / 3.0 + VAR(Tape
) * 2 * VAR(ToDepth
)
1788 / (VAR(Tape
) + VAR(ToDepth
));
1790 real L2
, sinB
, cosB
, dz2
, D2
;
1792 real comp
= handle_compass(&var_comp
);
1798 if (D2
<= (real
)0.0) {
1799 /* FIXME: Should use FrDepth sometimes... */
1800 real vsum
= VAR(Tape
) + 2 * VAR(ToDepth
);
1801 dx
= dy
= (real
)0.0;
1802 vx
= vy
= var(Q_POS
) / 3.0;
1803 /* FIXME: Should use FrDepth sometimes... */
1804 vz
= var(Q_POS
) / 3.0 + VAR(Tape
) * 2 * VAR(ToDepth
) / vsum
;
1806 /* FIXME: Should use FrDepth sometimes... */
1807 dz
= (dz
* VAR(Tape
) + tape
* 2 * VAR(ToDepth
)) / vsum
;
1809 dz
= (dz
* VAR(Tape
) - tape
* 2 * VAR(ToDepth
)) / vsum
;
1813 /* FIXME: Should use FrDepth sometimes... */
1814 real F
= VAR(Tape
) * L2
+ 2 * VAR(ToDepth
) * D2
;
1818 vx
= var(Q_POS
) / 3.0 +
1819 sinB
* sinB
* F
/ D2
+ var_comp
* dy
* dy
;
1820 vy
= var(Q_POS
) / 3.0 +
1821 cosB
* cosB
* F
/ D2
+ var_comp
* dx
* dx
;
1822 /* FIXME: Should use FrDepth sometimes... */
1823 vz
= var(Q_POS
) / 3.0 + 2 * VAR(ToDepth
);
1825 #ifndef NO_COVARIANCES
1826 cxy
= sinB
* cosB
* (F
/ D2
+ var_comp
* D2
);
1827 /* FIXME: Should use FrDepth sometimes... */
1828 cyz
= -2 * VAR(ToDepth
) * dy
/ D
;
1829 czx
= -2 * VAR(ToDepth
) * dx
/ D
;
1832 /* FIXME: If there's a clino reading, check it against the depth reading,
1834 * if (VAL(Clino) != HUGE_REAL || VAL(BackClino) != HUGE_REAL) { ... }
1837 addlegbyname(fr
, to
, fToFirst
, dx
, dy
, dz
, vx
, vy
, vz
1838 #ifndef NO_COVARIANCES
1846 process_cartesian(prefix
*fr
, prefix
*to
, bool fToFirst
)
1848 real dx
= (VAL(Dx
) * pcs
->units
[Q_DX
] - pcs
->z
[Q_DX
]) * pcs
->sc
[Q_DX
];
1849 real dy
= (VAL(Dy
) * pcs
->units
[Q_DY
] - pcs
->z
[Q_DY
]) * pcs
->sc
[Q_DY
];
1850 real dz
= (VAL(Dz
) * pcs
->units
[Q_DZ
] - pcs
->z
[Q_DZ
]) * pcs
->sc
[Q_DZ
];
1852 addlegbyname(fr
, to
, fToFirst
, dx
, dy
, dz
, VAR(Dx
), VAR(Dy
), VAR(Dz
)
1853 #ifndef NO_COVARIANCES
1861 data_cartesian(void)
1863 prefix
*fr
= NULL
, *to
= NULL
;
1865 bool fMulti
= false;
1867 reading first_stn
= End
;
1869 const reading
*ordering
;
1873 for (ordering
= pcs
->ordering
; ; ordering
++) {
1875 switch (*ordering
) {
1877 fr
= read_prefix(PFX_STATION
|PFX_ALLOW_ROOT
);
1878 if (first_stn
== End
) first_stn
= Fr
;
1881 to
= read_prefix(PFX_STATION
|PFX_ALLOW_ROOT
);
1882 if (first_stn
== End
) first_stn
= To
;
1886 to
= read_prefix(PFX_STATION
);
1889 case Dx
: case Dy
: case Dz
:
1890 read_reading(*ordering
, false);
1894 case IgnoreAllAndNewLine
:
1899 if (!process_cartesian(fr
, to
, first_stn
== To
))
1906 if (isData(ch
)) break;
1917 process_cartesian(fr
, to
, first_stn
== To
);
1924 } while (isComm(ch
));
1926 default: BUG("Unknown reading in ordering");
1932 process_cylpolar(prefix
*fr
, prefix
*to
, bool fToFirst
, bool fDepthChange
)
1934 real tape
= VAL(Tape
);
1938 #ifndef NO_COVARIANCES
1942 handle_comp_units();
1944 /* depth gauge readings increase upwards with default calibration */
1946 SVX_ASSERT(VAL(FrDepth
) == 0.0);
1947 dz
= VAL(ToDepth
) * pcs
->units
[Q_DEPTH
] - pcs
->z
[Q_DEPTH
];
1948 dz
*= pcs
->sc
[Q_DEPTH
];
1950 dz
= VAL(ToDepth
) - VAL(FrDepth
);
1951 dz
*= pcs
->units
[Q_DEPTH
] * pcs
->sc
[Q_DEPTH
];
1954 /* adjusted tape is negative -- probably the calibration is wrong */
1955 if (tape
< (real
)0.0) {
1956 compile_diagnostic(DIAG_WARN
, /*Negative adjusted tape reading*/79);
1959 if (VAL(Comp
) == HUGE_REAL
&& VAL(BackComp
) == HUGE_REAL
) {
1961 dx
= dy
= (real
)0.0;
1962 vx
= vy
= var(Q_POS
) / 3.0 + dz
* dz
* var(Q_PLUMB
);
1963 /* FIXME: Should use FrDepth sometimes... */
1964 vz
= var(Q_POS
) / 3.0 + 2 * VAR(ToDepth
);
1968 real comp
= handle_compass(&var_comp
);
1975 vx
= var(Q_POS
) / 3.0 +
1976 VAR(Tape
) * sinB
* sinB
+ var_comp
* dy
* dy
;
1977 vy
= var(Q_POS
) / 3.0 +
1978 VAR(Tape
) * cosB
* cosB
+ var_comp
* dx
* dx
;
1979 /* FIXME: Should use FrDepth sometimes... */
1980 vz
= var(Q_POS
) / 3.0 + 2 * VAR(ToDepth
);
1982 #ifndef NO_COVARIANCES
1983 cxy
= (VAR(Tape
) - var_comp
* tape
* tape
) * sinB
* cosB
;
1986 addlegbyname(fr
, to
, fToFirst
, dx
, dy
, dz
, vx
, vy
, vz
1987 #ifndef NO_COVARIANCES
1994 /* Process tape/compass/clino, diving, and cylpolar styles of survey data
1995 * Also handles topofil (fromcount/tocount or count) in place of tape */
1999 prefix
*fr
= NULL
, *to
= NULL
;
2000 reading first_stn
= End
;
2002 bool fTopofil
= false, fMulti
= false;
2004 clino_type ctype
, backctype
;
2006 unsigned long compass_dat_flags
= 0;
2008 const reading
*ordering
;
2010 VAL(Tape
) = VAL(BackTape
) = HUGE_REAL
;
2011 VAL(Comp
) = VAL(BackComp
) = HUGE_REAL
;
2012 VAL(FrCount
) = VAL(ToCount
) = 0;
2013 VAL(FrDepth
) = VAL(ToDepth
) = 0;
2014 VAL(Left
) = VAL(Right
) = VAL(Up
) = VAL(Down
) = HUGE_REAL
;
2017 ctype
= backctype
= CTYPE_OMIT
;
2018 fDepthChange
= false;
2020 /* ordering may omit clino reading, so set up default here */
2021 /* this is also used if clino reading is the omit character */
2022 VAL(Clino
) = VAL(BackClino
) = 0;
2026 /* We clear these flags in the normal course of events, but if there's an
2027 * error in a reading, we might not, so make sure it has been cleared here.
2029 pcs
->flags
&= ~(BIT(FLAGS_ANON_ONE_END
) | BIT(FLAGS_IMPLICIT_SPLAY
));
2030 for (ordering
= pcs
->ordering
; ; ordering
++) {
2032 switch (*ordering
) {
2034 fr
= read_prefix(PFX_STATION
|PFX_ALLOW_ROOT
|PFX_ANON
);
2035 if (first_stn
== End
) first_stn
= Fr
;
2038 to
= read_prefix(PFX_STATION
|PFX_ALLOW_ROOT
|PFX_ANON
);
2039 if (first_stn
== End
) first_stn
= To
;
2042 // Compass DAT is always From then To.
2044 fr
= read_prefix(PFX_STATION
);
2045 scan_compass_station_name(fr
);
2048 to
= read_prefix(PFX_STATION
);
2049 scan_compass_station_name(to
);
2053 to
= read_prefix(PFX_STATION
);
2058 DIR_NULL
=-1, DIR_FORE
, DIR_BACK
2060 static const sztok dir_tab
[] = {
2066 tok
= match_tok(dir_tab
, TABSIZE(dir_tab
));
2074 compile_diagnostic(DIAG_ERR
|DIAG_BUF
|DIAG_SKIP
, /*Found ā%sā, expecting āFā or āBā*/131, s_str(&token
));
2080 case Tape
: case BackTape
: {
2081 reading r
= *ordering
;
2082 read_reading(r
, true);
2083 if (VAL(r
) == HUGE_REAL
) {
2085 compile_diagnostic_token_show(DIAG_ERR
, /*Expecting numeric field, found ā%sā*/9);
2086 /* Avoid also warning about omitted tape reading. */
2091 } else if (VAL(r
) < (real
)0.0) {
2092 compile_diagnostic_reading(DIAG_WARN
, r
, /*Negative tape reading*/60);
2097 VAL(FrCount
) = VAL(ToCount
);
2098 LOC(FrCount
) = LOC(ToCount
);
2099 WID(FrCount
) = WID(ToCount
);
2100 read_reading(ToCount
, false);
2104 read_reading(FrCount
, false);
2107 read_reading(ToCount
, false);
2110 case Comp
: case BackComp
:
2111 read_bearing_or_omit(*ordering
);
2113 case Clino
: case BackClino
: {
2114 reading r
= *ordering
;
2115 clino_type
* p_ctype
= (r
== Clino
? &ctype
: &backctype
);
2116 read_reading(r
, true);
2117 if (VAL(r
) == HUGE_REAL
) {
2118 VAL(r
) = handle_plumb(p_ctype
);
2119 if (VAL(r
) != HUGE_REAL
) break;
2120 compile_diagnostic_token_show(DIAG_ERR
, /*Expecting numeric field, found ā%sā*/9);
2125 *p_ctype
= CTYPE_READING
;
2128 case FrDepth
: case ToDepth
:
2129 read_reading(*ordering
, false);
2132 VAL(FrDepth
) = VAL(ToDepth
);
2133 LOC(FrDepth
) = LOC(ToDepth
);
2134 WID(FrDepth
) = WID(ToDepth
);
2135 read_reading(ToDepth
, false);
2138 fDepthChange
= true;
2140 read_reading(ToDepth
, false);
2142 case CompassDATComp
:
2143 read_bearing_or_omit(Comp
);
2144 if (is_compass_NaN(VAL(Comp
))) VAL(Comp
) = HUGE_REAL
;
2146 case CompassDATBackComp
:
2147 read_bearing_or_omit(BackComp
);
2148 if (is_compass_NaN(VAL(BackComp
))) VAL(BackComp
) = HUGE_REAL
;
2150 case CompassDATClino
: case CompassDATBackClino
: {
2152 clino_type
* p_ctype
;
2153 if (*ordering
== CompassDATClino
) {
2158 p_ctype
= &backctype
;
2160 read_reading(r
, false);
2161 if (is_compass_NaN(VAL(r
))) {
2163 *p_ctype
= CTYPE_OMIT
;
2165 *p_ctype
= CTYPE_READING
;
2169 case CompassDATLeft
: case CompassDATRight
:
2170 case CompassDATUp
: case CompassDATDown
: {
2171 /* FIXME: need to actually make use of these entries! */
2172 reading actual
= Left
+ (*ordering
- CompassDATLeft
);
2173 read_reading(actual
, false);
2174 if (VAL(actual
) < 0) VAL(actual
) = HUGE_REAL
;
2177 case CompassDATFlags
:
2184 while (ch
>= 'A' && ch
<= 'Z') {
2185 compass_dat_flags
|= BIT(ch
- 'A');
2187 * L (exclude from length)
2189 * P (no plot) (mapped to FLAG_SURFACE)
2191 * FIXME: Defined flags we currently ignore:
2192 * C (no adjustment) (set all (co)variances to 0? Then
2193 * we need to handle a loop of such legs or a traverse
2194 * of such legs between two fixed points...)
2201 compass_dat_flags
= 0;
2211 case IgnoreAllAndNewLine
:
2220 VAL(Tape
) = VAL(ToCount
) - VAL(FrCount
);
2221 LOC(Tape
) = LOC(ToCount
);
2222 WID(Tape
) = WID(ToCount
);
2224 /* Note: frdepth == todepth test works regardless of fDepthChange
2225 * (frdepth always zero, todepth is change of depth) and also
2226 * works for STYLE_NORMAL (both remain 0) */
2227 if (TSTBIT(pcs
->infer
, INFER_EQUATES
) &&
2228 (VAL(Tape
) == (real
)0.0 || VAL(Tape
) == HUGE_REAL
) &&
2229 (VAL(BackTape
) == (real
)0.0 || VAL(BackTape
) == HUGE_REAL
) &&
2230 VAL(FrDepth
) == VAL(ToDepth
)) {
2231 if (!TSTBIT(pcs
->infer
, INFER_EQUATES_SELF_OK
) || fr
!= to
)
2232 process_equate(fr
, to
);
2233 goto inferred_equate
;
2241 VAL(Tape
) *= pcs
->units
[Q_COUNT
] * pcs
->sc
[Q_COUNT
];
2242 } else if (VAL(Tape
) != HUGE_REAL
) {
2243 VAL(Tape
) *= pcs
->units
[Q_LENGTH
];
2244 VAL(Tape
) -= pcs
->z
[Q_LENGTH
];
2245 VAL(Tape
) *= pcs
->sc
[Q_LENGTH
];
2247 if (VAL(BackTape
) != HUGE_REAL
) {
2248 VAL(BackTape
) *= pcs
->units
[Q_BACKLENGTH
];
2249 VAL(BackTape
) -= pcs
->z
[Q_BACKLENGTH
];
2250 VAL(BackTape
) *= pcs
->sc
[Q_BACKLENGTH
];
2251 if (VAL(Tape
) != HUGE_REAL
) {
2252 real diff
= VAL(Tape
) - VAL(BackTape
);
2253 if (sqrd(diff
/ 3.0) > VAR(Tape
) + VAR(BackTape
)) {
2254 /* fore and back readings differ by more than 3 sds */
2255 /* TRANSLATORS: %s is replaced by the amount the readings disagree
2256 * by, e.g. "0.12m" or "0.2ft". */
2257 warn_readings_differ(/*TAPE reading and BACKTAPE reading disagree by %s*/97,
2258 diff
, get_length_units(Q_LENGTH
));
2260 VAL(Tape
) = VAL(Tape
) / VAR(Tape
) + VAL(BackTape
) / VAR(BackTape
);
2261 VAR(Tape
) = (VAR(Tape
) + VAR(BackTape
)) / 4;
2262 VAL(Tape
) *= VAR(Tape
);
2264 VAL(Tape
) = VAL(BackTape
);
2265 VAR(Tape
) = VAR(BackTape
);
2267 } else if (VAL(Tape
) == HUGE_REAL
) {
2268 compile_diagnostic_reading(DIAG_ERR
, Tape
, /*Tape reading may not be omitted*/94);
2269 goto inferred_equate
;
2271 implicit_splay
= TSTBIT(pcs
->flags
, FLAGS_IMPLICIT_SPLAY
);
2272 pcs
->flags
&= ~(BIT(FLAGS_ANON_ONE_END
) | BIT(FLAGS_IMPLICIT_SPLAY
));
2273 save_flags
= pcs
->flags
;
2274 if (implicit_splay
) {
2275 pcs
->flags
|= BIT(FLAGS_SPLAY
);
2277 switch (pcs
->style
) {
2279 r
= process_normal(fr
, to
, (first_stn
== To
) ^ fRev
,
2283 /* FIXME: Handle any clino readings */
2284 r
= process_diving(fr
, to
, (first_stn
== To
) ^ fRev
,
2287 case STYLE_CYLPOLAR
:
2288 r
= process_cylpolar(fr
, to
, (first_stn
== To
) ^ fRev
,
2292 r
= 0; /* avoid warning */
2295 pcs
->flags
= save_flags
;
2298 /* Swap fr and to back to how they were for next line */
2307 ctype
= backctype
= CTYPE_OMIT
;
2308 fDepthChange
= false;
2310 /* ordering may omit clino reading, so set up default here */
2311 /* this is also used if clino reading is the omit character */
2312 VAL(Clino
) = VAL(BackClino
) = 0;
2313 LOC(Clino
) = LOC(BackClino
) = -1;
2314 WID(Clino
) = WID(BackClino
) = 0;
2322 if (isData(ch
)) break;
2335 /* Compass ignore flag is 'X' */
2336 if ((compass_dat_flags
& BIT('X' - 'A'))) {
2346 VAL(Tape
) = VAL(ToCount
) - VAL(FrCount
);
2347 LOC(Tape
) = LOC(ToCount
);
2348 WID(Tape
) = WID(ToCount
);
2350 /* Note: frdepth == todepth test works regardless of fDepthChange
2351 * (frdepth always zero, todepth is change of depth) and also
2352 * works for STYLE_NORMAL (both remain 0) */
2353 if (TSTBIT(pcs
->infer
, INFER_EQUATES
) &&
2354 (VAL(Tape
) == (real
)0.0 || VAL(Tape
) == HUGE_REAL
) &&
2355 (VAL(BackTape
) == (real
)0.0 || VAL(BackTape
) == HUGE_REAL
) &&
2356 VAL(FrDepth
) == VAL(ToDepth
)) {
2357 if (!TSTBIT(pcs
->infer
, INFER_EQUATES_SELF_OK
) || fr
!= to
)
2358 process_equate(fr
, to
);
2363 VAL(Tape
) *= pcs
->units
[Q_COUNT
] * pcs
->sc
[Q_COUNT
];
2364 } else if (VAL(Tape
) != HUGE_REAL
) {
2365 VAL(Tape
) *= pcs
->units
[Q_LENGTH
];
2366 VAL(Tape
) -= pcs
->z
[Q_LENGTH
];
2367 VAL(Tape
) *= pcs
->sc
[Q_LENGTH
];
2369 if (VAL(BackTape
) != HUGE_REAL
) {
2370 VAL(BackTape
) *= pcs
->units
[Q_BACKLENGTH
];
2371 VAL(BackTape
) -= pcs
->z
[Q_BACKLENGTH
];
2372 VAL(BackTape
) *= pcs
->sc
[Q_BACKLENGTH
];
2373 if (VAL(Tape
) != HUGE_REAL
) {
2374 real diff
= VAL(Tape
) - VAL(BackTape
);
2375 if (sqrd(diff
/ 3.0) > VAR(Tape
) + VAR(BackTape
)) {
2376 /* fore and back readings differ by more than 3 sds */
2377 /* TRANSLATORS: %s is replaced by the amount the readings disagree
2378 * by, e.g. "0.12m" or "0.2ft". */
2379 warn_readings_differ(/*TAPE reading and BACKTAPE reading disagree by %s*/97,
2380 diff
, get_length_units(Q_LENGTH
));
2382 VAL(Tape
) = VAL(Tape
) / VAR(Tape
) + VAL(BackTape
) / VAR(BackTape
);
2383 VAR(Tape
) = (VAR(Tape
) + VAR(BackTape
)) / 4;
2384 VAL(Tape
) *= VAR(Tape
);
2386 VAL(Tape
) = VAL(BackTape
);
2387 VAR(Tape
) = VAR(BackTape
);
2389 } else if (VAL(Tape
) == HUGE_REAL
) {
2390 compile_diagnostic_reading(DIAG_ERR
, Tape
, /*Tape reading may not be omitted*/94);
2394 implicit_splay
= TSTBIT(pcs
->flags
, FLAGS_IMPLICIT_SPLAY
);
2395 pcs
->flags
&= ~(BIT(FLAGS_ANON_ONE_END
) | BIT(FLAGS_IMPLICIT_SPLAY
));
2396 save_flags
= pcs
->flags
;
2397 if (implicit_splay
) {
2398 pcs
->flags
|= BIT(FLAGS_SPLAY
);
2400 if ((compass_dat_flags
& BIT('S' - 'A'))) {
2401 /* 'S' means "splay". */
2402 pcs
->flags
|= BIT(FLAGS_SPLAY
);
2404 if ((compass_dat_flags
& BIT('P' - 'A'))) {
2405 /* 'P' means "Exclude this shot from plotting", but the use
2406 * suggested in the Compass docs is for surface data, and legs
2407 * with this flag "[do] not support passage modeling".
2409 * Even if it's actually being used for a different
2410 * purpose, Survex programs don't show surface legs
2411 * by default so FLAGS_SURFACE matches fairly well.
2413 pcs
->flags
|= BIT(FLAGS_SURFACE
);
2415 if ((compass_dat_flags
& BIT('L' - 'A'))) {
2416 /* 'L' means "exclude from length" - map this to Survex's
2417 * FLAGS_DUPLICATE. */
2418 pcs
->flags
|= BIT(FLAGS_DUPLICATE
);
2420 switch (pcs
->style
) {
2422 process_normal(fr
, to
, (first_stn
== To
) ^ fRev
,
2426 /* FIXME: Handle any clino readings */
2427 process_diving(fr
, to
, (first_stn
== To
) ^ fRev
,
2430 case STYLE_CYLPOLAR
:
2431 process_cylpolar(fr
, to
, (first_stn
== To
) ^ fRev
,
2437 pcs
->flags
= save_flags
;
2445 } while (isComm(ch
));
2448 BUG("Unknown reading in ordering");
2454 process_lrud(prefix
*stn
)
2456 SVX_ASSERT(next_lrud
);
2457 lrud
* xsect
= osnew(lrud
);
2459 xsect
->l
= (VAL(Left
) * pcs
->units
[Q_LEFT
] - pcs
->z
[Q_LEFT
]) * pcs
->sc
[Q_LEFT
];
2460 xsect
->r
= (VAL(Right
) * pcs
->units
[Q_RIGHT
] - pcs
->z
[Q_RIGHT
]) * pcs
->sc
[Q_RIGHT
];
2461 xsect
->u
= (VAL(Up
) * pcs
->units
[Q_UP
] - pcs
->z
[Q_UP
]) * pcs
->sc
[Q_UP
];
2462 xsect
->d
= (VAL(Down
) * pcs
->units
[Q_DOWN
] - pcs
->z
[Q_DOWN
]) * pcs
->sc
[Q_DOWN
];
2463 xsect
->meta
= pcs
->meta
;
2464 if (pcs
->meta
) ++pcs
->meta
->ref_count
;
2467 next_lrud
= &(xsect
->next
);
2476 const reading
*ordering
;
2478 for (ordering
= pcs
->ordering
; ; ordering
++) {
2480 switch (*ordering
) {
2482 stn
= read_prefix(PFX_STATION
);
2484 case Left
: case Right
: case Up
: case Down
: {
2485 reading r
= *ordering
;
2486 read_reading(r
, true);
2487 if (VAL(r
) == HUGE_REAL
) {
2489 compile_diagnostic_token_show(DIAG_ERR
, /*Expecting numeric field, found ā%sā*/9);
2507 default: BUG("Unknown reading in ordering");
2513 process_nosurvey(prefix
*fr
, prefix
*to
, bool fToFirst
)
2517 /* Suppress "unused fixed point" warnings for these stations */
2518 fr
->sflags
|= BIT(SFLAGS_USED
);
2519 to
->sflags
|= BIT(SFLAGS_USED
);
2521 /* add to linked list which is dealt with after network is solved */
2522 link
= osnew(nosurveylink
);
2524 link
->to
= StnFromPfx(to
);
2525 link
->fr
= StnFromPfx(fr
);
2527 link
->fr
= StnFromPfx(fr
);
2528 link
->to
= StnFromPfx(to
);
2530 link
->flags
= pcs
->flags
| (STYLE_NOSURVEY
<< FLAGS_STYLE_BIT0
);
2531 link
->meta
= pcs
->meta
;
2532 if (pcs
->meta
) ++pcs
->meta
->ref_count
;
2533 link
->next
= nosurveyhead
;
2534 nosurveyhead
= link
;
2541 prefix
*fr
= NULL
, *to
= NULL
;
2543 bool fMulti
= false;
2545 reading first_stn
= End
;
2547 const reading
*ordering
;
2551 for (ordering
= pcs
->ordering
; ; ordering
++) {
2553 switch (*ordering
) {
2555 fr
= read_prefix(PFX_STATION
|PFX_ALLOW_ROOT
);
2556 if (first_stn
== End
) first_stn
= Fr
;
2559 to
= read_prefix(PFX_STATION
|PFX_ALLOW_ROOT
);
2560 if (first_stn
== End
) first_stn
= To
;
2564 to
= read_prefix(PFX_STATION
);
2569 case IgnoreAllAndNewLine
:
2574 if (!process_nosurvey(fr
, to
, first_stn
== To
))
2577 if (ordering
[1] == End
) {
2581 } while (isComm(ch
));
2591 if (isData(ch
)) break;
2602 (void)process_nosurvey(fr
, to
, first_stn
== To
);
2609 } while (isComm(ch
));
2611 default: BUG("Unknown reading in ordering");
2616 /* totally ignore a line of survey data */