/*! \fn double ln_solve_kepler (double E, double M); * \param E Orbital eccentricity * \param M Mean anomaly * \return Eccentric anomaly * * Calculate the eccentric anomaly. * This method was devised by Roger Sinnott. (Sky and Telescope, Vol 70, pg 159) */ double ln_solve_kepler (double e, double M) { double Eo = M_PI_2; double F, M1; double D = M_PI_4; int i; /* covert to radians */ M = ln_deg_to_rad (M); F = sgn (M); M = fabs (M) / (2.0 * M_PI); M = (M - (int)M) * 2.0 * M_PI * F; if (M < 0) M = M + 2.0 * M_PI; F = 1.0; if (M > M_PI) F = -1.0; if (M > M_PI) M = 2.0 * M_PI - M; for (i = 0; i < KEPLER_STEPS; i++) { M1 = Eo - e * sin (Eo); Eo = Eo + D * sgn (M - M1); D /= 2.0; } Eo *= F; /* back to degrees */ Eo = ln_rad_to_deg (Eo); return Eo; }
/*! \fn double ln_get_refraction_adj (double altitude, double atm_pres, double temp) * \param altitude The altitude of the object above the horizon in degrees * \param atm_pres Atmospheric pressure in milibars * \param temp Temperature in degrees C. * \return Adjustment in objects altitude in degrees. * * Calculate the adjustment in altitude of a body due to atmosphric * refraction. This value varies over altitude, pressure and temperature. * * Note: Default values for pressure and teperature are 1010 mBar and 10C * respectively. */ double ln_get_refraction_adj (double altitude, double atm_pres, double temp) { long double R; /* equ 16.3 */ R = 1.0 / tan (ln_deg_to_rad (altitude + (7.31 / (altitude + 4.4)))); R -= 0.06 * sin (ln_deg_to_rad (14.7 * (R / 60.0) + 13.0)); /* take into account of atm press and temp */ R *= ((atm_pres / 1010) * (283 / (273 + temp))); /* convert from arcminutes to degrees */ R /= 60.0; return R; }
/* Equ 20.2, 20.4 pg 126 */ void ln_get_equ_prec2 (struct ln_equ_posn * mean_position, double fromJD, double toJD, struct ln_equ_posn * position) { long double t, t2, t3, A, B, C, zeta, eta, theta, ra, dec, mean_ra, mean_dec, T, T2; /* change original ra and dec to radians */ mean_ra = ln_deg_to_rad (mean_position->ra); mean_dec = ln_deg_to_rad (mean_position->dec); /* calc t, T, zeta, eta and theta Equ 20.2 */ T = ((long double) (fromJD - JD2000)) / 36525.0; T *= 1.0 / 3600.0; t = ((long double) (toJD - fromJD)) / 36525.0; t *= 1.0 / 3600.0; T2 = T * T; t2 = t * t; t3 = t2 *t; zeta = (2306.2181 + 1.39656 * T - 0.000139 * T2) * t + (0.30188 - 0.000344 * T) * t2 + 0.017998 * t3; eta = (2306.2181 + 1.39656 * T - 0.000139 * T2) * t + (1.09468 + 0.000066 * T) * t2 + 0.018203 * t3; theta = (2004.3109 - 0.85330 * T - 0.000217 * T2) * t - (0.42665 + 0.000217 * T) * t2 - 0.041833 * t3; zeta = ln_deg_to_rad (zeta); eta = ln_deg_to_rad (eta); theta = ln_deg_to_rad (theta); /* calc A,B,C equ 20.4 */ A = cosl (mean_dec) * sinl (mean_ra + zeta); B = cosl (theta) * cosl (mean_dec) * cosl (mean_ra + zeta) - sinl (theta) * sinl (mean_dec); C = sinl (theta) * cosl (mean_dec) * cosl (mean_ra + zeta) + cosl (theta) * sinl (mean_dec); ra = atan2l (A,B) + eta; /* check for object near celestial pole */ if (mean_dec > (0.4 * M_PI) || mean_dec < (-0.4 * M_PI)) { /* close to pole */ dec = acosl (sqrt(A * A + B * B)); if (mean_dec < 0.) dec *= -1; /* 0 <= acos() <= PI */ } else { /* not close to pole */ dec = asinl (C); } /* change to degrees */ position->ra = ln_range_degrees (ln_rad_to_deg (ra)); position->dec = ln_rad_to_deg (dec); }
/* equ 30.1 */ double ln_get_ell_true_anomaly (double e, double E) { double v; E = ln_deg_to_rad (E); v = sqrt ((1.0 + e) / (1.0 - e)) * tan (E / 2.0); v = 2.0 * atan (v); v = ln_range_degrees (ln_rad_to_deg (v)); return v; }
/* Equ 20.3, 20.4 pg 126 */ void ln_get_equ_prec (struct ln_equ_posn * mean_position, double JD, struct ln_equ_posn * position) { long double t, t2, t3, A, B, C, zeta, eta, theta, ra, dec, mean_ra, mean_dec; /* change original ra and dec to radians */ mean_ra = ln_deg_to_rad (mean_position->ra); mean_dec = ln_deg_to_rad (mean_position->dec); /* calc t, zeta, eta and theta for J2000.0 Equ 20.3 */ t = (JD - JD2000) / 36525.0; t *= 1.0 / 3600.0; t2 = t * t; t3 = t2 *t; zeta = 2306.2181 * t + 0.30188 * t2 + 0.017998 * t3; eta = 2306.2181 * t + 1.09468 * t2 + 0.041833 * t3; theta = 2004.3109 * t - 0.42665 * t2 - 0.041833 * t3; zeta = ln_deg_to_rad (zeta); eta = ln_deg_to_rad (eta); theta = ln_deg_to_rad (theta); /* calc A,B,C equ 20.4 */ A = cosl (mean_dec) * sinl (mean_ra + zeta); B = cosl (theta) * cosl (mean_dec) * cosl (mean_ra + zeta) - sinl (theta) * sinl (mean_dec); C = sinl (theta) * cosl (mean_dec) * cosl (mean_ra + zeta) + cosl (theta) * sinl (mean_dec); ra = atan2l (A,B) + eta; /* check for object near celestial pole */ if (mean_dec > (0.4 * M_PI) || mean_dec < (-0.4 * M_PI)) { /* close to pole */ dec = acosl (sqrt(A * A + B * B)); if (mean_dec < 0.) dec *= -1; /* 0 <= acos() <= PI */ } else { /* not close to pole */ dec = asinl (C); } /* change to degrees */ position->ra = ln_range_degrees (ln_rad_to_deg (ra)); position->dec = ln_rad_to_deg (dec); }
/*! \fn double ln_get_heliocentric_time_diff (double JD, struct ln_equ_posn * object) * \param JD Julian day * \param object Pointer to object (RA, DEC) for which heliocentric correction will be caculated * * \return Heliocentric correction in fraction of day * * Calculate heliocentric corection for object at given coordinates and on given * date. */ double ln_get_heliocentric_time_diff (double JD, struct ln_equ_posn *object) { double theta, ra, dec, c_dec, obliq; struct ln_nutation nutation; struct ln_helio_posn earth; ln_get_nutation (JD, &nutation); ln_get_earth_helio_coords (JD, &earth); theta = ln_deg_to_rad (ln_range_degrees (earth.L + 180)); ra = ln_deg_to_rad (object->ra); dec = ln_deg_to_rad (object->dec); c_dec = cos (dec); obliq = ln_deg_to_rad (nutation.ecliptic); /* L.Binnendijk Properties of Double Stars, Philadelphia, University of Pennselvania Press, pp. 228-232, 1960 */ return -0.0057755 * earth.R * ( cos (theta) * cos (ra) * c_dec + sin (theta) * (sin (obliq) * sin (dec) + cos (obliq) * c_dec * sin (ra))); }
/*! \fn void ln_get_ell_helio_rect_posn (struct ln_ell_orbit* orbit, double JD, struct ln_rect_posn* posn); * \param orbit Orbital parameters of object. * \param JD Julian day * \param posn Position pointer to store objects position * * Calculate the objects rectangular heliocentric position given it's orbital * elements for the given julian day. */ void ln_get_ell_helio_rect_posn (struct ln_ell_orbit* orbit, double JD, struct ln_rect_posn* posn) { double A,B,C; double F,G,H; double P,Q,R; double sin_e, cos_e; double a,b,c; double sin_omega, sin_i, cos_omega, cos_i; double M,v,E,r; /* J2000 obliquity of the ecliptic */ sin_e = 0.397777156; cos_e = 0.917482062; /* equ 33.7 */ sin_omega = sin (ln_deg_to_rad (orbit->omega)); cos_omega = cos (ln_deg_to_rad (orbit->omega)); sin_i = sin (ln_deg_to_rad (orbit->i)); cos_i = cos (ln_deg_to_rad (orbit->i)); F = cos_omega; G = sin_omega * cos_e; H = sin_omega * sin_e; P = -sin_omega * cos_i; Q = cos_omega * cos_i * cos_e - sin_i * sin_e; R = cos_omega * cos_i * sin_e + sin_i * cos_e; /* equ 33.8 */ A = atan2 (F,P); B = atan2 (G,Q); C = atan2 (H,R); a = sqrt (F * F + P * P); b = sqrt (G * G + Q * Q); c = sqrt (H * H + R * R); /* get daily motion */ if (orbit->n == 0) orbit->n = ln_get_ell_mean_motion (orbit->a); /* get mean anomaly */ M = ln_get_ell_mean_anomaly (orbit->n, JD - orbit->JD); /* get eccentric anomaly */ E = ln_solve_kepler (orbit->e, M); /* get true anomaly */ v = ln_get_ell_true_anomaly (orbit->e, E); /* get radius vector */ r = ln_get_ell_radius_vector (orbit->a, orbit->e, E); /* equ 33.9 */ posn->X = r * a * sin (A + ln_deg_to_rad(orbit->w + v)); posn->Y = r * b * sin (B + ln_deg_to_rad(orbit->w + v)); posn->Z = r * c * sin (C + ln_deg_to_rad(orbit->w + v)); }
/*! \fn void ln_get_par_helio_rect_posn (struct ln_par_orbit* orbit, double JD, struct ln_rect_posn* posn); * \param orbit Orbital parameters of object. * \param JD Julian day * \param posn Position pointer to store objects position * * Calculate the objects rectangular heliocentric position given it's orbital * elements for the given julian day. */ void ln_get_par_helio_rect_posn (struct ln_par_orbit* orbit, double JD, struct ln_rect_posn* posn) { double A,B,C; double F,G,H; double P,Q,R; double sin_e, cos_e; double a,b,c; double sin_omega, sin_i, cos_omega, cos_i; double r,v,t; /* time since perihelion */ t = JD - orbit->JD; /* J2000 obliquity of the ecliptic */ sin_e = 0.397777156; cos_e = 0.917482062; /* equ 33.7 */ sin_omega = sin (ln_deg_to_rad (orbit->omega)); cos_omega = cos (ln_deg_to_rad (orbit->omega)); sin_i = sin (ln_deg_to_rad (orbit->i)); cos_i = cos (ln_deg_to_rad (orbit->i)); F = cos_omega; G = sin_omega * cos_e; H = sin_omega * sin_e; P = -sin_omega * cos_i; Q = cos_omega * cos_i * cos_e - sin_i * sin_e; R = cos_omega * cos_i * sin_e + sin_i * cos_e; /* equ 33.8 */ A = atan2 (F,P); B = atan2 (G,Q); C = atan2 (H,R); a = sqrt (F * F + P * P); b = sqrt (G * G + Q * Q); c = sqrt (H * H + R * R); /* get true anomaly */ v = ln_get_par_true_anomaly (orbit->q, t); /* get radius vector */ r = ln_get_par_radius_vector (orbit->q, t); /* equ 33.9 */ posn->X = r * a * sin (A + ln_deg_to_rad(orbit->w + v)); posn->Y = r * b * sin (B + ln_deg_to_rad(orbit->w + v)); posn->Z = r * c * sin (C + ln_deg_to_rad(orbit->w + v)); }
double ln_get_apparent_sidereal_time (double JD) { double correction, sidereal; struct ln_nutation nutation; /* get the mean sidereal time */ sidereal = ln_get_mean_sidereal_time (JD); /* add corrections for nutation in longitude and for the true obliquity of the ecliptic */ ln_get_nutation (JD, &nutation); correction = (nutation.longitude / 15.0 * cos (ln_deg_to_rad(nutation.obliquity))); sidereal += correction; return sidereal; }
/*! \fn double ln_get_ell_body_phase_angle (double JD, struct ln_ell_orbit * orbit); * \param JD Julian day * \param orbit Orbital parameters * \return Phase angle. * * Calculate the phase angle of the body. The angle Sun - body - Earth. */ double ln_get_ell_body_phase_angle (double JD, struct ln_ell_orbit * orbit) { double r,R,d; double E,M; double phase; /* get mean anomaly */ if (orbit->n == 0) orbit->n = ln_get_ell_mean_motion (orbit->a); M = ln_get_ell_mean_anomaly (orbit->n, JD - orbit->JD); /* get eccentric anomaly */ E = ln_solve_kepler (orbit->e, M); /* get radius vector */ r = ln_get_ell_radius_vector (orbit->a, orbit->e, E); /* get solar and Earth distances */ R = ln_get_ell_body_earth_dist (JD, orbit); d = ln_get_ell_body_solar_dist (JD, orbit); phase = (r * r + d * d - R * R) / ( 2.0 * r * d ); return ln_range_degrees(acos (ln_deg_to_rad (phase))); }
int ln_get_object_rst_horizon_offset (double JD, struct ln_lnlat_posn * observer, struct ln_equ_posn * object, long double horizon, struct ln_rst_time * rst, double ut_offset) { int jd; long double O, JD_UT, H0, H1; double Hat, Har, Has, altr, alts; double mt, mr, ms, mst, msr, mss; double dmt, dmr, dms; int ret, i; if (isnan (ut_offset)) { JD_UT = JD; } else { /* convert local sidereal time into degrees for 0h of UT on day JD */ jd = (int)JD; JD_UT = jd + ut_offset; } O = ln_get_apparent_sidereal_time (JD_UT); O *= 15.0; /* equ 15.1 */ H0 = (sin (ln_deg_to_rad (horizon)) - sin (ln_deg_to_rad (observer->lat)) * sin (ln_deg_to_rad (object->dec))); H1 = (cos (ln_deg_to_rad (observer->lat)) * cos (ln_deg_to_rad (object->dec))); H1 = H0 / H1; ret = check_coords (observer, H1, horizon, object); if (ret) return ret; H0 = acos (H1); H0 = ln_rad_to_deg (H0); /* equ 15.2 */ mt = (object->ra - observer->lng - O) / 360.0; mr = mt - H0 / 360.0; ms = mt + H0 / 360.0; for (i = 0; i < 3; i++) { /* put in correct range */ if (mt > 1.0) mt--; else if (mt < 0) mt++; if (mr > 1.0) mr--; else if (mr < 0) mr++; if (ms > 1.0) ms--; else if (ms < 0) ms++; /* find sidereal time at Greenwich, in degrees, for each m */ mst = O + 360.985647 * mt; msr = O + 360.985647 * mr; mss = O + 360.985647 * ms; /* find local hour angle */ Hat = mst + observer->lng - object->ra; Har = msr + observer->lng - object->ra; Has = mss + observer->lng - object->ra; /* find altitude for rise and set */ altr = sin (ln_deg_to_rad (observer->lat)) * sin (ln_deg_to_rad (object->dec)) + cos (ln_deg_to_rad (observer->lat)) * cos (ln_deg_to_rad (object->dec)) * cos (ln_deg_to_rad (Har)); alts = sin (ln_deg_to_rad (observer->lat)) * sin (ln_deg_to_rad (object->dec)) + cos (ln_deg_to_rad (observer->lat)) * cos (ln_deg_to_rad (object->dec)) * cos (ln_deg_to_rad (Has)); /* must be in degrees */ altr = ln_rad_to_deg (altr); alts = ln_rad_to_deg (alts); /* corrections for m */ ln_range_degrees (Hat); if (Hat > 180.0) Hat -= 360; dmt = -(Hat / 360.0); dmr = (altr - horizon) / (360 * cos (ln_deg_to_rad (object->dec)) * cos (ln_deg_to_rad (observer->lat)) * sin (ln_deg_to_rad (Har))); dms = (alts - horizon) / (360 * cos (ln_deg_to_rad (object->dec)) * cos (ln_deg_to_rad (observer->lat)) * sin (ln_deg_to_rad (Has))); /* add corrections and change to JD */ mt += dmt; mr += dmr; ms += dms; if (mt <= 1 && mt >= 0 && mr <= 1 && mr >= 0 && ms <= 1 && ms >= 0) break; } rst->rise = JD_UT + mr; rst->transit = JD_UT + mt; rst->set = JD_UT + ms; /* not circumpolar */ return 0; }
int ln_get_motion_body_rst_horizon_offset (double JD, struct ln_lnlat_posn * observer, get_motion_body_coords_t get_motion_body_coords, void * orbit, double horizon, struct ln_rst_time * rst, double ut_offset) { int jd; double T, O, JD_UT, H0, H1; double Hat, Har, Has, altr, alts; double mt, mr, ms, mst, msr, mss, nt, nr, ns; struct ln_equ_posn sol1, sol2, sol3, post, posr, poss; double dmt, dmr, dms; int ret, i; /* dynamical time diff */ T = ln_get_dynamical_time_diff (JD); if (isnan (ut_offset)) { JD_UT = JD; } else { jd = (int)JD; JD_UT = jd + ut_offset; } O = ln_get_apparent_sidereal_time (JD_UT); O *= 15.0; /* get body coords for JD_UT -1, JD_UT and JD_UT + 1 */ get_motion_body_coords (JD_UT - 1.0, orbit, &sol1); get_motion_body_coords (JD_UT, orbit, &sol2); get_motion_body_coords (JD_UT + 1.0, orbit, &sol3); /* equ 15.1 */ H0 = (sin(ln_deg_to_rad (horizon)) - sin(ln_deg_to_rad(observer->lat)) * sin(ln_deg_to_rad(sol2.dec))); H1 = (cos(ln_deg_to_rad(observer->lat)) * cos(ln_deg_to_rad(sol2.dec))); H1 = H0 / H1; ret = check_coords (observer, H1, horizon, &sol2); if (ret) return ret; H0 = acos (H1); H0 = ln_rad_to_deg (H0); /* correct ra values for interpolation - put them to the same side of circle */ if ((sol1.ra - sol2.ra) > 180.0) sol2.ra += 360; if ((sol2.ra - sol3.ra) > 180.0) sol3.ra += 360; if ((sol3.ra - sol2.ra) > 180.0) sol3.ra -= 360; if ((sol2.ra - sol1.ra) > 180.0) sol3.ra -= 360; for (i = 0; i < 3; i++) { /* equ 15.2 */ mt = (sol2.ra - observer->lng - O) / 360.0; mr = mt - H0 / 360.0; ms = mt + H0 / 360.0; /* put in correct range */ if (mt > 1.0 ) mt--; else if (mt < 0.0) mt++; if (mr > 1.0 ) mr--; else if (mr < 0.0) mr++; if (ms > 1.0 ) ms--; else if (ms < 0.0) ms++; /* find sidereal time at Greenwich, in degrees, for each m*/ mst = O + 360.985647 * mt; msr = O + 360.985647 * mr; mss = O + 360.985647 * ms; nt = mt + T / 86400.0; nr = mr + T / 86400.0; ns = ms + T / 86400.0; /* interpolate ra and dec for each m, except for transit dec (dec2) */ posr.ra = ln_interpolate3 (nr, sol1.ra, sol2.ra, sol3.ra); posr.dec = ln_interpolate3 (nr, sol1.dec, sol2.dec, sol3.dec); post.ra = ln_interpolate3 (nt, sol1.ra, sol2.ra, sol3.ra); poss.ra = ln_interpolate3 (ns, sol1.ra, sol2.ra, sol3.ra); poss.dec = ln_interpolate3 (ns, sol1.dec, sol2.dec, sol3.dec); /* find local hour angle */ Hat = mst + observer->lng - post.ra; Har = msr + observer->lng - posr.ra; Has = mss + observer->lng - poss.ra; /* find altitude for rise and set */ altr = sin(ln_deg_to_rad(observer->lat)) * sin(ln_deg_to_rad(posr.dec)) + cos(ln_deg_to_rad(observer->lat)) * cos(ln_deg_to_rad(posr.dec)) * cos(ln_deg_to_rad (Har)); alts = sin(ln_deg_to_rad(observer->lat)) * sin(ln_deg_to_rad(poss.dec)) + cos(ln_deg_to_rad(observer->lat)) * cos(ln_deg_to_rad(poss.dec)) * cos(ln_deg_to_rad (Has)); /* corrections for m */ dmt = - (Hat / 360.0); dmr = (altr - horizon) / (360 * cos(ln_deg_to_rad(posr.dec)) * cos(ln_deg_to_rad(observer->lat)) * sin(ln_deg_to_rad(Har))); dms = (alts - horizon) / (360 * cos(ln_deg_to_rad(poss.dec)) * cos(ln_deg_to_rad(observer->lat)) * sin(ln_deg_to_rad(Has))); /* add corrections and change to JD */ mt += dmt; mr += dmr; ms += dms; if (mt <= 1 && mt >= 0 && mr <= 1 && mr >= 0 && ms <= 1 && ms >= 0) break; } rst->rise = JD_UT + mr; rst->transit = JD_UT + mt; rst->set = JD_UT + ms; /* not circumpolar */ return 0; }
/* equ 30.2 */ double ln_get_ell_radius_vector (double a, double e, double E) { return a * ( 1.0 - e * cos (ln_deg_to_rad (E))); }
void ln_get_nutation (double JD, struct ln_nutation * nutation) { long double D,M,MM,F,O,T,T2,T3,JDE; long double coeff_sine, coeff_cos; long double argument; int i; /* should we bother recalculating nutation */ if (fabs(JD - c_JD) > LN_NUTATION_EPOCH_THRESHOLD) { /* set the new epoch */ c_JD = JD; /* get julian ephemeris day */ JDE = ln_get_jde (JD); /* calc T */ T = (JDE - 2451545.0)/36525; T2 = T * T; T3 = T2 * T; /* calculate D,M,M',F and Omega */ D = 297.85036 + 445267.111480 * T - 0.0019142 * T2 + T3 / 189474.0; M = 357.52772 + 35999.050340 * T - 0.0001603 * T2 - T3 / 300000.0; MM = 134.96298 + 477198.867398 * T + 0.0086972 * T2 + T3 / 56250.0; F = 93.2719100 + 483202.017538 * T - 0.0036825 * T2 + T3 / 327270.0; O = 125.04452 - 1934.136261 * T + 0.0020708 * T2 + T3 / 450000.0; /* convert to radians */ D = ln_deg_to_rad (D); M = ln_deg_to_rad (M); MM = ln_deg_to_rad (MM); F = ln_deg_to_rad (F); O = ln_deg_to_rad (O); /* calc sum of terms in table 21A */ for (i=0; i< TERMS; i++) { /* calc coefficients of sine and cosine */ coeff_sine = (coefficients[i].longitude1 + (coefficients[i].longitude2 * T)); coeff_cos = (coefficients[i].obliquity1 + (coefficients[i].obliquity2 * T)); argument = arguments[i].D * D + arguments[i].M * M + arguments[i].MM * MM + arguments[i].F * F + arguments[i].O * O; c_longitude += coeff_sine * sin(argument); c_obliquity += coeff_cos * cos(argument); } /* change to arcsecs */ c_longitude /= 10000; c_obliquity /= 10000; /* change to degrees */ c_longitude /= (60 * 60); c_obliquity /= (60 * 60); /* calculate mean ecliptic - Meeus 2nd edition, eq. 22.2 */ c_ecliptic = 23.0 + 26.0 / 60.0 + 21.448 / 3600.0 - 46.8150/3600 * T - 0.00059/3600 * T2 + 0.001813/3600 * T3; /* c_ecliptic += c_obliquity; * Uncomment this if function should return true obliquity rather than mean obliquity */ } /* return results */ nutation->longitude = c_longitude; nutation->obliquity = c_obliquity; nutation->ecliptic = c_ecliptic; }