1 /*
2  *  This library is free software; you can redistribute it and/or
3  *  modify it under the terms of the GNU Lesser General Public
4  *  License as published by the Free Software Foundation; either
5  *  version 2 of the License, or (at your option) any later version.
6  *
7  *  This library is distributed in the hope that it will be useful,
8  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10  *  Lesser General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License
13  *  along with this program; if not, write to the Free Software
14  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15  *
16  *  Copyright (C) 2000 - 2005 Liam Girdwood <lgirdwood@gmail.com>
17  */
18 
19 module nova.transform;
20 
21 import std.math;
22 
23 import nova.utility;
24 import nova.sidereal_time;
25 import nova.nutation;
26 import nova.precession;
27 import nova.ln_types;
28 
29 extern (C) {
30 
31 /*! \fn void ln_get_rect_from_helio(struct ln_helio_posn *object, struct ln_rect_posn *position);
32 * \param object Object heliocentric coordinates
33 * \param position Pointer to store new position
34 *
35 * Transform an objects heliocentric ecliptical coordinates
36 * into heliocentric rectangular coordinates.
37 */
38 /* Equ 37.1 Pg 264
39 */
40 @nogc void ln_get_rect_from_helio(const ref ln_helio_posn object,
41 	ref ln_rect_posn position) nothrow
42 {
43 	double sin_e, cos_e;
44 	double cos_B, sin_B, sin_L, cos_L;
45 
46 	/* ecliptic J2000 */
47 	sin_e = 0.397777156;
48 	cos_e = 0.917482062;
49 
50 	/* calc common values */
51 	cos_B = cos(ln_deg_to_rad(object.B));
52 	cos_L = cos(ln_deg_to_rad(object.L));
53 	sin_B = sin(ln_deg_to_rad(object.B));
54 	sin_L = sin(ln_deg_to_rad(object.L));
55 
56 	/* equ 37.1 */
57 	position.X = object.R * cos_L * cos_B;
58 	position.Y = object.R * (sin_L * cos_B * cos_e - sin_B * sin_e);
59 	position.Z = object.R * (sin_L * cos_B * sin_e + sin_B * cos_e);
60 }
61 
62 /*! \fn void ln_get_hrz_from_equ(struct ln_equ_posn *object, struct ln_lnlat_posn *observer, double JD, struct ln_hrz_posn *position)
63 * \param object Object coordinates.
64 * \param observer Observer cordinates.
65 * \param JD Julian day
66 * \param position Pointer to store new position.
67 *
68 * Transform an objects equatorial coordinates into horizontal coordinates
69 * for the given julian day and observers position.
70 *
71 * 0 deg azimuth = south, 90 deg = west.
72 */
73 /* Equ 12.1,12.2 pg 88
74 *
75 * TODO:
76 * Transform horizontal coordinates to galactic coordinates.
77 */
78 
79 @nogc void ln_get_hrz_from_equ(const ref ln_equ_posn object,
80 	const ref ln_lnlat_posn observer, double JD, ref ln_hrz_posn position) nothrow
81 {
82 	double sidereal;
83 
84 	/* get mean sidereal time in hours*/
85 	sidereal = ln_get_mean_sidereal_time(JD);
86 	ln_get_hrz_from_equ_sidereal_time (object, observer, sidereal, position);
87 }
88 
89 
90 @nogc void ln_get_hrz_from_equ_sidereal_time(const ref ln_equ_posn object,
91 	const ref ln_lnlat_posn observer, double sidereal,
92 	ref ln_hrz_posn position) nothrow
93 {
94 	real H, ra, latitude, declination, A, Ac, As, h, Z, Zs;
95 
96 	/* change sidereal_time from hours to radians*/
97 	sidereal *= 2.0 * PI / 24.0;
98 
99 	/* calculate hour angle of object at observers position */
100 	ra = ln_deg_to_rad(object.ra);
101 	H = sidereal + ln_deg_to_rad(observer.lng) - ra;
102 
103 	/* hence formula 12.5 and 12.6 give */
104 	/* convert to radians - hour angle, observers latitude, object declination */
105 	latitude = ln_deg_to_rad(observer.lat);
106 	declination = ln_deg_to_rad(object.dec);
107 
108 	/* formula 12.6 *; missuse of A (you have been warned) */
109 	A = sin(latitude) * sin(declination) + cos(latitude) *
110 		cos(declination) * cos(H);
111 	h = asin(A);
112 
113 	/* convert back to degrees */
114 	position.alt = ln_rad_to_deg(h);
115 
116 	/* zenith distance, Telescope Control 6.8a */
117 	Z = acos(A);
118 
119 	/* is'n there better way to compute that? */
120 	Zs = sin(Z);
121 
122 	/* sane check for zenith distance; don't try to divide by 0 */
123 	if (fabs(Zs) < 1e-5) {
124 		if (object.dec > 0.0)
125 			position.az = 180.0;
126 		else
127 			position.az = 0.0;
128 		if ((object.dec > 0.0 && observer.lat > 0.0)
129 		   || (object.dec < 0.0 && observer.lat < 0.0))
130 		  	position.alt = 90.0;
131 		else
132 		  	position.alt = -90.0;
133 		return;
134 	}
135 
136 	/* formulas TC 6.8d Taff 1991, pp. 2 and 13 - vector transformations */
137 	As = (cos(declination) * sin(H)) / Zs;
138 	Ac = (sin(latitude) * cos(declination) * cos(H) - cos(latitude) *
139 			sin(declination)) / Zs;
140 
141 	// don't blom at atan2
142 	if (Ac == 0.0 && As == 0.0) {
143 	        if (object.dec > 0)
144 			position.az = 180.0;
145 		else
146 			position.az = 0.0;
147 		return;
148 	}
149 	A = atan2(As, Ac);
150 
151 	/* convert back to degrees */
152 	position.az = ln_range_degrees(ln_rad_to_deg(A));
153 }
154 
155 /*! \fn void ln_get_equ_from_hrz(struct ln_hrz_posn *object, struct ln_lnlat_posn *observer, double JD, struct ln_equ_posn *position)
156 * \param object Object coordinates.
157 * \param observer Observer cordinates.
158 * \param JD Julian day
159 * \param position Pointer to store new position.
160 *
161 * Transform an objects horizontal coordinates into equatorial coordinates
162 * for the given julian day and observers position.
163 */
164 @nogc void ln_get_equ_from_hrz(const ref ln_hrz_posn object,
165 	const ref ln_lnlat_posn observer, double JD, ref ln_equ_posn position) nothrow
166 {
167 	real H, longitude, declination, latitude, A, h, sidereal;
168 
169 	/* change observer/object position into radians */
170 
171 	/* object alt/az */
172 	A = ln_deg_to_rad(object.az);
173 	h = ln_deg_to_rad(object.alt);
174 
175 	/* observer long / lat */
176 	longitude = ln_deg_to_rad(observer.lng);
177 	latitude = ln_deg_to_rad(observer.lat);
178 
179 	/* equ on pg89 */
180 	H = atan2(sin(A), (cos(A) * sin(latitude) + tan(h) * cos(latitude)));
181 	declination = sin(latitude) * sin(h) - cos(latitude) * cos(h) * cos(A);
182 	declination = asin(declination);
183 
184 	/* get ra = sidereal - longitude + H and change sidereal to radians*/
185 	sidereal = ln_get_apparent_sidereal_time(JD);
186 	sidereal *= 2.0 * PI / 24.0;
187 
188 	position.ra = ln_range_degrees(ln_rad_to_deg(sidereal - H + longitude));
189 	position.dec = ln_rad_to_deg(declination);
190 }
191 
192 /*! \fn void ln_get_equ_from_ecl(struct ln_lnlat_posn *object, double JD, struct ln_equ_posn *position)
193 * \param object Object coordinates.
194 * \param JD Julian day
195 * \param position Pointer to store new position.
196 *
197 * Transform an objects ecliptical coordinates into equatorial coordinates
198 * for the given julian day.
199 */
200 /* Equ 12.3, 12.4 pg 89
201 */
202 @nogc void ln_get_equ_from_ecl(const ref ln_lnlat_posn object, double JD,
203 	ref ln_equ_posn position) nothrow
204 {
205 	double ra, declination, longitude, latitude;
206 	ln_nutation nutation;
207 
208 	/* get obliquity of ecliptic and change it to rads */
209 	ln_get_nutation(JD, nutation);
210 	nutation.ecliptic = ln_deg_to_rad(nutation.ecliptic);
211 
212 	/* change object's position into radians */
213 
214 	/* object */
215 	longitude = ln_deg_to_rad(object.lng);
216 	latitude = ln_deg_to_rad(object.lat);
217 
218 	/* Equ 12.3, 12.4 */
219 	ra = atan2((sin(longitude) * cos(nutation.ecliptic) -
220 		tan(latitude) * sin(nutation.ecliptic)), cos(longitude));
221 	declination = sin(latitude) * cos(nutation.ecliptic) + cos(latitude) *
222 		sin(nutation.ecliptic) * sin(longitude);
223 	declination = asin(declination);
224 
225 	/* store in position */
226 	position.ra = ln_range_degrees(ln_rad_to_deg(ra));
227 	position.dec = ln_rad_to_deg(declination);
228 }
229 
230 /*! \fn void ln_get_ecl_from_equ(struct ln_equ_posn *object, double JD, struct ln_lnlat_posn *position)
231 * \param object Object coordinates in B1950. Use ln_get_equ_prec2 to transform from J2000.
232 * \param JD Julian day
233 * \param position Pointer to store new position.
234 *
235 * Transform an objects equatorial cordinates into ecliptical coordinates
236 * for the given julian day.
237 */
238 /* Equ 12.1, 12.2 Pg 88
239 */
240 @nogc void ln_get_ecl_from_equ(const ref ln_equ_posn object, double JD,
241 	ref ln_lnlat_posn position) nothrow
242 {
243 	double ra, declination, latitude, longitude;
244 	ln_nutation nutation;
245 
246 	/* object position */
247 	ra = ln_deg_to_rad(object.ra);
248 	declination = ln_deg_to_rad(object.dec);
249 	ln_get_nutation(JD, nutation);
250 	nutation.ecliptic = ln_deg_to_rad(nutation.ecliptic);
251 
252 	/* Equ 12.1, 12.2 */
253 	longitude = atan2((sin(ra) * cos(nutation.ecliptic) + tan(declination) *
254 		sin(nutation.ecliptic)), cos(ra));
255 	latitude = sin(declination) * cos(nutation.ecliptic) - cos(declination) *
256 		sin(nutation.ecliptic) * sin(ra);
257 	latitude = asin(latitude);
258 
259 	/* store in position */
260 	position.lat = ln_rad_to_deg(latitude);
261 	position.lng = ln_range_degrees(ln_rad_to_deg(longitude));
262 }
263 
264 /*! \fn void ln_get_ecl_from_rect(struct ln_rect_posn *rect, struct ln_lnlat_posn *posn)
265 * \param rect Rectangular coordinates.
266 * \param posn Pointer to store new position.
267 *
268 * Transform an objects rectangular coordinates into ecliptical coordinates.
269 */
270 /* Equ 33.2
271 */
272 @nogc void ln_get_ecl_from_rect(const ref ln_rect_posn rect, ref ln_lnlat_posn posn) nothrow
273 {
274 	double t;
275 
276 	t = sqrt(rect.X * rect.X + rect.Y * rect.Y);
277 	posn.lng = ln_range_degrees(ln_rad_to_deg(atan2(rect.X, rect.Y)));
278 	posn.lat = ln_rad_to_deg(atan2(t, rect.Z));
279 }
280 
281 /*! \fn void ln_get_equ_from_gal(struct ln_gal_posn *gal, struct ln_equ_posn *equ)
282 * \param gal Galactic coordinates.
283 * \param equ B1950 equatorial coordinates. Use ln_get_equ_prec2 to transform to J2000.
284 *
285 * Transform an object galactic coordinates into B1950 equatorial coordinate.
286 */
287 /* Pg 94 */
288 @nogc void ln_get_equ_from_gal(const ref ln_gal_posn gal, ref ln_equ_posn equ) nothrow
289 {
290 	double RAD_27_4, SIN_27_4, COS_27_4;
291 	double l_123, cos_l_123;
292 	double sin_b, cos_b, rad_gal_b;
293 	double y;
294 
295 	RAD_27_4 = ln_deg_to_rad(27.4);
296 	SIN_27_4 = sin(RAD_27_4);
297 	COS_27_4 = cos(RAD_27_4);
298 
299 	l_123 = ln_deg_to_rad(gal.l - 123);
300 	cos_l_123 = cos(l_123);
301 
302 	rad_gal_b = ln_deg_to_rad(gal.b);
303 
304 	sin_b = sin(rad_gal_b);
305 	cos_b = cos(rad_gal_b);
306 
307 	y = atan2(sin(l_123), cos_l_123 * SIN_27_4 - (sin_b / cos_b) * COS_27_4);
308 	equ.ra = ln_range_degrees(ln_rad_to_deg(y) + 12.25);
309 	equ.dec =
310 		ln_rad_to_deg(asin(sin_b * SIN_27_4 + cos_b * COS_27_4 * cos_l_123));
311 }
312 
313 /*! \fn void ln_get_equ2000_from_gal(struct ln_gal_posn *gal, struct ln_equ_posn *equ)
314 * \param gal Galactic coordinates.
315 * \param equ J2000 equatorial coordinates.
316 *
317 * Transform an object galactic coordinates into equatorial coordinate.
318 */
319 @nogc void ln_get_equ2000_from_gal(const ref ln_gal_posn gal, ref ln_equ_posn equ) nothrow
320 {
321 	ln_get_equ_from_gal(gal, equ);
322 	ln_get_equ_prec2(equ, B1950, JD2000, equ);
323 }
324 
325 /*! \fn ln_get_gal_from_equ(struct ln_equ_posn *equ, struct ln_gal_posn *gal)
326 * \param equ B1950 equatorial coordinates.
327 * \param gal Galactic coordinates.
328 *
329 * Transform an object B1950 equatorial coordinate into galactic coordinates.
330 */
331 /* Pg 94 */
332 @nogc void ln_get_gal_from_equ(const ref ln_equ_posn equ, ref ln_gal_posn gal) nothrow
333 {
334 	double RAD_27_4, SIN_27_4, COS_27_4;
335 	double ra_192_25, cos_ra_192_25;
336 	double rad_equ_dec;
337 	double cos_dec, sin_dec;
338 	double x;
339 
340 	RAD_27_4 = ln_deg_to_rad(27.4);
341 	SIN_27_4 = sin(RAD_27_4);
342 	COS_27_4 = cos(RAD_27_4);
343 
344 	ra_192_25 = ln_deg_to_rad(192.25 - equ.ra);
345 	cos_ra_192_25 = cos(ra_192_25);
346 
347 	rad_equ_dec = ln_deg_to_rad(equ.dec);
348 
349 	sin_dec = sin(rad_equ_dec);
350 	cos_dec = cos(rad_equ_dec);
351 
352 	x = atan2(sin(ra_192_25),
353 		cos_ra_192_25 * SIN_27_4 - (sin_dec / cos_dec) * COS_27_4);
354 	gal.l = ln_range_degrees(303 - ln_rad_to_deg(x));
355 	gal.b = ln_rad_to_deg(asin(sin_dec * SIN_27_4 + cos_dec *
356 		COS_27_4 * cos_ra_192_25));
357 }
358 
359 /*! \fn void ln_get_gal_from_equ2000(struct ln_equ_posn *equ, struct ln_gal_posn *gal)
360 * \param equ J2000 equatorial coordinates.
361 * \param gal Galactic coordinates.
362 *
363 * Transform an object J2000 equatorial coordinate into galactic coordinates.
364 */
365 @nogc void ln_get_gal_from_equ2000(const ref ln_equ_posn equ, ref ln_gal_posn gal) nothrow
366 {
367 	ln_equ_posn equ_1950;
368 	ln_get_equ_prec2(equ, JD2000, B1950, equ_1950);
369 	ln_get_gal_from_equ(equ_1950, gal);
370 }
371 
372 /*! \example transforms.c
373  *
374  * Examples of how to use transformation functions.
375  */
376 
377 }