1 /*
2  # This file is part of the Astrometry.net suite.
3  # Licensed under a 3-clause BSD style license - see LICENSE
4  */
5 #include <math.h>
6 #include <stdio.h>
7 #include <string.h>
8 #include <assert.h>
9 
10 #include "os-features.h"
11 #include "2mass.h"
12 #include "starutil.h"
13 
twomass_is_value_null(float val)14 int twomass_is_value_null(float val) {
15     return (!isfinite(val));
16 }
17 
18 // parses a value that may be null (represented by "\N" in the 2MASS data files).
19 // expects the value to be followed by "|".
parse_null(const char ** pcursor,float * dest)20 static int parse_null(const char** pcursor, float* dest) {
21     int nchars;
22     if (!strncmp(*pcursor, "\\N|", 3)) {
23         *dest = TWOMASS_NULL;
24         *pcursor += 3;
25         return 0;
26     }
27     if (sscanf(*pcursor, "%f|%n", dest, &nchars) != 1) {
28         return -1;
29     }
30     *pcursor += nchars;
31     return 0;
32 }
33 
parse_quality_flag(char flag,char * val)34 static int parse_quality_flag(char flag, char* val) {
35     switch (flag) {
36     case 'X':
37         *val = TWOMASS_QUALITY_NO_BRIGHTNESS;
38         break;
39     case 'U':
40         *val = TWOMASS_QUALITY_UPPER_LIMIT_MAG;
41         break;
42     case 'F':
43         *val = TWOMASS_QUALITY_NO_SIGMA;
44         break;
45     case 'E':
46         *val = TWOMASS_QUALITY_BAD_FIT;
47         break;
48     case 'A':
49         *val = TWOMASS_QUALITY_A;
50         break;
51     case 'B':
52         *val = TWOMASS_QUALITY_B;
53         break;
54     case 'C':
55         *val = TWOMASS_QUALITY_C;
56         break;
57     case 'D':
58         *val = TWOMASS_QUALITY_D;
59         break;
60     default:
61         return -1;
62     }
63     return 0;
64 }
65 
parse_cc_flag(char flag,char * val)66 static int parse_cc_flag(char flag, char* val) {
67     switch (flag) {
68     case 'p':
69         *val = TWOMASS_CC_PERSISTENCE;
70         break;
71     case 'c':
72         *val = TWOMASS_CC_CONFUSION;
73         break;
74     case 'd':
75         *val = TWOMASS_CC_DIFFRACTION;
76         break;
77     case 's':
78         *val = TWOMASS_CC_STRIPE;
79         break;
80     case 'b':
81         *val = TWOMASS_CC_BANDMERGE;
82         break;
83     case '0':
84         *val = TWOMASS_CC_NONE;
85         break;
86     default:
87         return -1;
88     }
89     return 0;
90 }
91 
92 
93 #define ensure(c, f) { if (c!='|') { fprintf(stderr, "Expected '|' following field %s in 2MASS line.\n", f); return -1; }}
94 
95 //#define printval printf
96 //#define printval(s,args...) printf(s,args)
97 #define printval(s,args...) {}
98 
twomass_cc_flag(unsigned char val,unsigned char flag)99 int twomass_cc_flag(unsigned char val, unsigned char flag) {
100     return (val == flag);
101 }
102 
twomass_quality_flag(unsigned char val,unsigned char flag)103 int twomass_quality_flag(unsigned char val, unsigned char flag) {
104     return (val == flag);
105 }
106 
twomass_is_null_float(float f)107 int twomass_is_null_float(float f) {
108     return isnan(f);
109 }
110 
twomass_parse_entry(struct twomass_entry * e,const char * line)111 int twomass_parse_entry(struct twomass_entry* e, const char* line) {
112     const char* cursor;
113     int nchars;
114     int i;
115     double vals1[5];
116     float val2;
117     float val3;
118 
119     cursor = line;
120     for (i=0; i<5; i++) {
121         char* names[] = { "ra", "dec", "err_maj", "err_min", "err_ang" };
122         if (sscanf(cursor, "%lf|%n", vals1+i, &nchars) != 1) {
123             fprintf(stderr, "Failed to parse \"%s\" entry in 2MASS line.\n", names[i]);
124             return -1;
125         }
126         cursor += nchars;
127     }
128 
129     // note: the bracketed units are the units used in the [source file : struct];
130     // if only unit is given then it's the same in both.
131 
132     e->ra = vals1[0];  // [deg]
133     e->dec = vals1[1]; // [deg]
134     e->err_major = arcsec2deg(vals1[2]); // [arcsec : deg]
135     e->err_minor = arcsec2deg(vals1[3]); // [arcsec : deg]
136     e->err_angle = vals1[4]; // [deg]
137 
138     printval("ra %g, dec %g, err_major %g, err_minor %g, err_angle %i\n", e->ra, e->dec, e->err_major, e->err_minor, e->err_angle);
139 
140     strncpy(e->designation, cursor, 17);
141     e->designation[17] = '\0';
142     if (strlen(e->designation) != 17) {
143         fprintf(stderr, "Failed to parse \"designation\" entry in 2MASS line.\n");
144         return -1;
145     }
146     cursor += 18;
147 
148     printval("designation %s\n", e->designation);
149 
150     for (i=0; i<12; i++) {
151         char* names[] = {
152             "j_m", "j_cmsig", "j_msigcom", "j_snr",
153             "h_m", "h_cmsig", "h_msigcom", "h_snr",
154             "k_m", "k_cmsig", "k_msigcom", "k_snr" };
155         float* dests[] = {
156             &e->j_m, &e->j_cmsig, &e->j_msigcom, &e->j_snr,
157             &e->h_m, &e->h_cmsig, &e->h_msigcom, &e->h_snr,
158             &e->k_m, &e->k_cmsig, &e->k_msigcom, &e->k_snr };
159         // these are all [mag] or unitless
160         if (parse_null(&cursor, dests[i])) {
161             fprintf(stderr, "Failed to parse \"%s\" entry in 2MASS line.\n", names[i]);
162             return -1;
163         }
164     }
165 
166     printval("j_m %g, j_cmsig %g, j_msigcom %g, j_snr %g.\n", e->j_m, e->j_cmsig, e->j_msigcom, e->j_snr);
167     printval("h_m %g, h_cmsig %g, h_msigcom %g, h_snr %g.\n", e->h_m, e->h_cmsig, e->h_msigcom, e->h_snr);
168     printval("k_m %g, k_cmsig %g, k_msigcom %g, k_snr %g.\n", e->k_m, e->k_cmsig, e->k_msigcom, e->k_snr);
169 
170     for (i=0; i<3; i++) {
171         char bands[] = { 'j', 'h', 'k' };
172         char* quals[] = { &e->j_quality, &e->h_quality, &e->k_quality };
173         if (parse_quality_flag(*cursor, quals[i])) {
174             fprintf(stderr, "Failed to parse '%c_quality' entry in 2MASS line.\n", bands[i]);
175             return -1;
176         }
177         cursor++;
178     }
179 
180     printval("j X=%i, U=%i, F=%i, E=%i, A=%i, B=%i, C=%i, D=%i\n",
181              twomass_quality_flag(e->j_quality, TWOMASS_QUALITY_NO_BRIGHTNESS),
182              twomass_quality_flag(e->j_quality, TWOMASS_QUALITY_UPPER_LIMIT_MAG),
183              twomass_quality_flag(e->j_quality, TWOMASS_QUALITY_NO_SIGMA),
184              twomass_quality_flag(e->j_quality, TWOMASS_QUALITY_BAD_FIT),
185              twomass_quality_flag(e->j_quality, TWOMASS_QUALITY_A),
186              twomass_quality_flag(e->j_quality, TWOMASS_QUALITY_B),
187              twomass_quality_flag(e->j_quality, TWOMASS_QUALITY_C),
188              twomass_quality_flag(e->j_quality, TWOMASS_QUALITY_D));
189 
190     printval("h X=%i, U=%i, F=%i, E=%i, A=%i, B=%i, C=%i, D=%i\n",
191              twomass_quality_flag(e->h_quality, TWOMASS_QUALITY_NO_BRIGHTNESS),
192              twomass_quality_flag(e->h_quality, TWOMASS_QUALITY_UPPER_LIMIT_MAG),
193              twomass_quality_flag(e->h_quality, TWOMASS_QUALITY_NO_SIGMA),
194              twomass_quality_flag(e->h_quality, TWOMASS_QUALITY_BAD_FIT),
195              twomass_quality_flag(e->h_quality, TWOMASS_QUALITY_A),
196              twomass_quality_flag(e->h_quality, TWOMASS_QUALITY_B),
197              twomass_quality_flag(e->h_quality, TWOMASS_QUALITY_C),
198              twomass_quality_flag(e->h_quality, TWOMASS_QUALITY_D));
199 
200     printval("k X=%i, U=%i, F=%i, E=%i, A=%i, B=%i, C=%i, D=%i\n",
201              twomass_quality_flag(e->k_quality, TWOMASS_QUALITY_NO_BRIGHTNESS),
202              twomass_quality_flag(e->k_quality, TWOMASS_QUALITY_UPPER_LIMIT_MAG),
203              twomass_quality_flag(e->k_quality, TWOMASS_QUALITY_NO_SIGMA),
204              twomass_quality_flag(e->k_quality, TWOMASS_QUALITY_BAD_FIT),
205              twomass_quality_flag(e->k_quality, TWOMASS_QUALITY_A),
206              twomass_quality_flag(e->k_quality, TWOMASS_QUALITY_B),
207              twomass_quality_flag(e->k_quality, TWOMASS_QUALITY_C),
208              twomass_quality_flag(e->k_quality, TWOMASS_QUALITY_D));
209 
210     ensure(*cursor, "flags");
211     cursor++;
212 
213     for (i=0; i<3; i++) {
214         uint8_t* dests[] = { &e->j_read_flag, &e->h_read_flag, &e->k_read_flag };
215 
216         if (*cursor < '0' || *cursor > '9') {
217             fprintf(stderr, "Error parsing read_flag from 2MASS entry.\n");
218             return -1;
219         }
220         *(dests[i]) = *cursor - '0';
221         cursor++;
222     }
223     printval("read_flag j=%i, h=%i, k=%i.\n", e->j_read_flag, e->h_read_flag, e->k_read_flag);
224 
225     ensure(*cursor, "read_flag");
226     cursor++;
227 
228     for (i=0; i<3; i++) {
229         uint8_t* dests[] = { &e->j_blend_flag, &e->h_blend_flag, &e->k_blend_flag };
230         if (*cursor < '0' || *cursor > '9') {
231             fprintf(stderr, "Failed to parse blend_flag field in a 2MASS entry.\n");
232             return -1;
233         }
234         *(dests[i]) = *cursor - '0';
235         cursor++;
236     }
237     printval("blend_flag j=%i, h=%i, k=%i.\n", e->j_blend_flag, e->h_blend_flag, e->k_blend_flag);
238 
239     ensure(*cursor, "blend_flag");
240     cursor++;
241 
242     for (i=0; i<3; i++) {
243         char bands[] = { 'j', 'h', 'k' };
244         char* ccs[] = { &e->j_cc, &e->h_cc, &e->k_cc };
245         if (parse_cc_flag(*cursor, ccs[i])) {
246             fprintf(stderr, "Failed to parse '%c_cc' entry in 2MASS line.\n", bands[i]);
247             return -1;
248         }
249         cursor++;
250     }
251 
252     printval("j_confusion: p=%i, c=%i, d=%i, s=%i, b=%i.\n",
253              twomass_cc_flag(e->j_cc, TWOMASS_CC_PERSISTENCE),
254              twomass_cc_flag(e->j_cc, TWOMASS_CC_CONFUSION),
255              twomass_cc_flag(e->j_cc, TWOMASS_CC_DIFFFRACTION),
256              twomass_cc_flag(e->j_cc, TWOMASS_CC_STRIPE),
257              twomass_cc_flag(e->j_cc, TWOMASS_CC_BANDMERGE));
258     printval("h_confusion: p=%i, c=%i, d=%i, s=%i, b=%i.\n",
259              twomass_cc_flag(e->h_cc, TWOMASS_CC_PERSISTENCE),
260              twomass_cc_flag(e->h_cc, TWOMASS_CC_CONFUSION),
261              twomass_cc_flag(e->h_cc, TWOMASS_CC_DIFFFRACTION),
262              twomass_cc_flag(e->h_cc, TWOMASS_CC_STRIPE),
263              twomass_cc_flag(e->h_cc, TWOMASS_CC_BANDMERGE));
264     printval("k_confusion: p=%i, c=%i, d=%i, s=%i, b=%i.\n",
265              twomass_cc_flag(e->k_cc, TWOMASS_CC_PERSISTENCE),
266              twomass_cc_flag(e->k_cc, TWOMASS_CC_CONFUSION),
267              twomass_cc_flag(e->k_cc, TWOMASS_CC_DIFFFRACTION),
268              twomass_cc_flag(e->k_cc, TWOMASS_CC_STRIPE),
269              twomass_cc_flag(e->k_cc, TWOMASS_CC_BANDMERGE));
270 
271     ensure(*cursor, "confusion_flag");
272     cursor++;
273 
274     assert(*cursor >= '0' && *cursor <= '9');
275     e->j_ndet_M = *cursor - '0';
276     cursor++;
277     assert(*cursor >= '0' && *cursor <= '9');
278     e->j_ndet_N = *cursor - '0';
279     cursor++;
280     assert(*cursor >= '0' && *cursor <= '9');
281     e->h_ndet_M = *cursor - '0';
282     cursor++;
283     assert(*cursor >= '0' && *cursor <= '9');
284     e->h_ndet_N = *cursor - '0';
285     cursor++;
286     assert(*cursor >= '0' && *cursor <= '9');
287     e->k_ndet_M = *cursor - '0';
288     cursor++;
289     assert(*cursor >= '0' && *cursor <= '9');
290     e->k_ndet_N = *cursor - '0';
291     cursor++;
292 
293     printval("j_ndet %i/%i\n", e->j_ndet_M, e->j_ndet_N);
294     printval("h_ndet %i/%i\n", e->h_ndet_M, e->h_ndet_N);
295     printval("k_ndet %i/%i\n", e->k_ndet_M, e->k_ndet_N);
296 
297     ensure(*cursor, "ndet");
298     cursor++;
299 
300     if (sscanf(cursor, "%f|%n", &e->proximity, &nchars) != 1) {
301         fprintf(stderr, "Failed to parse 'proximity' entry in 2MASS line.\n");
302         return -1;
303     }
304     cursor += nchars;
305     e->proximity = arcsec2deg(e->proximity); // [arcsec : deg]
306     printval("proximity %g\n", e->proximity);
307 
308     if (parse_null(&cursor, &val2)) {
309         fprintf(stderr, "Failed to parse 'prox_angle' entry in 2MASS line.\n");
310         return -1;
311     }
312     if (twomass_is_null_float(val2))
313         e->prox_angle = TWOMASS_NULL;
314     else
315         e->prox_angle = val2; // [deg]
316 
317     printval("proximity_angle %i\n", e->prox_angle);
318 
319     if (sscanf(cursor, "%u|%n", &e->prox_key, &nchars) != 1) {
320         fprintf(stderr, "Failed to parse 'prox_key' entry in 2MASS line.\n");
321         return -1;
322     }
323     cursor += nchars;
324     printval("proximity_key %i\n", e->prox_key);
325 
326     if (*cursor < '0' || *cursor > '2') {
327         fprintf(stderr, "Failed to parse 'galaxy_contam' entry in 2MASS line.\n");
328         return -1;
329     }
330     e->galaxy_contam = *cursor - '0';
331     cursor ++;
332     printval("galaxy contamination %i\n", e->galaxy_contam);
333 
334     ensure(*cursor, "galaxy_contam");
335     cursor++;
336 
337     if (*cursor < '0' || *cursor > '1') {
338         fprintf(stderr, "Failed to parse 'minor_planet' entry in 2MASS line.\n");
339         return -1;
340     }
341     e->minor_planet = (anbool)(*cursor - '0');
342     cursor++;
343     printval("minor planet %i\n", e->minor_planet);
344 
345     ensure(*cursor, "minor_planet");
346     cursor++;
347 
348     if (sscanf(cursor, "%u|%n", &e->key, &nchars) != 1) {
349         fprintf(stderr, "Failed to parse 'key' entry in 2MASS line.\n");
350         return -1;
351     }
352     cursor += nchars;
353     printval("key %i\n", e->key);
354 
355     switch (*cursor) {
356     case 'n':
357         e->northern_hemisphere = TRUE;
358         break;
359     case 's':
360         e->northern_hemisphere = FALSE;
361         break;
362     default:
363         fprintf(stderr, "Failed to parse 'northern_hemisphere' entry in 2MASS line.\n");
364         return -1;
365     }
366     cursor ++;
367     printval("hemisphere %s\n", (e->northern_hemisphere ? "N" : "S"));
368 
369     ensure(*cursor, "northern_hemisphere");
370     cursor++;
371 
372     {
373         unsigned int yr, mon, day, scan;
374         if (sscanf(cursor, "%u-%u-%u|%n", &yr, &mon, &day, &nchars) != 3) {
375             fprintf(stderr, "Failed to parse 'date' entry in 2MASS line.\n");
376             return -1;
377         }
378         cursor += nchars;
379         e->date_year = yr;
380         e->date_month = mon;
381         e->date_day = day;
382 
383         if (sscanf(cursor, "%u|%n", &scan, &nchars) != 1) {
384             fprintf(stderr, "Failed to parse 'scan' entry in 2MASS line.\n");
385             return -1;
386         }
387         cursor += nchars;
388         e->scan = scan;
389 
390     }
391     printval("date %i/%i/%i\n", e->date_year, e->date_month, e->date_day);
392     printval("scan %i\n", e->scan);
393 
394     if (sscanf(cursor, "%f|%f|%n", &e->glon, &e->glat, &nchars) != 2) { // [deg], [deg]
395         fprintf(stderr, "Failed to parse 'glon/glat' entry in 2MASS line.\n");
396         return -1;
397     }
398     cursor += nchars;
399     printval("glon %g, glat %g.\n", e->glon, e->glat);
400 
401     if (sscanf(cursor, "%f|%lf|%n", &e->x_scan, &e->jdate, &nchars) != 2) {
402         fprintf(stderr, "Failed to parse 'x_scan/jdate' entry in 2MASS line.\n");
403         return -1;
404     }
405     cursor += nchars;
406     e->x_scan = arcsec2deg(e->x_scan); // [arcsec : deg]
407     printval("x_scan %g, jdate %g.\n", e->x_scan, e->jdate); // [day]
408 
409     for (i=0; i<9; i++) {
410         char* names[] = {
411             "j_psfchi", "h_psfchi", "k_psfchi",
412             "j_m_stdap", "j_msig_stdap",
413             "h_m_stdap", "h_msig_stdap",
414             "k_m_stdap", "k_msig_stdap" };
415         // all [mag] or unitless
416         float* dests[] = {
417             &e->j_psfchi, &e->h_psfchi, &e->k_psfchi,
418             &e->j_m_stdap, &e->j_msig_stdap,
419             &e->h_m_stdap, &e->h_msig_stdap,
420             &e->k_m_stdap, &e->k_msig_stdap, };
421         if (parse_null(&cursor, dests[i])) {
422             fprintf(stderr, "Failed to parse \"%s\" entry in 2MASS line.\n", names[i]);
423             return -1;
424         }
425         printval("%s: %g\n", names[i], *dests[i]);
426     }
427 
428     {
429         int dist_ns;
430         int dist_ew;
431         char ns;
432         char ew;
433         if (sscanf(cursor, "%i|%i|%c%c|%n", &dist_ns, &dist_ew, &ns, &ew, &nchars) != 4) {
434             fprintf(stderr, "Failed to parse 'dist_edge_ns/dest_edge_ew/dist_edge_flag' entries in 2MASS line.\n");
435             return -1;
436         }
437         cursor += nchars;
438         e->dist_edge_ns = arcsec2deg(dist_ns); // [arcsec : deg]
439         e->dist_edge_ew = arcsec2deg(dist_ew); // [arcsec : deg]
440         switch (ns) {
441         case 'n':
442             e->dist_flag_ns = TRUE;
443             break;
444         case 's':
445             e->dist_flag_ns = FALSE;
446             break;
447         default:
448             fprintf(stderr, "Failed to parse 'dist_edge_flag' entry in 2MASS line.\n");
449             return -1;
450         }
451         switch (ew) {
452         case 'e':
453             e->dist_flag_ew = TRUE;
454             break;
455         case 'w':
456             e->dist_flag_ew = FALSE;
457             break;
458         default:
459             fprintf(stderr, "Failed to parse 'dist_edge_flag' entry in 2MASS line.\n");
460             return -1;
461         }
462     }
463     printval("dist_ns %f %c, dest_ew %f %c.\n", e->dist_edge_ns, (e->dist_flag_ns ? 'N' : 'S'),
464              e->dist_edge_ew, (e->dist_flag_ew ? 'E' : 'W'));
465 
466     if ((*cursor < '0') || (*cursor > '9')) {
467         fprintf(stderr, "Failed to parse 'dup_src' entry in 2MASS line.\n");
468         fprintf(stderr, "line: \"%s\"\n", line);
469         fprintf(stderr, "cursor: \"%s\"\n", cursor);
470         return -1;
471     }
472     e->dup_src = *cursor - '0';
473     cursor++;
474     printval("dup_src %i\n", e->dup_src);
475 
476     ensure(*cursor, "dup_src");
477     cursor++;
478 
479     if ((*cursor == '1') || (*cursor == '0')) {
480         e->use_src = (anbool)(*cursor - '0');
481         cursor++;
482     } else {
483         fprintf(stderr, "Failed to parse 'use_src' entry in 2MASS line.\n");
484         return -1;
485     }
486     printval("use_src %i\n", e->use_src);
487 
488     ensure(*cursor, "use_src");
489     cursor++;
490 
491     switch (*cursor) {
492     case '0':
493         e->association = TWOMASS_ASSOCIATION_NONE;
494         break;
495     case 'T':
496         e->association = TWOMASS_ASSOCIATION_TYCHO;
497         break;
498     case 'U':
499         e->association = TWOMASS_ASSOCIATION_USNOA2;
500         break;
501     }
502     cursor++;
503 
504     printval("association %i\n", e->association);
505 
506     ensure(*cursor, "association");
507     cursor++;
508 
509     if (parse_null(&cursor, &val2) || // dist_opt
510         parse_null(&cursor, &val3) || // phi_opt
511         parse_null(&cursor, &e->b_m_opt) ||
512         parse_null(&cursor, &e->vr_m_opt)) {
513         fprintf(stderr, "Failed to parse 'dist_opt/phi_opt/b_m_opt/vr_m_opt' entries in 2MASS line.\n");
514         return -1;
515     }
516     if (twomass_is_null_float(val2))
517         e->dist_opt = TWOMASS_NULL;
518     else
519         e->dist_opt = arcsec2deg(val2); // [arcsec : deg]
520 
521     if (twomass_is_null_float(val3))
522         e->phi_opt = TWOMASS_NULL;
523     else
524         e->phi_opt = val3;
525 
526     printval("dist_opt %g, phi_opt %g, b_m_opt %g, vr_m_opt %g.\n", e->dist_opt, e->phi_opt, e->b_m_opt, e->vr_m_opt);
527 
528     if ((*cursor >= '0') && (*cursor <= '9')) {
529         e->nopt_mchs = *cursor - '0';
530         cursor++;
531     } else {
532         fprintf(stderr, "Failed to parse 'nopt_mchs' entry in 2MASS line.\n");
533         return -1;
534     }
535     cursor++;
536     printval("nopt_matches %i\n", e->nopt_mchs);
537 
538     if (!strncmp(cursor, "\\N|", 3)) {
539         e->xsc_key = TWOMASS_KEY_NULL;
540         cursor += 3;
541     } else {
542         int ival;
543         if (sscanf(cursor, "%i|%n", &ival, &nchars) != 1) {
544             fprintf(stderr, "Failed to parse 'xsc_key' entry in 2MASS line.\n");
545             return -1;
546         }
547         cursor += nchars;
548     }
549 
550     printval("XSC key %i\n", e->xsc_key);
551 
552     {
553         int scan, coadd_key, coadd;
554         if (sscanf(cursor, "%i|%i|%i%n", &scan, &coadd_key, &coadd, &nchars) != 3) {
555             fprintf(stderr, "Failed to parse 'scan/coadd_key/coadd' entries in 2MASS line.\n");
556             return -1;
557         }
558         e->scan_key = scan;
559         e->coadd_key = coadd_key;
560         e->coadd = coadd;
561         cursor += nchars;
562     }
563 
564     printval("scan_key %i, coadd_key %i, coadd %i.\n", e->scan_key, e->coadd_key, e->coadd);
565 
566     return 0;
567 }
568 
569