;------------------------------------------------------------------------------
; NAME: TIMEPDS
;
; PURPOSE:
; To extract time from a PDS label or ASCII table, express it as
; either Julian Date, decimal day of year, or decimal day from some
; specific user-specified date. Then to store the date in an IDL
; variable.
;
; CALLING SEQUENCE: Result = TIMEPDS(label, month, day, [/doy, /jd])
;
; INPUTS:
; label: IDL string array containing the PDS label associated with
; the file. First use HEADPDS.PRO to retrieve the label.
;
; month: Month from which to start counting. If either /doy or /jd
; are set, month should not be specified.
;
; day: Day from which to start counting. If either /doy or /jd are
; set, day should not be specified.
;
; OUTPUTS:
; If the PDS file is a PDS IMAGE:
; If no optional keywords are present or if /doy is present:
; Result = Single precision floating point scalar
; If /jd is present:
; Result = Double precision floating point scalar for /jd
;
; If the file is a PDS ASCII table:
; If no optional keywords are present or if /doy is set:
; Result = Single precision floating point array
; If /jd is present:
; Result = Double precision floating point array for /jd
;
; OPTIONAL INPUT KEYWORDS:
; DOY: If present and non zero, then the output will be the decimal
; day of year of the observation.
;
; JD: If present and non zero, then the output will be the Julian date
; of the observation.
;
; EXAMPLE:
; Read a PDS label associated with an image to get the day of year of
; the observation:
;
; IDL> LABEL = HEADPDS('TEST.LBL')
; IDL> doy = TIMEPDS(LABEL, /DOY)
;
; Obtain an array of decimal dates counting from January 22 using values
; from a PDS ASCII index table:
;
; IDL> LABEL = HEADPDS('INDEX.LBL')
; IDL> cu_arr = TIMEPDS(LABEL, 1, 22)
;
; WARNINGS:
; The default setting, with no keywords set, is to give the date(s) as
; decimal day counting from the user-specified 'month' and 'day' values,
; whereas if /doy is set, counting begins on 0 January.
;
; When present and non-zero, the optional keyword /doy or /jd
; should take the place of the keywords, 'month' and 'day'.
;
; TIMEPDS requires at least a date to be present in the PDS label or
; table. An error will occur if there is a time but no date.
;
; PROCEDURES USED:
; Functions: PDSPAR, TASCPDS
;
; MODIFICATION HISTORY:
; Written by: M. Barker [Jul 27, 1999]
; Last modified: A. Cardesin [Apr 13, 2005]
;
; For a complete list of modifications, see changelog.txt file.
;
;------------------------------------------------------------------------------
;------------------------------------------------------------------------------
; define function that calculates julian date:
;------------------------------------------------------------------------------
function JDATE, yyyy_start, mm_start, dd_start, time
; We subtract .5 from the julian date because we want the date at 12am
; local time, not 12 noon. Then we can add the decimal time, which was
; calculated earlier. We can assume that the date will always be in the
; 20th or 21st centuries, thus we also subtract 2400000 from the julian
; date.
return, (julday(mm_start,dd_start,yyyy_start) -.5 + double(time)) - 2400000
end
;------------------------------------------------------------------------------
; define function that determines if the year is a leap year:
;------------------------------------------------------------------------------
function LPYEAR, year
; if it is a leap year, then the year is evenly divisible by 4:
rem = year MOD 4
; centurial years are not leap years except when divisible by 400:
rem2 = year MOD 100
rem3 = year MOD 400
; return 1 if it is a leap year, otherwise return 0
IF ( (NOT rem AND rem2) OR (NOT rem3) ) THEN return, 1 ELSE return, 0
end
;------------------------------------------------------------------------------
; define function that calculates day of year:
;------------------------------------------------------------------------------
function DAY_OF_YEAR, yyyy_start, mm_start, dd_start, time
CASE mm_start OF
1: final_doy = dd_start
2: final_doy = 31 + dd_start
3: final_doy = 59 + dd_start
4: final_doy = 90 + dd_start
5: final_doy = 120 + dd_start
6: final_doy = 151 + dd_start
7: final_doy = 181 + dd_start
8: final_doy = 212 + dd_start
9: final_doy = 243 + dd_start
10: final_doy = 273 + dd_start
11: final_doy = 304 + dd_start
12: final_doy = 334 + dd_start
ELSE: message,'ERROR - Invalid month.'
ENDCASE
; If it is a leap year and the month is not 1 or 2, then add 1 to the
; day of year, otherwise we do not need to account for the extra day.
IF lpyear(yyyy_start) AND (mm_start GT 2) $
THEN return, float(final_doy + time + 1) $
ELSE return, float(final_doy + time)
end
;------------------------------------------------------------------------------
; define function that calculates day from user-specified date:
;------------------------------------------------------------------------------
function CUSTOM, final_doy, yyyy_start, mm_start, dd_start, usr_month, usr_day
IF usr_month GT mm_start OR ( (usr_month EQ mm_start) $
AND (usr_day GT dd_start) ) $
THEN message,'ERROR - User-supplied date cannot be after observation date.'
; calculate doy for user-specified date
CASE usr_month OF
1: usr_doy = usr_day
2: usr_doy = 31 + usr_day
3: usr_doy = 59 + usr_day
4: usr_doy = 90 + usr_day
5: usr_doy = 120 + usr_day
6: usr_doy = 151 + usr_day
7: usr_doy = 181 + usr_day
8: usr_doy = 212 + usr_day
9: usr_doy = 243 + usr_day
10: usr_doy = 273 + usr_day
11: usr_doy = 304 + usr_day
12: usr_doy = 334 + usr_day
ELSE: message,'ERROR - Invalid month provided upon call to TIMEPDS'
ENDCASE
IF lpyear(yyyy_start) AND (usr_month GT 2) THEN usr_doy = usr_doy + 1
return, (final_doy - usr_doy)
end
;------------------------------------------------------------------------------
; define function that converts day of year to calendar date
;------------------------------------------------------------------------------
function CAL_DATE, yyyy_start, doy
; If it is a leap year and the day of year is greater than 60, then
; subtract 1 to normalize the doy.
IF lpyear(yyyy_start) AND (doy GE 60) THEN doy2 = doy - 1 ELSE doy2 = doy
CASE 1 OF
doy2 LE 31: mm_start = 1
doy2 LE 59: mm_start = 2
doy2 LE 90: mm_start = 3
doy2 LE 120: mm_start = 4
doy2 LE 151: mm_start = 5
doy2 LE 181: mm_start = 6
doy2 LE 212: mm_start = 7
doy2 LE 243: mm_start = 8
doy2 LE 273: mm_start = 9
doy2 LE 304: mm_start = 10
doy2 LE 334: mm_start = 11
doy2 LE 357: mm_start = 12
ELSE: message,'ERROR - Invalid day of year in PDS label'
ENDCASE
; Find the day of the month by calculating the difference between the
; doy and the doy of the first of each month.
CASE mm_start OF
1: dd_start = doy2
2: dd_start = doy2 - 31
3: dd_start = doy2 - 59
4: dd_start = doy2 - 90
5: dd_start = doy2 - 120
6: dd_start = doy2 - 151
7: dd_start = doy2 - 181
8: dd_start = doy2 - 212
9: dd_start = doy2 - 243
10: dd_start = doy2 - 273
11: dd_start = doy2 - 304
12: dd_start = doy2 - 334
ELSE: message,'ERROR - Invalid month in PDS label'
ENDCASE
cal_arr = fltarr(2)
cal_arr(0) = mm_start
cal_arr(1) = dd_start
return, cal_arr
end
;------------------------------------------------------------------------------
; define function that separates date into mm,dd,ccyy
;------------------------------------------------------------------------------
function DATE_SEP, date
; First, we separate the date into its components using '-' as a
; separator.
if (!VERSION.RELEASE GT 5.2) then begin
date_arr = strsplit (date, '-', /EXTRACT)
endif else begin
date_arr = str_sep(date,'-') ; obsolete in IDL v. > 5.2
endelse
; Declare the date array which we will be returning:
d_arr = fltarr(4)
; Initialize the last element of the date array, day of year, to zero:
d_arr(3) = 0
; There are three possible date formats that are supported by PDSSBN and
; the PDSREAD set of tools: 1) CCYY-MM-DD, 2) YY-DOY, and 3) CCYY-DOY.
; If there are 3 elements, the format is CCYY-MM-DD
IF n_elements(date_arr) EQ 3 THEN BEGIN
year = float(date_arr(0))
month = float(date_arr(1))
day = float(date_arr(2))
ENDIF ELSE BEGIN
; if there are 2 elements, the format is YY-DOY or CCYY-DOY
IF n_elements(date_arr) EQ 2 THEN BEGIN
; determine if format is either YY-DOY or CCYY-DOY
IF strlen(date_arr(0)) EQ 4 THEN year = float(date_arr(0)) $
ELSE year = 1900 + float(date_arr(0)) ;01,02,...?
; day of year
d_arr(3) = float(date_arr(1))
; call cal_date function to calculate month and day of month
cal_arr = cal_date(year, d_arr(3))
month = cal_arr(0)
day = cal_arr(1)
ENDIF ELSE BEGIN
message,'ERROR - Invalid date format in PDS label'
ENDELSE
ENDELSE
d_arr(0) = year
d_arr(1) = month
d_arr(2) = day
return, d_arr
end
;------------------------------------------------------------------------------
; define function that calculates decimal time of day
;------------------------------------------------------------------------------
function TIME_SEP, tm
; Separate the time into its components using ':' as a separator
if (!VERSION.RELEASE GT 5.2) then begin
time_arr = strsplit(tm, ':', /EXTRACT)
endif else begin
time_arr = str_sep(tm,':') ; obsolete in IDL v. > 5.2
endelse
; We know that at least the hour and mins will be provided.
hour = double(time_arr(0))
min = double(time_arr(1))
; Initialize seconds to zero.
sec = 0
; test to see if seconds are provided and if 'Z' is present
IF n_elements(time_arr) EQ 3 THEN BEGIN
z = strpos(time_arr(2), 'Z')
IF z NE -1 THEN BEGIN
sec = strmid(time_arr(2), 0, z)
sec = double(sec)
ENDIF ELSE sec = double(time_arr(2))
ENDIF
; Return time as a decimal fraction of day
return, float( (hour + min/60 + sec/3600)/24 )
end
;------------------------------------------------------------------------------
; define parent function, TIMEPDS:
;------------------------------------------------------------------------------
function TIMEPDS, label, usr_month, usr_day, DOY = doy, JD = jd
ON_ERROR, 1
IF n_params() NE 1 AND n_params() NE 3 THEN BEGIN
print, 'Syntax: result = TIMEPDS(label, month, day, [/DOY, /JD])'
print, 'Note: If an optional keyword is present and nonzero, it should'
print, 'take the place of month and day.'
return, -1
ENDIF
; Read object to determine type of data in file
object = pdspar(label,'OBJECT')
IF !ERR EQ -1 THEN message, 'ERROR - '+label+' missing required OBJECT keyword'
; a value may not be provided, so initialize it to zero
time = 0
month = 0
day = 0
year = 0
FOR i = 0, (n_elements(object) - 1) DO BEGIN
; test to see if filename is an image
IF (object(i) EQ 'IMAGE') THEN BEGIN
; Now we look for 'TIME' and 'DATE' keywords and if neither are provided,
; an error occurs. The following 'TIME' keywords are supported:
; START_TIME, OBSERVATION_TIME, UT_TIME, SPACECRAFT_START_TIME. The
; following 'DATE' keywords are supported: OBSERVATION_DATE, UT_DATE.
time_flag = 1
date_flag = 1
start_tm = pdspar(label, 'START_TIME')
IF !ERR EQ -1 THEN start_tm = pdspar(label, 'OBSERVATION_TIME')
IF !ERR EQ -1 THEN start_tm = pdspar(label, 'UT_TIME')
IF !ERR EQ -1 THEN start_tm = pdspar(label, 'SPACECRAFT_START_TIME')
IF !ERR EQ -1 THEN time_flag = 0
start_dt = pdspar(label, 'OBSERVATION_DATE')
IF !ERR EQ -1 THEN start_dt = pdspar(label, 'UT_DATE')
IF !ERR EQ -1 THEN date_flag = 0
IF (NOT time_flag AND NOT date_flag) THEN $
message, 'ERROR - '+label+' missing a time and date keyword'
; There are a number of possible combinations of time and/or date
; keywords that appear in a PDS label. First, any keyword containing
; the substring, 'TIME', will have a time and maybe a date, as well, but
; it cannot have only a date. Similarly, any keyword containing the
; substring, 'DATE', will have a date and maybe a time, but it
; cannot have only a time.
;
; First, we check for a 'TIME' keyword:
IF time_flag THEN BEGIN
; There are two possibilities for the format of this value:
; 1) time or 2) dateTtime. We will attempt to separate the
; value using 'T' as a separator. If the resulting array has 2
; elements, we know the format is of 2) from above, but if it
; has just 1 element, the format is of 1).
if (!VERSION.RELEASE GT 5.2) then begin
sep_arr = strsplit(start_tm(0), 'T', /EXTRACT)
endif else begin
sep_arr = str_sep(start_tm(0),'T') ; obsolete in IDL v. > 5.2
endelse
IF n_elements(sep_arr) EQ 2 THEN BEGIN
; PDS standards dictate that the time is after the date. Call
; the function, time_sep, to calculate the decimal time and
; date_sep to calculate the day.
time = time_sep(sep_arr(1))
d_arr = date_sep(sep_arr(0))
year = d_arr(0)
month = d_arr(1)
day = d_arr(2)
doy3 = d_arr(3)
ENDIF ELSE BEGIN
time = time_sep(sep_arr(0))
; There must be a date provided in the PDS label for timepds to
; work.
IF (NOT date_flag) $
THEN message,'ERROR - '+label+' missing required date'
ENDELSE
ENDIF
; Check for the date keyword:
IF date_flag THEN BEGIN
if (!VERSION.RELEASE GT 5.2) then begin
sep_arr = strsplit(start_dt(0), 'T', /EXTRACT)
endif else begin
sep_arr = str_sep(start_dt(0),'T') ; obsolete in IDL v. > 5.2
endelse
; The date is the first element no matter what...
d_arr = date_sep(sep_arr(0))
year = d_arr(0)
month = d_arr(1)
day = d_arr(2)
doy3 = d_arr(3)
; Check to see if the time is also provided:
IF n_elements(sep_arr) EQ 2 THEN time = time_sep(sep_arr(1))
ENDIF
IF keyword_set(jd) THEN BEGIN
juldate = jdate(year, month, day, time)
print, 'Julian Date: 2400000 +', juldate
return, juldate
ENDIF ELSE BEGIN
; We test to see if doy was provided, otherwise we must
; calculate it.
IF doy3 THEN final_doy = doy3 + time $
ELSE final_doy = day_of_year(year, month, day, time)
IF keyword_set(doy) THEN return, final_doy $
ELSE return, custom(final_doy, year, month, day, usr_month, usr_day)
ENDELSE
ENDIF ELSE BEGIN
; Test to see if the object is a table:
IF (object(i) EQ 'TABLE') OR (object(i) EQ 'INDEX_TABLE') THEN BEGIN
inform=pdspar(label,'INTERCHANGE_FORMAT')
IF !ERR EQ -1 $
THEN message,'ERROR - '+label+' missing required INTERCHANGE_FORMAT keyword'
IF inform(0) EQ 'BINARY' $
THEN message, 'ERROR - PDS table must be an ASCII table file.'
; We must get the file pointer "^TABLE = " or "^INDEX_TABLE = "
pointer = pdspar(label,'TABLE')
IF !ERR EQ -1 THEN filename = pdspar(label, 'INDEX_TABLE')
IF !ERR EQ -1 $
THEN message, 'ERROR - No pointers to table data found in ' + label
; Find positions of double quotes surrounding filename:
leftpos = strpos(pointer(0),'"')
IF leftpos EQ -1 $
THEN message, 'ERROR - No filename found in pointer to image data'
rightpos = strpos(pointer(0),'"',leftpos + 1)
; Extract filename from inside double quotes:
filename = strmid(pointer(0), leftpos + 1, rightpos - leftpos - 1)
; Remove leading and trailing blanks:
filename = strtrim(filename, 2)
; Read the table and label:
;;A.Cardesin 13-04-2005
;;Modified: function name corrected'tascpds_test'-->'tascpds'
data = tascpds(filename, label, /SILENT)
n_columns = pdspar(label, 'COLUMNS')
IF !ERR EQ -1 $
THEN message, 'ERROR - '+label+' missing required COLUMNS keyword' $
ELSE n_columns = fix(n_columns(0))
; We initialize these indexes to -1 because later we will test
; to see which ones are provided:
time_col = -1
date_col = -1
doy3 = 0
FOR i = 0, n_columns DO BEGIN
; In the structure returned by tascpds, search the column_names
; element for the keywords and assign the appropriate element of
; the structure to an array.
col_name = data.column_names(i)
CASE 1 OF
col_name EQ 'START_TIME' OR col_name EQ 'OBSERVATION_TIME' OR $
col_name EQ 'UT_TIME' OR col_name EQ 'SPACECRAFT_START_TIME': $
BEGIN
time_col = i
times_arr = data.(i)
dummy_arr = times_arr
END
col_name EQ 'OBSERVATION_DATE' OR col_name EQ 'UT_DATE': BEGIN
date_col = i
dates_arr = data.(i)
dummy_arr = dates_arr
END
ELSE:
ENDCASE
ENDFOR
IF (time_col EQ -1) AND (date_col EQ -1) $
THEN message, 'ERROR - Invalid or no time keyword in PDS label'
; We use the dummy_arr to declare the final arrays:
n_el = n_elements(dummy_arr)
jd_arr = dblarr(n_el)
doy_arr = fltarr(n_el)
cu_arr = fltarr(n_el)
; Now, we will go through the date and/or time array(s) one
; element at a time and perform the necessary calculations.
FOR k = 0, n_el - 1 DO BEGIN
; IF there is a 'time' and a separate 'date' keyword:
IF (time_col NE -1) AND (date_col NE -1) THEN BEGIN
d_arr = date_sep(dates_arr(k))
year = d_arr(0)
month = d_arr(1)
day = d_arr(2)
doy3 = d_arr(3)
time = time_sep(times_arr(k))
ENDIF ELSE BEGIN
; if there is just a 'TIME' keyword, it must contain
; both a date and time in the format, dateTtime.
IF (time_col NE -1) AND (date_col EQ -1) THEN BEGIN
if (!VERSION.RELEASE GT 5.2) then begin
sep_arr = strsplit(times_arr(k), 'T', /EXTRACT)
endif else begin
sep_arr = str_sep(times_arr(k),'T') ; obsolete in IDL> 5.2
endelse
d_arr = date_sep(sep_arr(0))
year = d_arr(0)
month = d_arr(1)
day = d_arr(2)
doy3 = d_arr(3)
time = time_sep(sep_arr(1))
ENDIF
; if there is just a 'DATE' keyword, it must contain
; at least a date and maybe a time in the format, dateTtime.
IF (time_col EQ -1) AND (date_col NE -1) THEN BEGIN
if (!VERSION.RELEASE GT 5.2) then begin
sep_arr = strsplit(dates_arr(k), 'T', /EXTRACT)
endif else begin
sep_arr = str_sep(dates_arr(k),'T') ; obsolete in IDL>5.2
endelse
d_arr = date_sep(sep_arr(0))
year = d_arr(0)
month = d_arr(1)
day = d_arr(2)
doy3 = d_arr(3)
IF n_elements(sep_arr) EQ 2 THEN time = time_sep(sep_arr(1))
ENDIF
ENDELSE
IF keyword_set(jd) $
THEN jd_arr(k) = jdate(year, month, day, time) $
ELSE BEGIN
IF doy3 THEN doy_arr(k) = doy3 + time $
ELSE doy_arr(k) = day_of_year(year, month, day, time)
IF not keyword_set(doy) $
THEN cu_arr(k)=custom(doy_arr(k),year, $
month,day,usr_month,usr_day)
ENDELSE
ENDFOR
IF keyword_set(jd) THEN return, jd_arr
IF keyword_set(doy) THEN return, doy_arr
return, cu_arr
ENDIF
ENDELSE
ENDFOR
message, 'Could not determine type of data in PDS file'
end