	PRO GET_CDS_PNTCAL, DATE_OBS, OPS_L, OPS_R, X, Y, ERRMSG=ERRMSG
;+
; Project     :	SOHO - CDS
;
; Name        :	GET_CDS_PNTCAL
;
; Purpose     :	Gets a pointing calibration curve from database
;
; Category    :	CDS, Calibration, Class3
;
; Explanation :	Looks up the correct calibration curve for the pointing as a
;		function of OPS position.  First, the software finds the
;		largest effective date which is on or earlier than the
;		observation date.  If there are multiple entries with the same
;		effective date, then the one with the latest entry date is
;		used.
;
;		The pointing calibration database allows for a variety of
;		mathematical models of the OPS.  Currently, the only pointing
;		calibration equations which are recognized are the following.
;
;			1.  COEFF = (L0, R0, Rho, KX, KY)
;			    DL=L-L0, DR=R-R0, DP=DL+DR, DM=DL-DR
;			    X = -KX * (DM + DM*DP / Rho)
;			    Y = -KY * (DP - (DM^2 + DP^2)/(2*Rho))
;
;			2.  COEFF = (L0, R0, Rho, KX, KY, Alpha)
;			    Same as 1, except DR = Alpha*(R-R0)
;
; Syntax      :	GET_CDS_PNTCAL, DATE_OBS, OPS_L, OPS_R, X, Y
;
; Examples    :	GET_CDS_PNTCAL, '1996/09/16', 2048, 2048, X, Y
;
; Inputs      :	DATE_OBS = The date the observation was made.
;
;		OPS_L	 = The Offset Pointing System mechanism position for
;			   the left leg.  Floating point values and arrays are
;			   allowed.
;
;		OPS_R	 = The OPS position for the right leg.
;
; Opt. Inputs :	None.
;
; Outputs     :	X, Y	 = The calculated solar pointing in arcseconds, not
;			   counting any corrections for the spacecraft
;			   pointing.
;
; Opt. Outputs:	None.
;
; Keywords    :	ERRMSG = If defined and passed, then any error messages will be
;			 returned to the user in this parameter rather than
;			 depending on the MESSAGE routine in IDL.  If no errors
;			 are encountered, then a null string is returned.  In
;			 order to use this feature, ERRMSG must be defined
;			 first, e.g.
;
;				ERRMSG = ''
;				GET_CDS_PNTCAL, ERRMSG=ERRMSG, ...
;				IF ERRMSG NE '' THEN ...
;
; Calls       :	ANYTIM2TAI, DBOPEN, DBFIND, ANYTIM2UTC, DBSORT, DBEXT, DBCLOSE
;
; Common      :	None
;
; Restrictions:	OPS_L and OPS_R must be values, not unpacked telemetry words.
;		See OPS_DECODE for more information.
;
; Side effects:	None.
;
; Prev. Hist. :	None.
;
; History     :	Version 1, 16-Sep-1996, William Thompson, GSFC
;               Version 2, 8-Oct-2001, Zarro (EITI/GSFC) - propagated
;                error check for missing db item
; Contact     :	WTHOMPSON
;-
;
	ON_ERROR, 2
;
;  Check the input parameters.
;
	IF N_PARAMS() NE 5 THEN BEGIN
	    MESSAGE = 'Syntax:  GET_CDS_PNTCAL, DATE_OBS, OPS_L, OPS_R, X, Y'
	    GOTO, HANDLE_ERROR
	ENDIF
;
	IF (N_ELEMENTS(OPS_L) EQ 0) OR (N_ELEMENTS(OPS_R) EQ 0) THEN BEGIN
	    MESSAGE = 'OPS_L,OPS_R are not defined'
	    GOTO, HANDLE_ERROR
	END ELSE IF N_ELEMENTS(OPS_L) NE N_ELEMENTS(OPS_R) THEN BEGIN
	    MESSAGE = 'OPS_L and OPS_R must have the same number of elements'
	    GOTO, HANDLE_ERROR
	ENDIF
;
;  Convert the observation date to TAI format.  Reformat the time to
;  millisecond accuracy.  This is necessary so that the times are written out
;  in a controlled way.
;
	TAI_OBS = DOUBLE( STRING( ANYTIM2TAI(DATE_OBS), FORMAT='(F15.3)' ))
;
;  Find all entries in the database matching the search criteria.  Ignore any
;  equations sets which are not understood.
;
	DBOPEN, 'pointing'
	MAX_EQ_NUM = 2
	ENTRIES = DBFIND('DATE_EFF<' + TRIM(TAI_OBS,'(F15.3)') +	$
		',EQ_NUM<' + TRIM(MAX_EQ_NUM), /SILENT)
;
;  If nothing found, then pick the earliest date in the database.
;
	IF ENTRIES(0) LE 0 THEN BEGIN
	    MESSAGE, /CONTINUE, 'No data found for date ' +	$
		    ANYTIM2UTC(DATE_OBS,/VMS) + ', using earliest date'
	    ENTRIES = DBFIND('EQ_NUM<' + TRIM(MAX_EQ_NUM), /SILENT)
	    IF ENTRIES(0) LE 0 THEN BEGIN
	     MESSAGE='No pointing data found with EQ_NUM LE '+ TRIM(MAX_EQ_NUM)
	     GOTO, HANDLE_ERROR
	    ENDIF
	    IF N_ELEMENTS(ENTRIES) GT 1 THEN	$
		    ENTRIES = DBSORT(ENTRIES, 'DATE_EFF')
;
;  Otherwise, pick the latest date matching the search criteria.
;
	END ELSE IF N_ELEMENTS(ENTRIES) GT 1 THEN	$
		ENTRIES = DBSORT(ENTRIES, 'DATE_EFF', REVERSE=[1])
;
;  Find all entries with the selected effective date, and sort on the entry
;  date.
;
	DBEXT, ENTRIES(0), 'date_eff', DATE_EFF
	ENTRIES = DBFIND('EQ_NUM<' + TRIM(MAX_EQ_NUM) + ',DATE_EFF=' +	$
		TRIM(DATE_EFF,'(F15.3)'), /SILENT)
	IF N_ELEMENTS(ENTRIES) GT 1 THEN	$
		ENTRIES = DBSORT(ENTRIES, 'DATE', REVERSE=[1])
;
;  Extract the data.
;
	DBEXT, ENTRIES(0), 'eq_num,coeff', EQ_NUM, COEFF
;
;  Apply the data to the input OPS positions.
;
	CASE EQ_NUM(0) OF
	    1: BEGIN
		L0  = COEFF(0)
		R0  = COEFF(1)
		RHO = COEFF(2)
		KX  = COEFF(3)
		KY  = COEFF(4)
		DL = OPS_L - L0  &  DR = OPS_R - R0
		DP = DL + DR
		DM = DL - DR
		X = -KX * (DM + DM*DP / RHO)
		Y = -KY * (DP - (DM^2 + DP^2) / (2*RHO))
		END
	    2: BEGIN
		L0  = COEFF(0)
		R0  = COEFF(1)
		RHO = COEFF(2)
		KX  = COEFF(3)
		KY  = COEFF(4)
		AL  = COEFF(5)
		DL = OPS_L - L0  &  DR = AL*(OPS_R - R0)
		DP = DL + DR
		DM = DL - DR
		X = -KX * (DM + DM*DP / RHO)
		Y = -KY * (DP - (DM^2 + DP^2) / (2*RHO))
		END
	ENDCASE
	GOTO, FINISH
;
;  Error handling point.
;
HANDLE_ERROR:
	IF N_ELEMENTS(ERRMSG) NE 0 THEN ERRMSG = 'GET_CDS_PNTCAL: ' + MESSAGE $
		ELSE MESSAGE, MESSAGE
;
FINISH:
	DBCLOSE
	RETURN
	END
