1 | /* misc handy functions.
|
---|
2 | * every system has such, no?
|
---|
3 | * 4/20/98 now_lst() always just returns apparent time
|
---|
4 | */
|
---|
5 |
|
---|
6 | #include <stdio.h>
|
---|
7 | #include <math.h>
|
---|
8 | #include <stdlib.h>
|
---|
9 | #include <string.h>
|
---|
10 |
|
---|
11 | #include "astro.h"
|
---|
12 |
|
---|
13 | /* zero from loc for len bytes */
|
---|
14 | void
|
---|
15 | zero_mem (void *loc, unsigned len)
|
---|
16 | {
|
---|
17 | (void) memset (loc, 0, len);
|
---|
18 | }
|
---|
19 |
|
---|
20 | /* given min and max and an approximate number of divisions desired,
|
---|
21 | * fill in ticks[] with nicely spaced values and return how many.
|
---|
22 | * N.B. return value, and hence number of entries to ticks[], might be as
|
---|
23 | * much as 2 more than numdiv.
|
---|
24 | */
|
---|
25 | int
|
---|
26 | tickmarks (double min, double max, int numdiv, double ticks[])
|
---|
27 | {
|
---|
28 | static int factor[] = { 1, 2, 5 };
|
---|
29 | double minscale;
|
---|
30 | double delta;
|
---|
31 | double lo;
|
---|
32 | double v;
|
---|
33 | int n;
|
---|
34 |
|
---|
35 | minscale = fabs(max - min);
|
---|
36 | delta = minscale/numdiv;
|
---|
37 | for (n=0; n < (int)(sizeof(factor)/sizeof(factor[0])); n++) {
|
---|
38 | double scale;
|
---|
39 | double x = delta/factor[n];
|
---|
40 | if ((scale = (pow(10.0, ceil(log10(x)))*factor[n])) < minscale)
|
---|
41 | minscale = scale;
|
---|
42 | }
|
---|
43 | delta = minscale;
|
---|
44 |
|
---|
45 | lo = floor(min/delta);
|
---|
46 | for (n = 0; (v = delta*(lo+n)) < max+delta; )
|
---|
47 | ticks[n++] = v;
|
---|
48 |
|
---|
49 | return (n);
|
---|
50 | }
|
---|
51 |
|
---|
52 | /* given an Obj *, return its type as a descriptive string.
|
---|
53 | * if it's of type fixed then return its class description.
|
---|
54 | * N.B. we return the address of static storage -- do not free or change.
|
---|
55 | */
|
---|
56 | char *
|
---|
57 | obj_description (Obj *op)
|
---|
58 | {
|
---|
59 | typedef struct {
|
---|
60 | char classcode;
|
---|
61 | char *desc;
|
---|
62 | } CC;
|
---|
63 |
|
---|
64 | #define NFCM ((int)(sizeof(fixed_class_map)/sizeof(fixed_class_map[0])))
|
---|
65 | static CC fixed_class_map[] = {
|
---|
66 | {'A', "Cluster of Galaxies"},
|
---|
67 | {'B', "Binary System"},
|
---|
68 | {'C', "Globular Cluster"},
|
---|
69 | {'D', "Double Star"},
|
---|
70 | {'F', "Diffuse Nebula"},
|
---|
71 | {'G', "Spiral Galaxy"},
|
---|
72 | {'H', "Spherical Galaxy"},
|
---|
73 | {'J', "Radio"},
|
---|
74 | {'K', "Dark Nebula"},
|
---|
75 | {'L', "Pulsar"},
|
---|
76 | {'M', "Multiple Star"},
|
---|
77 | {'N', "Bright Nebula"},
|
---|
78 | {'O', "Open Cluster"},
|
---|
79 | {'P', "Planetary Nebula"},
|
---|
80 | {'Q', "Quasar"},
|
---|
81 | {'R', "Supernova Remnant"},
|
---|
82 | {'S', "Star"},
|
---|
83 | {'T', "Star-like Object"},
|
---|
84 | {'U', "Cluster, with nebulosity"},
|
---|
85 | {'V', "Variable Star"},
|
---|
86 | {'Y', "Supernova"},
|
---|
87 | };
|
---|
88 |
|
---|
89 | #define NBCM ((int)(sizeof(binary_class_map)/sizeof(binary_class_map[0])))
|
---|
90 | static CC binary_class_map[] = {
|
---|
91 | {'a', "Astrometric binary"},
|
---|
92 | {'c', "Cataclysmic variable"},
|
---|
93 | {'e', "Eclipsing binary"},
|
---|
94 | {'x', "High-mass X-ray binary"},
|
---|
95 | {'y', "Low-mass X-ray binary"},
|
---|
96 | {'o', "Occultation binary"},
|
---|
97 | {'s', "Spectroscopic binary"},
|
---|
98 | {'t', "1-line spectral binary"},
|
---|
99 | {'u', "2-line spectral binary"},
|
---|
100 | {'v', "Spectrum binary"},
|
---|
101 | {'b', "Visual binary"},
|
---|
102 | {'d', "Visual binary, apparent"},
|
---|
103 | {'q', "Visual binary, optical"},
|
---|
104 | {'r', "Visual binary, physical"},
|
---|
105 | {'p', "Exoplanet"},
|
---|
106 | };
|
---|
107 |
|
---|
108 | switch (op->o_type) {
|
---|
109 | case FIXED:
|
---|
110 | if (op->f_class) {
|
---|
111 | int i;
|
---|
112 | for (i = 0; i < NFCM; i++)
|
---|
113 | if (fixed_class_map[i].classcode == op->f_class)
|
---|
114 | return (fixed_class_map[i].desc);
|
---|
115 | }
|
---|
116 | return ("Fixed");
|
---|
117 | case PARABOLIC:
|
---|
118 | return ("Solar - Parabolic");
|
---|
119 | case HYPERBOLIC:
|
---|
120 | return ("Solar - Hyperbolic");
|
---|
121 | case ELLIPTICAL:
|
---|
122 | return ("Solar - Elliptical");
|
---|
123 | case BINARYSTAR:
|
---|
124 | if (op->f_class) {
|
---|
125 | int i;
|
---|
126 | for (i = 0; i < NFCM; i++)
|
---|
127 | if (binary_class_map[i].classcode == op->f_class)
|
---|
128 | return (binary_class_map[i].desc);
|
---|
129 | }
|
---|
130 | return ("Binary system");
|
---|
131 | case PLANET: {
|
---|
132 | static char nsstr[16];
|
---|
133 | static Obj *biop;
|
---|
134 |
|
---|
135 | if (op->pl_code == SUN)
|
---|
136 | return ("Star");
|
---|
137 | if (op->pl_code == MOON)
|
---|
138 | return ("Moon of Earth");
|
---|
139 | if (op->pl_moon == X_PLANET)
|
---|
140 | return ("Planet");
|
---|
141 | if (!biop)
|
---|
142 | getBuiltInObjs (&biop);
|
---|
143 | sprintf (nsstr, "Moon of %s", biop[op->pl_code].o_name);
|
---|
144 | return (nsstr);
|
---|
145 | }
|
---|
146 | case EARTHSAT:
|
---|
147 | return ("Earth Sat");
|
---|
148 | default:
|
---|
149 | printf ("obj_description: unknown type: 0x%x\n", op->o_type);
|
---|
150 | abort();
|
---|
151 | return (NULL); /* for lint */
|
---|
152 | }
|
---|
153 | }
|
---|
154 |
|
---|
155 | /* given a Now *, find the local apparent sidereal time, in hours.
|
---|
156 | */
|
---|
157 | void
|
---|
158 | now_lst (Now *np, double *lstp)
|
---|
159 | {
|
---|
160 | static double last_mjd = -23243, last_lng = 121212, last_lst;
|
---|
161 | double eps, lst, deps, dpsi;
|
---|
162 |
|
---|
163 | if (last_mjd == mjd && last_lng == lng) {
|
---|
164 | *lstp = last_lst;
|
---|
165 | return;
|
---|
166 | }
|
---|
167 |
|
---|
168 | utc_gst (mjd_day(mjd), mjd_hr(mjd), &lst);
|
---|
169 | lst += radhr(lng);
|
---|
170 |
|
---|
171 | obliquity(mjd, &eps);
|
---|
172 | nutation(mjd, &deps, &dpsi);
|
---|
173 | lst += radhr(dpsi*cos(eps+deps));
|
---|
174 |
|
---|
175 | range (&lst, 24.0);
|
---|
176 |
|
---|
177 | last_mjd = mjd;
|
---|
178 | last_lng = lng;
|
---|
179 | *lstp = last_lst = lst;
|
---|
180 | }
|
---|
181 |
|
---|
182 | /* convert ra to ha, in range -PI .. PI.
|
---|
183 | * need dec too if not already apparent.
|
---|
184 | */
|
---|
185 | void
|
---|
186 | radec2ha (Now *np, double ra, double dec, double *hap)
|
---|
187 | {
|
---|
188 | double ha, lst;
|
---|
189 |
|
---|
190 | if (epoch != EOD)
|
---|
191 | as_ap (np, epoch, &ra, &dec);
|
---|
192 | now_lst (np, &lst);
|
---|
193 | ha = hrrad(lst) - ra;
|
---|
194 | if (ha < -PI) ha += 2*PI;
|
---|
195 | if (ha > PI) ha -= 2*PI;
|
---|
196 | *hap = ha;
|
---|
197 | }
|
---|
198 |
|
---|
199 | /* given a circle and a line segment, find a segment of the line inside the
|
---|
200 | * circle.
|
---|
201 | * return 0 and the segment end points if one exists, else -1.
|
---|
202 | * We use a parametric representation of the line:
|
---|
203 | * x = x1 + (x2-x1)*t and y = y1 + (y2-y1)*t, 0 < t < 1
|
---|
204 | * and a centered representation of the circle:
|
---|
205 | * (x - xc)**2 + (y - yc)**2 = r**2
|
---|
206 | * and solve for the t's that work, checking for usual conditions.
|
---|
207 | */
|
---|
208 | int
|
---|
209 | lc (
|
---|
210 | int cx, int cy, int cw, /* circle bbox corner and width */
|
---|
211 | int x1, int y1, int x2, int y2, /* line segment endpoints */
|
---|
212 | int *sx1, int *sy1, int *sx2, int *sy2) /* segment inside the circle */
|
---|
213 | {
|
---|
214 | int dx = x2 - x1;
|
---|
215 | int dy = y2 - y1;
|
---|
216 | int r = cw/2;
|
---|
217 | int xc = cx + r;
|
---|
218 | int yc = cy + r;
|
---|
219 | int A = x1 - xc;
|
---|
220 | int B = y1 - yc;
|
---|
221 | double a = dx*dx + dy*dy; /* O(2 * 2**16 * 2**16) */
|
---|
222 | double b = 2*(dx*A + dy*B); /* O(4 * 2**16 * 2**16) */
|
---|
223 | double c = A*A + B*B - r*r; /* O(2 * 2**16 * 2**16) */
|
---|
224 | double d = b*b - 4*a*c; /* O(2**32 * 2**32) */
|
---|
225 | double sqrtd;
|
---|
226 | double t1, t2;
|
---|
227 |
|
---|
228 | if (d <= 0)
|
---|
229 | return (-1); /* containing line is purely outside circle */
|
---|
230 |
|
---|
231 | sqrtd = sqrt(d);
|
---|
232 | t1 = (-b - sqrtd)/(2.0*a);
|
---|
233 | t2 = (-b + sqrtd)/(2.0*a);
|
---|
234 |
|
---|
235 | if (t1 >= 1.0 || t2 <= 0.0)
|
---|
236 | return (-1); /* segment is purely outside circle */
|
---|
237 |
|
---|
238 | /* we know now that some part of the segment is inside,
|
---|
239 | * ie, t1 < 1 && t2 > 0
|
---|
240 | */
|
---|
241 |
|
---|
242 | if (t1 <= 0.0) {
|
---|
243 | /* (x1,y1) is inside circle */
|
---|
244 | *sx1 = x1;
|
---|
245 | *sy1 = y1;
|
---|
246 | } else {
|
---|
247 | *sx1 = (int)(x1 + dx*t1);
|
---|
248 | *sy1 = (int)(y1 + dy*t1);
|
---|
249 | }
|
---|
250 |
|
---|
251 | if (t2 >= 1.0) {
|
---|
252 | /* (x2,y2) is inside circle */
|
---|
253 | *sx2 = x2;
|
---|
254 | *sy2 = y2;
|
---|
255 | } else {
|
---|
256 | *sx2 = (int)(x1 + dx*t2);
|
---|
257 | *sy2 = (int)(y1 + dy*t2);
|
---|
258 | }
|
---|
259 |
|
---|
260 | return (0);
|
---|
261 | }
|
---|
262 |
|
---|
263 | /* compute visual magnitude using the H/G parameters used in the Astro Almanac.
|
---|
264 | * these are commonly used for asteroids.
|
---|
265 | */
|
---|
266 | void
|
---|
267 | hg_mag (
|
---|
268 | double h, double g,
|
---|
269 | double rp, /* sun-obj dist, AU */
|
---|
270 | double rho, /* earth-obj dist, AU */
|
---|
271 | double rsn, /* sun-earth dist, AU */
|
---|
272 | double *mp)
|
---|
273 | {
|
---|
274 | double psi_t, Psi_1, Psi_2, beta;
|
---|
275 | double c;
|
---|
276 | double tb2;
|
---|
277 |
|
---|
278 | c = (rp*rp + rho*rho - rsn*rsn)/(2*rp*rho);
|
---|
279 | if (c <= -1)
|
---|
280 | beta = PI;
|
---|
281 | else if (c >= 1)
|
---|
282 | beta = 0;
|
---|
283 | else
|
---|
284 | beta = acos(c);;
|
---|
285 | tb2 = tan(beta/2.0);
|
---|
286 | /* psi_t = exp(log(tan(beta/2.0))*0.63); */
|
---|
287 | psi_t = pow (tb2, 0.63);
|
---|
288 | Psi_1 = exp(-3.33*psi_t);
|
---|
289 | /* psi_t = exp(log(tan(beta/2.0))*1.22); */
|
---|
290 | psi_t = pow (tb2, 1.22);
|
---|
291 | Psi_2 = exp(-1.87*psi_t);
|
---|
292 | *mp = h + 5.0*log10(rp*rho);
|
---|
293 | if (Psi_1 || Psi_2) *mp -= 2.5*log10((1-g)*Psi_1 + g*Psi_2);
|
---|
294 | }
|
---|
295 |
|
---|
296 | /* given faintest desired mag, mag step magstp, image scale and object
|
---|
297 | * magnitude and size, return diameter to draw object, in pixels, or 0 if
|
---|
298 | * dimmer than fmag.
|
---|
299 | */
|
---|
300 | int
|
---|
301 | magdiam (
|
---|
302 | int fmag, /* faintest mag */
|
---|
303 | int magstp, /* mag range per dot size */
|
---|
304 | double scale, /* rads per pixel */
|
---|
305 | double mag, /* magnitude */
|
---|
306 | double size) /* rads, or 0 */
|
---|
307 | {
|
---|
308 | int diam, sized;
|
---|
309 |
|
---|
310 | if (mag > fmag)
|
---|
311 | return (0);
|
---|
312 | diam = (int)((fmag - mag)/magstp + 1);
|
---|
313 | sized = (int)(size/scale + 0.5);
|
---|
314 | if (sized > diam)
|
---|
315 | diam = sized;
|
---|
316 |
|
---|
317 | return (diam);
|
---|
318 | }
|
---|
319 |
|
---|
320 | /* computer visual magnitude using the g/k parameters commonly used for comets.
|
---|
321 | */
|
---|
322 | void
|
---|
323 | gk_mag (
|
---|
324 | double g, double k,
|
---|
325 | double rp, /* sun-obj dist, AU */
|
---|
326 | double rho, /* earth-obj dist, AU */
|
---|
327 | double *mp)
|
---|
328 | {
|
---|
329 | *mp = g + 5.0*log10(rho) + 2.5*k*log10(rp);
|
---|
330 | }
|
---|
331 |
|
---|
332 | /* given a string convert to floating point and return it as a double.
|
---|
333 | * this is to isolate possible unportabilities associated with declaring atof().
|
---|
334 | * it's worth it because atof() is often some 50% faster than sscanf ("%lf");
|
---|
335 | */
|
---|
336 | double
|
---|
337 | atod (char *buf)
|
---|
338 | {
|
---|
339 | return (strtod (buf, NULL));
|
---|
340 | }
|
---|
341 |
|
---|
342 | /* solve a spherical triangle:
|
---|
343 | * A
|
---|
344 | * / \
|
---|
345 | * / \
|
---|
346 | * c / \ b
|
---|
347 | * / \
|
---|
348 | * / \
|
---|
349 | * B ____________ C
|
---|
350 | * a
|
---|
351 | *
|
---|
352 | * given A, b, c find B and a in range 0..B..2PI and 0..a..PI, respectively..
|
---|
353 | * cap and Bp may be NULL if not interested in either one.
|
---|
354 | * N.B. we pass in cos(c) and sin(c) because in many problems one of the sides
|
---|
355 | * remains constant for many values of A and b.
|
---|
356 | */
|
---|
357 | void
|
---|
358 | solve_sphere (double A, double b, double cc, double sc, double *cap, double *Bp)
|
---|
359 | {
|
---|
360 | double cb = cos(b), sb = sin(b);
|
---|
361 | double sA, cA = cos(A);
|
---|
362 | double x, y;
|
---|
363 | double ca;
|
---|
364 | double B;
|
---|
365 |
|
---|
366 | ca = cb*cc + sb*sc*cA;
|
---|
367 | if (ca > 1.0) ca = 1.0;
|
---|
368 | if (ca < -1.0) ca = -1.0;
|
---|
369 | if (cap)
|
---|
370 | *cap = ca;
|
---|
371 |
|
---|
372 | if (!Bp)
|
---|
373 | return;
|
---|
374 |
|
---|
375 | sA = sin(A);
|
---|
376 | y = sA*sb*sc;
|
---|
377 | x = cb - ca*cc;
|
---|
378 | B = y ? (x ? atan2(y,x) : (y>0 ? PI/2 : -PI/2)) : (x>=0 ? 0 : PI);
|
---|
379 |
|
---|
380 | *Bp = B;
|
---|
381 | range (Bp, 2*PI);
|
---|
382 | }
|
---|
383 |
|
---|
384 | /* #define WANT_MATHERR if your system supports it. it gives SGI fits.
|
---|
385 | */
|
---|
386 | #undef WANT_MATHERR
|
---|
387 | #if defined(WANT_MATHERR)
|
---|
388 | /* attempt to do *something* reasonable when a math function blows.
|
---|
389 | */
|
---|
390 | matherr (xp)
|
---|
391 | struct exception *xp;
|
---|
392 | {
|
---|
393 | static char *names[8] = {
|
---|
394 | "acos", "asin", "atan2", "pow",
|
---|
395 | "exp", "log", "log10", "sqrt"
|
---|
396 | };
|
---|
397 | int i;
|
---|
398 |
|
---|
399 | /* catch-all */
|
---|
400 | xp->retval = 0.0;
|
---|
401 |
|
---|
402 | for (i = 0; i < sizeof(names)/sizeof(names[0]); i++)
|
---|
403 | if (strcmp (xp->name, names[i]) == 0)
|
---|
404 | switch (i) {
|
---|
405 | case 0: /* acos */
|
---|
406 | xp->retval = xp->arg1 >= 1.0 ? 0.0 : -PI;
|
---|
407 | break;
|
---|
408 | case 1: /* asin */
|
---|
409 | xp->retval = xp->arg1 >= 1.0 ? PI/2 : -PI/2;
|
---|
410 | break;
|
---|
411 | case 2: /* atan2 */
|
---|
412 | if (xp->arg1 == 0.0)
|
---|
413 | xp->retval = xp->arg2 < 0.0 ? PI : 0.0;
|
---|
414 | else if (xp->arg2 == 0.0)
|
---|
415 | xp->retval = xp->arg1 < 0.0 ? -PI/2 : PI/2;
|
---|
416 | else
|
---|
417 | xp->retval = 0.0;
|
---|
418 | break;
|
---|
419 | case 3: /* pow */
|
---|
420 | /* FALLTHRU */
|
---|
421 | case 4: /* exp */
|
---|
422 | xp->retval = xp->o_type == OVERFLOW ? 1e308 : 0.0;
|
---|
423 | break;
|
---|
424 | case 5: /* log */
|
---|
425 | /* FALLTHRU */
|
---|
426 | case 6: /* log10 */
|
---|
427 | xp->retval = xp->arg1 <= 0.0 ? -1e308 : 0;
|
---|
428 | break;
|
---|
429 | case 7: /* sqrt */
|
---|
430 | xp->retval = 0.0;
|
---|
431 | break;
|
---|
432 | }
|
---|
433 |
|
---|
434 | return (1); /* suppress default error handling */
|
---|
435 | }
|
---|
436 | #endif
|
---|
437 |
|
---|
438 | /* given the difference in two RA's, in rads, return their difference,
|
---|
439 | * accounting for wrap at 2*PI. caller need *not* first force it into the
|
---|
440 | * range 0..2*PI.
|
---|
441 | */
|
---|
442 | double
|
---|
443 | delra (double dra)
|
---|
444 | {
|
---|
445 | double fdra = fmod(fabs(dra), 2*PI);
|
---|
446 |
|
---|
447 | if (fdra > PI)
|
---|
448 | fdra = 2*PI - fdra;
|
---|
449 | return (fdra);
|
---|
450 | }
|
---|
451 |
|
---|
452 | /* return 1 if object is considered to be "deep sky", else 0.
|
---|
453 | * The only things deep-sky are fixed objects other than stars.
|
---|
454 | */
|
---|
455 | int
|
---|
456 | is_deepsky (Obj *op)
|
---|
457 | {
|
---|
458 | int deepsky = 0;
|
---|
459 |
|
---|
460 | if (is_type(op, FIXEDM)) {
|
---|
461 | switch (op->f_class) {
|
---|
462 | case 'T':
|
---|
463 | case 'B':
|
---|
464 | case 'D':
|
---|
465 | case 'M':
|
---|
466 | case 'S':
|
---|
467 | case 'V':
|
---|
468 | break;
|
---|
469 | default:
|
---|
470 | deepsky = 1;
|
---|
471 | break;
|
---|
472 | }
|
---|
473 | }
|
---|
474 |
|
---|
475 | return (deepsky);
|
---|
476 | }
|
---|
477 |
|
---|
478 | /* For RCS Only -- Do Not Edit */
|
---|
479 | static char *rcsid[2] = {(char *)rcsid, "@(#) $RCSfile: misc.c,v $ $Date: 2005-01-17 10:13:05 $ $Revision: 1.4 $ $Name: not supported by cvs2svn $"};
|
---|