/*****************************************************************************
******************************************************************************
**<AUTO>
**
** FILE:	calibrate.c
**
**<HTML>
**	This file contains C functions to apply photometric and astrometric
**	calibrations.
**</HTML>
**</AUTO>
**
**
** ENVIRONMENT:
**	ANSI C.
**
******************************************************************************
******************************************************************************
*/
#include <dervish.h>
#include <astrotools.h>
#include <slalib.h>
#include "taCalibObj.h"
#include "arDetectionFlag.h"
#include "taCosmic.h"

/* Error = -9999 represents an undefined value and error, error = -1000
 * represents undefined error (but value is defined), and for values that
 * lack an associated error, -9999 represents an undefined value. */
#define taUndefined(x) (fabs((x) + 9999.) < 0.01 ? 1 : 0)
#define taErrUndefined(x) (fabs((x) + 1000.) < 0.01 ? 1 : 0)


/*****************************************************************************
******************************************************************************
**<AUTO EXTRACT>
**
** ROUTINE:	taGood
**
**<HTML>
**	Determine whether an object is "GOOD" (i.e., it is considered to be
**	a good measured object, and is thus eligible as either a primary or
**	secondary detection in the survey) based on the values of its
**	"objc_flags".  The "objc_flags" are passed in as an integer.  The
**	routine returns 1 if the object is good, 0 if it is bad.
**<p>
**	An object is considered good if it is not deblended
**	and not a BRIGHT object.  The elimination of parents with children
**	and bright objects cuts out multiple versions of the same object
**	before matching/resolving the edges of scan lines.
**</HTML>
**</AUTO>
******************************************************************************
******************************************************************************
*/
int
taGood(int flags        /* IN: object's "objc_flags" */)
{
  return ! (flags & AR_DFLAG_BRIGHT) &&
    (! (flags & AR_DFLAG_BLENDED) ||
     ((flags & AR_DFLAG_BLENDED) && (flags & AR_DFLAG_NODEBLEND)));
}


/*****************************************************************************
******************************************************************************
**<AUTO EXTRACT>
**
** ROUTINE:	taTransCosmicSet
**
**<HTML>
**	Set the cosmic magnitudes and scatters used by the astrotools TRANS
**	routines, based on the passed in cosmic colors and scatters.
**	This is unfortunately convoluted, as the TRANS functions want cosmic
**	magnitudes, but we think in terms of cosmic colors.  This routine
**	makes the necessary transformations.
**<P>
**	NOTE: this code works only if the scatter decreases along the
**	cosmicScatter array (that is, cosmicScatter[i] > cosmicScatter[i+1]).
**</HTML>
**</AUTO>
******************************************************************************
******************************************************************************
*/
void
taTransCosmicSet(const double cosmicColor[4],   /* IN: cosmic colors, in order
						 *     u-g, g-r, r-i, i-z */
		 const double cosmicScatter[4]  /* IN: cosmic scatter, in order
						 *     u-g, g-r, r-i, i-z */)
{
  float cosmicMag[5], cosmicMagScatter[5];
  int i;

  /* Start by setting z = 0 and zErr = 0, and work backwards.
   * NOTE: this code works only if the scatter decreases along the
   * cosmicScatter array (that is, cosmicScatter[i] > cosmicScatter[i+1]). */
  cosmicMag[4] = 0.;
  cosmicMagScatter[4] = 0.;
  for (i = 3; i >= 0; i--)
    {
      cosmicMag[i] = cosmicColor[i] + cosmicMag[i+1];  /* e.g., i=(i-z)+z */
      cosmicMagScatter[i] = sqrt(cosmicScatter[i]*cosmicScatter[i] -
				 cosmicMagScatter[i+1]*cosmicMagScatter[i+1]);
    }
  atCosmicMagSet(cosmicMag);
  atCosmicMagScatterSet(cosmicMagScatter);
  return;
}


/*****************************************************************************
******************************************************************************
**
** taLCaliApply --- Handle bad magnitude measurements.
**
******************************************************************************
******************************************************************************
*/
int taLCaliApply 
(
 int filterIndex,		/* IN: filterIndex 0=u, 1=g, 2=r, 3=i, 4=z */
 int maggies,			/* IN: 0=asinhmag, 1=maggie flux, 2= Jy flux */
 double counts,			/* IN: counts in the reference filter */
 double countsErr,		/* IN: error in counts */
 double cCounts,		/* IN: count in the adjacent filter */
 double cCountsErr,		/* IN: error in cCounts */
 double intTime,		/* IN: exposure time in the reference filter */
 double cIntTime,		/* IN: exposure time in the adjacent filter */
 double airmass,		/* IN: airmass  (sec(Z)) */
 double zeropt,			/* IN: zeropoint (a') */
 double extinct,		/* IN: extinction prime (k') */
 double colorTerm,		/* IN: color term (b') */
 double secColorTerm,		/* IN: second order color term (c') */
 int sign,			/* IN: usually +1 for u g r i; -1 for z  (no longer used) */
 double cosmicColor,		/* IN: cosmic reference-adjacent color */
 double cosmicError,		/* IN: scatter in cosmicColor */
 double *calMag,		/* OUT: calibrated luptitude */
 double *calMagErr,		/* OUT: error in calMag */
 int *status			/* OUT: status */
 )
{
  int returnValue = 0;
  double zpAirmass = -1000.0;
  double zpColor = -1000.0;
  double faintbprime = 0.0;
	

  /* If counts undefined, return -9999 indicating undefined results. */
  if (taUndefined(countsErr))
    {
      *calMag = -9999.;
      *calMagErr = -9999.;
      return returnValue;
    }

  /* If adjacent filter counts and/or error are undefined, set adjacent
   * counts negative which will force atLCaliApply to use the cosmic color. */
  if (taUndefined(cCountsErr) || taErrUndefined(cCountsErr)) cCounts = -1000.;

  /* Calibrate */
  /*returnValue = atLCaliApply(counts, countsErr, cCounts, cCountsErr, intTime,
			     cIntTime, airmass, zeropt, extinct, colorTerm,
			     secColorTerm, sign, cosmicColor, cosmicError,
			     calMag, calMagErr, status);
	*/
  returnValue = atnLCaliApply(counts, countsErr, cCounts, cCountsErr, intTime,
			     cIntTime, airmass, zeropt, extinct, colorTerm,
			     secColorTerm, cosmicColor, cosmicError, zpAirmass, zpColor,
				filterIndex, faintbprime, maggies,
			     calMag, calMagErr, status);

  /* If counts error undefined, return -1000 indicating the error only is
   * undefined. */
  if (taErrUndefined(countsErr)) *calMagErr = -1000.;
  return returnValue;
}


/*****************************************************************************
******************************************************************************
**<AUTO EXTRACT>
**
** ROUTINE:	taObjCalibrate
**
**<HTML>
**	Calibrate a single TA_CALIB_OBJ.  The calibration is done
**	in place.  Thus, the TA_CALIB_OBJ should contain instrumental
**	quantities on input, and will contain calibrated quantities on output.
**	The argument "trans" is a pointer to a 5-element array of pointers to
**	TRANS structures, in the order u, g, r, i, z.  These are the current
**	'best' astrometric transformations.  The argument "transPhoto" is
**	the TRANS structures used when photo was run, corrected using
**	photo's "rowOffset" and "colOffset", and are required to
**	reproduce the position offsets in each filter.
**	For DCR corrections in astrometric transformations, PSF magnitudes are
**	used for stars and model magnitudes for galaxies.
**	<p>
**	If "raw" is set to 1, then lengths, magnitudes, surface brightnesses
**	and angles are not calibrated.  Celestial coordinates and extinction
**	values are still calculated, however.
**</HTML>
**</AUTO>
******************************************************************************
******************************************************************************
*/
RET_CODE
taObjCalibrate(TA_CALIB_OBJ *obj,      /* MOD: Object to calibrate */
	       const TA_PTRANS *ptrans,/* IN:  Photometric calibration */
	       const TRANS *trans[5],  /* IN:  Current 'best' astrometric
					*      calibration (ugriz) */
	       const TRANS *transPhoto[5], /* IN:  astrometric calibration
					    *      used by photo, corrected
					    *      for photo's determined
					    *      rowOffset and colOffset
					    *      (ugriz)*/
	       double node,	       /* IN:  GC ascending node (degrees) */
	       double incl	       /* IN:  GC inclination (degrees) */,
	       char refFilter,	       /* IN:  Ref. filter (primary position)*/
	       double expTime	       /* IN:  Exposure time (seconds) */,
	       int raw                 /* IN:  1=don't calibrate lengths,
					*      magnitudes, surface brightesses,
					*      and angles; 0=do */)
{
  TA_DETECTION *det = NULL, *adjDet = NULL;
  int m, p;
  double scale, scale2, mag, magErr, mu, nu, ra, dec, cosDec, dust, ra2, dec2;
  double pa, refRa, refDec;
  double psf[TA_NFILTERS], psfErr[TA_NFILTERS];
  double model[TA_NFILTERS], modelErr[TA_NFILTERS];
  float transMag[TA_NFILTERS], transMagErr[TA_NFILTERS];
  /* Filter order in array fields */
  const char *filters = "ugriz";
  /*adjacent filter to use in colorterms*/
  const int adj[TA_NFILTERS]  = {1, 2, 3, 4, 3};
  /* sign to use for color terms */
  const int sign[TA_NFILTERS] = {1, 1, 1, 1, -1}; 
  int status, refIndex = -1, nProf, c;
  double modelcolor,altProfCounts,altProfCountsErr;
  double savePsfCounts=0,savePsfCountsErr=0;
  double saveFiberCounts=0,saveFiberCountsErr=0;
  double saveCountsModel=0,saveCountsModelErr=0;
  double savePetroCounts=0,savePetroCountsErr=0;
  double saveDevCounts=0,saveDevCountsErr=0;
  double saveExpCounts=0,saveExpCountsErr=0;
  double saveSky=0,saveSkyErr=0;
  float movingScale;
 	 
  /* Error = -9999 represents an undefined value and error, error = -1000
   * represents undefined error (but value is defined), and for values that
   * lack an associated error, -9999 represents an undefined value.  Don't
   * calibrate undefined values. */

  /* For each filter ... */
  for (m = 0; m < TA_NFILTERS; m++)
    {
      /* Get pointers to the detection, as well as the detection in the
       * adjacent filter used to create the color used in the photometric
       * calibrations and the cosmic color index. */
      det = &(obj->detection[m]);
      adjDet = &(obj->detection[adj[m]]);
      c = cosmicIdx[m];

      if (raw == 0)
	{
	  /* Scale calibrations (pixels -> arcsecs) */
	  scale = at_deg2Asec * sqrt((double)(trans[m]->b * trans[m]->f -
					      trans[m]->e * trans[m]->c));
	  if (! taUndefined(det->petroRad.err))
	    {
	      det->petroRad.val *= scale;
	      if (! taErrUndefined(det->petroRad.err))
		det->petroRad.err *= scale;
	    }
	  if (! taUndefined(det->petroR50.err))
	    {
	      det->petroR50.val *= scale;
	      if (! taErrUndefined(det->petroR50.err))
		det->petroR50.err *= scale;
	    }
	  if (! taUndefined(det->petroR90.err))
	    {
	      det->petroR90.val *= scale;
	      if (! taErrUndefined(det->petroR90.err))
		det->petroR90.err *= scale;
	    }
	  if (! taUndefined(det->r_deV.err))
	    {
	      det->r_deV.val *= scale;
	      if (! taErrUndefined(det->r_deV.err)) det->r_deV.err *= scale;
	    }
	  if (! taUndefined(det->r_exp.err))
	    {
	      det->r_exp.val *= scale;
	      if (! taErrUndefined(det->r_exp.err)) det->r_exp.err *= scale;
	    }

	  /* Isophotal values */
	  if (! taUndefined(det->iso_rowc.err))
	    det->iso_rowc.val -= det->iso_rowc.grad * ptrans->dZero[m];
	  if (! taUndefined(det->iso_colc.err))
	    det->iso_colc.val -= det->iso_colc.grad * ptrans->dZero[m];
	  if (! taUndefined(det->iso_a.err))
	    {
	      det->iso_a.grad *= scale;
	      det->iso_a.val = det->iso_a.val * scale
		- det->iso_a.grad * ptrans->dZero[m];
	      if (! taErrUndefined(det->iso_a.err)) det->iso_a.err *= scale;
	    }
	  if (! taUndefined(det->iso_b.err))
	    {
	      det->iso_b.grad *= scale;
	      det->iso_b.val = det->iso_b.val * scale
		- det->iso_b.grad * ptrans->dZero[m];
	      if (! taErrUndefined(det->iso_b.err)) det->iso_b.err *= scale;
	    }
	  if (! taUndefined(det->iso_phi.err))
	    det->iso_phi.val -= det->iso_phi.grad * ptrans->dZero[m];
	  
	  /* Flux calibrations (counts -> luptitudes) */

	/* This code overwrites the det-> structure, causing problems on the z, i-z iteration */

	/*printf("psfcounts %f %f\n",det->psfCounts.val,adjDet->psfCounts.val);*/
	  taLCaliApply(m,0,det->psfCounts.val, det->psfCounts.err,
		       (m==4)?savePsfCounts:adjDet->psfCounts.val, (m==4)?savePsfCountsErr:adjDet->psfCounts.err,
		       expTime, expTime, trans[m]->airmass, ptrans->a[m].val,
		       ptrans->k[m].val, ptrans->b[m].val,ptrans->c[m].val,
		       sign[m], cosmicColor[c], cosmicScatter[c],
		       &mag, &magErr, &status);

          savePsfCounts=det->psfCounts.val;
          savePsfCountsErr=det->psfCounts.err;
	  det->psfCounts.val = mag;
	  det->psfCounts.err = magErr;

	/*printf("fibercounts %f %f\n",det->fiberCounts.val,adjDet->fiberCounts.val);*/
	  taLCaliApply(m,0,det->fiberCounts.val, det->fiberCounts.err,
		       (m==4)?saveFiberCounts:adjDet->fiberCounts.val, (m==4)?saveFiberCountsErr:adjDet->fiberCounts.err,
		       expTime, expTime, trans[m]->airmass, ptrans->a[m].val,
		       ptrans->k[m].val, ptrans->b[m].val,ptrans->c[m].val,
		       sign[m], cosmicColor[c], cosmicScatter[c],
		       &mag, &magErr, &status);
          saveFiberCounts=det->fiberCounts.val;
          saveFiberCountsErr=det->fiberCounts.err;
	  det->fiberCounts.val = mag;
	  det->fiberCounts.err = magErr;
	/*printf("petrocounts %f %f\n",det->petroCounts.val,adjDet->petroCounts.val);*/
	
	  taLCaliApply(m,0,det->petroCounts.val, det->petroCounts.err,
		       (m==4)?savePetroCounts:adjDet->petroCounts.val, (m==4)?savePetroCountsErr:adjDet->petroCounts.err,
		       expTime, expTime, trans[m]->airmass, ptrans->a[m].val,
		       ptrans->k[m].val, ptrans->b[m].val,ptrans->c[m].val,
		       sign[m], cosmicColor[c], cosmicScatter[c],
		       &mag, &magErr, &status);
          savePetroCounts=det->petroCounts.val;
          savePetroCountsErr=det->petroCounts.err;
	  det->petroCounts.val = mag;
	  det->petroCounts.err = magErr;
	  taLCaliApply(m,0,det->counts_deV.val, det->counts_deV.err,
		       (m==4)?saveDevCounts:adjDet->counts_deV.val, (m==4)?saveDevCountsErr:adjDet->counts_deV.err,
		       expTime, expTime, trans[m]->airmass, ptrans->a[m].val,
		       ptrans->k[m].val, ptrans->b[m].val,ptrans->c[m].val,
		       sign[m], cosmicColor[c], cosmicScatter[c],
		       &mag, &magErr, &status);
          saveDevCounts=det->counts_deV.val;
          saveDevCountsErr=det->counts_deV.err;
	  det->counts_deV.val = mag;
	  det->counts_deV.err = magErr;

	  taLCaliApply(m,0,det->counts_exp.val, det->counts_exp.err,
		       (m==4)?saveExpCounts:adjDet->counts_exp.val, (m==4)?saveExpCountsErr:adjDet->counts_exp.err,
		       expTime, expTime, trans[m]->airmass, ptrans->a[m].val,
		       ptrans->k[m].val, ptrans->b[m].val,ptrans->c[m].val,
		       sign[m], cosmicColor[c], cosmicScatter[c],
		       &mag, &magErr, &status);
          saveExpCounts=det->counts_exp.val;
          saveExpCountsErr=det->counts_exp.err;
	  det->counts_exp.val = mag;
	  det->counts_exp.err = magErr;

	  taLCaliApply(m,0,det->counts_model.val, det->counts_model.err,
	         (m==4)?saveCountsModel:adjDet->counts_model.val, (m==4)?saveCountsModelErr:adjDet->counts_model.err,
		       expTime, expTime, trans[m]->airmass, ptrans->a[m].val,
		       ptrans->k[m].val, ptrans->b[m].val,ptrans->c[m].val,
		       sign[m], cosmicColor[c], cosmicScatter[c],
		       &mag, &magErr, &status);

        if (((m==4)?saveCountsModelErr:adjDet->counts_model.err) <= 0 ||
            det->counts_model.err <= 0 ||
            ((m==4)?saveCountsModel:adjDet->counts_model.val) /
            ((m==4)?saveCountsModelErr:adjDet->counts_model.err) < 3. ||
            det->counts_model.val / det->counts_model.err < 3.) {
                modelcolor = -9999;
        } else {
                modelcolor = det->counts_model.val/((m==4)?saveCountsModel:adjDet->counts_model.val);
        }

          saveCountsModel=det->counts_model.val;
          saveCountsModelErr=det->counts_model.err;

	  det->counts_model.val = mag;
	  det->counts_model.err = magErr;

	  /* Surface brightnesses (counts/pixel^2 -> luptitudes/arcsecs^2) */
	  scale2 = scale * scale;

	  taLCaliApply(m,0,det->sky.val / scale2, det->sky.err / scale2,
		       (m==4)?saveSky:(adjDet->sky.val / scale2), (m==4)?saveSkyErr:(adjDet->sky.err / scale2),
		       expTime, expTime, trans[m]->airmass, ptrans->a[m].val,
		       ptrans->k[m].val, ptrans->b[m].val,ptrans->c[m].val,
		       sign[m], cosmicSkyColor[c], cosmicSkyScatter[c],
		       &mag, &magErr, &status);
          saveSky=det->sky.val / scale2;
          saveSkyErr =det->sky.err / scale2 ;
	  det->sky.val = mag;
	  det->sky.err = magErr;
	  nProf = det->nProf;
	  for (p = 0; p < nProf; p++)
	    {
	      /* Use model colors for profiles */
	      if (taUndefined(modelcolor))
		{
		  altProfCounts = -9999;
		  altProfCountsErr = -9999;
		}
	      else
		{
		  altProfCounts = det->profile[p].val / scale2 /modelcolor;
		  altProfCountsErr = 0;
		}
	      taLCaliApply(m,1,det->profile[p].val / scale2,
			   det->profile[p].err / scale2,
			   /*adjDet->profile[p].val / scale2,*/
			   altProfCounts, altProfCountsErr,
			   expTime, expTime, trans[m]->airmass,
			   ptrans->a[m].val, ptrans->k[m].val,
			   ptrans->b[m].val, ptrans->c[m].val,
			   sign[m], cosmicColor[c], cosmicScatter[c], &mag,
			   &magErr, &status);
	      det->profile[p].val = mag;
	      det->profile[p].err = magErr;
	    }	  
	  
	  /* Save following magnitudes for use in astrometric calibrations */
	  psf[m] = det->psfCounts.val;
	  psfErr[m] = det->psfCounts.err;
	  model[m] = det->counts_model.val;
	  modelErr[m] = det->counts_model.err;
	}
      else
	{
	  /* Must calculate magnitudes for use in astrometric calibrations */
	  if (ptrans != NULL)
	    {
	      /* Use photometric transformation */
	  taLCaliApply(m,0,det->psfCounts.val, det->psfCounts.err,
		       (m==4)?savePsfCounts:adjDet->psfCounts.val, (m==4)?savePsfCountsErr:adjDet->psfCounts.err,
		       expTime, expTime, trans[m]->airmass, ptrans->a[m].val,
		       ptrans->k[m].val, ptrans->b[m].val,ptrans->c[m].val,
		       sign[m], cosmicColor[c], cosmicScatter[c],
		       &mag, &magErr, &status);
          savePsfCounts=det->psfCounts.val;
          savePsfCountsErr=det->psfCounts.err;
	  psf[m] = mag;
	  psfErr[m] = magErr;
	  taLCaliApply(m,0,det->counts_model.val, det->counts_model.err,
	         (m==4)?saveCountsModel:adjDet->counts_model.val, (m==4)?saveCountsModelErr:adjDet->counts_model.err,
		       expTime, expTime, trans[m]->airmass, ptrans->a[m].val,
		       ptrans->k[m].val, ptrans->b[m].val,ptrans->c[m].val,
		       sign[m], cosmicColor[c], cosmicScatter[c],
		       &mag, &magErr, &status);

          saveCountsModel=det->counts_model.val;
          saveCountsModelErr=det->counts_model.err;
	  model[m] = mag;
	  modelErr[m] = magErr;
	    }
	  else
	    {
	      /* Photometric transformation not available.  Force use of
	       * cosmic colors by setting mag errors negative. */
	      psfErr[m] = -1.;
	      modelErr[m] = -1.;
	    }
	}
    }

  /* Now positions.  Use PSF magnitudes for DCR corrections for stars, model
   * magnitudes for galaxies.. */
  for (m = 0; m < TA_NFILTERS; m++)
    {
      if (obj->objc_type == AR_OBJECT_TYPE_STAR)
	{
	  transMag[m] = psf[m];
	  transMagErr[m] = psfErr[m];
	}
      else
	{
	  transMag[m] = model[m];
	  transMagErr[m] = modelErr[m];
	}
      if (refFilter == filters[m]) refIndex = m;
    }
  if (refIndex == -1)
    {
      shErrStackPush("taObjCalibrate: illegal refFilter");
      return SH_GENERIC_ERROR;
    }
  atTransApply(trans[refIndex], refFilter, obj->objc_rowc.val, 0.,
	       obj->objc_colc.val, 0., transMag, transMagErr, &mu, NULL,
	       &nu, NULL);
  atGCToEq(mu, nu, &(obj->ra), &(obj->dec), node, incl);
  atEqToSurvey(obj->ra, obj->dec, &(obj->lambda), &(obj->eta));
  atEqToGal(obj->ra, obj->dec, &(obj->l), &(obj->b));

  /* Photo's position in reference filter */
  atTransApply(transPhoto[refIndex], refFilter,
	       obj->detection[refIndex].rowc.val, 0.,
	       obj->detection[refIndex].colc.val, 0., transMag, transMagErr,
	       &mu, NULL, &nu, NULL);
  atGCToEq(mu, nu, &refRa, &refDec, node, incl);
  cosDec = cos(refDec * at_deg2Rad);

  for (m = 0; m < TA_NFILTERS; m++)
    {
      /* Photo measured position offsets in each filter */
      if (m == refIndex)
	{
	  obj->offsetRa[m] = 0.;
	  obj->offsetDec[m] = 0.;
	}
      else
	{
	  atTransApply(transPhoto[m], filters[m], obj->detection[m].rowc.val,
		       0., obj->detection[m].colc.val, 0., transMag,
		       transMagErr, &mu, NULL, &nu, NULL);
	  atGCToEq(mu, nu, &ra, &dec, node, incl);
	  obj->offsetRa[m] = (ra - refRa) * cosDec * at_deg2Asec;
	  obj->offsetDec[m] = (dec - refDec) * at_deg2Asec;
	}

      /* Position angles. */
      if (raw == 0)
	{
	  atTransApply(trans[m], filters[m], obj->detection[m].rowc.val, 0.,
		       obj->detection[m].colc.val, 0., transMag, transMagErr,
		       &mu, NULL, &nu, NULL);
	  atGCToEq(mu, nu, &ra, &dec, node, incl);
	  atTransApply(trans[m], filters[m],
		       obj->detection[m].rowc.val + 100., 0.,
		       obj->detection[m].colc.val, 0., transMag, transMagErr,
		       &mu, NULL, &nu, NULL);
	  atGCToEq(mu, nu, &ra2, &dec2, node, incl);
	  pa = 90. - slaDbear(ra * at_deg2Rad, dec * at_deg2Rad,
			      ra2 * at_deg2Rad, dec2 * at_deg2Rad) *at_rad2Deg;
	  if (! taUndefined(obj->detection[m].iso_phi.err))
	    obj->detection[m].iso_phi.val =
	      slaDranrm((obj->detection[m].iso_phi.val+pa)*at_deg2Rad) *
	    at_rad2Deg;
	  if (! taUndefined(obj->detection[m].phi_deV.err))
	    obj->detection[m].phi_deV.val =
	      slaDranrm((obj->detection[m].phi_deV.val+pa)*at_deg2Rad) *
	    at_rad2Deg;
	  if (! taUndefined(obj->detection[m].phi_exp.err))
	    obj->detection[m].phi_exp.val =
	      slaDranrm((obj->detection[m].phi_exp.val+pa)*at_deg2Rad) *
	    at_rad2Deg;
	}
    }

  /* Get the extinction */
  if (atExtinction(obj->l, obj->b, &(obj->reddening[0]), &(obj->reddening[1]),
                   &(obj->reddening[2]), &(obj->reddening[3]),
		   &(obj->reddening[4])) != SH_SUCCESS)
    return SH_GENERIC_ERROR;

  /* Convert the velocities to physical units of deg/day from pixels/frame */
 	/* This movingScale constant is what it takes to convert normal
	   photo frames pixels/frame values to deg/day values */	

	movingScale = 15.041060*24.0/1361.0;
	obj->rowv.val *=  movingScale;
	obj->rowv.err *=  movingScale;
	obj->colv.val *=  movingScale;
	obj->colv.err *=  movingScale;

  /* Succesful return */
  return SH_SUCCESS;
}
