	PRO LIST_DSN, START, EEND, DSN, N_FOUND, ERRMSG=ERRMSG, NOMODES=NOMODES
;+
; Project     :	SOHO - CDS
;
; Name        :	LIST_DSN
;
; Purpose     :	List the SoHO DSN contacts for a given period
;
; Explanation :	Extracts all the SoHO Deep Space Network (DSN) contact periods
;		in the input time range.
;
; Use         :	LIST_DSN, START, END, DSN, N_FOUND
;
; Inputs      :	START, END = The range of date/time values to use in searching
;			     the database.  Can be in any standard CDS time
;			     format.
;
; Opt. Inputs :	None.
;
; Outputs     :	DSN	= A structure variable containing the following tags
;			  for each planned contact period:
;
;			START_TIME = Date/time of beginning of the DSN contact
;				     period, in TAI format
;
;			END_TIME   = Date/time of end of DSN contact period, in
;				     TAI format
;
;			MODE	   = Telemetry mode.  This is a number with the
;				     following possible values:
;
;						Value	Mnemonic
;
;						  1	   LR
;						  2	   MR
;						  3	   HR
;						  4	   IDLE
;
;				     For most SOHO instruments except MDI, the
;				     primary distinction is between low rate
;				     (MODE EQ 1) and medium rate and above
;				     (MODE GE 2).
;
;			SUBMODE    = Telemetry submode.  This is a number with
;				     the following possible values:
;
;						Value	Meaning
;
;						  1	Standard
;						  2	EIT prime
;						  3	SUMER prime
;						  4	CDS prime
;
;		N_FOUND	= Number of planned contact periods found.
;
; Opt. Outputs:	None.
;
; Keywords    :	
;	NOMODES	  = If set, then the DSN contact periods are not broken out by
;		    modes and submodes.  The MODE and SUBMODE tags in the
;		    returned structure will be zero.
;
;       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 = ''
;                       LIST_DSN, ERRMSG=ERRMSG, ... 
;                       IF ERRMSG NE '' THEN ...
;
;
; Calls       :	DATATYPE, DBOPEN, DBFIND, DBEXT, DBCLOSE, TRIM, GET_INSTRUMENT
;
; Common      :	None.
;
; Restrictions:	None.
;
; Side effects:	If the number of planned contact periods found is zero, then
;		the output parameter DSN is not modified.
;
; Category    :	Planning, Database.
;
; Prev. Hist. :	None.
;
; Written     :	William Thompson, GSFC, 31 March 1995
;
; Modified    :	Version 1, William Thompson, GSFC, 3 April 1995
;		Version 2, William Thompson, GSFC, 7 April 1995
;			Fixed bug with changing modes.
;		Version 3, William Thompson, GSFC, 11 April 1995
;			Added keyword NOMODES.
;		Version 4, William Thompson, GSFC, 24 January 1996
;			Modified so that default telemetry mode is 2 and
;			default telemetry submode is 1, in case those databases
;			are empty.
;
; Version     :	Version 4, 24 January 1996
;-
;
	ON_ERROR, 2
;
;  Check the number of parameters.
;
        IF N_PARAMS() LT 4 THEN BEGIN
           MESSAGE = 'Syntax:  LIST_DSN, START, END, DSN, N_FOUND'
	   GOTO, HANDLE_ERROR
        ENDIF
;
;  Check the input parameters.
;
	N_FOUND = 0
	IF N_ELEMENTS(START) NE 1 THEN BEGIN
           MESSAGE = 'START must be a scalar'
           GOTO, HANDLE_ERROR
	END ELSE IF N_ELEMENTS(EEND) NE 1 THEN BEGIN
           MESSAGE = 'END must be a scalar'
           GOTO, HANDLE_ERROR
	ENDIF
;
;  Convert the input dates to TAI format.
;
	IF DATATYPE(START,1) EQ 'Double' THEN TAI_START = START ELSE	$
		TAI_START = UTC2TAI(START)
	IF DATATYPE(EEND,1) EQ 'Double' THEN TAI_END = EEND ELSE	$
		TAI_END = UTC2TAI(EEND)
;
;  Open the resource database.
;
	DBOPEN, 'resource,resource_type'
;
;  Find all the entries in the requested time range.
;
	ENTRIES = DBFIND('END_TIME>' + TRIM(TAI_START,'(F15.3)') +	$
		',START_TIME<' + TRIM(TAI_END,'(F15.3)') +	$
		',RES_TYPE=DSN_CONTACT', /SILENT)
	IF !ERR EQ 0 THEN BEGIN
		N_FOUND = 0
		GOTO, FINISH
	END ELSE BEGIN
		ENTRIES = ENTRIES(UNIQ(ENTRIES))
		N_FOUND = N_ELEMENTS(ENTRIES)
	ENDELSE
;
;  Extract the requested entries, sorted by start times.
;
	ENTRIES = DBSORT(ENTRIES,'start_time')
	DBEXT, ENTRIES, 'start_time,end_time', START_TIME, END_TIME
;
;  Remove any entries that only touch the requested time range.
;
	W = WHERE((END_TIME NE TAI_START) AND (START_TIME NE TAI_END), N_FOUND)
	IF N_FOUND EQ 0 THEN GOTO, FINISH
	START_TIME = START_TIME(W)
	END_TIME = END_TIME(W)
;
;  Get the first start time and the last end time.
;
	FIRST_START = START_TIME(0)
	LAST_END = END_TIME(N_ELEMENTS(END_TIME)-1)
;
;  Unless the NOMODES keyword was set, find the telemetry mode at the start of
;  the first DSN contact time.
;
	IF KEYWORD_SET(NOMODES) THEN BEGIN
		MODE = 0
		SUBMODE = 0
	END ELSE BEGIN
		DBOPEN, 'telem_mode'
		ENTRIES = DBFIND('start_time<' + TRIM(FIRST_START,'(F15.3)'), $
			/SILENT)
		IF ENTRIES(0) LE 0 THEN MODE = 2 ELSE BEGIN
			ENTRIES = DBSORT(ENTRIES,'start_time',/REVERSE)
			DBEXT, ENTRIES(0), 'mode', MODE
		ENDELSE
;
;  Find the telemetry submode at the start of the first DSN contact time.
;
		DBOPEN, 'telem_submode'
		ENTRIES = DBFIND('start_time<' + TRIM(FIRST_START,'(F15.3)'), $
			/SILENT)
		IF ENTRIES(0) LE 0 THEN SUBMODE = 1 ELSE BEGIN
			ENTRIES = DBSORT(ENTRIES,'start_time',/REVERSE)
			DBEXT, ENTRIES(0), 'submode', SUBMODE
		ENDELSE
	ENDELSE
;
;  Define the structure that the data will be returned in.
;
	DSN = { START_TIME: 0.0D0,	$
		END_TIME: 0.0D0,	$
		MODE: MODE,		$
		SUBMODE: SUBMODE}
;
	IF N_FOUND EQ 1 THEN BEGIN
		DSN.START_TIME	= START_TIME(0)
		DSN.END_TIME	= END_TIME(0)
	END ELSE BEGIN
		DSN = REPLICATE(DSN, N_FOUND)
		DSN.START_TIME	= START_TIME
		DSN.END_TIME	= END_TIME
	ENDELSE
;
;  If the NOMODES keyword was set, we're finished now.
;
	IF KEYWORD_SET(NOMODES) THEN GOTO, FINISH
;
;  Find all the telemetry mode changes that occur during the relevant period.
;
	DBOPEN, 'telem_mode'
	ENTRIES = DBFIND('start_time>' + TRIM(FIRST_START,'(F15.3)') +	$
		',start_time<' + TRIM(LAST_END,'(F15.3)'), /SILENT)
;
	IF ENTRIES(0) GT 0 THEN BEGIN
		ENTRIES = DBSORT(ENTRIES,'start_time')
		DBEXT, ENTRIES, 'start_time,mode', MODE_START, MODE
;
;  For each telemetry mode change, change all the modes for DSN contact times
;  after that.
;
		FOR I = 0,N_ELEMENTS(MODE)-1 DO BEGIN
;
;  If the telemetry mode change occurs during a DSN contact time, then split
;  that entry into two first.
;
			W = WHERE((DSN.START_TIME LT MODE_START(I)) AND	$
				(DSN.END_TIME GT MODE_START(I)), COUNT)
			IF COUNT GT 0 THEN BEGIN
				IF COUNT GT 1 THEN BEGIN
					MESSAGE = 'There appear to be ' + $
						'overlapping DSN contact times'
					GOTO, HANDLE_ERROR
				ENDIF
				SAVE = DSN
				N_FOUND = N_FOUND + 1
				DSN = REPLICATE(DSN(0),N_FOUND)
				DSN(0) = SAVE(0:W(0))
				DSN(W(0)+1) = SAVE(W(0):*)
				DSN(W(0)).END_TIME = MODE_START(I)
				DSN(W(0)+1).START_TIME = MODE_START(I)
			ENDIF
;
;  Next change the mode for all DSN contact periods after the mode change.
;
			W = WHERE(DSN.START_TIME GE MODE_START(I), COUNT)
			IF COUNT GT 0 THEN DSN(W).MODE = MODE(I)
		ENDFOR
	ENDIF
;
;  Find all the telemetry submode changes that occur during the relevant
;  period.
;
	DBOPEN, 'telem_submode'
	ENTRIES = DBFIND('start_time>' + TRIM(FIRST_START,'(F15.3)') +	$
		',start_time<' + TRIM(LAST_END,'(F15.3)'), /SILENT)
;
	IF ENTRIES(0) GT 0 THEN BEGIN
		ENTRIES = DBSORT(ENTRIES,'start_time')
		DBEXT, ENTRIES, 'start_time,submode', SUBMODE_START, SUBMODE
;
;  For each telemetry submode change, change all the submodes for DSN contact
;  times after that.
;
		FOR I = 0,N_ELEMENTS(SUBMODE)-1 DO BEGIN
;
;  If the telemetry submode change occurs during a DSN contact time, then split
;  that entry into two first.
;
			W = WHERE((DSN.START_TIME LT SUBMODE_START(I)) AND $
				(DSN.END_TIME GT SUBMODE_START(I)), COUNT)
			IF COUNT GT 0 THEN BEGIN
				IF COUNT GT 1 THEN BEGIN
					MESSAGE = 'There appear to be ' + $
						'overlapping DSN contact times'
					GOTO, HANDLE_ERROR
				ENDIF
				SAVE = DSN
				N_FOUND = N_FOUND + 1
				DSN = REPLICATE(DSN(0),N_FOUND)
				DSN(0) = SAVE(0:W(0))
				DSN(W(0)+1) = SAVE(W(0):*)
				DSN(W(0)).END_TIME = SUBMODE_START(I)
				DSN(W(0)+1).START_TIME = SUBMODE_START(I)
			ENDIF
;
;  Next change the submode for all DSN contact periods after the submode change.
;
			W = WHERE(DSN.START_TIME GE SUBMODE_START(I), COUNT)
			IF COUNT GT 0 THEN DSN(W).SUBMODE = SUBMODE(I)
		ENDFOR
	ENDIF
;
	GOTO, FINISH
;
;  Handle any errors encountered.
;
HANDLE_ERROR:
	IF N_ELEMENTS(ERRMSG) NE 0 THEN		$
		ERRMSG = 'LIST_DSN: ' + MESSAGE ELSE MESSAGE, MESSAGE 
;
;  Close the database and return.
;
FINISH:
	DBCLOSE
;
	RETURN
	END
