-
Notifications
You must be signed in to change notification settings - Fork 0
/
timepds.pro
637 lines (487 loc) · 20.2 KB
/
timepds.pro
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
;+
; 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:
; July 27 1999, M. Barker
;
;------------------------------------------------------------------------------
;------------------------------------------------------------------------------
; 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