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