/*===========================================================================
  Copyright (C) 1995-2009 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 Massachusetts Ave, Cambridge, 
  MA 02139, USA.
 
  Correspondence 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
===========================================================================*/

/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
/* .IDENT       dispersion.c                               */
/* .AUTHORS     Pascal Ballester (ESO/Garching)            */
/*              Cristian Levin   (ESO/La Silla)            */
/* .KEYWORDS    Spectroscopy, Long-Slit, MOS               */
/* .PURPOSE                                                */
/* .VERSION     1.0  Package Modification 21-SEP-1993      */
/*  090723	last modif				   */
/* ------------------------------------------------------- */

/* Purpose:
   This module is related to the dispersion relation and its representation.
   The upper modules (lncalib.c, lnrebin.c) do not know which representation is
   used (Chebyshev polynomials, or any other), nor how it is stored (which
   descriptor, meaning of coefficients). This design enables to replace 
   this module lndisp.c by any other based on a different representation.
 */

/* Note:

   ehe general rule is that arrays start at index 1. However
   the module can accomodate any starting index, which is specified at 
   initialisation time (function initdisp).
 */

/* Algorithms:

   The module provides ten top-level routines related to the evaluation of the
   dispersion relation. Only those six routines can be used in applications 
   related to the dispersion relation (wavelength calibration, rebinning, ...).
   The lower level routines must be considered as private.

   --- void savedisp (save) ---

   Stores dispersion coefficients in array <save>.

   --- double fit_disp (ndata, degree, x, l) ---

   This routine evaluates the dispersion relation on the basis of ndata 
   couples of d values (x,l). Array indexes start at 0. The degree is 
   indicative. If ndata is too small, a smaller degree will be fitted 
   to the couples. Minimum value for ndata is 2. The procedure returns 
   a rough estimate of the average pixel size.

   --- void eval_disp (x, l, ndata) ---

   Applies the last previously estimated (or loaded) dispersion relation 
   to ndata values of x and estimates the corresponding wavelength l. 
   Arrays start at index 0.

   --- void initdisp (name, mode, start) ---

   Open (mode = OLD) or Create (mode = NEW) a storage table (named <name>).


   --- void finishdisp() ---

   Closes the storage table

   --- void writedisp(line, pix_y, world_y) ---

   Store in the storage table the dispersion relation corresponding to the row
   number <line> and world coordinate <y>.

   --- double readdisp(line) ---

   Retrieves from the storage table the dispersion relation corresponding 
   to the row number <line>.  The procedure returns a rough estimate of 
   the average pixel size.

   --- void setdisp(degree, coefs) ---

   Sets the coefficients of the dispersion relation. coefs is an array
   which element 0 is the constant term and other elements represent
   the coefficients of higher degree


 */

#include <dispersion.h>
#include <nrutil.h>

#include <ctype.h>
#include <stdio.h>

static int FIT_SUCCESS = 0; /* Sucess indicator for the fit */


#ifdef __STDC__
void printdisp (void)
#else
void printdisp ()
#endif

{
  int i;

  printf ("Dispersion Relation. Degree: %d. Refdeg: %d. MaxCoef:%d\n",
	  fdeg, refdeg, maxcoef);
  printf ("Coefficients: ");
  for (i = 1; i <= ncoef; i++)
    printf (" %f ", coef[i]);
  printf ("\n");

}
/* -------------------------------------------------------------------------- */

#ifdef __STDC__
void setrefdeg (int deg)
#else
void setrefdeg (deg)
     int deg;
#endif

{

  refdeg = deg;
  maxcoef = refdeg + 1;

}
/* -------------------------------------------------------------------------- */

#ifdef __STDC__
void set_zero (int deg)
#else
void set_zero (deg)
     int deg;
#endif


{

  int i;

  fdeg = deg;
  refdeg = deg;
  ncoef = deg + 1;
  maxcoef = deg + 1;

  for (i = 1; i <= ncoef; i++)
    coef[i] = 0;
}

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

#ifdef __STDC__
void 
setdisp (int deg, double coefs[])
#else
void 
setdisp (deg, coefs)
     int deg;
     double coefs[];
#endif

{
  int i;

  fdeg = deg;
  refdeg = deg;
  ncoef = deg + 1;
  maxcoef = deg + 1;

  for (i = 1; i <= ncoef; i++)
    coef[i] = coefs[i - 1];

  FIT_SUCCESS = 1;

}
/* -------------------------------------------------------------------------- */

#ifdef __STDC__
double fit_disp (int *ndata, int *deg, double x[], double l[])
#else
double fit_disp (ndata, deg, x, l)

     int *ndata;
     int *deg;
     double x[], l[];
#endif

{

  double *sig, **covar, *chisq, *xsh, *lsh;
  int *lista;
  int i;

  int maxdeg;
  /* printf ("NData : %d   ---   Deg value : %d\n", *ndata, *deg); */

  refdeg = *deg;
  maxdeg = *ndata - 1;
  if (refdeg > maxdeg)
    fdeg = maxdeg;
  else
    fdeg = refdeg;
  ncoef = fdeg + 1;
  maxcoef = refdeg + 1;

  FIT_SUCCESS = 0;

  for (i=0; i<MAXNCOE; i++) coef[i] = 0.;

  if (*ndata < 2)
    {
      printf ("Not enough lines (minimum is 2). \nNo dispersion relation computed\n");
      FIT_SUCCESS = -2;
      return (0.);
    }

  if (fdeg < 1)
    {
      printf ("Degree : %d. No dispersion relation fitted\n", *deg);
      FIT_SUCCESS = -1;
      return (0.);
    }

  covar = dmatrix (1, *ndata, 1, *ndata);
  chisq = dvector (0, *ndata);
  sig = dvector (1, *ndata);

  xsh = x + start_index - 1;
  lsh = l + start_index - 1;

  lista = ivector (1, ncoef);

  for (i = 1; i <= ncoef; i++)
    lista[i] = i;
  for (i = 1; i <= *ndata; i++)
    sig[i] = 1.;

  lfit2 (x, l, sig, *ndata, coef, ncoef, FUNCTION);

  free_dmatrix (covar, 1, *ndata, 1, *ndata);
  free_dvector (chisq, 0, *ndata);
  free_dvector (sig, 1, *ndata);
  free_ivector (lista, 1, ncoef);

  FIT_SUCCESS = 1;
  pixbin = coef[2];
  return (pixbin);

}

/* -------------------------------------------------------------------------- */
 
#ifdef __STDC__
void eval_disp (double x[], double l[], int n)
#else
void eval_disp (x, l, n)
     double x[], l[];
     int n;
#endif

{

  int i, icoef;
  double xp[MAXNCOE];

  if (FIT_SUCCESS <= 0) {
         printf("No dispersion relation fitted. No evaluation.\n");
         return;
         }

  for (i = start_index; i < (n + start_index); i++)
    {
      l[i] = 0.;
      FUNCTION (x[i], xp, ncoef);
      for (icoef = 1; icoef <= ncoef; icoef++)
	l[i] += coef[icoef] * xp[icoef];
    }
}
/* -------------------------------------------------------------------------- */

#ifdef __STDC__
void initdisp (char name[], char mode[], int start)
#else
void initdisp (name, mode, start)
     char name[], mode[];
     int start;
#endif

{

  int icoef;
  char colnam[30], numb[10];
  int actvals, null;
  int kunit;
  int ncol, nrow, nsort, allcol, allrow;


  start_index = start;

  if (toupper (mode[0]) == 'N')
    {
      if (TCTINI (name, F_TRANS, F_IO_MODE, 5, 10, &tide))
	SCTPUT ("**** Error while creating output table");
      nbline = 0;
    }
  else
    {
      if (TCTOPN (name, F_IO_MODE, &tide))
	SCTPUT ("**** Error while opening output table");
      SCDRDD (tide, "LNPIX", 1, 1, &actvals, &pixbin, &kunit, &null);
      SCDRDI (tide, "LNDEG", 1, 1, &actvals, &refdeg, &kunit, &null);
      SCDRDI (tide, "LNCOE", 1, 1, &actvals, &maxcoef, &kunit, &null);
      fdeg = refdeg, ncoef = maxcoef;
      TCIGET (tide, &ncol, &nrow, &nsort, &allcol, &allrow);
      nbline = nrow;
    }

  TCCSER (tide, ":ROW", &colline);
  if (colline == (-1))
    TCCINI (tide, D_I4_FORMAT, 1, "I6", "Row Number",
	    "ROW", &colline);

  TCCSER (tide, ":Y", &coly);
  if (coly == (-1))
    TCCINI (tide, D_R8_FORMAT, 1, "F8.2", "Y Value",
	    "Y", &coly);

  for (icoef = 1; icoef <= maxcoef; icoef++)
    {
      strcpy (colnam, ":COEF_");
      sprintf (numb, "%d", icoef);
      strcat (colnam, numb);
      TCCSER (tide, colnam, &colcoef[icoef]);
      if (colcoef[icoef] == (-1))
	TCCINI (tide, D_R8_FORMAT, 1, "F16.10", "Coefficients",
		colnam, &colcoef[icoef]);
    }


    TCCSER(tide, ":PIXEL", &linpix);
       if (linpix == (-1)) TCCINI(tide, D_R8_FORMAT, 1, "F10.3", 
                                 "Angstrom/pixel", "PIXEL", &linpix);
 
    TCCSER(tide, ":RMS", &linrms);
       if (linrms == (-1)) TCCINI(tide, D_R8_FORMAT, 1, "F10.3", "Angstrom",
                                "RMS", &linrms);

}

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

#ifdef __STDC__
void finishdisp (void)
#else
void finishdisp ()
#endif

{

  int kunit;

  SCDWRD (tide, "LNPIX", &pixbin, 1, 1, &kunit);
  SCDWRI (tide, "LNDEG", &refdeg, 1, 1, &kunit);
  SCDWRI (tide, "LNCOE", &maxcoef, 1, 1, &kunit);


  TCSINI(tide);
  TCTCLO (tide);

}
/* -------------------------------------------------------------------------- */

#ifdef __STDC__
double readdisp (int y)
#else
double readdisp (y)
     int y;
#endif

/* long int  y;     Pixel number of the row */
{

  int line, yval, linext, ydif, null, icoef;
  int mindif;

  linext = 0;
  mindif = -1;

  for (line = 1; line <= nbline; line++)
    {
      TCERDI (tide, line, colline, &yval, &null);
      if (!null)
	{
	  ydif = y - yval;
	  if (ydif < 0)
	    ydif = (-1) * ydif;
	  if (mindif < 0)
	    mindif = ydif;
	  if (ydif <= mindif)
	    mindif = ydif, linext = line;
	}
    }

  fdeg = refdeg, ncoef = maxcoef;

  for (icoef = 1; icoef <= ncoef; icoef++)
    TCERDD (tide, linext, colcoef[icoef], &coef[icoef], &null);

  FIT_SUCCESS = 1;

  return (pixbin);

}

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

#ifdef __STDC__
void writedisp (int line, int ypix, double y, double pixel, double rms)
#else
void writedisp (line, ypix, y, pixel, rms)
     int line, ypix;
     double y, pixel, rms;
#endif

{

  int icoef;

  TCEWRI (tide, line, colline, &ypix);
  TCEWRD (tide, line, coly, &y);

  if (nbline < line)
    nbline = line;

  for (icoef = 1; icoef <= maxcoef; icoef++)
    TCEWRD (tide, line, colcoef[icoef], &coef[icoef]);

 TCEWRD(tide, line, linpix,  &pixel);
 TCEWRD(tide, line, linrms,  &rms);

}

/**********************************************************************/
/*                   Private section of module lndisp.c               */
/**********************************************************************/

static float sqrarg;
#define SQR(a) (sqrarg=(a),sqrarg*sqrarg) 

#include "cpl_matrix.h"

void lfit2 (double x[], double y[], double sig[], int ndata, double a[],
       int ma,
       void (*funcs) (double, double *, int))
{
  lsqfit_nr(x, y, sig, ndata, a, ma, funcs);
}

#undef SQR 

