/* @(#)ast_coos.c	19.1 (ES0-DMD) 02/25/03 13:53:52 */
/*===========================================================================
  Copyright (C) 1995 European Southern Observatory (ESO)
 
  This program is free software; you can redistribute it and/or 
  modify it under the terms of the GNU General Public License as 
  published by the Free Software Foundation; either version 2 of 
  the License, or (at your option) any later version.
 
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
 
  You should have received a copy of the GNU General Public 
  License along with this program; if not, write to the Free 
  Software Foundation, Inc., 675 Massachusetss Ave, Cambridge, 
  MA 02139, USA.
 
  Corresponding concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/*+++++++++++++++       
.TYPE           Module
.IDENTIFICATION coos.c
.VERSION 1.0	24-Oct-1988: Creation
.VERSION 1.1	05-May-1989: Corrected bug in SuperGalactic
.Language       C
.AUTHOR         P.T Wallace [Starlink], Francois Ochsenbein [ESO-IPG]  
.KEYWORDS       Change of Coordinates

.ENV            

.COMMENTS
This module contains routines to transform coordinates
from /to one of the systems FK4, FK5, Galactic, Ecliptic, Supergalactic.

\begin{TeX}

The modification concerns the position as well as the
velocity vector (radial velocity and proper motions).
The complete position information is stored in a 
`s' structure containing the 6--vector $\{x y z \dot{x} ,\dot{y} \dot{z}\}$,
the epoch, the equinox, and the coordinate system.

The velocity vector $\vec{v}(t)$ contains the velocity expressed in $''$/year 
as:

$$\begin{array}{ll}
  v_0 = k \pi V_{r0} 		& \\
  v_1 = \mu_{\alpha0} \cos \delta_0 &  	\\
  v_2 = \mu_{\delta0} 		&  	
\end{array}$$

The value of $k$  ($0.21094502$) when $\pi$ is
expressed in " and $V_r$ in km/s
is defined in {\tt ASTRO.H} as the mnemonic {\tt km\_s\_pc}.

\end{TeX}

----------------------*/

#include <osdefos.h>	
#include <astro.h>
#include <trigo.h>
#include <ok.h>

static double R44[3][3], y40 = -99999999.e0, y41 = -99999999.e0;
static double R55[3][3], y50 = -99999999.e0, y51 = -99999999.e0;

static double RB0[3][3], yB0 = -99999999.e0;
static double RB1[3][3], yB1 = -99999999.e0;

static double RJ0[3][3], yJ0 = -99999999.e0;
static double RJ1[3][3], yJ1 = -99999999.e0;

static double RE0[3][3], yE0 = -99999999.e0;
static double RE1[3][3], yE1 = -99999999.e0;

static double Rl[3][3] = {	{1.e0, 0.e0, 0.e0},	/* Frame to Local */
				{0.e0, 1.e0, 0.e0},
				{0.e0, 0.e0, 1.e0}};
			
STATIC double GAL_2000[3][3] = {	/* J2000 to Supergalactic	*/
	{1.e0, 0.e0, 0.e0},
	{0.e0, 1.e0, 0.e0},
	{0.e0, 0.e0, 1.e0}};

EXTERN double gal_2000[3][3];		/* J2000 to galactic		*/
EXTERN double supergal[3][3];		/* Galactic to Supergalactic	*/

#define FINISH			goto FIN

/*============================================================================*
 *			Conversion of `s' structure
 *============================================================================*/
int tr_qvs(q, v, s)
/*+++
.PURPOSE   Transformation of Position + Mouvement to spos structure
.RETURNS   OK
---*/
	double q[2];	/* IN: Position ra + dec	*/
	double v[3];	/* IN: Velocity in Local Frame ("/yr)	*/
	struct spos *s;	/* OUT: Position in Phase space	*/
{
	int	i;
	
  tr_oR(q, Rl);			/* Rotation matrix for velocity	*/
  for (i=0; i<3; i++)	s->u[i] = Rl[0][i];
  tr_uu1(v, s->ud, Rl);		/* Velocity in General Frame	*/
  for (i=0; i<3; i++)	s->ud[i] /= (3600*180/PI);

  return(OK);
}

/*============================================================================*/
int tr_sqv(s, q, v)
/*+++
.PURPOSE   Transformation of spos structure (position in phase space)
		to Position + Velocity
.RETURNS   OK
---*/
	struct spos *s;	/* IN: Position in Phase space	*/
	double q[2];	/* OUT: Position ra + dec	*/
	double v[3];	/* OUT: Velocity in Local Frame ("/yr)	*/
{
	int	i;
	
  tr_uR(s->u, Rl);		/* Rotation matrix for velocity	*/
  tr_uo(s->u, q);		/* Convert to Angles		*/
  tr_uu(s->ud, v, Rl);		/* Velocity in Local Frame	*/
  for (i=0; i<3; i++)	v[i] *= (3600*180/PI);

  return(OK);
}

/*============================================================================*
 *			E-term computation
 *============================================================================*/

static int eterm(y, ev)
/*+++++++++++
.PURPOSE   Computation of e-term at specified date
.RETURNS   OK
.METHOD		Reference: Explanatory Suppl. to the Astron. Ephemeris, 
	section 2D, p. 48;	P.T. Wallace, Starlink.
------------*/
	double y; 	/* IN: Epoch, in Julian Years	*/
	double ev[3]; 	/* OUT: E-term modification	*/
{
  	double t, e, L, eps, cL;
  	
  t   = (y - 1900.e0)/1.e2;	/* 	Centuries	*/
  eps = 23.452   - 0.013*t;	/* Mean obliquity	*/
  e   = 0.016751 - 0.000042*t;	/* Eccentricity		*/
  L   = 281.221  + 1.719*t;	/* Mean longitude of perihelion	*/  
  
  e  *= (20.496/3600.e0)/DEG;	/* Component of aberration	*/
  cL  = cos(L);
  ev[0] =  e * sind(L);
  ev[1] = -e * cL * cosd(eps);
  ev[2] = -e * cL * sind(eps);

  return(OK);
}

/*============================================================================*
 *			Math utilities
 *============================================================================*/

static int add(u,v,t)
/*+++
.PURPOSE Add vector t*v to unit vector
.RETURNS OK
---*/
	double u[3];	/* MOD: Unit vector	*/
	double v[3];	/* MOD: Vector to add	*/
	double t;	/* IN: Factor		*/
{
	double  f;
	int	i;

  for (i=0; i<3; i++)
    	u[i] += t*v[i];

  f = hypot(hypot(u[0], u[1]), u[2]);
  
  for (i=0; i<3; i++)	u[i] /= f;
  for (i=0; i<3; i++)	v[i] /= f; 
  
  return(OK);
}

/*============================================================================*/
static int sub(u,v,t)
/*+++
.PURPOSE Substract vector t*v to unit vector
.RETURNS OK
---*/
	double u[3];	/* MOD: Unit vector	*/
	double v[3];	/* MOD: Vector to add	*/
	double t;	/* IN: Factor		*/
{
	double  f;
	int	i;

  for (i=0; i<3; i++)
    	u[i] -= t*v[i];

  f = hypot(hypot(u[0], u[1]), u[2]);
  for (i=0; i<3; i++)	u[i] /= f;
  for (i=0; i<3; i++)	v[i] /= f; 
  
  return(OK);
}

/*============================================================================*/
static int rotate(s, R, inv)
/*+++
.PURPOSE Rotate unit vector + derivative
.RETURNS OK
---*/
	struct spos *s;	/* MOD: Pos + derivative	*/
	double R[3][3];	/* IN: Rotation Matrix		*/
	int    inv;	/* IN: 1 to take inverse matrix	*/
{
  if (inv)
  	tr_uu1(s->u, s->u, R),
  	tr_uu1(s->ud, s->ud, R);
  else
  	tr_uu(s->u, s->u, R),
  	tr_uu(s->ud, s->ud, R);
}

/*============================================================================*
 *			General Coordinate Transformation
 *============================================================================*/
int tr_ss(s0, s1)
/*+++
.PURPOSE General Coordinate Transformation from frame s0->frame to frame 
	s1->frame.
.METHOD  For same frame, use standard transformations;
	use always J2000 intermediate frame for other transformations.
.RETURNS OK
---*/
	struct spos *s0;/* IN: Vector in Phase Space (input)	*/
	struct spos *s1;/* OUT: Vector in Phase Space (output)	*/
{
	double  ev[3];
	int	i;
	static  struct spos s;

  s = *s0;

  if (s0->frame == s1->frame)
  {			/* Add Proper Motion	*/
  	switch(s0->frame)
  	{ case _COO_FK4_:		/* FK4 system (equatorial)	*/
  		add(s.u, s.ud, s1->epoch - s0->epoch);
		if ((y40 != s0->equinox) || (y41 != s1->equinox))
			y40 = s0->equinox, 
			y41 = s1->equinox, 
			preb_R(R44, JYtoBY(y40), JYtoBY(y41));
		eterm(s0->equinox, ev);		/* E-term to substract	*/
		sub(s.u, ev, 1.e0);
		rotate(&s, R44, 0);
		eterm(s1->equinox, ev);		/* E-term to add	*/
		add(s.u, ev, 1.e0);
		break;

	  case _COO_FK5_:		/* FK5 system (equatorial)	*/
  		add(s.u, s.ud, s1->epoch - s0->epoch);
		if ((y50 != s0->equinox) || (y51 != s1->equinox))
			y50 = s0->equinox, 
			y51 = s1->equinox, 
			prej_R(R55, y50, y51);
		rotate(&s, R55, 0);
		break;

	  case _COO_ECL_:
	  	goto via_J2000;
		break;

	  case _COO_GAL_: case _COO_SUPERGAL_:
  		add(s.u, s.ud, s1->epoch - s0->epoch);
		break;

	  default:	return(NOK);
  	}
  	FINISH;
  }

	/* Convert first to J2000, epoch J2000 */

  via_J2000:
  
  switch(s0->frame)
  { case _COO_FK4_:			/* FK4 system (equatorial)	*/
	add(s.u, s.ud, 1950.e0 - JYtoBY(s0->epoch));
	eterm(s0->equinox, ev);		/* E-term to substract		*/
	sub(s.u, ev, 1.e0);
	if (yB0 != s0->equinox)
		yB0 = s0->equinox, 
		preb_R(RB0, JYtoBY(yB0), 1950.e0);
	rotate(&s, RB0, 0);
	eterm(BYtoJY(1950.e0), ev);	/* E-term to add		*/
	add(s.u, ev, 1.e0);
	fk4_5s(&s, &s);			/* Convert to J2000 ep+eq	*/
	break;

    case _COO_FK5_:		/* FK5 system (equatorial)	*/
	add(s.u, s.ud, 2000.e0 - s0->epoch);
	if (yJ0 != s0->equinox)
		yJ0 = s0->equinox, 
		prej_R(RJ0, yJ0, 2000.e0);
	rotate(&s, RJ0, 0);
	break;

    case _COO_GAL_:		/* Galactic coordinates		*/
	add(s.u, s.ud, 2000.e0 - s0->epoch);
	rotate(&s, gal_2000, 1);
	break;
    	
    case _COO_SUPERGAL_:	/* SuperGalactic coordinates	*/
	if (GAL_2000[0][1] == 0.e0)	tr_RR(gal_2000, GAL_2000, supergal);
	add(s.u, s.ud, 2000.e0 - s0->epoch);
	rotate(&s, GAL_2000, 1);
	break;

    case _COO_ECL_:		/* Ecliptic coordinates		*/
	add(s.u, s.ud, 2000.e0 - s0->epoch);	
	if (yE0 != s0->equinox)
		yE0 = s0->equinox, 
		ecl_R(RE0, s0->equinox);
	rotate(&s, RE0, 1);	/* Equatorial, epoch J2000 	*/
	if (yJ0 != s0->equinox)
		yJ0 = s0->equinox, 
		prej_R(RJ0, s0->equinox, 2000.e0);
	rotate(&s, RJ0, 0);
	break;
	
    default:	return(NOK);
  }
	/* Convert from (J2000, J2000)	*/

  switch(s1->frame)
  { case _COO_FK4_:			/* FK4 system (equatorial)	*/
	fk5_4s(&s, &s);			/* Convert to B1950 ep+eq	*/
	add(s.u, s.ud, JYtoBY(s1->epoch) - 1950.e0);	/* Add PM	*/
	eterm(BYtoJY(1950.e0), ev);	/* E-term to remove		*/
	sub(s.u, ev, 1.e0);
	if (yB1 != s1->equinox)
		yB1 = s1->equinox, 
		preb_R(RB1, 1950.e0, JYtoBY(yB1));
	rotate(&s, RB1, 0);
	eterm(s1->equinox, ev);		/* E-term to add		*/
	add(s.u, ev, 1.e0);
	break;

    case _COO_FK5_:		/* FK5 system (equatorial)	*/
	add(s.u, s.ud, s1->epoch - 2000.e0);	/* Add PM	*/
	if (yJ1 != s1->equinox)
		yJ1 = s1->equinox, 
		prej_R(RJ1, 2000.e0, yJ1);
	rotate(&s, RJ1, 0);
	break;

    case _COO_GAL_:		/* Galactic coordinates		*/
	add(s.u, s.ud, s1->epoch - 2000.e0);	/* Add PM	*/
	rotate(&s, gal_2000, 0);
	break;
    	
    case _COO_SUPERGAL_:	/* SuperGalactic coordinates	*/
	if (GAL_2000[0][1] == 0.e0)	tr_RR(gal_2000, GAL_2000, supergal);
	add(s.u, s.ud, s1->epoch - 2000.e0);	/* Add PM	*/
	rotate(&s, GAL_2000, 0);
	break;

    case _COO_ECL_:		/* Ecliptic coordinates		*/
	add(s.u, s.ud, s1->epoch - 2000.e0);	/* Add PM	*/
	if (yJ1 != s1->equinox)
		yJ1 = s1->equinox, 
		prej_R(RJ1, 2000.e0, yJ1);
	rotate(&s, RJ1, 0);
	if (yE1 != s1->equinox)
		yE1 = s1->equinox, 
		ecl_R(RE1, yE1);
	rotate(&s, RE1, 0);	/* Ecliptic	*/
	break;
	
    default:	return(NOK);
  }

  FIN:		/* Copy Result */
  for (i=0; i<6; i++)	s1->u[i] = s.u[i];
  return(OK);
}


