/* @(#)ast_traq.c	19.1 (ES0-DMD) 02/25/03 13:53:54 */
/*===========================================================================
  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
===========================================================================*/

/*+++++++++  
.MODULE    traq.c
.AUTHOR    Francois Ochsenbein [ESO]
.LANGUAGE  C
.CATEGORY  Conversion from character to binary

.COMMENTS
	Function naming is tr_operation; declarations and corresponding
macro definitions are in the header STR.H.

\begin{TeX}
\end{TeX}

.VERSION   1.0	03-Feb-1987: Creation
.VERSION   1.1	13-Oct-1988: Cosmetic Modifications. Error tracing changed
		(use tr_error)

-------------------------*/
 
#include <osdefos.h>
#include <tra.h>
#include <atype.h>
#include <macrogen.h>

#define issign(c)		((c == '+') || (c == '-'))
#define FINISH			goto FIN
#define GobbleSpaces(s,l)	s += oscspan(s, l, _SPACE_, main_ascii)

REXTERN struct trerr_s	*trerror;

static	int n_decimals 	= 0;	/* Number of decimals	*/
static  double spare 	= 0.0e0;
static	int ndl		= 0;	/* Number of decimals	*/
static	int ndb		= 0;	/* Number of decimals	*/

/*===========================================================================*/
static char	*tr_afa(str, l, x)
/*++++++
.PURPOSE Converts a string to a double positive value.
	Input in decimal.
.RETURNS Address of next char
.REMARKS Not traced. The static n_decimals contains the number of significative
	decimals in return (-1 for unknown number)
--------*/
	char	*str;	/* IN: String to scan	*/
	int	l;	/* IN: Length of str	*/
	double  *x;	/* OUT: result		*/
{
	register	char 	*p, *pe;
	register	unsigned long	n;
	double		value, base;

  *x = 0.0e0;
  n_decimals = -1;
  p = str;
  pe = p + l;

	/* Convert digits	*/

  n_decimals = 0;
  n = 0;
  for ( ; (isdigit(*p)) && (p < pe); p++)
	n = n * 10 + (*p - '0');
  *x = n;
  if (p >= pe)		FINISH;

	/* Test for decimal point	*/

  if (*p != '.')		FINISH;

	/* Scan decimal part		*/

  n = 0;
  for ( ++p; (isdigit(*p)) && (p < pe); p++, n_decimals++)
	n = n * 10 + (*p - '0');
  if (n == 0)			FINISH;

  value = n;
  base	= 1.e1;
  for (n = n_decimals; --n> 0; )
	base	*= 1.e1;

  *x += (value/base);

  FIN:
  return(p);
}
  
/*===========================================================================*/
static int ag0(str, l, g1)
/*++++++
.PURPOSE Converts a string to a double value; input may be
	in sexagesimal.
.RETURNS The precision, from 0 (degree) to 7 (.001"), or -1 (error or no data)
.REMARKS No error logging.
--------*/
	char	*str;	/* IN: String to scan	*/
	int	l;	/* IN: Length of str	*/
	double  *g1;	/* OUT: result		*/
{
	register	char 	*p, *pe;
	register	int	nd;
	static		char 	sign;
	double		value;

  sign = '+';
  p = str, pe = p + l;

	/* Skip leading blanks	*/

  GobbleSpaces(p, pe-p);
  trerror->errno = TRERR_VOID;
  if (p >= pe)	FINISH;

	/* Test the sign	*/

  if (issign(*p))	sign = *(p++);
  GobbleSpaces(p, pe-p);
  if (p >= pe)	FINISH;
  trerror->errno = 0;

	/* Convert the degrees number	*/

  p = tr_afa(p, pe - p, &spare);
  value = spare, nd = n_decimals;
  GobbleSpaces(p, pe-p);
  if (p >= pe)		FINISH;

	/* Convert minutes	*/

  p = tr_afa(p, pe - p, &spare);
  value += spare/60.e0;
  nd = n_decimals + 2;
  GobbleSpaces(p, pe-p);
  if (p >= pe)		FINISH;

	/* Convert seconds	*/

  p = tr_afa(p, pe - p, &spare);
  value += spare/3600.e0;
  nd = n_decimals + 4;
  GobbleSpaces(p, pe-p);

  FIN:
  if (sign == '-')	value = -value;
  if ((trerror->errno == 0) && (pe != p))
  	trerror->errno = TRERR_SEXA;

  if (trerror->errno)	nd = -1;
  else			*g1 = value;

  return(nd);
}  

/*===========================================================================*/
int tr_ao0(str, l, g1)
/*++++++
.PURPOSE Converts a string to a double value; input may be
	in sexagesimal.
.RETURNS The precision, from 0 (degree) to 7 (.001"), or -1 (error)
.REMARKS If an error is encountered, message logged.
--------*/
	char	*str;	/* IN: String to scan	*/
	int	l;	/* IN: Length of str	*/
	double  *g1;	/* OUT: result		*/
{
	register	int	nd;

  nd = ag0(str, l, g1);

  if (nd < 0)		/* Report Error */
  {	trerror->str = str,
	trerror->len = l,
	trerror->offset = l;
	tr_error();
  }

  return(nd);
}  

/*===========================================================================*/
static int tr_aoq(str, l, coo, flag)
/*++++++
.PURPOSE Converts a string to (longitude, latitude)
.RETURNS The accuracy, from -1 (error, no data) to 7 (0.001")
.REMARKS Longitude in range [0,360[. Not traced.
--------*/
	char	*str;	/* IN: String to scan	*/
	int	l;	/* IN: Length of str	*/
	double	coo[2];	/* OUT: resulting coordinates		*/
	int	flag;	/* IN: Flag 1 for RA + DEC, 0 for long + lat	*/
{
	register	char 	*p, *pe;
	register	int	nd;

  p = str, pe = p + l;
  nd  = -1;
  coo[0] = 0.e0, coo[1] = 0.e0;

	/* Skip leading blanks	*/

  GobbleSpaces(p, pe-p);
  trerror->errno = TRERR_VOID;
  if (p >= pe)	FINISH;

	/* Locate ALPHA / DELTA fields	*/

  p++;				/* Skip leading sign	*/
  trerror->errno = -1;
  trerror->msg = "Sign between RA(long) / Dec(lat)";
  p += oscspan(p, pe-p,_DIGIT_|_SPACE_, main_ascii);
  if (p == pe)	FINISH;
  if (*p == '.')	
  	p++, p += oscspan(p, pe-p,_DIGIT_|_SPACE_, main_ascii);
  if (p == pe)		FINISH;
  if (!issign(*p))	FINISH;

  trerror->msg = "RA(longitude)";
  ndl = ag0(str, p-str, coo);
  if (ndl < 0)		FINISH;
	
	/* Convert hours to degrees	*/

  if (flag)
  {	coo[0] *= 15.e0;		/* Convert to degrees	*/
	if (ndl > 0)	ndl--;
  }
  
	/* Longitude in range [0,360]	*/

  while (coo[0] < 0.e0)		coo[0] += 360.e0;
  while (coo[0] >= 360.e0)	coo[0] -= 360.e0;

	/* Convert Declination / Latitude	*/

  trerror->msg = "Dec(latitude)";
  ndb = ag0(p, pe - p, coo+1);	/* Check Range */
  if (ndb >= 0)
  	if ((coo[1] < -90.e0) || (coo[1] > 90.e0) )
		trerror->errno = -1, ndb = -1;

	/* Final Precision	*/
	
  nd = MIN (ndl, ndb);

  FIN:
  return(nd);
}

/*===========================================================================*/
int tr_ao(str, l, coo)
/*++++++
.PURPOSE Converts a string to (longitude, latitude)
.RETURNS The accuracy, from -1 (error, no data) to 7 (0.001")
.REMARKS Longitude in range [0,360[.
--------*/
	char	*str;	/* IN: String to scan	*/
	int	l;	/* IN: Length of str	*/
	double	coo[2];	/* OUT: resulting coordinates		*/
{
	int	nd;
	
  nd = tr_aoq(str, l, coo, 0);
  if (nd < 0)			/* Error */
  {	trerror->str = str;
  	trerror->len = l;
  	trerror->offset = l;
  	tr_error();
  }
  return(nd);
}

/*===========================================================================*/
int tr_aq(str, l, coo)
/*++++++
.PURPOSE Converts a string to (right ascension, declination)
.RETURNS The accuracy, from -1 (error, no data) to 7 (0.001")
.REMARKS Right ascension in degrees, in range [0,360[
--------*/
	char	*str;	/* IN: String to scan	*/
	int	l;	/* IN: Length of str	*/
	double	coo[2];	/* OUT: resulting coordinates		*/
{
	int	nd;
	
  nd = tr_aoq(str, l, coo, 1);
  if (nd < 0)			/* Error */
  {	trerror->str = str;
  	trerror->len = l;
  	trerror->offset = l;
  	tr_error();
  }
  return(nd);
}
