/* Topocentric Moon Monthly Calendar Keith Burnett (kburnett@btinternet.com) Overview -------- This program calculates the approximate values of various numbers of interest to lunar observers for a month at a time. I have written the program using 'standard' C code and libraries. For each day in the month, the program calculates the time of Moon transit, and then calculates for that time the optical libration of the Moon in selenographical longitude and latitude, the position angle of the Moon's rotation axis, the selenographic colongitude and latitude of the sub-Solar point, i.e. the point on the Moon's surface where the Sun is overhead, the position angle of the bright limb of the Moon (just add and subtract 90 degrees from this to get the position angles of the 'horns' of the crescent), and finally the percentage illumination of the Moon. The program also adds a character after the transit time indicating the state of the Sun, blank for night, 'a', 'n', 'c' for astronomical, nautical or civil twilight, and '*' if the Sun is above the horizon at the time of lunar transit. A further character is printed after the colongitude, an 'r' shows that the Sun is rising on the Moon, and an 's' indicates a waning Moon. Topocentric positions are calculated to your position on the Earth. Geocentric positions are calculated to the centre of the Earth. There is an appreciable difference for objects that are near to us, so that your displacement from the centre of the Earth changes your angle to the object by a significant amount. The quantities here are calculated using topocentric positions for the Moon and geocentric positions for the Sun, so that the resulting values should represent the Moon's face that you will see in your eyepiece fairly well. As the Moon is roughly 60 Earth radii away, the topocentric correction has a large effect on the Moon's position in your sky (sometimes up to one degree). The Sun is roughly 140,000 Earth radii away, so I assume that the topocentric correction to the Sun's position is so small as to have no appreciable effect on the illumination of the Moon's face. Your location moves round the Earth's rotation axis in a 'small circle' once a day, and the radius of the small circle depends on your latitude. The geocentric optical libration changes on a scale of the lunar month, as it arises in part from the elliptical nature of the Moon's orbit around the Earth. The size of the difference between the topocentric and geocentric librations will therefore change during a day as you move round your small circle. There will also be a 'constant' or more slowly changing topocentric correction depending on your latitude and the declination of the Moon. I imagine a smooth slow cycle with a small amplitude fast 'wiggle' superimposed on it from the topocentric librations. I have a strong suspicion that the diurnal component of the topocentric correction to the Moon's position is actually zero when the Moon transits, as at that time the centre of the Moon, you and the centre of the Earth are in a plane that includes your meridian. Most published books and articles about Moon observation will record the geocentric librations of the Moon, and these will be different from the figures produced by this program. Accuracy -------- I checked a modified version of this program calculating quantities for 0h UT each day against a topocentric ephemeris generated using the JPL Horizons online ephemeris generator. Quantities agreed to within 0.03 degrees for optical librations (as might be expected as that is the level at which the physical librations become important) and to within better than 0.005 for the colongitude. I chose only one month to check, and a graph of the colongitude errors looked as if there might be a periodic term of amplitude about 0.02 degrees or so. All output has been rounded to one decimal place for the selenographic coordinates, and whole degrees for the position angles. An earlier version of the program used the D46 formulas (0.3 degrees error in position) for the Moon coordinates - and I found the errors in librations were three times as bad. The current moonpos() function is good to 2 arcmin 'most of the time', and 5 arcmin worst case (odd spikes in the error signal over a complete saros). Algorithm --------- The 'logic' of the main() function looks a bit like this: Get inputs Do some error checking print title and column headings for each day in the month find the time of lunar transit using an iteration loop if no transit, print message and skip to next day find the days since J2000.0 corresponding to this time calculate the ecliptic coordinates of the Sun convert to equatorial coordinates find the sun's altitude code sun's altitude using a character ' anc*' find the ecliptic coordinates of the Moon convert to equatorial coordinates find altitude of Moon if Moon below horizon at transit print message and skip to next day apply topocentric correction to Moon equatorial coords convert back to get topo ecliptic coords find librations and pa of pole axis find approximate heliocentric coordinates of Moon use libration procedure to find coords of sub-solar point find if Sun rising 'r' or setting 's' over Moon find pa of bright limb using topocentric Moon coords find % illumination print line of output for that day next day print key to table end I have used 'pass by reference' a lot using pointers in the functions to get round the limitation that C functions can only return a single argument. Perhaps some structures for collections of coordinates would be more elegant. Command line ------------ The program takes a command line with four purely numerical arguments, - the year and month in the format yyyymm, - your time zone as a decimal number of hours before or after Greenwich, before Greenwich taken as negative - your longitude as a signed integer in the format +/-dddmm with West longitudes taken as negative, - your latitude as a signed integer in the format +/-ddmm with North latitudes taken as positive. For example, the command line >tmoon 200009 0 -00155 5230 > sept.txt will calculate a monthly Moon calendar for September 2000, in time zone 0 (UT) for longitude 1 deg 55 minutes West, and 52 deg 30 minutes North. In this example, the symbol '>' tells the operating system to 're-direct' the program output to a file called 'sept.txt'. Redirection of output depends on the compiler and operating system you are using - most support this feature. Error messages are printed to the console using fprintf(stderr, stuff). References ---------- The formulas for the Moon's geocentric coordinates, the siderial time, days since J2000.0, coordinate transformations and all the formulas for calculating the output quantities were modified and simplified from Jean Meeus' book 'Astronomical Algorithms' (1st Edition) published by Willmann-Bell, ISBN 0-943396-35-2. Chapter 51 was especially useful, but I have neglected the physical libration (or 'free space motion') of the Moon and the nutation, resulting in a simplification of the formulas. The formulas for the Sun's geocentric coordinates, and the common sense topocentric correction for the Moon's position (ignoring the polar flattening of Earth), were taken from the Astronomical Almanac pages C24 and D46. The iterative scheme to find the time of Moon transit each day was adapted from the 'Explanatory Supplement to the Astronomical Almanac', 1994, section 9.31 and 9.32. Mealy Mouthed Disclaimer ------------------------ This program was written as a bit of a giggle, and to improve my own understanding of grunge C and selenography. I wouldn't trust it for anything important if I were you. I take no responsibility for anything that might happen as a consequence of your use of this program or any bit of it for anything anywhere at anytime. I would like to hear from you if you find a mistake, or find a nice new application for bits of the code. */ #include <stdio.h> #include <stdlib.h> #include <math.h> #define RADS 0.0174532925199433 #define DEGS 57.2957795130823 #define TPI 6.28318530717959 /* ratio of earth radius to astronomical unit */ #define ER_OVER_AU 0.0000426352325194252 /* all prototypes here */ double getcoord(int coord); void getargs(int argc, char *argv[], int *y, int *m, double *tz, double *glong, double *glat); double range(double y); double rangerad(double y); double days(int y, int m, int dn, double hour); void moonpos(double, double *, double *, double *); void sunpos(double , double *, double *, double *); double moontransit(int y, int m, int d, double timezone, double glat, double glong, int *nt); double atan22(double y, double x); double epsilon(double d); void equatorial(double d, double *lon, double *lat, double *r); void ecliptic(double d, double *lon, double *lat, double *r); double gst(double d); void topo(double lst, double glat, double *alp, double *dec, double *r); double alt(double glat, double ha, double dec); void libration(double day, double lambda, double beta, double alpha, double *l, double *b, double *p); void illumination(double day, double lra, double ldec, double dr, double sra, double sdec, double *pabl, double *ill); int daysinmonth(int y, int m); int isleap(int y); static const char *usage = " Usage: tmoon date[yyyymm] timz[+/-h.hh] long[+/-dddmm] lat[+/-ddmm]\n" "example: tmoon 200009 0 -00155 5230\n"; static const char *keytext = "\n\nKey to column headings\n" "----------------------\n\n" " DY: Date in the month\n" "TRAN: Zone time of Moon transit, followed by a letter indicating\n" " the sun state, * means the Sun is up, blank is full night\n" " and c, n, a stand for civil, nautical and astronomical twilights\n" " ALT: Altitude of the Moon at transit\n" " L: Optical libration in longitude\n" " B: Libration in latitude\n" " PaX: Position angle of Moon's polar axis\n" " Co: Colongitude of the Sun, r means sunrise, s means lunar sunset\n" " Bs: Selenographic latitude of the subsolar point\n" "PaBl: Position angle of the bright limb\n" " %%: Percentage illumination of Moon's earth-facing disc\n"; int main(int argc, char *argv[]) { double mlambda, mbeta, mrv, slambda, sbeta, srv; double tlambda, tbeta, trv; double tz, dt, glong, glat, time; double lst, sunalt, shr, day, Co; double salpha, sdelta, malpha, mdelta, mhr, moonalt, l, b; double ls, bs, hlambda, hbeta, dratio, paxis, dummy, pabl, ill; int nt = 0, date, hour, min, y, m, longitude, latitude; char sunchar, moonchar, eastwest, northsouth; /* get the date, time zone, and observer's position */ getargs(argc, argv, &y, &m, &tz, &glong, &glat); /* print title and column headings */ printf("Topocentric lunar ephemeris\n"); printf("Year: %04d Month: %02d\n", y, m); longitude = atoi(argv[3]); latitude = atoi(argv[4]); if (longitude < 0) eastwest = 'W'; else eastwest = 'E'; if (latitude < 0) northsouth = 'S'; else northsouth = 'N'; printf("Location: %05d%1c, %04d%1c\n\n", abs(longitude), eastwest, abs(latitude), northsouth); printf("DY TRAN ALT L B PaX Co Bs PaBL %%\n"); printf("-------------------------------------------------------\n"); /* this is the main month loop - generates each line in the table */ for(date = 1; date <= daysinmonth(y, m); date++) { /* find the time of transit on this day */ dt = moontransit(y, m, date, tz, glat, glong, &nt); if (nt == 1) { printf("%02d ---- no transit this day\n", date); /* skip to next day */ continue; } day = days(y, m , date, dt * DEGS / 15) - tz/24; lst = gst(day) + glong; /* find Moon topocentric coordinates for libration calculations and check that the Moon is above the horizon at transit */ moonpos(day, &mlambda, &mbeta, &mrv); malpha = mlambda; mdelta = mbeta; equatorial(day, &malpha, &mdelta, &mrv); topo(lst, glat, &malpha, &mdelta, &mrv); mhr = rangerad(lst - malpha); moonalt = alt(glat, mhr, mdelta); if (moonalt < 0 * RADS) { printf(" %02d **** moon below horizon all day\n", date); /* skip to next day */ continue; } /* find sun altitude character */ sunpos(day, &slambda, &sbeta, &srv); salpha = slambda; sdelta = sbeta; equatorial(day, &salpha, &sdelta, &srv); shr = rangerad(lst - salpha); sunalt = alt(glat, shr, sdelta); if (sunalt > 0 * RADS) sunchar = '*'; if (sunalt < 0 * RADS) sunchar = 'c'; if (sunalt < - 6 * RADS) sunchar = 'n'; if (sunalt < -12 * RADS) sunchar = 'a'; if (sunalt < -18 * RADS) sunchar = ' '; /* Optical libration and Position angle of the Pole */ tlambda = malpha; tbeta = mdelta; trv = mrv; ecliptic(day, &tlambda, &tbeta, &trv); libration(day, tlambda, tbeta, malpha, &l, &b, &paxis); /* Selen Colongitude and latitude of sub solar point */ dratio = mrv / srv * ER_OVER_AU; hlambda = slambda + PI + dratio * cos(mbeta) * sin(slambda - mlambda); hbeta = dratio * mbeta; libration(day, hlambda, hbeta, salpha, &ls, &bs, &dummy); ls = rangerad(ls); if(ls < 90 * RADS) Co = 90 * RADS - ls; else Co = 450 * RADS - ls; if(Co < 90 * RADS || Co > 270 * RADS) moonchar = 'r'; else moonchar = 's'; /* PA of bright limb, and percentage illumination */ illumination(day, malpha, mdelta, dratio, salpha, sdelta, &pabl, &ill); /* convert transit time to hhmm */ time = dt * DEGS / 15; hour = floor(time); min = floor((time - hour) * 60 + 0.5); /* Print the line for this day */ printf("%02d %02d%02d", date, hour, min); printf("%1c ", sunchar); printf("%3.0f ", moonalt * DEGS); printf("% 3.1f % 2.1f ", l * DEGS, b * DEGS); printf("%3.0f ", paxis * DEGS); printf("%5.1f%1c % 2.1f ", Co * DEGS, moonchar, bs * DEGS); printf("%4.0f %3.0f\n", pabl * DEGS, ill * 100); } /* end of day in Month loop */ printf(keytext); exit(EXIT_SUCCESS); } /* end of main() */ /* getargs() gets the arguments from the command line, does some basic error checking, and converts arguments into numerical form. Arguments are passed back in pointers. Error messages print to stderr so re-direction of output to file won't leave users blind. Error checking prints list of all errors in a command line before quitting. */ void getargs(int argc, char *argv[], int *y, int *m, double *tz, double *glong, double *glat) { int date, latitude, longitude; int mflag = 0, yflag = 0, longflag = 0, latflag = 0, tzflag = 0; int longminflag = 0, latminflag = 0, dflag = 0; /* if not right number of arguments, then print example command line */ if (argc !=5) { fprintf(stderr, usage); exit(EXIT_FAILURE); } date = atoi(argv[1]); *y = date / 100; *m = date - *y * 100; *tz = (double) atof(argv[2]); longitude = atoi(argv[3]); latitude = atoi(argv[4]); *glong = RADS * getcoord(longitude); *glat = RADS * getcoord(latitude); /* set a flag for each error found */ if (*m > 12 || *m < 1) mflag = 1; if (*y > 2500) yflag = 1; if (date < 150001) dflag = 1; if (fabs((float) *glong) > 180 * RADS) longflag = 1; if (abs(longitude) % 100 > 59) longminflag = 1; if (fabs((float) *glat) > 90 * RADS) latflag = 1; if (abs(latitude) % 100 > 59) latminflag = 1; if (fabs((float) *tz) > 12) tzflag = 1; /* print all the errors found */ if (dflag == 1) { fprintf(stderr, "date: dates must be in form yyyymm, gregorian, and later than 1500 AD\n"); } if (yflag == 1) { fprintf(stderr, "date: too far in future - accurate from 1500 to 2500\n"); } if (mflag == 1) { fprintf(stderr, "date: month must be in range 0 to 12, eg - August 2000 is entered as 200008\n"); } if (tzflag == 1) { fprintf(stderr, "timz: must be in range +/- 12 hours, eg -6 for Chicago\n"); } if (longflag == 1) { fprintf(stderr, "long: must be in range +/- 180 degrees\n"); } if (longminflag == 1) { fprintf(stderr, "long: last two digits are arcmin - max 59\n"); } if (latflag == 1) { fprintf(stderr, " lat: must be in range +/- 90 degrees\n"); } if (latminflag == 1) { fprintf(stderr, " lat: last two digits are arcmin - max 59\n"); } /* quits if one or more flags set */ if (dflag + mflag + yflag + longflag + latflag + tzflag + longminflag + latminflag > 0) { exit(EXIT_FAILURE); } } /* returns coordinates in decimal degrees given the coord as a ddmm value stored in an integer. */ double getcoord(int coord) { int west = 1; double glg, deg; if (coord < 0) west = -1; glg = fabs((double) coord/100); deg = floor(glg); glg = west* (deg + (glg - deg)*100 / 60); return(glg); } /* days() takes the year, month, day in the month and decimal hours in the day and returns the number of days since J2000.0. Assumes Gregorian calendar. */ double days(int y, int m, int d, double h) { int a, b; double day; /* The lines below work from 1900 march to feb 2100 a = 367 * y - 7 * (y + (m + 9) / 12) / 4 + 275 * m / 9 + d; day = (double)a - 730531.5 + hour / 24; */ /* These lines work for any Gregorian date since 0 AD */ if (m ==1 || m==2) { m +=12; y -= 1; } a = y / 100; b = 2 - a + a/4; day = floor(365.25*(y + 4716)) + floor(30.6001*(m + 1)) + d + b - 1524.5 - 2451545 + h/24; return(day); } /* Returns 1 if y a leap year, and 0 otherwise, according to the Gregorian calendar */ int isleap(int y) { int a = 0; if(y % 4 == 0) a = 1; if(y % 100 == 0) a = 0; if(y % 400 == 0) a = 1; return(a); } /* Given the year and the month, function returns the number of days in the month. Valid for Gregorian calendar. */ int daysinmonth(int y, int m) { int b = 31; if(m == 2) { if(isleap(y) == 1) b= 29; else b = 28; } if(m == 4 || m == 6 || m == 9 || m == 11) b = 30; return(b); } /* moonpos() takes days from J2000.0 and returns ecliptic coordinates of moon in the pointers. Note call by reference. This function is within a couple of arcminutes most of the time, and is truncated from the Meeus Ch45 series, themselves truncations of ELP-2000. Returns moon distance in earth radii. Terms have been written out explicitly rather than using the table based method as only a small number of terms is retained. */ void moonpos(double d, double *lambda, double *beta, double *rvec) { double dl, dB, dR, L, D, M, M1, F, e, lm, bm, rm, t; t = d / 36525; L = range(218.3164591 + 481267.88134236 * t) * RADS; D = range(297.8502042 + 445267.1115168 * t) * RADS; M = range(357.5291092 + 35999.0502909 * t) * RADS; M1 = range(134.9634114 + 477198.8676313 * t - .008997 * t * t) * RADS; F = range(93.27209929999999 + 483202.0175273 * t - .0034029 * t * t) * RADS; e = 1 - .002516 * t; dl = 6288774 * sin(M1); dl += 1274027 * sin(2 * D - M1); dl += 658314 * sin(2 * D); dl += 213618 * sin(2 * M1); dl -= e * 185116 * sin(M); dl -= 114332 * sin(2 * F) ; dl += 58793 * sin(2 * D - 2 * M1); dl += e * 57066 * sin(2 * D - M - M1) ; dl += 53322 * sin(2 * D + M1); dl += e * 45758 * sin(2 * D - M); dl -= e * 40923 * sin(M - M1); dl -= 34720 * sin(D) ; dl -= e * 30383 * sin(M + M1) ; dl += 15327 * sin(2 * D - 2 * F) ; dl -= 12528 * sin(M1 + 2 * F); dl += 10980 * sin(M1 - 2 * F); lm = rangerad(L + dl / 1000000 * RADS); dB = 5128122 * sin(F); dB += 280602 * sin(M1 + F); dB += 277693 * sin(M1 - F); dB += 173237 * sin(2 * D - F); dB += 55413 * sin(2 * D - M1 + F); dB += 46271 * sin(2 * D - M1 - F); dB += 32573 * sin(2 * D + F); dB += 17198 * sin(2 * M1 + F); dB += 9266 * sin(2 * D + M1 - F); dB += 8822 * sin(2 * M1 - F); dB += e * 8216 * sin(2 * D - M - F); dB += 4324 * sin(2 * D - 2 * M1 - F); bm = dB / 1000000 * RADS; dR = -20905355 * cos(M1); dR -= 3699111 * cos(2 * D - M1); dR -= 2955968 * cos(2 * D); dR -= 569925 * cos(2 * M1); dR += e * 48888 * cos(M); dR -= 3149 * cos(2 * F); dR += 246158 * cos(2 * D - 2 * M1); dR -= e * 152138 * cos(2 * D - M - M1) ; dR -= 170733 * cos(2 * D + M1); dR -= e * 204586 * cos(2 * D - M); dR -= e * 129620 * cos(M - M1); dR += 108743 * cos(D); dR += e * 104755 * cos(M + M1); dR += 79661 * cos(M1 - 2 * F); rm = 385000.56 + dR / 1000; *lambda = lm; *beta = bm; /* distance to Moon must be in Earth radii */ *rvec = rm / 6378.14; } /* topomoon() takes the local siderial time, the geographical latitude of the observer, and pointers to the geocentric equatorial coordinates. The function overwrites the geocentric coordinates with topocentric coordinates on a simple spherical earth model (no polar flattening). Expects Moon-Earth distance in Earth radii. Formulas scavenged from Astronomical Almanac 'low precision formulae for Moon position' page D46. */ void topo(double lst, double glat, double *alp, double *dec, double *r) { double x, y, z, r1; x = *r * cos(*dec) * cos(*alp) - cos(glat) * cos(lst); y = *r * cos(*dec) * sin(*alp) - cos(glat) * sin(lst); z = *r * sin(*dec) - sin(glat); r1 = sqrt(x*x + y*y + z*z); *alp = atan22(y, x); *dec = asin(z / r1); *r = r1; } /* moontransit() takes date, the time zone and geographic longitude of observer and returns the time (decimal hours) of lunar transit on that day if there is one, and sets the notransit flag if there isn't. See Explanatory Supplement to Astronomical Almanac section 9.32 and 9.31 for the method. */ double moontransit(int y, int m, int d, double tz, double glat, double glong, int *notransit) { double hm, ht, ht1, lon, lat, rv, dnew, lst; int itcount; ht1 = 180 * RADS; ht = 0; itcount = 0; *notransit = 0; do { ht = ht1; itcount++; dnew = days(y, m, d, ht * DEGS/15) - tz/24; lst = gst(dnew) + glong; /* find the topocentric Moon ra (hence hour angle) and dec */ moonpos(dnew, &lon, &lat, &rv); equatorial(dnew, &lon, &lat, &rv); topo(lst, glat, &lon, &lat, &rv); hm = rangerad(lst - lon); ht1 = rangerad(ht - hm); /* if no convergence, then no transit on that day */ if (itcount > 30) { *notransit = 1; break; } } while (fabs(ht - ht1) > 0.04 * RADS); return(ht1); } /* Calculates the selenographic coordinates of either the sub Earth point (optical libration) or the sub-solar point (selen. coords of centre of bright hemisphere). Based on Meeus chapter 51 but neglects physical libration and nutation, with some simplification of the formulas. */ void libration(double day, double lambda, double beta, double alpha, double *l, double *b, double *p) { double i, f, omega, w, y, x, a, t, eps; t = day / 36525; i = 1.54242 * RADS; eps = epsilon(day); f = range(93.2720993 + 483202.0175273 * t - .0034029 * t * t) * RADS; omega = range(125.044555 - 1934.1361849 * t + .0020762 * t * t) * RADS; w = lambda - omega; y = sin(w) * cos(beta) * cos(i) - sin(beta) * sin(i); x = cos(w) * cos(beta); a = atan22(y, x); *l = a - f; /* kludge to catch cases of 'round the back' angles */ if (*l < -90 * RADS) *l += TPI; if (*l > 90 * RADS) *l -= TPI; *b = asin(-sin(w) * cos(beta) * sin(i) - sin(beta) * cos(i)); /* pa pole axis - not used for Sun stuff */ x = sin(i) * sin(omega); y = sin(i) * cos(omega) * cos(eps) - cos(i) * sin(eps); w = atan22(x, y); *p = rangerad(asin(sqrt(x*x + y*y) * cos(alpha - w) / cos(*b))); } /* Takes: days since J2000.0, eq coords Moon, ratio of moon to sun distance, eq coords Sun Returns: position angle of bright limb wrt NCP, percentage illumination of Sun */ void illumination(double day, double lra, double ldec, double dr, double sra, double sdec, double *pabl, double *ill) { double x, y, phi, i; y = cos(sdec) * sin(sra - lra); x = sin(sdec) * cos(ldec) - cos(sdec) * sin(ldec) * cos (sra - lra); *pabl = atan22(y, x); phi = acos(sin(sdec) * sin(ldec) + cos(sdec) * cos(ldec) * cos(sra-lra)); i = atan22(sin(phi) , (dr - cos(phi))); *ill = 0.5*(1 + cos(i)); } /* sunpos() takes days from J2000.0 and returns ecliptic longitude of Sun in the pointers. Latitude is zero at this level of precision, but pointer left in for consistency in number of arguments. This function is within 0.01 degree (1 arcmin) almost all the time for a century either side of J2000.0. This is from the 'low precision fomulas for the Sun' from C24 of Astronomical Alamanac */ void sunpos(double d, double *lambda, double *beta, double *rvec) { double L, g, ls, bs, rs; L = range(280.461 + .9856474 * d) * RADS; g = range(357.528 + .9856003 * d) * RADS; ls = L + (1.915 * sin(g) + .02 * sin(2 * g)) * RADS; bs = 0; rs = 1.00014 - .01671 * cos(g) - .00014 * cos(2 * g); *lambda = ls; *beta = bs; *rvec = rs; } /* this routine returns the altitude given the days since J2000.0 the hour angle and declination of the object and the latitude of the observer. Used to find the Sun's altitude to put a letter code on the transit time, and to find the Moon's altitude at transit just to make sure that the Moon is visible. */ double alt(double glat, double ha, double dec) { return(asin(sin(dec) * sin(glat) + cos(dec) * cos(glat) * cos(ha))); } /* returns an angle in degrees in the range 0 to 360 */ double range(double x) { double a, b; b = x / 360; a = 360 * (b - floor(b)); if (a < 0) a = 360 + a; return(a); } /* returns an angle in rads in the range 0 to two pi */ double rangerad(double x) { double a, b; b = x / TPI; a = TPI * (b - floor(b)); if (a < 0) a = TPI + a; return(a); } /* gets the atan2 function returning angles in the right order and range */ double atan22(double y, double x) { double a; a = atan2(y, x); if (a < 0) a += TPI; return(a); } /* returns mean obliquity of ecliptic in radians given days since J2000.0. */ double epsilon(double d) { double t = d/ 36525; return((23.4392911111111 - (t* (46.8150 + 0.00059*t)/3600)) *RADS); } /* replaces ecliptic coordinates with equatorial coordinates note: call by reference destroys original values R is unchanged. */ void equatorial(double d, double *lon, double *lat, double *r) { double eps, ceps, seps, l, b; l = *lon; b = * lat; eps = epsilon(d); ceps = cos(eps); seps = sin(eps); *lon = atan22(sin(l)*ceps - tan(b)*seps, cos(l)); *lat = asin(sin(b)*ceps + cos(b)*seps*sin(l)); } /* replaces equatorial coordinates with ecliptic ones. Inverse of above, but used to find topocentric ecliptic coords. */ void ecliptic(double d, double *lon, double *lat, double *r) { double eps, ceps, seps, alp, dec; alp = *lon; dec = *lat; eps = epsilon(d); ceps = cos(eps); seps = sin(eps); *lon = atan22(sin(alp)*ceps + tan(dec)*seps, cos(alp)); *lat = asin(sin(dec)*ceps - cos(dec)*seps*sin(alp)); } /* returns the siderial time at greenwich meridian as an angle in radians given the days since J2000.0 */ double gst( double d) { double t = d / 36525; double theta; theta = range(280.46061837 + 360.98564736629 * d + 0.000387933 * t * t); return(theta * RADS); }