| #include <u.h> |
| #include <libc.h> |
| #include <bio.h> |
| #include <draw.h> |
| #include <event.h> |
| #include "sky.h" |
| #include "strings.c" |
| |
| enum |
| { |
| NNGC=7840, /* number of NGC numbers [1..NNGC] */ |
| NIC = 5386, /* number of IC numbers */ |
| NNGCrec=NNGC+NIC, /* number of records in the NGC catalog (including IC's, starting at NNGC */ |
| NMrec=122, /* number of M records */ |
| NM=110, /* number of M numbers */ |
| NAbell=2712, /* number of records in the Abell catalog */ |
| NName=1000, /* number of prose names; estimated maximum (read from editable text file) */ |
| NBayer=1517, /* number of bayer entries */ |
| NSAO=258998, /* number of SAO stars */ |
| MAXcon=1932, /* maximum number of patches in a constellation */ |
| Ncon=88, /* number of constellations */ |
| Npatch=92053, /* highest patch number */ |
| }; |
| |
| char ngctype[NNGCrec]; |
| Mindexrec mindex[NMrec]; |
| Namerec name[NName]; |
| Bayerec bayer[NBayer]; |
| int32 con[MAXcon]; |
| ushort conindex[Ncon+1]; |
| int32 patchaddr[Npatch+1]; |
| |
| Record *rec; |
| Record *orec; |
| Record *cur; |
| |
| char *dir; |
| int saodb; |
| int ngcdb; |
| int abelldb; |
| int ngctypedb; |
| int mindexdb; |
| int namedb; |
| int bayerdb; |
| int condb; |
| int conindexdb; |
| int patchdb; |
| char parsed[3]; |
| int32 nrec; |
| int32 nreca; |
| int32 norec; |
| int32 noreca; |
| |
| Biobuf bin; |
| Biobuf bout; |
| |
| void |
| main(int argc, char *argv[]) |
| { |
| char *line; |
| |
| dir = unsharp(DIR); |
| Binit(&bin, 0, OREAD); |
| Binit(&bout, 1, OWRITE); |
| if(argc != 1) |
| dir = argv[1]; |
| astro("", 1); |
| while(line = Brdline(&bin, '\n')){ |
| line[Blinelen(&bin)-1] = 0; |
| lookup(line, 1); |
| Bflush(&bout); |
| } |
| if(display != nil){ |
| closedisplay(display); |
| /* automatic refresh of rio window is triggered by mouse */ |
| /* close(open("/dev/mouse", OREAD)); */ |
| } |
| return; |
| } |
| |
| void |
| reset(void) |
| { |
| nrec = 0; |
| cur = rec; |
| } |
| |
| void |
| grow(void) |
| { |
| nrec++; |
| if(nreca < nrec){ |
| nreca = nrec+50; |
| rec = realloc(rec, nreca*sizeof(Record)); |
| if(rec == 0){ |
| fprint(2, "scat: realloc fails\n"); |
| exits("realloc"); |
| } |
| } |
| cur = rec+nrec-1; |
| } |
| |
| void |
| copy(void) |
| { |
| if(noreca < nreca){ |
| noreca = nreca; |
| orec = realloc(orec, nreca*sizeof(Record)); |
| if(orec == 0){ |
| fprint(2, "scat: realloc fails\n"); |
| exits("realloc"); |
| } |
| } |
| memmove(orec, rec, nrec*sizeof(Record)); |
| norec = nrec; |
| } |
| |
| int |
| eopen(char *s) |
| { |
| char buf[128]; |
| int f; |
| |
| sprint(buf, "%s/%s.scat", dir, s); |
| f = open(buf, 0); |
| if(f<0){ |
| fprint(2, "scat: can't open %s\n", buf); |
| exits("open"); |
| } |
| return f; |
| } |
| |
| |
| void |
| Eread(int f, char *name, void *addr, int32 n) |
| { |
| if(read(f, addr, n) != n){ /* BUG! */ |
| fprint(2, "scat: read error on %s\n", name); |
| exits("read"); |
| } |
| } |
| |
| char* |
| skipbl(char *s) |
| { |
| while(*s!=0 && (*s==' ' || *s=='\t')) |
| s++; |
| return s; |
| } |
| |
| char* |
| skipstr(char *s, char *t) |
| { |
| while(*s && *s==*t) |
| s++, t++; |
| return skipbl(s); |
| } |
| |
| /* produce little-endian int32 at address l */ |
| int32 |
| Long(int32 *l) |
| { |
| uchar *p; |
| |
| p = (uchar*)l; |
| return (int32)p[0]|((int32)p[1]<<8)|((int32)p[2]<<16)|((int32)p[3]<<24); |
| } |
| |
| /* produce little-endian int32 at address l */ |
| int |
| Short(short *s) |
| { |
| uchar *p; |
| |
| p = (uchar*)s; |
| return p[0]|(p[1]<<8); |
| } |
| |
| void |
| nameopen(void) |
| { |
| Biobuf b; |
| int i; |
| char *l, *p; |
| |
| if(namedb == 0){ |
| namedb = eopen("name"); |
| Binit(&b, namedb, OREAD); |
| for(i=0; i<NName; i++){ |
| l = Brdline(&b, '\n'); |
| if(l == 0) |
| break; |
| p = strchr(l, '\t'); |
| if(p == 0){ |
| Badformat: |
| Bprint(&bout, "warning: name.scat bad format; line %d\n", i+1); |
| break; |
| } |
| *p++ = 0; |
| strcpy(name[i].name, l); |
| if(strncmp(p, "ngc", 3) == 0) |
| name[i].ngc = atoi(p+3); |
| else if(strncmp(p, "ic", 2) == 0) |
| name[i].ngc = atoi(p+2)+NNGC; |
| else if(strncmp(p, "sao", 3) == 0) |
| name[i].sao = atoi(p+3); |
| else if(strncmp(p, "abell", 5) == 0) |
| name[i].abell = atoi(p+5); |
| else |
| goto Badformat; |
| } |
| if(i == NName) |
| Bprint(&bout, "warning: too many names in name.scat (max %d); extra ignored\n", NName); |
| close(namedb); |
| |
| bayerdb = eopen("bayer"); |
| Eread(bayerdb, "bayer", bayer, sizeof bayer); |
| close(bayerdb); |
| for(i=0; i<NBayer; i++) |
| bayer[i].sao = Long(&bayer[i].sao); |
| } |
| } |
| |
| void |
| saoopen(void) |
| { |
| if(saodb == 0){ |
| nameopen(); |
| saodb = eopen("sao"); |
| } |
| } |
| |
| void |
| ngcopen(void) |
| { |
| if(ngcdb == 0){ |
| nameopen(); |
| ngcdb = eopen("ngc2000"); |
| ngctypedb = eopen("ngc2000type"); |
| Eread(ngctypedb, "ngctype", ngctype, sizeof ngctype); |
| close(ngctypedb); |
| } |
| } |
| |
| void |
| abellopen(void) |
| { |
| /* nothing extra to do with abell: it's directly indexed by number */ |
| if(abelldb == 0) |
| abelldb = eopen("abell"); |
| } |
| |
| void |
| patchopen(void) |
| { |
| Biobuf *b; |
| int32 l, m; |
| char buf[100]; |
| |
| if(patchdb == 0){ |
| patchdb = eopen("patch"); |
| sprint(buf, "%s/patchindex.scat", dir); |
| b = Bopen(buf, OREAD); |
| if(b == 0){ |
| fprint(2, "can't open %s\n", buf); |
| exits("open"); |
| } |
| for(m=0,l=0; l<=Npatch; l++) |
| patchaddr[l] = m += Bgetc(b)*4; |
| Bterm(b); |
| } |
| } |
| |
| void |
| mopen(void) |
| { |
| int i; |
| |
| if(mindexdb == 0){ |
| mindexdb = eopen("mindex"); |
| Eread(mindexdb, "mindex", mindex, sizeof mindex); |
| close(mindexdb); |
| for(i=0; i<NMrec; i++) |
| mindex[i].ngc = Short(&mindex[i].ngc); |
| } |
| } |
| |
| void |
| constelopen(void) |
| { |
| int i; |
| |
| if(condb == 0){ |
| condb = eopen("con"); |
| conindexdb = eopen("conindex"); |
| Eread(conindexdb, "conindex", conindex, sizeof conindex); |
| close(conindexdb); |
| for(i=0; i<Ncon+1; i++) |
| conindex[i] = Short((short*)&conindex[i]); |
| } |
| } |
| |
| void |
| lowercase(char *s) |
| { |
| for(; *s; s++) |
| if('A'<=*s && *s<='Z') |
| *s += 'a'-'A'; |
| } |
| |
| int |
| loadngc(int32 index) |
| { |
| static int failed; |
| int32 j; |
| |
| ngcopen(); |
| j = (index-1)*sizeof(NGCrec); |
| grow(); |
| cur->type = NGC; |
| cur->index = index; |
| seek(ngcdb, j, 0); |
| /* special case: NGC data may not be available */ |
| if(read(ngcdb, &cur->u.ngc, sizeof(NGCrec)) != sizeof(NGCrec)){ |
| if(!failed){ |
| fprint(2, "scat: NGC database not available\n"); |
| failed++; |
| } |
| cur->type = NONGC; |
| cur->u.ngc.ngc = 0; |
| cur->u.ngc.ra = 0; |
| cur->u.ngc.dec = 0; |
| cur->u.ngc.diam = 0; |
| cur->u.ngc.mag = 0; |
| return 0; |
| } |
| cur->u.ngc.ngc = Short(&cur->u.ngc.ngc); |
| cur->u.ngc.ra = Long(&cur->u.ngc.ra); |
| cur->u.ngc.dec = Long(&cur->u.ngc.dec); |
| cur->u.ngc.diam = Long(&cur->u.ngc.diam); |
| cur->u.ngc.mag = Short(&cur->u.ngc.mag); |
| return 1; |
| } |
| |
| int |
| loadabell(int32 index) |
| { |
| int32 j; |
| |
| abellopen(); |
| j = index-1; |
| grow(); |
| cur->type = Abell; |
| cur->index = index; |
| seek(abelldb, j*sizeof(Abellrec), 0); |
| Eread(abelldb, "abell", &cur->u.abell, sizeof(Abellrec)); |
| cur->u.abell.abell = Short(&cur->u.abell.abell); |
| if(cur->u.abell.abell != index){ |
| fprint(2, "bad format in abell catalog\n"); |
| exits("abell"); |
| } |
| cur->u.abell.ra = Long(&cur->u.abell.ra); |
| cur->u.abell.dec = Long(&cur->u.abell.dec); |
| cur->u.abell.glat = Long(&cur->u.abell.glat); |
| cur->u.abell.glong = Long(&cur->u.abell.glong); |
| cur->u.abell.rad = Long(&cur->u.abell.rad); |
| cur->u.abell.mag10 = Short(&cur->u.abell.mag10); |
| cur->u.abell.pop = Short(&cur->u.abell.pop); |
| cur->u.abell.dist = Short(&cur->u.abell.dist); |
| return 1; |
| } |
| |
| int |
| loadsao(int index) |
| { |
| if(index<=0 || index>NSAO) |
| return 0; |
| saoopen(); |
| grow(); |
| cur->type = SAO; |
| cur->index = index; |
| seek(saodb, (index-1)*sizeof(SAOrec), 0); |
| Eread(saodb, "sao", &cur->u.sao, sizeof(SAOrec)); |
| cur->u.sao.ra = Long(&cur->u.sao.ra); |
| cur->u.sao.dec = Long(&cur->u.sao.dec); |
| cur->u.sao.dra = Long(&cur->u.sao.dra); |
| cur->u.sao.ddec = Long(&cur->u.sao.ddec); |
| cur->u.sao.mag = Short(&cur->u.sao.mag); |
| cur->u.sao.mpg = Short(&cur->u.sao.mpg); |
| cur->u.sao.hd = Long(&cur->u.sao.hd); |
| return 1; |
| } |
| |
| int |
| loadplanet(int index, Record *r) |
| { |
| if(index<0 || index>NPlanet || planet[index].name[0]=='\0') |
| return 0; |
| grow(); |
| cur->type = Planet; |
| cur->index = index; |
| /* check whether to take new or existing record */ |
| if(r == nil) |
| memmove(&cur->u.planet, &planet[index], sizeof(Planetrec)); |
| else |
| memmove(&cur->u.planet, &r->u.planet, sizeof(Planetrec)); |
| return 1; |
| } |
| |
| int |
| loadpatch(int32 index) |
| { |
| int i; |
| |
| patchopen(); |
| if(index<=0 || index>Npatch) |
| return 0; |
| grow(); |
| cur->type = Patch; |
| cur->index = index; |
| seek(patchdb, patchaddr[index-1], 0); |
| cur->u.patch.nkey = (patchaddr[index]-patchaddr[index-1])/4; |
| Eread(patchdb, "patch", cur->u.patch.key, cur->u.patch.nkey*4); |
| for(i=0; i<cur->u.patch.nkey; i++) |
| cur->u.patch.key[i] = Long(&cur->u.patch.key[i]); |
| return 1; |
| } |
| |
| int |
| loadtype(int t) |
| { |
| int i; |
| |
| ngcopen(); |
| for(i=0; i<NNGCrec; i++) |
| if(t == (ngctype[i])){ |
| grow(); |
| cur->type = NGCN; |
| cur->index = i+1; |
| } |
| return 1; |
| } |
| |
| void |
| flatten(void) |
| { |
| int i, j, notflat; |
| Record *or; |
| int32 key; |
| |
| loop: |
| copy(); |
| reset(); |
| notflat = 0; |
| for(i=0,or=orec; i<norec; i++,or++){ |
| switch(or->type){ |
| default: |
| fprint(2, "bad type %d in flatten\n", or->type); |
| break; |
| |
| case NONGC: |
| break; |
| |
| case Planet: |
| case Abell: |
| case NGC: |
| case SAO: |
| grow(); |
| memmove(cur, or, sizeof(Record)); |
| break; |
| |
| case NGCN: |
| if(loadngc(or->index)) |
| notflat = 1; |
| break; |
| |
| case NamedSAO: |
| loadsao(or->index); |
| notflat = 1; |
| break; |
| |
| case NamedNGC: |
| if(loadngc(or->index)) |
| notflat = 1; |
| break; |
| |
| case NamedAbell: |
| loadabell(or->index); |
| notflat = 1; |
| break; |
| |
| case PatchC: |
| loadpatch(or->index); |
| notflat = 1; |
| break; |
| |
| case Patch: |
| for(j=1; j<or->u.patch.nkey; j++){ |
| key = or->u.patch.key[j]; |
| if((key&0x3F) == SAO) |
| loadsao((key>>8)&0xFFFFFF); |
| else if((key&0x3F) == Abell) |
| loadabell((key>>8)&0xFFFFFF); |
| else |
| loadngc((key>>16)&0xFFFF); |
| } |
| break; |
| } |
| } |
| if(notflat) |
| goto loop; |
| } |
| |
| int |
| ism(int index) |
| { |
| int i; |
| |
| for(i=0; i<NMrec; i++) |
| if(mindex[i].ngc == index) |
| return 1; |
| return 0; |
| } |
| |
| char* |
| alpha(char *s, char *t) |
| { |
| int n; |
| |
| n = strlen(t); |
| if(strncmp(s, t, n)==0 && (s[n]<'a' || 'z'<s[n])) |
| return skipbl(s+n); |
| return 0; |
| |
| } |
| |
| char* |
| text(char *s, char *t) |
| { |
| int n; |
| |
| n = strlen(t); |
| if(strncmp(s, t, n)==0 && (s[n]==0 || s[n]==' ' || s[n]=='\t')) |
| return skipbl(s+n); |
| return 0; |
| |
| } |
| |
| int |
| cull(char *s, int keep, int dobbox) |
| { |
| int i, j, nobj, keepthis; |
| Record *or; |
| char *t; |
| int dogrtr, doless, dom, dosao, dongc, doabell; |
| int mgrtr, mless; |
| char obj[100]; |
| |
| memset(obj, 0, sizeof(obj)); |
| nobj = 0; |
| dogrtr = 0; |
| doless = 0; |
| dom = 0; |
| dongc = 0; |
| dosao = 0; |
| doabell = 0; |
| mgrtr = mless= 0; |
| if(dobbox) |
| goto Cull; |
| for(;;){ |
| if(s[0] == '>'){ |
| dogrtr = 1; |
| mgrtr = 10 * strtod(s+1, &t); |
| if(mgrtr==0 && t==s+1){ |
| fprint(2, "bad magnitude\n"); |
| return 0; |
| } |
| s = skipbl(t); |
| continue; |
| } |
| if(s[0] == '<'){ |
| doless = 1; |
| mless = 10 * strtod(s+1, &t); |
| if(mless==0 && t==s+1){ |
| fprint(2, "bad magnitude\n"); |
| return 0; |
| } |
| s = skipbl(t); |
| continue; |
| } |
| if(t = text(s, "m")){ |
| dom = 1; |
| s = t; |
| continue; |
| } |
| if(t = text(s, "sao")){ |
| dosao = 1; |
| s = t; |
| continue; |
| } |
| if(t = text(s, "ngc")){ |
| dongc = 1; |
| s = t; |
| continue; |
| } |
| if(t = text(s, "abell")){ |
| doabell = 1; |
| s = t; |
| continue; |
| } |
| for(i=0; names[i].name; i++) |
| if(t = alpha(s, names[i].name)){ |
| if(nobj > 100){ |
| fprint(2, "too many object types\n"); |
| return 0; |
| } |
| obj[nobj++] = names[i].type; |
| s = t; |
| goto Continue; |
| } |
| break; |
| Continue:; |
| } |
| if(*s){ |
| fprint(2, "syntax error in object list\n"); |
| return 0; |
| } |
| |
| Cull: |
| flatten(); |
| copy(); |
| reset(); |
| if(dom) |
| mopen(); |
| if(dosao) |
| saoopen(); |
| if(dongc || nobj) |
| ngcopen(); |
| if(doabell) |
| abellopen(); |
| for(i=0,or=orec; i<norec; i++,or++){ |
| keepthis = !keep; |
| if(dobbox && inbbox(or->u.ngc.ra, or->u.ngc.dec)) |
| keepthis = keep; |
| if(doless && or->u.ngc.mag <= mless) |
| keepthis = keep; |
| if(dogrtr && or->u.ngc.mag >= mgrtr) |
| keepthis = keep; |
| if(dom && (or->type==NGC && ism(or->u.ngc.ngc))) |
| keepthis = keep; |
| if(dongc && or->type==NGC) |
| keepthis = keep; |
| if(doabell && or->type==Abell) |
| keepthis = keep; |
| if(dosao && or->type==SAO) |
| keepthis = keep; |
| for(j=0; j<nobj; j++) |
| if(or->type==NGC && or->u.ngc.type==obj[j]) |
| keepthis = keep; |
| if(keepthis){ |
| grow(); |
| memmove(cur, or, sizeof(Record)); |
| } |
| } |
| return 1; |
| } |
| |
| int |
| compar(const void *va, const void *vb) |
| { |
| Record *a=(Record*)va, *b=(Record*)vb; |
| |
| if(a->type == b->type) |
| return a->index - b->index; |
| return a->type - b->type; |
| } |
| |
| void |
| sort(void) |
| { |
| int i; |
| Record *r, *s; |
| |
| if(nrec == 0) |
| return; |
| qsort(rec, nrec, sizeof(Record), compar); |
| r = rec+1; |
| s = rec; |
| for(i=1; i<nrec; i++,r++){ |
| /* may have multiple instances of a planet in the scene */ |
| if(r->type==s->type && r->index==s->index && r->type!=Planet) |
| continue; |
| memmove(++s, r, sizeof(Record)); |
| } |
| nrec = (s+1)-rec; |
| } |
| |
| char greekbuf[128]; |
| |
| char* |
| togreek(char *s) |
| { |
| char *t; |
| int i, n; |
| Rune r; |
| |
| t = greekbuf; |
| while(*s){ |
| for(i=1; i<=24; i++){ |
| n = strlen(greek[i]); |
| if(strncmp(s, greek[i], n)==0 && (s[n]==' ' || s[n]=='\t')){ |
| s += n; |
| t += runetochar(t, &greeklet[i]); |
| goto Cont; |
| } |
| } |
| n = chartorune(&r, s); |
| for(i=0; i<n; i++) |
| *t++ = *s++; |
| Cont:; |
| } |
| *t = 0; |
| return greekbuf; |
| } |
| |
| char* |
| fromgreek(char *s) |
| { |
| char *t; |
| int i, n; |
| Rune r; |
| |
| t = greekbuf; |
| while(*s){ |
| n = chartorune(&r, s); |
| for(i=1; i<=24; i++){ |
| if(r == greeklet[i]){ |
| strcpy(t, greek[i]); |
| t += strlen(greek[i]); |
| s += n; |
| goto Cont; |
| } |
| } |
| for(i=0; i<n; i++) |
| *t++ = *s++; |
| Cont:; |
| } |
| *t = 0; |
| return greekbuf; |
| } |
| |
| #ifdef OLD |
| /* |
| * Old version |
| */ |
| int |
| coords(int deg) |
| { |
| int i; |
| int x, y; |
| Record *or; |
| int32 dec, ra, ndec, nra; |
| int rdeg; |
| |
| flatten(); |
| copy(); |
| reset(); |
| deg *= 2; |
| for(i=0,or=orec; i<norec; i++,or++){ |
| if(or->type == Planet) /* must keep it here */ |
| loadplanet(or->index, or); |
| dec = or->u.ngc.dec/MILLIARCSEC; |
| ra = or->u.ngc.ra/MILLIARCSEC; |
| rdeg = deg/cos((dec*PI)/180); |
| for(y=-deg; y<=+deg; y++){ |
| ndec = dec*2+y; |
| if(ndec/2>=90 || ndec/2<=-90) |
| continue; |
| /* fp errors hurt here, so we round 1' to the pole */ |
| if(ndec >= 0) |
| ndec = ndec*500*60*60 + 60000; |
| else |
| ndec = ndec*500*60*60 - 60000; |
| for(x=-rdeg; x<=+rdeg; x++){ |
| nra = ra*2+x; |
| if(nra/2 < 0) |
| nra += 360*2; |
| if(nra/2 >= 360) |
| nra -= 360*2; |
| /* fp errors hurt here, so we round up 1' */ |
| nra = nra/2*MILLIARCSEC + 60000; |
| loadpatch(patcha(angle(nra), angle(ndec))); |
| } |
| } |
| } |
| sort(); |
| return 1; |
| } |
| #endif |
| |
| /* |
| * New version attempts to match the boundaries of the plot better. |
| */ |
| int |
| coords(int deg) |
| { |
| int i; |
| int x, y, xx; |
| Record *or; |
| int32 min, circle; |
| double factor; |
| |
| flatten(); |
| circle = 360*MILLIARCSEC; |
| deg *= MILLIARCSEC; |
| |
| /* find center */ |
| folded = 0; |
| bbox(0, 0, 0); |
| /* now expand */ |
| factor = cos(angle((decmax+decmin)/2)); |
| if(factor < .2) |
| factor = .2; |
| factor = floor(1/factor); |
| folded = 0; |
| bbox(factor*deg, deg, 1); |
| Bprint(&bout, "%s to ", hms(angle(ramin))); |
| Bprint(&bout, "%s\n", hms(angle(ramax))); |
| Bprint(&bout, "%s to ", dms(angle(decmin))); |
| Bprint(&bout, "%s\n", dms(angle(decmax))); |
| copy(); |
| reset(); |
| for(i=0,or=orec; i<norec; i++,or++) |
| if(or->type == Planet) /* must keep it here */ |
| loadplanet(or->index, or); |
| min = ramin; |
| if(ramin > ramax) |
| min -= circle; |
| for(x=min; x<=ramax; x+=250*60*60){ |
| xx = x; |
| if(xx < 0) |
| xx += circle; |
| for(y=decmin; y<=decmax; y+=250*60*60) |
| if(-circle/4 < y && y < circle/4) |
| loadpatch(patcha(angle(xx), angle(y))); |
| } |
| sort(); |
| cull(nil, 1, 1); |
| return 1; |
| } |
| |
| void |
| pplate(char *flags) |
| { |
| int i; |
| int32 c; |
| int na, rah, ram, d1, d2; |
| double r0; |
| int ra, dec; |
| int32 ramin, ramax, decmin, decmax; /* all in degrees */ |
| Record *r; |
| int folded; |
| Angle racenter, deccenter, rasize, decsize, a[4]; |
| Picture *pic; |
| |
| rasize = -1.0; |
| decsize = -1.0; |
| na = 0; |
| for(;;){ |
| while(*flags==' ') |
| flags++; |
| if(('0'<=*flags && *flags<='9') || *flags=='+' || *flags=='-'){ |
| if(na >= 3) |
| goto err; |
| a[na++] = getra(flags); |
| while(*flags && *flags!=' ') |
| flags++; |
| continue; |
| } |
| if(*flags){ |
| err: |
| Bprint(&bout, "syntax error in plate\n"); |
| return; |
| } |
| break; |
| } |
| switch(na){ |
| case 0: |
| break; |
| case 1: |
| rasize = a[0]; |
| decsize = rasize; |
| break; |
| case 2: |
| rasize = a[0]; |
| decsize = a[1]; |
| break; |
| case 3: |
| case 4: |
| racenter = a[0]; |
| deccenter = a[1]; |
| rasize = a[2]; |
| if(na == 4) |
| decsize = a[3]; |
| else |
| decsize = rasize; |
| if(rasize<0.0 || decsize<0.0){ |
| Bprint(&bout, "negative sizes\n"); |
| return; |
| } |
| goto done; |
| } |
| folded = 0; |
| /* convert to milliarcsec */ |
| c = 1000*60*60; |
| Again: |
| if(nrec == 0){ |
| Bprint(&bout, "empty\n"); |
| return; |
| } |
| ramin = 0x7FFFFFFF; |
| ramax = -0x7FFFFFFF; |
| decmin = 0x7FFFFFFF; |
| decmax = -0x7FFFFFFF; |
| for(r=rec,i=0; i<nrec; i++,r++){ |
| if(r->type == Patch){ |
| radec(r->index, &rah, &ram, &dec); |
| ra = 15*rah+ram/4; |
| r0 = c/cos(RAD(dec)); |
| ra *= c; |
| dec *= c; |
| if(dec == 0) |
| d1 = c, d2 = c; |
| else if(dec < 0) |
| d1 = c, d2 = 0; |
| else |
| d1 = 0, d2 = c; |
| }else if(r->type==SAO || r->type==NGC || r->type==Abell){ |
| ra = r->u.ngc.ra; |
| dec = r->u.ngc.dec; |
| d1 = 0, d2 = 0, r0 = 0; |
| }else if(r->type==NGCN){ |
| loadngc(r->index); |
| continue; |
| }else if(r->type==NamedSAO){ |
| loadsao(r->index); |
| continue; |
| }else if(r->type==NamedNGC){ |
| loadngc(r->index); |
| continue; |
| }else if(r->type==NamedAbell){ |
| loadabell(r->index); |
| continue; |
| }else |
| continue; |
| if(dec+d2 > decmax) |
| decmax = dec+d2; |
| if(dec-d1 < decmin) |
| decmin = dec-d1; |
| if(folded){ |
| ra -= 180*c; |
| if(ra < 0) |
| ra += 360*c; |
| } |
| if(ra+r0 > ramax) |
| ramax = ra+r0; |
| if(ra < ramin) |
| ramin = ra; |
| } |
| if(!folded && ramax-ramin>270*c){ |
| folded = 1; |
| goto Again; |
| } |
| racenter = angle(ramin+(ramax-ramin)/2); |
| deccenter = angle(decmin+(decmax-decmin)/2); |
| if(rasize<0 || decsize<0){ |
| rasize = angle(ramax-ramin)*cos(deccenter); |
| decsize = angle(decmax-decmin); |
| } |
| done: |
| if(DEG(rasize)>1.1 || DEG(decsize)>1.1){ |
| Bprint(&bout, "plate too big: %s", ms(rasize)); |
| Bprint(&bout, " x %s\n", ms(decsize)); |
| Bprint(&bout, "trimming to 30'x30'\n"); |
| rasize = RAD(0.5); |
| decsize = RAD(0.5); |
| } |
| Bprint(&bout, "%s %s ", hms(racenter), dms(deccenter)); |
| Bprint(&bout, "%s", ms(rasize)); |
| Bprint(&bout, " x %s\n", ms(decsize)); |
| Bflush(&bout); |
| flatten(); |
| pic = image(racenter, deccenter, rasize, decsize); |
| if(pic == 0) |
| return; |
| Bprint(&bout, "plate %s locn %d %d %d %d\n", pic->name, pic->minx, pic->miny, pic->maxx, pic->maxy); |
| Bflush(&bout); |
| displaypic(pic); |
| } |
| |
| void |
| lookup(char *s, int doreset) |
| { |
| int i, j, k; |
| int rah, ram, deg; |
| char *starts, *inputline=s, *t, *u; |
| Record *r; |
| Rune c; |
| int32 n; |
| double x; |
| Angle ra; |
| |
| lowercase(s); |
| s = skipbl(s); |
| |
| if(*s == 0) |
| goto Print; |
| |
| if(t = alpha(s, "flat")){ |
| if(*t){ |
| fprint(2, "flat takes no arguments\n"); |
| return; |
| } |
| if(nrec == 0){ |
| fprint(2, "no records\n"); |
| return; |
| } |
| flatten(); |
| goto Print; |
| } |
| |
| if(t = alpha(s, "print")){ |
| if(*t){ |
| fprint(2, "print takes no arguments\n"); |
| return; |
| } |
| for(i=0,r=rec; i<nrec; i++,r++) |
| prrec(r); |
| return; |
| } |
| |
| if(t = alpha(s, "add")){ |
| lookup(t, 0); |
| return; |
| } |
| |
| if(t = alpha(s, "sao")){ |
| n = strtoul(t, &u, 10); |
| if(n<=0 || n>NSAO) |
| goto NotFound; |
| t = skipbl(u); |
| if(*t){ |
| fprint(2, "syntax error in sao\n"); |
| return; |
| } |
| if(doreset) |
| reset(); |
| if(!loadsao(n)) |
| goto NotFound; |
| goto Print; |
| } |
| |
| if(t = alpha(s, "ngc")){ |
| n = strtoul(t, &u, 10); |
| if(n<=0 || n>NNGC) |
| goto NotFound; |
| t = skipbl(u); |
| if(*t){ |
| fprint(2, "syntax error in ngc\n"); |
| return; |
| } |
| if(doreset) |
| reset(); |
| if(!loadngc(n)) |
| goto NotFound; |
| goto Print; |
| } |
| |
| if(t = alpha(s, "ic")){ |
| n = strtoul(t, &u, 10); |
| if(n<=0 || n>NIC) |
| goto NotFound; |
| t = skipbl(u); |
| if(*t){ |
| fprint(2, "syntax error in ic\n"); |
| return; |
| } |
| if(doreset) |
| reset(); |
| if(!loadngc(n+NNGC)) |
| goto NotFound; |
| goto Print; |
| } |
| |
| if(t = alpha(s, "abell")){ |
| n = strtoul(t, &u, 10); |
| if(n<=0 || n>NAbell) |
| goto NotFound; |
| if(doreset) |
| reset(); |
| if(!loadabell(n)) |
| goto NotFound; |
| goto Print; |
| } |
| |
| if(t = alpha(s, "m")){ |
| n = strtoul(t, &u, 10); |
| if(n<=0 || n>NM) |
| goto NotFound; |
| mopen(); |
| for(j=n-1; mindex[j].m<n; j++) |
| ; |
| if(doreset) |
| reset(); |
| while(mindex[j].m == n){ |
| if(mindex[j].ngc){ |
| grow(); |
| cur->type = NGCN; |
| cur->index = mindex[j].ngc; |
| } |
| j++; |
| } |
| goto Print; |
| } |
| |
| for(i=1; i<=Ncon; i++) |
| if(t = alpha(s, constel[i])){ |
| if(*t){ |
| fprint(2, "syntax error in constellation\n"); |
| return; |
| } |
| constelopen(); |
| seek(condb, 4L*conindex[i-1], 0); |
| j = conindex[i]-conindex[i-1]; |
| Eread(condb, "con", con, 4*j); |
| if(doreset) |
| reset(); |
| for(k=0; k<j; k++){ |
| grow(); |
| cur->type = PatchC; |
| cur->index = Long(&con[k]); |
| } |
| goto Print; |
| } |
| |
| if(t = alpha(s, "expand")){ |
| n = 0; |
| if(*t){ |
| if(*t<'0' && '9'<*t){ |
| Expanderr: |
| fprint(2, "syntax error in expand\n"); |
| return; |
| } |
| n = strtoul(t, &u, 10); |
| t = skipbl(u); |
| if(*t) |
| goto Expanderr; |
| } |
| coords(n); |
| goto Print; |
| } |
| |
| if(t = alpha(s, "plot")){ |
| if(nrec == 0){ |
| Bprint(&bout, "empty\n"); |
| return; |
| } |
| plot(t); |
| return; |
| } |
| |
| if(t = alpha(s, "astro")){ |
| astro(t, 0); |
| return; |
| } |
| |
| if(t = alpha(s, "plate")){ |
| pplate(t); |
| return; |
| } |
| |
| if(t = alpha(s, "gamma")){ |
| while(*t==' ') |
| t++; |
| u = t; |
| x = strtod(t, &u); |
| if(u > t) |
| gam.gamma = x; |
| Bprint(&bout, "%.2f\n", gam.gamma); |
| return; |
| } |
| |
| if(t = alpha(s, "keep")){ |
| if(!cull(t, 1, 0)) |
| return; |
| goto Print; |
| } |
| |
| if(t = alpha(s, "drop")){ |
| if(!cull(t, 0, 0)) |
| return; |
| goto Print; |
| } |
| |
| for(i=0; planet[i].name[0]; i++){ |
| if(t = alpha(s, planet[i].name)){ |
| if(doreset) |
| reset(); |
| loadplanet(i, nil); |
| goto Print; |
| } |
| } |
| |
| for(i=0; names[i].name; i++){ |
| if(t = alpha(s, names[i].name)){ |
| if(*t){ |
| fprint(2, "syntax error in type\n"); |
| return; |
| } |
| if(doreset) |
| reset(); |
| loadtype(names[i].type); |
| goto Print; |
| } |
| } |
| |
| switch(s[0]){ |
| case '"': |
| starts = ++s; |
| while(*s != '"') |
| if(*s++ == 0){ |
| fprint(2, "bad star name\n"); |
| return; |
| } |
| *s = 0; |
| if(doreset) |
| reset(); |
| j = nrec; |
| saoopen(); |
| starts = fromgreek(starts); |
| for(i=0; i<NName; i++) |
| if(equal(starts, name[i].name)){ |
| grow(); |
| if(name[i].sao){ |
| rec[j].type = NamedSAO; |
| rec[j].index = name[i].sao; |
| } |
| if(name[i].ngc){ |
| rec[j].type = NamedNGC; |
| rec[j].index = name[i].ngc; |
| } |
| if(name[i].abell){ |
| rec[j].type = NamedAbell; |
| rec[j].index = name[i].abell; |
| } |
| strcpy(rec[j].u.named.name, name[i].name); |
| j++; |
| } |
| if(parsename(starts)) |
| for(i=0; i<NBayer; i++) |
| if(bayer[i].name[0]==parsed[0] && |
| (bayer[i].name[1]==parsed[1] || parsed[1]==0) && |
| bayer[i].name[2]==parsed[2]){ |
| grow(); |
| rec[j].type = NamedSAO; |
| rec[j].index = bayer[i].sao; |
| strncpy(rec[j].u.named.name, starts, sizeof(rec[j].u.named.name)); |
| j++; |
| } |
| if(j == 0){ |
| *s = '"'; |
| goto NotFound; |
| } |
| break; |
| |
| case '0': case '1': case '2': case '3': case '4': |
| case '5': case '6': case '7': case '8': case '9': |
| strtoul(s, &t, 10); |
| if(*t != 'h'){ |
| BadCoords: |
| fprint(2, "bad coordinates %s\n", inputline); |
| break; |
| } |
| ra = DEG(getra(s)); |
| while(*s && *s!=' ' && *s!='\t') |
| s++; |
| rah = ra/15; |
| ra = ra-rah*15; |
| ram = ra*4; |
| deg = strtol(s, &t, 10); |
| if(t == s) |
| goto BadCoords; |
| /* degree sign etc. is optional */ |
| chartorune(&c, t); |
| if(c == 0xb0) |
| deg = DEG(getra(s)); |
| if(doreset) |
| reset(); |
| if(abs(deg)>=90 || rah>=24) |
| goto BadCoords; |
| if(!loadpatch(patch(rah, ram, deg))) |
| goto NotFound; |
| break; |
| |
| default: |
| fprint(2, "unknown command %s\n", inputline); |
| return; |
| } |
| |
| Print: |
| if(nrec == 0) |
| Bprint(&bout, "empty\n"); |
| else if(nrec <= 2) |
| for(i=0; i<nrec; i++) |
| prrec(rec+i); |
| else |
| Bprint(&bout, "%ld items\n", nrec); |
| return; |
| |
| NotFound: |
| fprint(2, "%s not found\n", inputline); |
| return; |
| } |
| |
| char *ngctypes[] = |
| { |
| [Galaxy] = "Gx", |
| [PlanetaryN] = "Pl", |
| [OpenCl] = "OC", |
| [GlobularCl] = "Gb", |
| [DiffuseN] = "Nb", |
| [NebularCl] = "C+N", |
| [Asterism] = "Ast", |
| [Knot] = "Kt", |
| [Triple] = "***", |
| [Double] = "D*", |
| [Single] = "*", |
| [Uncertain] = "?", |
| [Nonexistent] = "-", |
| [Unknown] = " ", |
| [PlateDefect] = "PD" |
| }; |
| |
| char* |
| ngcstring(int d) |
| { |
| if(d<Galaxy || d>PlateDefect) |
| return "can't happen"; |
| return ngctypes[d]; |
| } |
| |
| short descindex[NINDEX]; |
| |
| void |
| printnames(Record *r) |
| { |
| int i, ok, done; |
| |
| done = 0; |
| for(i=0; i<NName; i++){ /* stupid linear search! */ |
| ok = 0; |
| if(r->type==SAO && r->index==name[i].sao) |
| ok = 1; |
| if(r->type==NGC && r->u.ngc.ngc==name[i].ngc) |
| ok = 1; |
| if(r->type==Abell && r->u.abell.abell==name[i].abell) |
| ok = 1; |
| if(ok){ |
| if(done++ == 0) |
| Bprint(&bout, "\t"); |
| Bprint(&bout, " \"%s\"", togreek(name[i].name)); |
| } |
| } |
| if(done) |
| Bprint(&bout, "\n"); |
| } |
| |
| int |
| equal(char *s1, char *s2) |
| { |
| int c; |
| |
| while(*s1){ |
| if(*s1==' '){ |
| while(*s1==' ') |
| s1++; |
| continue; |
| } |
| while(*s2==' ') |
| s2++; |
| c=*s2; |
| if('A'<=*s2 && *s2<='Z') |
| c^=' '; |
| if(*s1!=c) |
| return 0; |
| s1++, s2++; |
| } |
| return 1; |
| } |
| |
| int |
| parsename(char *s) |
| { |
| char *blank; |
| int i; |
| |
| blank = strchr(s, ' '); |
| if(blank==0 || strchr(blank+1, ' ') || strlen(blank+1)!=3) |
| return 0; |
| blank++; |
| parsed[0] = parsed[1] = parsed[2] = 0; |
| if('0'<=s[0] && s[0]<='9'){ |
| i = atoi(s); |
| parsed[0] = i; |
| if(i > 100) |
| return 0; |
| }else{ |
| for(i=1; i<=24; i++) |
| if(strncmp(greek[i], s, strlen(greek[i]))==0){ |
| parsed[0]=100+i; |
| goto out; |
| } |
| return 0; |
| out: |
| if('0'<=s[strlen(greek[i])] && s[strlen(greek[i])]<='9') |
| parsed[1]=s[strlen(greek[i])]-'0'; |
| } |
| for(i=1; i<=88; i++) |
| if(strcmp(constel[i], blank)==0){ |
| parsed[2] = i; |
| return 1; |
| } |
| return 0; |
| } |
| |
| char* |
| dist_grp(int dg) |
| { |
| switch(dg){ |
| default: |
| return "unknown"; |
| case 1: |
| return "13.3-14.0"; |
| case 2: |
| return "14.1-14.8"; |
| case 3: |
| return "14.9-15.6"; |
| case 4: |
| return "15.7-16.4"; |
| case 5: |
| return "16.5-17.2"; |
| case 6: |
| return "17.3-18.0"; |
| case 7: |
| return ">18.0"; |
| } |
| } |
| |
| char* |
| rich_grp(int dg) |
| { |
| switch(dg){ |
| default: |
| return "unknown"; |
| case 0: |
| return "30-40"; |
| case 1: |
| return "50-79"; |
| case 2: |
| return "80-129"; |
| case 3: |
| return "130-199"; |
| case 4: |
| return "200-299"; |
| case 5: |
| return ">=300"; |
| } |
| } |
| |
| char* |
| nameof(Record *r) |
| { |
| NGCrec *n; |
| SAOrec *s; |
| Abellrec *a; |
| static char buf[128]; |
| int i; |
| |
| switch(r->type){ |
| default: |
| return nil; |
| case SAO: |
| s = &r->u.sao; |
| if(s->name[0] == 0) |
| return nil; |
| if(s->name[0] >= 100){ |
| i = snprint(buf, sizeof buf, "%C", greeklet[s->name[0]-100]); |
| if(s->name[1]) |
| i += snprint(buf+i, sizeof buf-i, "%d", s->name[1]); |
| }else |
| i = snprint(buf, sizeof buf, " %d", s->name[0]); |
| snprint(buf+i, sizeof buf-i, " %s", constel[(uchar)s->name[2]]); |
| break; |
| case NGC: |
| n = &r->u.ngc; |
| if(n->type >= Uncertain) |
| return nil; |
| if(n->ngc <= NNGC) |
| snprint(buf, sizeof buf, "NGC%4d ", n->ngc); |
| else |
| snprint(buf, sizeof buf, "IC%4d ", n->ngc-NNGC); |
| break; |
| case Abell: |
| a = &r->u.abell; |
| snprint(buf, sizeof buf, "Abell%4d", a->abell); |
| break; |
| } |
| return buf; |
| } |
| |
| void |
| prrec(Record *r) |
| { |
| NGCrec *n; |
| SAOrec *s; |
| Abellrec *a; |
| Planetrec *p; |
| int i, rah, ram, dec, nn; |
| int32 key; |
| |
| if(r) switch(r->type){ |
| default: |
| fprint(2, "can't prrec type %d\n", r->type); |
| exits("type"); |
| |
| case Planet: |
| p = &r->u.planet; |
| Bprint(&bout, "%s", p->name); |
| Bprint(&bout, "\t%s %s", |
| hms(angle(p->ra)), |
| dms(angle(p->dec))); |
| Bprint(&bout, " %3.2f° %3.2f°", |
| p->az/(double)MILLIARCSEC, p->alt/(double)MILLIARCSEC); |
| Bprint(&bout, " %s", |
| ms(angle(p->semidiam))); |
| if(r->index <= 1) |
| Bprint(&bout, " %g", p->phase); |
| Bprint(&bout, "\n"); |
| break; |
| |
| case NGC: |
| n = &r->u.ngc; |
| if(n->ngc <= NNGC) |
| Bprint(&bout, "NGC%4d ", n->ngc); |
| else |
| Bprint(&bout, "IC%4d ", n->ngc-NNGC); |
| Bprint(&bout, "%s ", ngcstring(n->type)); |
| if(n->mag == UNKNOWNMAG) |
| Bprint(&bout, "----"); |
| else |
| Bprint(&bout, "%.1f%c", n->mag/10.0, n->magtype); |
| Bprint(&bout, "\t%s %s\t%c%.1f'\n", |
| hm(angle(n->ra)), |
| dm(angle(n->dec)), |
| n->diamlim, |
| DEG(angle(n->diam))*60.); |
| prdesc(n->desc, desctab, descindex); |
| printnames(r); |
| break; |
| |
| case Abell: |
| a = &r->u.abell; |
| Bprint(&bout, "Abell%4d %.1f %.2f° %dMpc", a->abell, a->mag10/10.0, |
| DEG(angle(a->rad)), a->dist); |
| Bprint(&bout, "\t%s %s\t%.2f %.2f\n", |
| hm(angle(a->ra)), |
| dm(angle(a->dec)), |
| DEG(angle(a->glat)), |
| DEG(angle(a->glong))); |
| Bprint(&bout, "\tdist grp: %s rich grp: %s %d galaxies/°²\n", |
| dist_grp(a->distgrp), |
| rich_grp(a->richgrp), |
| a->pop); |
| printnames(r); |
| break; |
| |
| case SAO: |
| s = &r->u.sao; |
| Bprint(&bout, "SAO%6ld ", r->index); |
| if(s->mag==UNKNOWNMAG) |
| Bprint(&bout, "---"); |
| else |
| Bprint(&bout, "%.1f", s->mag/10.0); |
| if(s->mpg==UNKNOWNMAG) |
| Bprint(&bout, ",---"); |
| else |
| Bprint(&bout, ",%.1f", s->mpg/10.0); |
| Bprint(&bout, " %s %s %.4fs %.3f\"", |
| hms(angle(s->ra)), |
| dms(angle(s->dec)), |
| DEG(angle(s->dra))*(4*60), |
| DEG(angle(s->ddec))*(60*60)); |
| Bprint(&bout, " %.3s %c %.2s %ld %d", |
| s->spec, s->code, s->compid, s->hd, s->hdcode); |
| if(s->name[0]) |
| Bprint(&bout, " \"%s\"", nameof(r)); |
| Bprint(&bout, "\n"); |
| printnames(r); |
| break; |
| |
| case Patch: |
| radec(r->index, &rah, &ram, &dec); |
| Bprint(&bout, "%dh%dm %d°", rah, ram, dec); |
| key = r->u.patch.key[0]; |
| Bprint(&bout, " %s", constel[key&0xFF]); |
| if((key>>=8) & 0xFF) |
| Bprint(&bout, " %s", constel[key&0xFF]); |
| if((key>>=8) & 0xFF) |
| Bprint(&bout, " %s", constel[key&0xFF]); |
| if((key>>=8) & 0xFF) |
| Bprint(&bout, " %s", constel[key&0xFF]); |
| for(i=1; i<r->u.patch.nkey; i++){ |
| key = r->u.patch.key[i]; |
| switch(key&0x3F){ |
| case SAO: |
| Bprint(&bout, " SAO%ld", (key>>8)&0xFFFFFF); |
| break; |
| case Abell: |
| Bprint(&bout, " Abell%ld", (key>>8)&0xFFFFFF); |
| break; |
| default: /* NGC */ |
| nn = (key>>16)&0xFFFF; |
| if(nn > NNGC) |
| Bprint(&bout, " IC%d", nn-NNGC); |
| else |
| Bprint(&bout, " NGC%d", nn); |
| Bprint(&bout, "(%s)", ngcstring(key&0x3F)); |
| break; |
| } |
| } |
| Bprint(&bout, "\n"); |
| break; |
| |
| case NGCN: |
| if(r->index <= NNGC) |
| Bprint(&bout, "NGC%ld\n", r->index); |
| else |
| Bprint(&bout, "IC%ld\n", r->index-NNGC); |
| break; |
| |
| case NamedSAO: |
| Bprint(&bout, "SAO%ld \"%s\"\n", r->index, togreek(r->u.named.name)); |
| break; |
| |
| case NamedNGC: |
| if(r->index <= NNGC) |
| Bprint(&bout, "NGC%ld \"%s\"\n", r->index, togreek(r->u.named.name)); |
| else |
| Bprint(&bout, "IC%ld \"%s\"\n", r->index-NNGC, togreek(r->u.named.name)); |
| break; |
| |
| case NamedAbell: |
| Bprint(&bout, "Abell%ld \"%s\"\n", r->index, togreek(r->u.named.name)); |
| break; |
| |
| case PatchC: |
| radec(r->index, &rah, &ram, &dec); |
| Bprint(&bout, "%dh%dm %d\n", rah, ram, dec); |
| break; |
| } |
| } |