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