Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <draw.h>
5 #include <event.h>
6 #include "sky.h"
7 #include "strings.c"
9 enum
10 {
11 NNGC=7840, /* number of NGC numbers [1..NNGC] */
12 NIC = 5386, /* number of IC numbers */
13 NNGCrec=NNGC+NIC, /* number of records in the NGC catalog (including IC's, starting at NNGC */
14 NMrec=122, /* number of M records */
15 NM=110, /* number of M numbers */
16 NAbell=2712, /* number of records in the Abell catalog */
17 NName=1000, /* number of prose names; estimated maximum (read from editable text file) */
18 NBayer=1517, /* number of bayer entries */
19 NSAO=258998, /* number of SAO stars */
20 MAXcon=1932, /* maximum number of patches in a constellation */
21 Ncon=88, /* number of constellations */
22 Npatch=92053, /* highest patch number */
23 };
25 char ngctype[NNGCrec];
26 Mindexrec mindex[NMrec];
27 Namerec name[NName];
28 Bayerec bayer[NBayer];
29 int32 con[MAXcon];
30 ushort conindex[Ncon+1];
31 int32 patchaddr[Npatch+1];
33 Record *rec;
34 Record *orec;
35 Record *cur;
37 char *dir;
38 int saodb;
39 int ngcdb;
40 int abelldb;
41 int ngctypedb;
42 int mindexdb;
43 int namedb;
44 int bayerdb;
45 int condb;
46 int conindexdb;
47 int patchdb;
48 char parsed[3];
49 int32 nrec;
50 int32 nreca;
51 int32 norec;
52 int32 noreca;
54 Biobuf bin;
55 Biobuf bout;
57 void
58 main(int argc, char *argv[])
59 {
60 char *line;
62 dir = unsharp(DIR);
63 Binit(&bin, 0, OREAD);
64 Binit(&bout, 1, OWRITE);
65 if(argc != 1)
66 dir = argv[1];
67 astro("", 1);
68 while(line = Brdline(&bin, '\n')){
69 line[Blinelen(&bin)-1] = 0;
70 lookup(line, 1);
71 Bflush(&bout);
72 }
73 if(display != nil){
74 closedisplay(display);
75 /* automatic refresh of rio window is triggered by mouse */
76 /* close(open("/dev/mouse", OREAD)); */
77 }
78 return;
79 }
81 void
82 reset(void)
83 {
84 nrec = 0;
85 cur = rec;
86 }
88 void
89 grow(void)
90 {
91 nrec++;
92 if(nreca < nrec){
93 nreca = nrec+50;
94 rec = realloc(rec, nreca*sizeof(Record));
95 if(rec == 0){
96 fprint(2, "scat: realloc fails\n");
97 exits("realloc");
98 }
99 }
100 cur = rec+nrec-1;
103 void
104 copy(void)
106 if(noreca < nreca){
107 noreca = nreca;
108 orec = realloc(orec, nreca*sizeof(Record));
109 if(orec == 0){
110 fprint(2, "scat: realloc fails\n");
111 exits("realloc");
114 memmove(orec, rec, nrec*sizeof(Record));
115 norec = nrec;
118 int
119 eopen(char *s)
121 char buf[128];
122 int f;
124 sprint(buf, "%s/%s.scat", dir, s);
125 f = open(buf, 0);
126 if(f<0){
127 fprint(2, "scat: can't open %s\n", buf);
128 exits("open");
130 return f;
134 void
135 Eread(int f, char *name, void *addr, int32 n)
137 if(read(f, addr, n) != n){ /* BUG! */
138 fprint(2, "scat: read error on %s\n", name);
139 exits("read");
143 char*
144 skipbl(char *s)
146 while(*s!=0 && (*s==' ' || *s=='\t'))
147 s++;
148 return s;
151 char*
152 skipstr(char *s, char *t)
154 while(*s && *s==*t)
155 s++, t++;
156 return skipbl(s);
159 /* produce little-endian int32 at address l */
160 int32
161 Long(int32 *l)
163 uchar *p;
165 p = (uchar*)l;
166 return (int32)p[0]|((int32)p[1]<<8)|((int32)p[2]<<16)|((int32)p[3]<<24);
169 /* produce little-endian int32 at address l */
170 int
171 Short(short *s)
173 uchar *p;
175 p = (uchar*)s;
176 return p[0]|(p[1]<<8);
179 void
180 nameopen(void)
182 Biobuf b;
183 int i;
184 char *l, *p;
186 if(namedb == 0){
187 namedb = eopen("name");
188 Binit(&b, namedb, OREAD);
189 for(i=0; i<NName; i++){
190 l = Brdline(&b, '\n');
191 if(l == 0)
192 break;
193 p = strchr(l, '\t');
194 if(p == 0){
195 Badformat:
196 Bprint(&bout, "warning: name.scat bad format; line %d\n", i+1);
197 break;
199 *p++ = 0;
200 strcpy(name[i].name, l);
201 if(strncmp(p, "ngc", 3) == 0)
202 name[i].ngc = atoi(p+3);
203 else if(strncmp(p, "ic", 2) == 0)
204 name[i].ngc = atoi(p+2)+NNGC;
205 else if(strncmp(p, "sao", 3) == 0)
206 name[i].sao = atoi(p+3);
207 else if(strncmp(p, "abell", 5) == 0)
208 name[i].abell = atoi(p+5);
209 else
210 goto Badformat;
212 if(i == NName)
213 Bprint(&bout, "warning: too many names in name.scat (max %d); extra ignored\n", NName);
214 close(namedb);
216 bayerdb = eopen("bayer");
217 Eread(bayerdb, "bayer", bayer, sizeof bayer);
218 close(bayerdb);
219 for(i=0; i<NBayer; i++)
220 bayer[i].sao = Long(&bayer[i].sao);
224 void
225 saoopen(void)
227 if(saodb == 0){
228 nameopen();
229 saodb = eopen("sao");
233 void
234 ngcopen(void)
236 if(ngcdb == 0){
237 nameopen();
238 ngcdb = eopen("ngc2000");
239 ngctypedb = eopen("ngc2000type");
240 Eread(ngctypedb, "ngctype", ngctype, sizeof ngctype);
241 close(ngctypedb);
245 void
246 abellopen(void)
248 /* nothing extra to do with abell: it's directly indexed by number */
249 if(abelldb == 0)
250 abelldb = eopen("abell");
253 void
254 patchopen(void)
256 Biobuf *b;
257 int32 l, m;
258 char buf[100];
260 if(patchdb == 0){
261 patchdb = eopen("patch");
262 sprint(buf, "%s/patchindex.scat", dir);
263 b = Bopen(buf, OREAD);
264 if(b == 0){
265 fprint(2, "can't open %s\n", buf);
266 exits("open");
268 for(m=0,l=0; l<=Npatch; l++)
269 patchaddr[l] = m += Bgetc(b)*4;
270 Bterm(b);
274 void
275 mopen(void)
277 int i;
279 if(mindexdb == 0){
280 mindexdb = eopen("mindex");
281 Eread(mindexdb, "mindex", mindex, sizeof mindex);
282 close(mindexdb);
283 for(i=0; i<NMrec; i++)
284 mindex[i].ngc = Short(&mindex[i].ngc);
288 void
289 constelopen(void)
291 int i;
293 if(condb == 0){
294 condb = eopen("con");
295 conindexdb = eopen("conindex");
296 Eread(conindexdb, "conindex", conindex, sizeof conindex);
297 close(conindexdb);
298 for(i=0; i<Ncon+1; i++)
299 conindex[i] = Short((short*)&conindex[i]);
303 void
304 lowercase(char *s)
306 for(; *s; s++)
307 if('A'<=*s && *s<='Z')
308 *s += 'a'-'A';
311 int
312 loadngc(int32 index)
314 static int failed;
315 int32 j;
317 ngcopen();
318 j = (index-1)*sizeof(NGCrec);
319 grow();
320 cur->type = NGC;
321 cur->index = index;
322 seek(ngcdb, j, 0);
323 /* special case: NGC data may not be available */
324 if(read(ngcdb, &cur->u.ngc, sizeof(NGCrec)) != sizeof(NGCrec)){
325 if(!failed){
326 fprint(2, "scat: NGC database not available\n");
327 failed++;
329 cur->type = NONGC;
330 cur->u.ngc.ngc = 0;
331 cur->u.ngc.ra = 0;
332 cur->u.ngc.dec = 0;
333 cur->u.ngc.diam = 0;
334 cur->u.ngc.mag = 0;
335 return 0;
337 cur->u.ngc.ngc = Short(&cur->u.ngc.ngc);
338 cur->u.ngc.ra = Long(&cur->u.ngc.ra);
339 cur->u.ngc.dec = Long(&cur->u.ngc.dec);
340 cur->u.ngc.diam = Long(&cur->u.ngc.diam);
341 cur->u.ngc.mag = Short(&cur->u.ngc.mag);
342 return 1;
345 int
346 loadabell(int32 index)
348 int32 j;
350 abellopen();
351 j = index-1;
352 grow();
353 cur->type = Abell;
354 cur->index = index;
355 seek(abelldb, j*sizeof(Abellrec), 0);
356 Eread(abelldb, "abell", &cur->u.abell, sizeof(Abellrec));
357 cur->u.abell.abell = Short(&cur->u.abell.abell);
358 if(cur->u.abell.abell != index){
359 fprint(2, "bad format in abell catalog\n");
360 exits("abell");
362 cur->u.abell.ra = Long(&cur->u.abell.ra);
363 cur->u.abell.dec = Long(&cur->u.abell.dec);
364 cur->u.abell.glat = Long(&cur->u.abell.glat);
365 cur->u.abell.glong = Long(&cur->u.abell.glong);
366 cur->u.abell.rad = Long(&cur->u.abell.rad);
367 cur->u.abell.mag10 = Short(&cur->u.abell.mag10);
368 cur->u.abell.pop = Short(&cur->u.abell.pop);
369 cur->u.abell.dist = Short(&cur->u.abell.dist);
370 return 1;
373 int
374 loadsao(int index)
376 if(index<=0 || index>NSAO)
377 return 0;
378 saoopen();
379 grow();
380 cur->type = SAO;
381 cur->index = index;
382 seek(saodb, (index-1)*sizeof(SAOrec), 0);
383 Eread(saodb, "sao", &cur->u.sao, sizeof(SAOrec));
384 cur->u.sao.ra = Long(&cur->u.sao.ra);
385 cur->u.sao.dec = Long(&cur->u.sao.dec);
386 cur->u.sao.dra = Long(&cur->u.sao.dra);
387 cur->u.sao.ddec = Long(&cur->u.sao.ddec);
388 cur->u.sao.mag = Short(&cur->u.sao.mag);
389 cur->u.sao.mpg = Short(&cur->u.sao.mpg);
390 cur->u.sao.hd = Long(&cur->u.sao.hd);
391 return 1;
394 int
395 loadplanet(int index, Record *r)
397 if(index<0 || index>NPlanet || planet[index].name[0]=='\0')
398 return 0;
399 grow();
400 cur->type = Planet;
401 cur->index = index;
402 /* check whether to take new or existing record */
403 if(r == nil)
404 memmove(&cur->u.planet, &planet[index], sizeof(Planetrec));
405 else
406 memmove(&cur->u.planet, &r->u.planet, sizeof(Planetrec));
407 return 1;
410 int
411 loadpatch(int32 index)
413 int i;
415 patchopen();
416 if(index<=0 || index>Npatch)
417 return 0;
418 grow();
419 cur->type = Patch;
420 cur->index = index;
421 seek(patchdb, patchaddr[index-1], 0);
422 cur->u.patch.nkey = (patchaddr[index]-patchaddr[index-1])/4;
423 Eread(patchdb, "patch", cur->u.patch.key, cur->u.patch.nkey*4);
424 for(i=0; i<cur->u.patch.nkey; i++)
425 cur->u.patch.key[i] = Long(&cur->u.patch.key[i]);
426 return 1;
429 int
430 loadtype(int t)
432 int i;
434 ngcopen();
435 for(i=0; i<NNGCrec; i++)
436 if(t == (ngctype[i])){
437 grow();
438 cur->type = NGCN;
439 cur->index = i+1;
441 return 1;
444 void
445 flatten(void)
447 int i, j, notflat;
448 Record *or;
449 int32 key;
451 loop:
452 copy();
453 reset();
454 notflat = 0;
455 for(i=0,or=orec; i<norec; i++,or++){
456 switch(or->type){
457 default:
458 fprint(2, "bad type %d in flatten\n", or->type);
459 break;
461 case NONGC:
462 break;
464 case Planet:
465 case Abell:
466 case NGC:
467 case SAO:
468 grow();
469 memmove(cur, or, sizeof(Record));
470 break;
472 case NGCN:
473 if(loadngc(or->index))
474 notflat = 1;
475 break;
477 case NamedSAO:
478 loadsao(or->index);
479 notflat = 1;
480 break;
482 case NamedNGC:
483 if(loadngc(or->index))
484 notflat = 1;
485 break;
487 case NamedAbell:
488 loadabell(or->index);
489 notflat = 1;
490 break;
492 case PatchC:
493 loadpatch(or->index);
494 notflat = 1;
495 break;
497 case Patch:
498 for(j=1; j<or->u.patch.nkey; j++){
499 key = or->u.patch.key[j];
500 if((key&0x3F) == SAO)
501 loadsao((key>>8)&0xFFFFFF);
502 else if((key&0x3F) == Abell)
503 loadabell((key>>8)&0xFFFFFF);
504 else
505 loadngc((key>>16)&0xFFFF);
507 break;
510 if(notflat)
511 goto loop;
514 int
515 ism(int index)
517 int i;
519 for(i=0; i<NMrec; i++)
520 if(mindex[i].ngc == index)
521 return 1;
522 return 0;
525 char*
526 alpha(char *s, char *t)
528 int n;
530 n = strlen(t);
531 if(strncmp(s, t, n)==0 && (s[n]<'a' || 'z'<s[n]))
532 return skipbl(s+n);
533 return 0;
537 char*
538 text(char *s, char *t)
540 int n;
542 n = strlen(t);
543 if(strncmp(s, t, n)==0 && (s[n]==0 || s[n]==' ' || s[n]=='\t'))
544 return skipbl(s+n);
545 return 0;
549 int
550 cull(char *s, int keep, int dobbox)
552 int i, j, nobj, keepthis;
553 Record *or;
554 char *t;
555 int dogrtr, doless, dom, dosao, dongc, doabell;
556 int mgrtr, mless;
557 char obj[100];
559 memset(obj, 0, sizeof(obj));
560 nobj = 0;
561 dogrtr = 0;
562 doless = 0;
563 dom = 0;
564 dongc = 0;
565 dosao = 0;
566 doabell = 0;
567 mgrtr = mless= 0;
568 if(dobbox)
569 goto Cull;
570 for(;;){
571 if(s[0] == '>'){
572 dogrtr = 1;
573 mgrtr = 10 * strtod(s+1, &t);
574 if(mgrtr==0 && t==s+1){
575 fprint(2, "bad magnitude\n");
576 return 0;
578 s = skipbl(t);
579 continue;
581 if(s[0] == '<'){
582 doless = 1;
583 mless = 10 * strtod(s+1, &t);
584 if(mless==0 && t==s+1){
585 fprint(2, "bad magnitude\n");
586 return 0;
588 s = skipbl(t);
589 continue;
591 if(t = text(s, "m")){
592 dom = 1;
593 s = t;
594 continue;
596 if(t = text(s, "sao")){
597 dosao = 1;
598 s = t;
599 continue;
601 if(t = text(s, "ngc")){
602 dongc = 1;
603 s = t;
604 continue;
606 if(t = text(s, "abell")){
607 doabell = 1;
608 s = t;
609 continue;
611 for(i=0; names[i].name; i++)
612 if(t = alpha(s, names[i].name)){
613 if(nobj > 100){
614 fprint(2, "too many object types\n");
615 return 0;
617 obj[nobj++] = names[i].type;
618 s = t;
619 goto Continue;
621 break;
622 Continue:;
624 if(*s){
625 fprint(2, "syntax error in object list\n");
626 return 0;
629 Cull:
630 flatten();
631 copy();
632 reset();
633 if(dom)
634 mopen();
635 if(dosao)
636 saoopen();
637 if(dongc || nobj)
638 ngcopen();
639 if(doabell)
640 abellopen();
641 for(i=0,or=orec; i<norec; i++,or++){
642 keepthis = !keep;
643 if(dobbox && inbbox(or->u.ngc.ra, or->u.ngc.dec))
644 keepthis = keep;
645 if(doless && or->u.ngc.mag <= mless)
646 keepthis = keep;
647 if(dogrtr && or->u.ngc.mag >= mgrtr)
648 keepthis = keep;
649 if(dom && (or->type==NGC && ism(or->u.ngc.ngc)))
650 keepthis = keep;
651 if(dongc && or->type==NGC)
652 keepthis = keep;
653 if(doabell && or->type==Abell)
654 keepthis = keep;
655 if(dosao && or->type==SAO)
656 keepthis = keep;
657 for(j=0; j<nobj; j++)
658 if(or->type==NGC && or->u.ngc.type==obj[j])
659 keepthis = keep;
660 if(keepthis){
661 grow();
662 memmove(cur, or, sizeof(Record));
665 return 1;
668 int
669 compar(const void *va, const void *vb)
671 Record *a=(Record*)va, *b=(Record*)vb;
673 if(a->type == b->type)
674 return a->index - b->index;
675 return a->type - b->type;
678 void
679 sort(void)
681 int i;
682 Record *r, *s;
684 if(nrec == 0)
685 return;
686 qsort(rec, nrec, sizeof(Record), compar);
687 r = rec+1;
688 s = rec;
689 for(i=1; i<nrec; i++,r++){
690 /* may have multiple instances of a planet in the scene */
691 if(r->type==s->type && r->index==s->index && r->type!=Planet)
692 continue;
693 memmove(++s, r, sizeof(Record));
695 nrec = (s+1)-rec;
698 char greekbuf[128];
700 char*
701 togreek(char *s)
703 char *t;
704 int i, n;
705 Rune r;
707 t = greekbuf;
708 while(*s){
709 for(i=1; i<=24; i++){
710 n = strlen(greek[i]);
711 if(strncmp(s, greek[i], n)==0 && (s[n]==' ' || s[n]=='\t')){
712 s += n;
713 t += runetochar(t, &greeklet[i]);
714 goto Cont;
717 n = chartorune(&r, s);
718 for(i=0; i<n; i++)
719 *t++ = *s++;
720 Cont:;
722 *t = 0;
723 return greekbuf;
726 char*
727 fromgreek(char *s)
729 char *t;
730 int i, n;
731 Rune r;
733 t = greekbuf;
734 while(*s){
735 n = chartorune(&r, s);
736 for(i=1; i<=24; i++){
737 if(r == greeklet[i]){
738 strcpy(t, greek[i]);
739 t += strlen(greek[i]);
740 s += n;
741 goto Cont;
744 for(i=0; i<n; i++)
745 *t++ = *s++;
746 Cont:;
748 *t = 0;
749 return greekbuf;
752 #ifdef OLD
753 /*
754 * Old version
755 */
756 int
757 coords(int deg)
759 int i;
760 int x, y;
761 Record *or;
762 int32 dec, ra, ndec, nra;
763 int rdeg;
765 flatten();
766 copy();
767 reset();
768 deg *= 2;
769 for(i=0,or=orec; i<norec; i++,or++){
770 if(or->type == Planet) /* must keep it here */
771 loadplanet(or->index, or);
772 dec = or->u.ngc.dec/MILLIARCSEC;
773 ra = or->u.ngc.ra/MILLIARCSEC;
774 rdeg = deg/cos((dec*PI)/180);
775 for(y=-deg; y<=+deg; y++){
776 ndec = dec*2+y;
777 if(ndec/2>=90 || ndec/2<=-90)
778 continue;
779 /* fp errors hurt here, so we round 1' to the pole */
780 if(ndec >= 0)
781 ndec = ndec*500*60*60 + 60000;
782 else
783 ndec = ndec*500*60*60 - 60000;
784 for(x=-rdeg; x<=+rdeg; x++){
785 nra = ra*2+x;
786 if(nra/2 < 0)
787 nra += 360*2;
788 if(nra/2 >= 360)
789 nra -= 360*2;
790 /* fp errors hurt here, so we round up 1' */
791 nra = nra/2*MILLIARCSEC + 60000;
792 loadpatch(patcha(angle(nra), angle(ndec)));
796 sort();
797 return 1;
799 #endif
801 /*
802 * New version attempts to match the boundaries of the plot better.
803 */
804 int
805 coords(int deg)
807 int i;
808 int x, y, xx;
809 Record *or;
810 int32 min, circle;
811 double factor;
813 flatten();
814 circle = 360*MILLIARCSEC;
815 deg *= MILLIARCSEC;
817 /* find center */
818 folded = 0;
819 bbox(0, 0, 0);
820 /* now expand */
821 factor = cos(angle((decmax+decmin)/2));
822 if(factor < .2)
823 factor = .2;
824 factor = floor(1/factor);
825 folded = 0;
826 bbox(factor*deg, deg, 1);
827 Bprint(&bout, "%s to ", hms(angle(ramin)));
828 Bprint(&bout, "%s\n", hms(angle(ramax)));
829 Bprint(&bout, "%s to ", dms(angle(decmin)));
830 Bprint(&bout, "%s\n", dms(angle(decmax)));
831 copy();
832 reset();
833 for(i=0,or=orec; i<norec; i++,or++)
834 if(or->type == Planet) /* must keep it here */
835 loadplanet(or->index, or);
836 min = ramin;
837 if(ramin > ramax)
838 min -= circle;
839 for(x=min; x<=ramax; x+=250*60*60){
840 xx = x;
841 if(xx < 0)
842 xx += circle;
843 for(y=decmin; y<=decmax; y+=250*60*60)
844 if(-circle/4 < y && y < circle/4)
845 loadpatch(patcha(angle(xx), angle(y)));
847 sort();
848 cull(nil, 1, 1);
849 return 1;
852 void
853 pplate(char *flags)
855 int i;
856 int32 c;
857 int na, rah, ram, d1, d2;
858 double r0;
859 int ra, dec;
860 int32 ramin, ramax, decmin, decmax; /* all in degrees */
861 Record *r;
862 int folded;
863 Angle racenter, deccenter, rasize, decsize, a[4];
864 Picture *pic;
866 rasize = -1.0;
867 decsize = -1.0;
868 na = 0;
869 for(;;){
870 while(*flags==' ')
871 flags++;
872 if(('0'<=*flags && *flags<='9') || *flags=='+' || *flags=='-'){
873 if(na >= 3)
874 goto err;
875 a[na++] = getra(flags);
876 while(*flags && *flags!=' ')
877 flags++;
878 continue;
880 if(*flags){
881 err:
882 Bprint(&bout, "syntax error in plate\n");
883 return;
885 break;
887 switch(na){
888 case 0:
889 break;
890 case 1:
891 rasize = a[0];
892 decsize = rasize;
893 break;
894 case 2:
895 rasize = a[0];
896 decsize = a[1];
897 break;
898 case 3:
899 case 4:
900 racenter = a[0];
901 deccenter = a[1];
902 rasize = a[2];
903 if(na == 4)
904 decsize = a[3];
905 else
906 decsize = rasize;
907 if(rasize<0.0 || decsize<0.0){
908 Bprint(&bout, "negative sizes\n");
909 return;
911 goto done;
913 folded = 0;
914 /* convert to milliarcsec */
915 c = 1000*60*60;
916 Again:
917 if(nrec == 0){
918 Bprint(&bout, "empty\n");
919 return;
921 ramin = 0x7FFFFFFF;
922 ramax = -0x7FFFFFFF;
923 decmin = 0x7FFFFFFF;
924 decmax = -0x7FFFFFFF;
925 for(r=rec,i=0; i<nrec; i++,r++){
926 if(r->type == Patch){
927 radec(r->index, &rah, &ram, &dec);
928 ra = 15*rah+ram/4;
929 r0 = c/cos(RAD(dec));
930 ra *= c;
931 dec *= c;
932 if(dec == 0)
933 d1 = c, d2 = c;
934 else if(dec < 0)
935 d1 = c, d2 = 0;
936 else
937 d1 = 0, d2 = c;
938 }else if(r->type==SAO || r->type==NGC || r->type==Abell){
939 ra = r->u.ngc.ra;
940 dec = r->u.ngc.dec;
941 d1 = 0, d2 = 0, r0 = 0;
942 }else if(r->type==NGCN){
943 loadngc(r->index);
944 continue;
945 }else if(r->type==NamedSAO){
946 loadsao(r->index);
947 continue;
948 }else if(r->type==NamedNGC){
949 loadngc(r->index);
950 continue;
951 }else if(r->type==NamedAbell){
952 loadabell(r->index);
953 continue;
954 }else
955 continue;
956 if(dec+d2 > decmax)
957 decmax = dec+d2;
958 if(dec-d1 < decmin)
959 decmin = dec-d1;
960 if(folded){
961 ra -= 180*c;
962 if(ra < 0)
963 ra += 360*c;
965 if(ra+r0 > ramax)
966 ramax = ra+r0;
967 if(ra < ramin)
968 ramin = ra;
970 if(!folded && ramax-ramin>270*c){
971 folded = 1;
972 goto Again;
974 racenter = angle(ramin+(ramax-ramin)/2);
975 deccenter = angle(decmin+(decmax-decmin)/2);
976 if(rasize<0 || decsize<0){
977 rasize = angle(ramax-ramin)*cos(deccenter);
978 decsize = angle(decmax-decmin);
980 done:
981 if(DEG(rasize)>1.1 || DEG(decsize)>1.1){
982 Bprint(&bout, "plate too big: %s", ms(rasize));
983 Bprint(&bout, " x %s\n", ms(decsize));
984 Bprint(&bout, "trimming to 30'x30'\n");
985 rasize = RAD(0.5);
986 decsize = RAD(0.5);
988 Bprint(&bout, "%s %s ", hms(racenter), dms(deccenter));
989 Bprint(&bout, "%s", ms(rasize));
990 Bprint(&bout, " x %s\n", ms(decsize));
991 Bflush(&bout);
992 flatten();
993 pic = image(racenter, deccenter, rasize, decsize);
994 if(pic == 0)
995 return;
996 Bprint(&bout, "plate %s locn %d %d %d %d\n", pic->name, pic->minx, pic->miny, pic->maxx, pic->maxy);
997 Bflush(&bout);
998 displaypic(pic);
1001 void
1002 lookup(char *s, int doreset)
1004 int i, j, k;
1005 int rah, ram, deg;
1006 char *starts, *inputline=s, *t, *u;
1007 Record *r;
1008 Rune c;
1009 int32 n;
1010 double x;
1011 Angle ra;
1013 lowercase(s);
1014 s = skipbl(s);
1016 if(*s == 0)
1017 goto Print;
1019 if(t = alpha(s, "flat")){
1020 if(*t){
1021 fprint(2, "flat takes no arguments\n");
1022 return;
1024 if(nrec == 0){
1025 fprint(2, "no records\n");
1026 return;
1028 flatten();
1029 goto Print;
1032 if(t = alpha(s, "print")){
1033 if(*t){
1034 fprint(2, "print takes no arguments\n");
1035 return;
1037 for(i=0,r=rec; i<nrec; i++,r++)
1038 prrec(r);
1039 return;
1042 if(t = alpha(s, "add")){
1043 lookup(t, 0);
1044 return;
1047 if(t = alpha(s, "sao")){
1048 n = strtoul(t, &u, 10);
1049 if(n<=0 || n>NSAO)
1050 goto NotFound;
1051 t = skipbl(u);
1052 if(*t){
1053 fprint(2, "syntax error in sao\n");
1054 return;
1056 if(doreset)
1057 reset();
1058 if(!loadsao(n))
1059 goto NotFound;
1060 goto Print;
1063 if(t = alpha(s, "ngc")){
1064 n = strtoul(t, &u, 10);
1065 if(n<=0 || n>NNGC)
1066 goto NotFound;
1067 t = skipbl(u);
1068 if(*t){
1069 fprint(2, "syntax error in ngc\n");
1070 return;
1072 if(doreset)
1073 reset();
1074 if(!loadngc(n))
1075 goto NotFound;
1076 goto Print;
1079 if(t = alpha(s, "ic")){
1080 n = strtoul(t, &u, 10);
1081 if(n<=0 || n>NIC)
1082 goto NotFound;
1083 t = skipbl(u);
1084 if(*t){
1085 fprint(2, "syntax error in ic\n");
1086 return;
1088 if(doreset)
1089 reset();
1090 if(!loadngc(n+NNGC))
1091 goto NotFound;
1092 goto Print;
1095 if(t = alpha(s, "abell")){
1096 n = strtoul(t, &u, 10);
1097 if(n<=0 || n>NAbell)
1098 goto NotFound;
1099 if(doreset)
1100 reset();
1101 if(!loadabell(n))
1102 goto NotFound;
1103 goto Print;
1106 if(t = alpha(s, "m")){
1107 n = strtoul(t, &u, 10);
1108 if(n<=0 || n>NM)
1109 goto NotFound;
1110 mopen();
1111 for(j=n-1; mindex[j].m<n; j++)
1113 if(doreset)
1114 reset();
1115 while(mindex[j].m == n){
1116 if(mindex[j].ngc){
1117 grow();
1118 cur->type = NGCN;
1119 cur->index = mindex[j].ngc;
1121 j++;
1123 goto Print;
1126 for(i=1; i<=Ncon; i++)
1127 if(t = alpha(s, constel[i])){
1128 if(*t){
1129 fprint(2, "syntax error in constellation\n");
1130 return;
1132 constelopen();
1133 seek(condb, 4L*conindex[i-1], 0);
1134 j = conindex[i]-conindex[i-1];
1135 Eread(condb, "con", con, 4*j);
1136 if(doreset)
1137 reset();
1138 for(k=0; k<j; k++){
1139 grow();
1140 cur->type = PatchC;
1141 cur->index = Long(&con[k]);
1143 goto Print;
1146 if(t = alpha(s, "expand")){
1147 n = 0;
1148 if(*t){
1149 if(*t<'0' && '9'<*t){
1150 Expanderr:
1151 fprint(2, "syntax error in expand\n");
1152 return;
1154 n = strtoul(t, &u, 10);
1155 t = skipbl(u);
1156 if(*t)
1157 goto Expanderr;
1159 coords(n);
1160 goto Print;
1163 if(t = alpha(s, "plot")){
1164 if(nrec == 0){
1165 Bprint(&bout, "empty\n");
1166 return;
1168 plot(t);
1169 return;
1172 if(t = alpha(s, "astro")){
1173 astro(t, 0);
1174 return;
1177 if(t = alpha(s, "plate")){
1178 pplate(t);
1179 return;
1182 if(t = alpha(s, "gamma")){
1183 while(*t==' ')
1184 t++;
1185 u = t;
1186 x = strtod(t, &u);
1187 if(u > t)
1188 gam.gamma = x;
1189 Bprint(&bout, "%.2f\n", gam.gamma);
1190 return;
1193 if(t = alpha(s, "keep")){
1194 if(!cull(t, 1, 0))
1195 return;
1196 goto Print;
1199 if(t = alpha(s, "drop")){
1200 if(!cull(t, 0, 0))
1201 return;
1202 goto Print;
1205 for(i=0; planet[i].name[0]; i++){
1206 if(t = alpha(s, planet[i].name)){
1207 if(doreset)
1208 reset();
1209 loadplanet(i, nil);
1210 goto Print;
1214 for(i=0; names[i].name; i++){
1215 if(t = alpha(s, names[i].name)){
1216 if(*t){
1217 fprint(2, "syntax error in type\n");
1218 return;
1220 if(doreset)
1221 reset();
1222 loadtype(names[i].type);
1223 goto Print;
1227 switch(s[0]){
1228 case '"':
1229 starts = ++s;
1230 while(*s != '"')
1231 if(*s++ == 0){
1232 fprint(2, "bad star name\n");
1233 return;
1235 *s = 0;
1236 if(doreset)
1237 reset();
1238 j = nrec;
1239 saoopen();
1240 starts = fromgreek(starts);
1241 for(i=0; i<NName; i++)
1242 if(equal(starts, name[i].name)){
1243 grow();
1244 if(name[i].sao){
1245 rec[j].type = NamedSAO;
1246 rec[j].index = name[i].sao;
1248 if(name[i].ngc){
1249 rec[j].type = NamedNGC;
1250 rec[j].index = name[i].ngc;
1252 if(name[i].abell){
1253 rec[j].type = NamedAbell;
1254 rec[j].index = name[i].abell;
1256 strcpy(rec[j].u.named.name, name[i].name);
1257 j++;
1259 if(parsename(starts))
1260 for(i=0; i<NBayer; i++)
1261 if(bayer[i].name[0]==parsed[0] &&
1262 (bayer[i].name[1]==parsed[1] || parsed[1]==0) &&
1263 bayer[i].name[2]==parsed[2]){
1264 grow();
1265 rec[j].type = NamedSAO;
1266 rec[j].index = bayer[i].sao;
1267 strncpy(rec[j].u.named.name, starts, sizeof(rec[j].u.named.name));
1268 j++;
1270 if(j == 0){
1271 *s = '"';
1272 goto NotFound;
1274 break;
1276 case '0': case '1': case '2': case '3': case '4':
1277 case '5': case '6': case '7': case '8': case '9':
1278 strtoul(s, &t, 10);
1279 if(*t != 'h'){
1280 BadCoords:
1281 fprint(2, "bad coordinates %s\n", inputline);
1282 break;
1284 ra = DEG(getra(s));
1285 while(*s && *s!=' ' && *s!='\t')
1286 s++;
1287 rah = ra/15;
1288 ra = ra-rah*15;
1289 ram = ra*4;
1290 deg = strtol(s, &t, 10);
1291 if(t == s)
1292 goto BadCoords;
1293 /* degree sign etc. is optional */
1294 chartorune(&c, t);
1295 if(c == 0xb0)
1296 deg = DEG(getra(s));
1297 if(doreset)
1298 reset();
1299 if(abs(deg)>=90 || rah>=24)
1300 goto BadCoords;
1301 if(!loadpatch(patch(rah, ram, deg)))
1302 goto NotFound;
1303 break;
1305 default:
1306 fprint(2, "unknown command %s\n", inputline);
1307 return;
1310 Print:
1311 if(nrec == 0)
1312 Bprint(&bout, "empty\n");
1313 else if(nrec <= 2)
1314 for(i=0; i<nrec; i++)
1315 prrec(rec+i);
1316 else
1317 Bprint(&bout, "%ld items\n", nrec);
1318 return;
1320 NotFound:
1321 fprint(2, "%s not found\n", inputline);
1322 return;
1325 char *ngctypes[] =
1327 [Galaxy] = "Gx",
1328 [PlanetaryN] = "Pl",
1329 [OpenCl] = "OC",
1330 [GlobularCl] = "Gb",
1331 [DiffuseN] = "Nb",
1332 [NebularCl] = "C+N",
1333 [Asterism] = "Ast",
1334 [Knot] = "Kt",
1335 [Triple] = "***",
1336 [Double] = "D*",
1337 [Single] = "*",
1338 [Uncertain] = "?",
1339 [Nonexistent] = "-",
1340 [Unknown] = " ",
1341 [PlateDefect] = "PD"
1344 char*
1345 ngcstring(int d)
1347 if(d<Galaxy || d>PlateDefect)
1348 return "can't happen";
1349 return ngctypes[d];
1352 short descindex[NINDEX];
1354 void
1355 printnames(Record *r)
1357 int i, ok, done;
1359 done = 0;
1360 for(i=0; i<NName; i++){ /* stupid linear search! */
1361 ok = 0;
1362 if(r->type==SAO && r->index==name[i].sao)
1363 ok = 1;
1364 if(r->type==NGC && r->u.ngc.ngc==name[i].ngc)
1365 ok = 1;
1366 if(r->type==Abell && r->u.abell.abell==name[i].abell)
1367 ok = 1;
1368 if(ok){
1369 if(done++ == 0)
1370 Bprint(&bout, "\t");
1371 Bprint(&bout, " \"%s\"", togreek(name[i].name));
1374 if(done)
1375 Bprint(&bout, "\n");
1378 int
1379 equal(char *s1, char *s2)
1381 int c;
1383 while(*s1){
1384 if(*s1==' '){
1385 while(*s1==' ')
1386 s1++;
1387 continue;
1389 while(*s2==' ')
1390 s2++;
1391 c=*s2;
1392 if('A'<=*s2 && *s2<='Z')
1393 c^=' ';
1394 if(*s1!=c)
1395 return 0;
1396 s1++, s2++;
1398 return 1;
1401 int
1402 parsename(char *s)
1404 char *blank;
1405 int i;
1407 blank = strchr(s, ' ');
1408 if(blank==0 || strchr(blank+1, ' ') || strlen(blank+1)!=3)
1409 return 0;
1410 blank++;
1411 parsed[0] = parsed[1] = parsed[2] = 0;
1412 if('0'<=s[0] && s[0]<='9'){
1413 i = atoi(s);
1414 parsed[0] = i;
1415 if(i > 100)
1416 return 0;
1417 }else{
1418 for(i=1; i<=24; i++)
1419 if(strncmp(greek[i], s, strlen(greek[i]))==0){
1420 parsed[0]=100+i;
1421 goto out;
1423 return 0;
1424 out:
1425 if('0'<=s[strlen(greek[i])] && s[strlen(greek[i])]<='9')
1426 parsed[1]=s[strlen(greek[i])]-'0';
1428 for(i=1; i<=88; i++)
1429 if(strcmp(constel[i], blank)==0){
1430 parsed[2] = i;
1431 return 1;
1433 return 0;
1436 char*
1437 dist_grp(int dg)
1439 switch(dg){
1440 default:
1441 return "unknown";
1442 case 1:
1443 return "13.3-14.0";
1444 case 2:
1445 return "14.1-14.8";
1446 case 3:
1447 return "14.9-15.6";
1448 case 4:
1449 return "15.7-16.4";
1450 case 5:
1451 return "16.5-17.2";
1452 case 6:
1453 return "17.3-18.0";
1454 case 7:
1455 return ">18.0";
1459 char*
1460 rich_grp(int dg)
1462 switch(dg){
1463 default:
1464 return "unknown";
1465 case 0:
1466 return "30-40";
1467 case 1:
1468 return "50-79";
1469 case 2:
1470 return "80-129";
1471 case 3:
1472 return "130-199";
1473 case 4:
1474 return "200-299";
1475 case 5:
1476 return ">=300";
1480 char*
1481 nameof(Record *r)
1483 NGCrec *n;
1484 SAOrec *s;
1485 Abellrec *a;
1486 static char buf[128];
1487 int i;
1489 switch(r->type){
1490 default:
1491 return nil;
1492 case SAO:
1493 s = &r->u.sao;
1494 if(s->name[0] == 0)
1495 return nil;
1496 if(s->name[0] >= 100){
1497 i = snprint(buf, sizeof buf, "%C", greeklet[s->name[0]-100]);
1498 if(s->name[1])
1499 i += snprint(buf+i, sizeof buf-i, "%d", s->name[1]);
1500 }else
1501 i = snprint(buf, sizeof buf, " %d", s->name[0]);
1502 snprint(buf+i, sizeof buf-i, " %s", constel[(uchar)s->name[2]]);
1503 break;
1504 case NGC:
1505 n = &r->u.ngc;
1506 if(n->type >= Uncertain)
1507 return nil;
1508 if(n->ngc <= NNGC)
1509 snprint(buf, sizeof buf, "NGC%4d ", n->ngc);
1510 else
1511 snprint(buf, sizeof buf, "IC%4d ", n->ngc-NNGC);
1512 break;
1513 case Abell:
1514 a = &r->u.abell;
1515 snprint(buf, sizeof buf, "Abell%4d", a->abell);
1516 break;
1518 return buf;
1521 void
1522 prrec(Record *r)
1524 NGCrec *n;
1525 SAOrec *s;
1526 Abellrec *a;
1527 Planetrec *p;
1528 int i, rah, ram, dec, nn;
1529 int32 key;
1531 if(r) switch(r->type){
1532 default:
1533 fprint(2, "can't prrec type %d\n", r->type);
1534 exits("type");
1536 case Planet:
1537 p = &r->u.planet;
1538 Bprint(&bout, "%s", p->name);
1539 Bprint(&bout, "\t%s %s",
1540 hms(angle(p->ra)),
1541 dms(angle(p->dec)));
1542 Bprint(&bout, " %3.2f° %3.2f°",
1543 p->az/(double)MILLIARCSEC, p->alt/(double)MILLIARCSEC);
1544 Bprint(&bout, " %s",
1545 ms(angle(p->semidiam)));
1546 if(r->index <= 1)
1547 Bprint(&bout, " %g", p->phase);
1548 Bprint(&bout, "\n");
1549 break;
1551 case NGC:
1552 n = &r->u.ngc;
1553 if(n->ngc <= NNGC)
1554 Bprint(&bout, "NGC%4d ", n->ngc);
1555 else
1556 Bprint(&bout, "IC%4d ", n->ngc-NNGC);
1557 Bprint(&bout, "%s ", ngcstring(n->type));
1558 if(n->mag == UNKNOWNMAG)
1559 Bprint(&bout, "----");
1560 else
1561 Bprint(&bout, "%.1f%c", n->mag/10.0, n->magtype);
1562 Bprint(&bout, "\t%s %s\t%c%.1f'\n",
1563 hm(angle(n->ra)),
1564 dm(angle(n->dec)),
1565 n->diamlim,
1566 DEG(angle(n->diam))*60.);
1567 prdesc(n->desc, desctab, descindex);
1568 printnames(r);
1569 break;
1571 case Abell:
1572 a = &r->u.abell;
1573 Bprint(&bout, "Abell%4d %.1f %.2f° %dMpc", a->abell, a->mag10/10.0,
1574 DEG(angle(a->rad)), a->dist);
1575 Bprint(&bout, "\t%s %s\t%.2f %.2f\n",
1576 hm(angle(a->ra)),
1577 dm(angle(a->dec)),
1578 DEG(angle(a->glat)),
1579 DEG(angle(a->glong)));
1580 Bprint(&bout, "\tdist grp: %s rich grp: %s %d galaxies/°²\n",
1581 dist_grp(a->distgrp),
1582 rich_grp(a->richgrp),
1583 a->pop);
1584 printnames(r);
1585 break;
1587 case SAO:
1588 s = &r->u.sao;
1589 Bprint(&bout, "SAO%6ld ", r->index);
1590 if(s->mag==UNKNOWNMAG)
1591 Bprint(&bout, "---");
1592 else
1593 Bprint(&bout, "%.1f", s->mag/10.0);
1594 if(s->mpg==UNKNOWNMAG)
1595 Bprint(&bout, ",---");
1596 else
1597 Bprint(&bout, ",%.1f", s->mpg/10.0);
1598 Bprint(&bout, " %s %s %.4fs %.3f\"",
1599 hms(angle(s->ra)),
1600 dms(angle(s->dec)),
1601 DEG(angle(s->dra))*(4*60),
1602 DEG(angle(s->ddec))*(60*60));
1603 Bprint(&bout, " %.3s %c %.2s %ld %d",
1604 s->spec, s->code, s->compid, s->hd, s->hdcode);
1605 if(s->name[0])
1606 Bprint(&bout, " \"%s\"", nameof(r));
1607 Bprint(&bout, "\n");
1608 printnames(r);
1609 break;
1611 case Patch:
1612 radec(r->index, &rah, &ram, &dec);
1613 Bprint(&bout, "%dh%dm %d°", rah, ram, dec);
1614 key = r->u.patch.key[0];
1615 Bprint(&bout, " %s", constel[key&0xFF]);
1616 if((key>>=8) & 0xFF)
1617 Bprint(&bout, " %s", constel[key&0xFF]);
1618 if((key>>=8) & 0xFF)
1619 Bprint(&bout, " %s", constel[key&0xFF]);
1620 if((key>>=8) & 0xFF)
1621 Bprint(&bout, " %s", constel[key&0xFF]);
1622 for(i=1; i<r->u.patch.nkey; i++){
1623 key = r->u.patch.key[i];
1624 switch(key&0x3F){
1625 case SAO:
1626 Bprint(&bout, " SAO%ld", (key>>8)&0xFFFFFF);
1627 break;
1628 case Abell:
1629 Bprint(&bout, " Abell%ld", (key>>8)&0xFFFFFF);
1630 break;
1631 default: /* NGC */
1632 nn = (key>>16)&0xFFFF;
1633 if(nn > NNGC)
1634 Bprint(&bout, " IC%d", nn-NNGC);
1635 else
1636 Bprint(&bout, " NGC%d", nn);
1637 Bprint(&bout, "(%s)", ngcstring(key&0x3F));
1638 break;
1641 Bprint(&bout, "\n");
1642 break;
1644 case NGCN:
1645 if(r->index <= NNGC)
1646 Bprint(&bout, "NGC%ld\n", r->index);
1647 else
1648 Bprint(&bout, "IC%ld\n", r->index-NNGC);
1649 break;
1651 case NamedSAO:
1652 Bprint(&bout, "SAO%ld \"%s\"\n", r->index, togreek(r->u.named.name));
1653 break;
1655 case NamedNGC:
1656 if(r->index <= NNGC)
1657 Bprint(&bout, "NGC%ld \"%s\"\n", r->index, togreek(r->u.named.name));
1658 else
1659 Bprint(&bout, "IC%ld \"%s\"\n", r->index-NNGC, togreek(r->u.named.name));
1660 break;
1662 case NamedAbell:
1663 Bprint(&bout, "Abell%ld \"%s\"\n", r->index, togreek(r->u.named.name));
1664 break;
1666 case PatchC:
1667 radec(r->index, &rah, &ram, &dec);
1668 Bprint(&bout, "%dh%dm %d\n", rah, ram, dec);
1669 break;