/* Convert comet elements in the formats used by the Minor Planet Center (and in the Minor Planet Electronic Circulars) to Home Planet CSV format. Multiple comet announcements can be extracted from a file containing other irrelevant information. This allows you, for example, to process an entire Unix mail folder and extract elements from all archived circulars within. Designed and implemented by John Walker in November of 1994. Revision 1 by John Walker in April 1997 to use presence of eccentricity to flag periodic comet format. Elliptical Orbital Elements --------------------------- PERIODIC COMET SHOEMAKER 4 (1994k) . . . T = 1994 Oct. 31.231 TT Peri. = 196.521 e = 0.52851 Node = 92.603 2000.0 q = 2.92503 AU Incl. = 25.345 a = 6.20375 AU n = 0.063786 P = 15.45 years 1 2 3 4 5 6 7 01234567890123456789012345678901234567890123456789012345678901234567890 Some time after the original version of this program was released, IAUCs dropped the "PERIODIC " designation, relying on the presence or absence of eccentricity to distinguish elliptical and parabolic elements. The form for periodic elements as of this revision (April 1997) is as follows: COMET P/1997 B1 (KOBAYASHI) . . . T = 1997 Mar. 4.295 TT Peri. = 184.202 e = 0.76831 Node = 329.032 2000.0 q = 2.06219 AU Incl. = 12.490 a = 8.90047 AU n = 0.037118 P = 26.55 years 1 2 3 4 5 6 7 01234567890123456789012345678901234567890123456789012345678901234567890 Parabolic Orbital Elements -------------------------- COMET MUELLER (1994c) . . . T = 1993 Dec. 3.990 TT Peri. = 102.512 Node = 4.933 2000.0 q = 1.81118 AU Incl. = 145.454 1 2 3 4 5 6 7 01234567890123456789012345678901234567890123456789012345678901234567890 Output Format ------------- Name,Perihelion time,Perihelion AU,Eccentricity,Long. perihelion,Long. node,Inclination,Semimajor axis,Period P/1996 P1 (IRAS),1996-10-29.9968,1.702690,0.696589,356.8876,357.7007,45.9617,5.611833 AU,13.294 years C/1996 Q1 (TABUR),1996-11-3.562,0.84202,1,57.234,31.516,73.234,,Parabolic C/1996 P2 (RUSSELL-WATSON),1996-2-29.518,2.01067,1,188.661,101.556,29.134,,Parabolic P/1996 N2 (ELST-PIZARRO),1996-4-16.0159,2.617717,0.170415,132.4528,160.3483,1.3754,3.155453 AU,5.605 years C/1996 R1 (HERGENROTHER-SPAHR),1996-8-28.566,1.90096,1,139.250,149.648,145.876,,Parabolic P/1996 R2 (LAGERKVIST),1997-1-30.55,2.4783,0.3685,339.83,40.72,2.55,3.9245 AU,7.77 years P/1996 S2 (SHOEMAKER-HOLT 1),1997-11-20.2541,3.047026,0.321273,210.2130,214.5275,4.3617,4.489325 AU,9.512 years P/1996 U1 (SHOEMAKER-LEVY 3),1998-3-4.9475,2.817332,0.247914,181.3886,303.7122,5.0086,3.746025 AU,7.250 years */ #include #include #include #include #ifdef unix #define _strnicmp strncasecmp #define READMODE "r" #else /* On DOS-like systems we read the input file in binary mode so that a ^Z embedded in a Unix mail folder (they get there when a DOS user embeds a file in a mail message) doesn't truncate the input. */ #define READMODE "rb" #endif /* Define ECHO to embed the source lines from which each line of CSV was generated. This is handy for debugging when the format changes. */ #ifdef ECHO #define ECHOLINE(x) strcpy(s[x], s[0]) #else #define ECHOLINE(x) #endif static char s[8][132]; /* Buffer to hold complete entry */ static int rs(char *s, FILE *fp) { if (fgets(s, 132, fp) == NULL) { return 0; } while (strlen(s) > 0 && s[strlen(s) - 1] < ' ') { s[strlen(s) - 1] = 0; } return 1; } static void sc(char *b, int line, int start, int end) { char *c = s[line]; int i, n; for (i = 0, n = start; n <= end; n++) { if (c[n] == 0) { break; } if (!isspace(c[n])) { b[i++] = c[n]; } } b[i] = 0; } static void sct(char *b, int line, int start, int end) { char *c = s[line], sb[132]; memcpy(sb, c + start, (end - start) + 1); sb[(end - start) + 1] = 0; while (strlen(sb) > 0 && isspace(sb[strlen(sb) - 1])) { sb[strlen(sb) - 1] = 0; } c = sb; while (*c && isspace(*c)) { c++; } strcpy(b, c); } int main(int argc, char *argv[]) { int i, running = 1, written = 0; FILE *fi = stdin, *fo = stdout; /* Input and output file names */ static char cname[80], epoch[80], periau[80], perilong[80], eccen[80], longnode[80], inclin[80], semimaj[80], period[80]; if (argc > 1) { fi = fopen(argv[1], READMODE); /* Input file */ if (fi == NULL) { fprintf(stderr, "Cannot open input file %s\n", argv[1]); return 2; } if (argc > 2) { fo = fopen(argv[2], "w"); /* Output file */ if (fo == NULL) { fprintf(stderr, "Cannot open output file %s\n", argv[2]); return 2; } } } fprintf(fo, "Name,Perihelion time,Perihelion AU,Eccentricity,Long. perihelion,\ Long. node,Inclination,Semimajor axis,Period\n"); while (running) { int periodic = -1; char *u, *v; while (1) { if (!rs(s[0], fi)) { running = 0; break; } if (strncmp(s[0], "COMET ", 6) == 0) { sct(cname, 0, 6, 80); periodic = 0; break; } else if (strncmp(s[0], "PERIODIC COMET ", 15) == 0) { sct(cname, 0, 15, 80); periodic = 1; break; } } if (!running) { break; } ECHOLINE(1); /* Scan until we either find a line which begins with the "T =" specification or run into another comet header (indicating this circular didn't contain elements. */ while (1) { char t[132]; if (!rs(s[0], fi)) { running = 0; break; } if (strncmp(s[0], "COMET ", 6) == 0) { sct(cname, 0, 6, 80); periodic = 0; ECHOLINE(1); continue; } else if (strncmp(s[0], "PERIODIC COMET ", 15) == 0) { sct(cname, 0, 15, 80); periodic = 1; ECHOLINE(1); continue; } ECHOLINE(2); sc(t, 0, 0, 80); if (strncmp(t, "T=", 2) == 0 && isdigit(t[2])) { char y[20], m[20], daf[20]; static char mname[] = "janfebmaraprmayjunjulaugsepoctnovdec"; sscanf(u = strchr(s[0], '=') + 1, "%s %s", y, m); for (i = 0; i < 12; i++) { if (_strnicmp(m, mname + i * 3, 3) == 0) { sprintf(m, "%d", i + 1); break; } } /* Tiptoe up to the start of the year, since in the case of "Sept.199x" the month runs into the first digit of the year. This code is resiliant in case a space is added later. */ while (*u && isspace(*u)) { u++; } while (*u && !isspace(*u)) { u++; } while (*u && isspace(*u)) { u++; } while (*u && !isdigit(*u)) { u++; } sscanf(u, "%s", daf); sprintf(epoch, "%s-%s-%s", y, m, daf); v = strchr(u, '='); if (v != NULL) { sc(perilong, 0, (v + 1) - s[0], 80); } else { continue; } /* Process second line. */ if (!rs(s[0], fi)) { running = 0; break; } ECHOLINE(3); sc(t, 0, 0, 80); if (strncmp(t, "e=", 2) == 0) { sscanf(u = strchr(s[0], '=') + 1, "%s", eccen); v = strchr(u, '='); periodic = 1; } else { strcpy(eccen, "1"); /* Parabolic */ v = strchr(s[0], '='); } if (v != NULL) { sscanf(v + 1, "%s", longnode); } else { continue; } /* Process third line. */ if (!rs(s[0], fi)) { running = 0; break; } ECHOLINE(4); u = strchr(s[0], '='); if (u != NULL) { v = strchr(u + 1, '='); } if (u != NULL && v != NULL) { sscanf(u + 1, "%s", periau); sscanf(v + 1, "%s", inclin); } else { continue; } /* Process fourth line if comet is periodic. */ strcpy(semimaj, ""); strcpy(period, "Parabolic"); if (periodic) { if (!rs(s[0], fi)) { running = 0; break; } ECHOLINE(5); u = strchr(s[0], '='); if (u != NULL) { v = strchr(u + 1, '='); if (v != NULL) { v = strchr(v + 1, '='); } } if (u != NULL && v != NULL) { char t1[30], t2[30]; sscanf(u + 1, "%s %s", t1, t2); sprintf(semimaj, "%s %s", t1, t2); sscanf(v + 1, "%s %s", t1, t2); sprintf(period, "%s %s", t1, t2); } else { continue; } } break; } } if (!running) { break; } #ifdef ECHO fprintf(fo, "\n\ 1 2 3 4 5 6 7\n\ 01234567890123456789012345678901234567890123456789012345678901234567890\n"); for (i = 1; i <= (periodic ? 5 : 4); i++) { fprintf(fo, "%s\n", s[i]); } #endif /* Output information in CSV format. */ fprintf(fo, "%s,%s,%s,%s,%s,%s,%s,%s,%s\n", cname, epoch, periau, eccen, perilong, longnode, inclin, semimaj, period); written++; } fclose(fi); fclose(fo); fprintf(stderr, "%d record%s written.\n", written, written == 1 ? "" : "s"); return 0; }