;+
; PROCEDURE:
; makecdf2, data, sktfile=sktfile, cdffile=cdffile, $
; gattributes=gattr, vattributes=vattr, overwrite=overwrite, $
; status=status, verbose=verbose
;
; PURPOSE:
; Creates a CDF file from a structure of arrays
;
; INPUT:
; data:
; (this sounds complicated to describe, but see the EXAMPLE below)
; The structure containing the data to write out to CDF.
; The 'data' structure will contain exactly one field for each variable that
; is to be written to the output CDF (with the exception that additional
; variables 'Epoch' and 'Time_PB5' will be written to the CDF file, if they
; have been specified in the skeleton file input via the 'sktfile' keyword
; parameter).
; Each field of the 'data' structure is itself a structure containing exactly
; 4 fields, named 'name', 'value', 'recvary', and 'fill'.
; The 'name' field of the n-th field of 'data' should be the name of the n-th
; CDF variable in the output CDF file. The 'value' field of the n-th field of
; 'data' should be an array containing the values of the n-th variable (the
; i-th element of the array is the value at the i-th time). The 'recvary'
; field of the n-th field of 'data' should be 1 if the n-th variable is time
; varying and 'recvary' should be 0 if the n-th variable is time invariant.
; Time invariant variables are generally things like various kinds of array
; descriptors that don't depend on the time. The 'fill' field of the n-th field
; of 'data' should be 1 if the variable should have its values overwritten with
; ISTP standard FILLVAL's for all times for which data are missing or invalid,
; as specified by the values of the 'quality_flag' variable, otherwise, 'fill'
; should be zero.
;
; NOTE: the first field of the 'data' structure must contain the time values
; in seconds since 01-01-1970/00:00:00 UT.
;
; NOTE: all of the time variant variable arrays in the 'data' structure must
; be based on the exact same time array (that set of times given in the first
; field of the 'data' structure). If you have a set of arrays to write out
; to CDF which are not all based on the same time array, you must first do
; the appropriate interpolations to generate a set of arrays that are all
; based on the same time array. See the routine 'time_align.pro' for one
; simple way to do this with tplot variables.
;
; KEYWORDS:
; sktfile:
; name of the skeleton file that is to be used to specify the global attributes
; and their values, variable attributes and their values, and variable types and
; sizes. The value used for this parameter should not include any '.skt' suffix.
; cdffile:
; Name of CDF file to be created. Do not include any '.cdf' suffix.
; gattributes:
; FIX
; vattributes:
; FIX
; overwrite:
; if set, overwrite any existing CDF file with the specified name (default
; is to not overwrite any such existing file).
; status:
; status is 0 on successful return, nonzero on unsuccessful return.
; A routine that calls makecdf2 should in general use the status keyword parameter
; and verify that the CDF write has completed successfully.
; verbose:
; if set, display diagnostic messages. Useful for debugging.
;
; EXAMPLE:
; Consider making a CDF file of the FAST EESA summary data, as is done by the IDL
; routine 'fast_e_summary.pro'. Assume that an appropriate skeleton file named
; 'fa_k0_ees_template.skt' has been created, containing the appropriate variable
; definitions and the appropriate global and variable scope attributes and their
; values. Assume that all the standard data necessary has been stored with
; 'store_data' in IDL.
;
; Then to make the CDF file named 'fa_k0_ees.cdf'containing the variables 'unix_time',
; 'el_0', 'el_90', 'el_180', 'el_en', 'el_low', 'el_low_pa', 'el_high',
; 'el_high_pa', 'JEe', and 'Je', you could give the following IDL commands:
;
; > get_data, 'el_0', data=el_0
; > get_data, 'el_90', data=el_90
; > get_data, 'el_180', data=el_180
; > get_data, 'el_low', data=el_low
; > get_data, 'el_high', data=el_high
; > get_data, 'JEe', data=JEe
; > get_data, 'Je', data=Je
; >
; > data = {unix_time: {name:'unix_time', value:el_0.x, recvary:1, fill:0}, $
; > el_0: {name:'el_0', value:el_0.y, recvary:1, fill:1}, $
; > el_90: {name:'el_90', value:el_90.y, recvary:1, fill:1}, $
; > el_180: {name:'el_180', value:el_180.y, recvary:1, fill:1}, $
; > el_en: {name:'el_en', value:el_0.v, recvary:1, fill:1}, $
; > el_low: {name:'el_low', value:el_low.y, recvary:1, fill:1}, $
; > el_low_pa: {name:'el_low_pa', value:el_low.v, recvary:1, fill:1}, $
; > el_high: {name:'el_high', value:el_high.y, recvary:1, fill:1}, $
; > el_high_pa: {name:'el_high_pa', value:el_high.v, recvary:1, fill:1}, $
; > JEe: {name:'JEe', value:JEe.y, recvary:1, fill:1}, $
; > Je: {name:'Je', value:Je.y, recvary:1, fill:1}}
; >
; > makecdf2, data, sktfile='fa_k0_ees_template', $
; cdffile='fa_k0_ees', status=status, /overwrite
; > if status ne 0 then begin
; > message, /info, 'makecdf2 failed.'
; > return
; > endif
;
; Note that in the above, the name of the field containing time was named 'unix_time',
; and not 'time'. In general, CDF variables can be named anything you want, but there
; are a few special exceptions.
; IF A CDF CONTAINS AN 'EPOCH' VARIABLE, THE FOLLOWING VARIABLE NAMES SHOULD NOT BE
; USED: TIME, YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MSEC, IYEAR, IMONTH, IDAY,
; IHOUR, IMINUTE, ISECOND, IMSEC. THIS IS BECAUSE MANY STANDARD CDF ANALYSIS TOOLS
; USE THESE NAMES FOR SPECIFIC PURPOSES. This is because of certain assumptions
; made by various software tools developed by CDHF.
;
; SEE ALSO:
; "time_align"
;
; VERSION: @(#)makecdf2.pro 1.2 98/08/13
;-
pro makecdf2, data, $
sktfile=sktfile, $
cdffile=cdffile, $
gattributes=gattr, $
vattributes=vattr, $
verbose=verbose, $
overwrite=overwrite, $
status=status
status = -1
if not keyword_set(cdffile) then begin
message, /info, "The keyword parameter 'cdffile' must be set"
return
endif
if keyword_set(verbose) then verbose = fix(verbose) else verbose = fix(0)
if keyword_set(overwrite) then begin
on_ioerror, create
id = cdf_open(cdffile)
cdf_delete,id
create:
on_ioerror,null
cmd_overwrite = '-delete '
endif else begin
cmd_overwrite = '-nodelete '
endelse
if keyword_set(sktfile) then begin
; attempt to create the cdf file from the skt file silently, returning the command status
cmd = '$CDF_BIN/skeletoncdf -cdf ' + cdffile + ' ' + cmd_overwrite + ' ' + sktfile
cmd = '(' + cmd + ' ); echo $status'
if verbose then begin
print, 'makecdf2: creating CDF from sktfile with cmd = ', cmd
endif
spawn, cmd, stat, count=stat_size
if stat(stat_size - 1) eq 0 then begin
id = cdf_open(cdffile)
endif else begin
for k = 0, stat_size - 2 do print, stat(k)
print, 'skeletoncdf failed to initialize CDF file using SKT file = ', sktfile
return
endelse
endif else begin
id = cdf_create(cdffile, /SINGLE)
endelse
;
; If the 'Epoch' variable has been defined in the skeleton table, write its values
;
if cdf_var_exists(id, 'Epoch') then begin
epoch0 = 719528.d * 24.* 3600. * 1000. ;Jan 1, 1970
epoch = data.(0).value * 1000. + epoch0
cdf_varput, id, 'Epoch', epoch
endif
;
; If 'Time_PB5' variable was defined via the skeleton table, write its values
;
if cdf_var_exists(id, 'Time_PB5') then begin
pb5 = time_pb5(data.(0).value)
pb5_shifted = dimen_shift(pb5, -1)
cdf_varput, id, 'Time_PB5', pb5_shifted
; leave out the writing of the min and max to SCALEMIN and SCALEMAX for now
endif
;
; If 'unix_time' variable was defined via the skeleton table, write its min/max
;
; leave out for now
;
; If 'quality_flag' variable was defined via the skeleton table, write its values
;
if verbose then print, 'makecdf2: starting processing of quality_flag'
if cdf_var_exists(id, 'quality_flag') then begin
quality_flag = make_quality(data)
cdf_varput, id, 'quality_flag', quality_flag
endif
;
; If the 'post_gap_flag' variable has been defined in the skeleton table, write its values
; post_gap_flag will be 1 for the first data point for which both (quality ne 255) and
; (quality of previous data point eq 255), else post_gap_flag = 0.
;
if verbose then print, 'makecdf2: starting processing of post_gap_flag'
if cdf_var_exists(id, 'post_gap_flag') then begin
quality_shift = shift(quality_flag, 1)
quality_shift(0) = 0
post_gap_flag = (quality_flag ne 255) and (quality_shift eq 255)
cdf_varput, id, 'post_gap_flag', post_gap_flag
endif
;
; Write out all the data, as Z Variables
;
iszvar = 1
for i = 0, n_tags(data) - 1 do begin
name = data.(i).name
value = data.(i).value
fill = data.(i).fill
value_element = reform(value(0,*,*,*,*))
sz = size(value_element)
if sz(0) eq 1 then begin
if sz(1) eq 1 then value_element = value_element(0)
endif
type = data_type(value_element)
dims = dimen(value_element)
ndims = ndimen(value_element)
recnovary = data.(i).recvary eq 0
if cdf_var_exists(id, name) then begin
; set all elements of the value to be stored to the type-correct FILLVAL
; wherever quality_flag = 255.
if fill eq 1 then begin
case type of
1: fillval = byte(-128)
2: fillval = fix(-32768)
3: fillval = long(-2147483648)
4: fillval = float(-1.0e31)
5: fillval = double(-1.0d31)
endcase
times = where(quality_flag eq 255, count)
if count gt 0 then value(times,*,*,*,*,*,*) = fillval
endif
; shift the value array so that the time index is the last index, instead of the first.
; value_shifted = dimen_shift(value, -1)
cdf_varput, id, name, value, /zvariable
endif
endfor
;
; if the gattr keyword parameter is set, set the gattrs appropriately
;
if keyword_set(gattr) then begin
n_gattrs = n_tags(gattr)
for i = 0, n_gattrs - 1 do begin
attr_name = gattr.(i).name
attr_value = gattr.(i).value
attr_replace = gattr.(i).replace
if not cdf_attr_exists(id, attr_name) then begin
attr_id = cdf_attcreate(id, attr_name, /GLOBAL_SCOPE)
endif
if cdf_attr_exists(id, attr_name, scope=scope) then begin
if scope ne 'GLOBAL_SCOPE' then begin
message, /info, 'attribute ' + attr_name + $
' in gattr keyword param is not GLOBAL_SCOPE'
return
endif
endif
cdf_attinq, id, attr_name, name, scope, max_entry, max_zentry
if attr_replace then begin
if max_entry eq -1 then begin
attr_entry = 0
endif else begin
attr_entry = max_entry
endelse
endif else begin
attr_entry = max_entry + 1
endelse
cdf_attput, id, attr_name, attr_entry, attr_value
endfor
endif
;
; if the vattr keyword parameter is set, set the vattrs appropriately
;
if keyword_set(vattr) then begin
n_vattrs = n_tags(vattr)
for i = 0, n_vattrs - 1 do begin
attr_name = vattr.(i).name
var_name = vattr.(i).entry
attr_value = vattr.(i).value
if not cdf_attr_exists(id, attr_name) then begin
attr_id = cdf_attcreate(id, attr_name, /VARIABLE_SCOPE)
endif
if cdf_attr_exists(id, attr_name, scope=scope) then begin
if scope ne 'VARIABLE_SCOPE' then begin
message, /info, 'attribute ' + attr_name + $
' in vattr keyword param is not VARIABLE_SCOPE'
return
endif
endif
cdf_attinq, id, attr_name, name, scope, max_entry, max_zentry
cdf_attput, id, attr_name, var_name, attr_value
endfor
endif
cdf_close, id
status = 0
return
end