/*
 *  This file is part of libcxxsupport.
 *
 *  libcxxsupport 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.
 *
 *  libcxxsupport 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 libcxxsupport; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

/*
 *  libcxxsupport is being developed at the Max-Planck-Institut fuer Astrophysik
 *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt
 *  (DLR).
 */

/*! \file trafos.cc
 *  Celestial coordinate transformations.
 *
 *  Copyright (C) 2005 Max-Planck-Society
 * \author Martin Reinecke
 */

#include "trafos.h"
#include "lsconstants.h"

using namespace std;

vec3 Trafo::xcc_dp_precess (const vec3 &iv, double iepoch,
  double oepoch)
  {
  // Z-axis rotation by OBL_LONG:
  double Tm = ((oepoch+iepoch)*0.5 - 1900.) *0.01;
  double gp_long  = degr2rad * ((oepoch-iepoch) * (50.2564+0.0222*Tm) / 3600.);
  double obl_long =
    degr2rad * (180. - (173. + (57.06+54.77*Tm) / 60.)) + gp_long*0.5;
  double dco = cos(obl_long), dso = sin(obl_long);
  vec3 ov (iv.x*dco-iv.y*dso, iv.x*dso+iv.y*dco, iv.z);

  // X-axis rotation by dE:
  double dE = degr2rad * ((oepoch-iepoch) * (0.4711-0.0007*Tm) / 3600.);
  double dce = cos(dE), dse = sin(dE);
  double temp = ov.y*dce - ov.z*dse;
  ov.z = ov.y*dse + ov.z*dce;
  ov.y = temp;

  // Z-axis rotation by GP_LONG - OBL_LONG:
  double dL = gp_long - obl_long;
  double dcl = cos(dL), dsl = sin(dL);
  temp = ov.x*dcl - ov.y*dsl;
  ov.y = ov.x*dsl + ov.y*dcl;
  ov.x = temp;

  return ov;
  }

double Trafo::get_epsilon (double epoch)
  {
  double T = (epoch - 1900.) * 0.01; 
  return
    degr2rad * (23.452294 - 0.0130125*T - 1.63889e-6*T*T + 5.02778e-7*T*T*T);
  }

/*! Routine to convert from ecliptic coordinates to equatorial (celestial)
    coordinates at the given epoch.  Adapted from the COBLIB routine by Dr.
    E. Wright. */
vec3 Trafo::xcc_dp_e_to_q (const vec3 &iv, double epoch)
  {
  double epsilon=get_epsilon(epoch);
  double dc = cos(epsilon), ds = sin(epsilon);
  return vec3 (iv.x, dc*iv.y-ds*iv.z, dc*iv.z+ds*iv.y);
  }

vec3 Trafo::xcc_dp_q_to_e (const vec3 &iv, double epoch)
  {
  double epsilon=-get_epsilon(epoch);
  double dc = cos(epsilon), ds = sin(epsilon);
  return vec3 (iv.x, dc*iv.y-ds*iv.z, dc*iv.z+ds*iv.y);
  }

/*! Routine to convert galactic coordinates to ecliptic (celestial) 
    coordinates at the given epoch.  First the conversion to ecliptic 
    2000 is done, then if necessary the results are precessed. */
vec3 Trafo::xcc_dp_g_to_e (const vec3 &iv, double epoch)
  {
  static const rotmatrix T (-0.054882486,  0.494116468, -0.867661702,
                            -0.993821033, -0.110993846, -0.000346354,
                            -0.096476249,  0.862281440,  0.497154957);
  vec3 hv=T.Transform(iv);

  if (!approx(epoch,2000.))
    hv=xcc_dp_precess(hv,2000.,epoch);

  return hv;
  }

/*! Routine to convert ecliptic (celestial) coordinates at the given
    epoch to galactic coordinates.  The ecliptic coordinates are first
    precessed to 2000.0, then converted. */
vec3 Trafo::xcc_dp_e_to_g (const vec3 &iv, double epoch)
  {
  static const rotmatrix T (-0.054882486, -0.993821033, -0.096476249,
                             0.494116468, -0.110993846,  0.862281440,
                            -0.867661702, -0.000346354,  0.497154957);
  vec3 hv=iv;
  if (!approx(epoch,2000.))
    hv=xcc_dp_precess(hv,epoch,2000.);

  return T.Transform(hv);
  }

/*! Function to convert between standard coordinate systems, including
    precession. */
vec3 Trafo::xcc_v_convert(const vec3 &iv, double iepoch, double oepoch,
  coordsys isys,coordsys osys)
  {
  vec3 xv;
  if (isys == Ecliptic)
    xv=iv;
  else if (isys == Equatorial)
    xv = xcc_dp_q_to_e(iv,iepoch);
  else if (isys == Galactic)
    xv = xcc_dp_g_to_e(iv,iepoch);
  else
    planck_fail("Unsupported input coordinate system");

  vec3 yv = approx(iepoch,oepoch) ? xv : xcc_dp_precess(xv,iepoch,oepoch);

  vec3 ov;
  if (osys == Ecliptic)
    ov = yv;
  else if (osys == Equatorial)
    ov = xcc_dp_e_to_q(yv,oepoch);
  else if (osys == Galactic)
    ov = xcc_dp_e_to_g(yv,oepoch);
  else
    planck_fail("Unsupported output coordinate system");

  return ov;
  }

void Trafo::coordsys2matrix (double iepoch, double oepoch,
  coordsys isys, coordsys osys, rotmatrix &matrix)
  {
  vec3 v1p = xcc_v_convert(vec3(1,0,0),iepoch,oepoch,isys,osys),
       v2p = xcc_v_convert(vec3(0,1,0),iepoch,oepoch,isys,osys),
       v3p = xcc_v_convert(vec3(0,0,1),iepoch,oepoch,isys,osys);
  v1p.Normalize(); v2p.Normalize(); v3p.Normalize();
  matrix=rotmatrix(v1p,v2p,v3p);
  }
