;------------------------------------------------------------------------------ ; 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