コード例 #1
0
/*! \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;
}
コード例 #2
0
ファイル: refraction.c プロジェクト: AustinW/CS411SampleCode
/*! \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;
}
コード例 #3
0
ファイル: precession.c プロジェクト: AustinW/CS411SampleCode
/* 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);
}
コード例 #4
0
/* 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;
}
コード例 #5
0
ファイル: precession.c プロジェクト: AustinW/CS411SampleCode
/* 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);
}
コード例 #6
0
/*! \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)));
}
コード例 #7
0
/*! \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));
}
コード例 #8
0
/*! \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));
}
コード例 #9
0
ファイル: sidereal_time.c プロジェクト: jacoblwood/Sunset
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;
}
コード例 #10
0
/*! \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)));
}
コード例 #11
0
ファイル: rise_set.c プロジェクト: AustinW/CS411SampleCode
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;
}
コード例 #12
0
ファイル: rise_set.c プロジェクト: AustinW/CS411SampleCode
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;
}
コード例 #13
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)));
}
コード例 #14
0
ファイル: nutation.c プロジェクト: KSoto/iPhone-GPS-Sunrise
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;
}