Newer
Older
'VL image', $ ; 0
'UV image', $ ; 1
'PCU accumulation matrix', $ ; 2
'VL temporal standard deviation', $ ; 3
'UV temporal standard deviation', $ ; 4
'VL cosmic-ray matrix', $ ; 5
'UV cosmic-ray matrix', $ ; 6
'PCU event list', $ ; 7
'PCU test event list', $ ; 8
'VL light-curve' $ ; 9
)
metis_obj_size = list( $
2048L, $ ; 0
1024L, $ ; 1
1024L, $ ; 2
2048L, $ ; 3
1024L, $ ; 4
2048L, $ ; 5
1024L $ ; 6
)
input = json_parse('input/contents.json', /toarray, /tostruct)
kernels = [ $
input.tls_file_name, $
input.tsc_file_name $
]
; load the spice kernels
cspice_furnsh, kernels
journal, 'SPICE kernel files correctly loaded:'
journal, ' sclk kernel = ' + input.tsc_file_name
journal, ' leap seconds kernel = ' + input.tls_file_name
fits_info, input.file_name, n_ext = n_ext, extname = extname, /silent
data = mrdfits(input.file_name, ext_no, primary_header, /silent)
; if the data is not an image, read the data binary-table extension
if fxpar(primary_header, 'NAXIS') eq 0 then begin
ext_no += 1
data_bin_table = mrdfits(input.file_name, ext_no, data_extension_header, /silent)
endif else data_bin_table = !null
; read the metadata extension
ext_no += 1
metadata_bin_table = mrdfits(input.file_name, ext_no, metadata_extension_header, /silent)
; identify the data product and its nominal size
datatype = fxpar(metadata_extension_header, 'DATATYPE')
if datatype le 6 then begin
naxis = 2
naxis1 = metis_obj_size[datatype]
naxis2 = naxis1
endif else begin
naxis = 0
endelse
; if the data product is an accumulation matrix, look for the accumulation vector extension and read it if it exists
if datatype eq 2 then begin
if fxpar(metadata_extension_header, 'M') eq 256 then begin
ext_no += 1
accumul_vector = mrdfits(input.file_name, ext_no, vector_extension_header, /silent)
journal, 'L0 FITS file correctly read:'
journal, ' datatype = ' + string(datatype, format = '(I0)')
if datatype le 6 then journal, ' size = ' + string(naxis1, format = '(I0)') + '×' + string(naxis2, format = '(I0)')
; NOTE - the radialization extension is ignored
; read the planning data
planning_data = json_parse(input.planning_file_name, /toarray, /tostruct)
; definitions for the primary header
; version of the fits file
version = string(input.l1_version + 1, format = '(I02)')
; creation and acquisition times in utc
obt_beg = fxpar(primary_header, 'OBT_BEG')
obt_end = fxpar(primary_header, 'OBT_END')
obt_avg = (obt_beg + obt_end) / 2.0D
date_beg = solo_obt2utc(decode_obt(obt_beg, /from_double))
date_end = solo_obt2utc(decode_obt(obt_end, /from_double))
date_avg = solo_obt2utc(decode_obt(obt_avg, /from_double))
date_beg_string = strmid(date_beg, 0, 19)
date_beg_string = date_beg_string.replace('-', '')
date_beg_string = date_beg_string.replace(':', '')
file_name_fields = strsplit(fxpar(primary_header, 'FILENAME'), '_', /extract)
file_name = 'solo_L1_' + file_name_fields[2] + '_' + date_beg_string + '_V' + version + '.fits'
out_file_name = 'output/' + file_name
; instrument keywords
if datatype eq 0 or datatype eq 3 or datatype eq 5 or datatype eq 9 then begin
filter = 'VL'
waveband = 'Visible light 580-640 nm'
endif else begin
filter = 'UV'
telescope = 'SOLO/Metis/' + detector
; campaign keywords
obs_id_fields = strsplit(planning_data.obs_id, '_', /extract)
soop_type = obs_id_fields[2]
obs_type = obs_id_fields[4]
; build the fits file extensions
; join the metadata extension header to the primary header removing unwanted keywords
j = where(strmid(metadata_extension_header, 0, 8) eq 'EXTNAME ')
k = where(strmid(metadata_extension_header, 0, 8) eq 'TFORM1 ')
; rebin the image if binning was applied during the acquisition
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
if datatype le 6 then begin
if fxpar(primary_header, 'COMPR') then $
bin_type = fxpar(primary_header, 'B0_BIN') else bin_type = 0
pol_id = fxpar(primary_header, 'POL_ID')
fpfilt = fxpar(primary_header, 'VLFPFILT', count = count)
if count gt 0 and pol_id eq 0 and fpfilt eq 0 and bin_type eq 0 then bin_type = 1
bin_fact = 2. ^ bin_type
if bin_fact gt 1. then begin
data = rebin(data, naxis1 / bin_fact, naxis2 / bin_fact)
data = data * bin_fact^2
if ~ isa(comment) then comment = !null
comment = [comment, 'Image was rebinned according to the commanded binning factor.']
journal, 'Data was binned during acquisition:'
journal, ' binning = ' + string(bin_fact, format = '(I1)') + '×' + string(bin_fact, format = '(I1)')
journal, ' data was correctly rebinned:'
journal, ' new size = ' + string(naxis1 / bin_fact, format = '(I0)') + '×' + string(naxis1 / bin_fact, format = '(I0)')
endif else begin
journal, 'Data was not binned during acquistion.'
endelse
endif
; correct for the effective exposure time
telapse = float(obt_end - obt_beg)
ndit = fxpar(metadata_extension_header, 'NDIT', missing = 1L)
ndit1 = fxpar(metadata_extension_header, 'NDIT1', missing = 1L)
ndit2 = fxpar(metadata_extension_header, 'NDIT2', missing = 1L)
nsumexp = ndit * ndit1 * ndit2
xposure = dit/1000. * nsumexp
datamin = min(data, /nan)
fxaddpar, primary_header, 'DATAMIN', datamin
comment = [comment, 'Image values were corrected for the total exposure time.']
if nsumexp gt 1 then journal, 'Image values were corrected for the total exposure time.'
; adjust the primary header (it is almost the same for all data product types)
fxaddpar, primary_header, 'PARENT', file_basename(input.file_name), 'Name of the parent file that got processed to the current one', before = 'APID'
fxaddpar, primary_header, 'DATE', date, 'Date and time of FITS file creation', before = 'OBT_BEG'
fxaddpar, primary_header, 'DATE-OBS', date_beg, 'Same as DATE-BEG', before = 'OBT_BEG'
fxaddpar, primary_header, 'DATE-BEG', date_beg, 'Start time of observation', before = 'OBT_BEG'
fxaddpar, primary_header, 'DATE-AVG', date_avg, 'Average time of observation', before = 'OBT_BEG'
fxaddpar, primary_header, 'DATE-END', date_end, 'End time of observation', before = 'OBT_BEG'
fxaddpar, primary_header, 'TIMESYS', 'UTC', 'System used for time keywords', before = 'OBT_BEG'
fxaddpar, primary_header, 'TIMRDER', 0.0, '[s] Estimated random error in time values', before = 'OBT_BEG'
fxaddpar, primary_header, 'TIMSYER', 0.0, '[s] Estimated systematic error in time values', before = 'OBT_BEG'
fxaddpar, primary_header, 'LEVEL', 'L1'
fxaddpar, primary_header, 'CREATOR', 'metis_l1_prep.pro'
fxaddpar, primary_header, 'OBSRVTRY', 'Solar Orbiter', 'Satellite name', before = 'INSTRUME'
fxaddpar, primary_header, 'TELESCOP', telescope, 'Telescope that took the measurement', before = 'INSTRUME'
fxaddpar, primary_header, 'DETECTOR', detector, 'Subunit/sensor', before = 'DATAMIN'
fxaddpar, primary_header, 'OBJECT', 'TBD', 'The use of the keyword OBJECT is [TBD]', before = 'DATAMIN'
fxaddpar, primary_header, 'OBS_MODE', planning_data.obs_mode, 'Observation mode', before = 'DATAMIN'
fxaddpar, primary_header, 'OBS_TYPE', obs_type, 'Encoded version of OBS_MODE', before = 'DATAMIN'
fxaddpar, primary_header, 'FILTER', filter, 'Filter used to acquire this image', before = 'DATAMIN'
fxaddpar, primary_header, 'WAVELNTH', wavelnth, '[nm] Characteristic wavelength of observation', before = 'DATAMIN'
fxaddpar, primary_header, 'WAVEMIN', wavemin, '[nm] Min. wavelength where response > 0.05 of max.', before = 'DATAMIN'
fxaddpar, primary_header, 'WAVEMAX', wavemax, '[nm] Max. wavelength where response > 0.05 of max.', before = 'DATAMIN'
fxaddpar, primary_header, 'WAVEBAND', waveband, 'Bandpass description', before = 'DATAMIN'
fxaddpar, primary_header, 'XPOSURE', xposure, '[s] Total effective exposure time', before = 'DATAMIN'
fxaddpar, primary_header, 'NSUMEXP', nsumexp, 'Number of detector readouts summed together', before = 'DATAMIN'
fxaddpar, primary_header, 'TELAPSE', telapse, '[s] Elapsed time between beginning and end of observation', before = 'DATAMIN'
fxaddpar, primary_header, 'SOOPNAME', planning_data.soop_name, 'Name of the SOOP campaign that the data belong to', before = 'DATAMIN'
fxaddpar, primary_header, 'SOOPTYPE', soop_type, 'Campaign ID(s) that the data belong to', before = 'DATAMIN'
fxaddpar, primary_header, 'OBS_ID', planning_data.obs_id, 'Unique ID of the individual observation', before = 'DATAMIN'
fxaddpar, primary_header, 'TARGET', 'TBD', 'Type of target from planning', before = 'DATAMIN'
fxaddpar, primary_header, 'BSCALE', 1, 'Ratio of physical to array value at 0 offset', before = 'DATAMIN'
fxaddpar, primary_header, 'BZERO', 0, 'Physical value for the array value 0', before = 'DATAMIN'
fxaddpar, primary_header, 'BTYPE', metis_datatype[datatype], 'Science data object type', before = 'DATAMIN'
fxaddpar, primary_header, 'BUNIT', 'DN', 'Units of physical value, after application of BSCALE and BZERO', before = 'DATAMIN'
fxaddpar, primary_header, 'IDB_VERS', input.idb_version, '', before = 'HDR_VERS'
fxaddpar, primary_header, 'INFO_URL', 'http://metis.oato.inaf.it', 'Link to more information on the instrument data', before = 'HISTORY'
fxaddpar, primary_header, 'NBIN1', bin_fact, 'Binning factor in the dimension 1', before = 'COMPRESS'
fxaddpar, primary_header, 'NBIN2', bin_fact, 'Binning factor in the dimension 2', before = 'COMPRESS'
fxaddpar, primary_header, 'NBIN', bin_fact * bin_fact, 'Product of all NBIN values above', before = 'COMPRESS'
fxaddpar, primary_header, 'PXBEG1', 1, 'First pixel that has been read out in dimension 1', before = 'COMPRESS'
fxaddpar, primary_header, 'PXBEG2', 1, 'First pixel that has been read out in dimension 2', before = 'COMPRESS'
fxaddpar, primary_header, 'PXEND1', naxis1, 'Last pixel that has been read out in dimension 1', before = 'COMPRESS'
fxaddpar, primary_header, 'PXEND2', naxis2, 'Last pixel that has been read out in dimension 2', before = 'COMPRESS'
endif
hk_table = json_parse(input.hk_file_name, /toarray, /tostruct)
; replace raw values with calibrated values in the primary header
if datatype eq 0 or datatype eq 3 or datatype eq 5 then begin
; NOTE - DACPOL parameters are not calibrated since a calibration curve does not exist in the IDB. Their calibration in physical units (e.g., voltages or angles) should be done later
; fxaddpar, primary_header, 'DAC1POL1', interpol_param(hk_table, 'NIT0E061', date_avg, empty_params = empty_params)
; fxaddpar, primary_header, 'DAC2POL1', interpol_param(hk_table, 'NIT0E062', date_avg, empty_params = empty_params)
; fxaddpar, primary_header, 'DAC1POL2', interpol_param(hk_table, 'NIT0E064', date_avg, empty_params = empty_params)
; fxaddpar, primary_header, 'DAC2POL2', interpol_param(hk_table, 'NIT0E065', date_avg, empty_params = empty_params)
; fxaddpar, primary_header, 'DAC1POL3', interpol_param(hk_table, 'NIT0E067', date_avg, empty_params = empty_params)
; fxaddpar, primary_header, 'DAC2POL3', interpol_param(hk_table, 'NIT0E068', date_avg, empty_params = empty_params)
; fxaddpar, primary_header, 'DAC1POL4', interpol_param(hk_table, 'NIT0E06A', date_avg, empty_params = empty_params)
; fxaddpar, primary_header, 'DAC2POL4', interpol_param(hk_table, 'NIT0E06B', date_avg, empty_params = empty_params)
fxaddpar, primary_header, 'TSENSOR', interpol_param(hk_table, 'NIT0E0E0', date_avg, empty_params = empty_params), '[degC] VLDA temperature'
fxaddpar, primary_header, 'PMPTEMP', interpol_param(hk_table, 'NIT0L00D', date_avg, empty_params = empty_params), '[degC] PMP temperature'
journal, 'Header keywords were calibrated using HK parameters:'
journal, ' TSENSOR = ' + string(fxpar(primary_header, 'TSENSOR'), format = '(F0)')
journal, ' PMPTEMP = ' + string(fxpar(primary_header, 'PMPTEMP'), format = '(F0)')
endif
if datatype eq 1 or datatype eq 4 or datatype eq 6 then begin
fxaddpar, primary_header, 'HVU_SCR', interpol_param(hk_table, 'NIT0E070', date_avg, empty_params = empty_params), '[raw] HVU Screen commanded voltage'
; CMDHVSCR e CMDHVMCP
fxaddpar, primary_header, 'HVU_MCP', interpol_param(hk_table, 'NIT0E071', date_avg, empty_params = empty_params), '[raw] HVU MCP commanded voltage'
fxaddpar, primary_header, 'HV_SCR_V', interpol_param(hk_table, 'NIT0E0B7', date_avg, empty_params = empty_params), '[V] HVU Screen + MCP read voltage', after = 'HVU_MCP'
fxaddpar, primary_header, 'HV_MCP_V', interpol_param(hk_table, 'NIT0E0B6', date_avg, empty_params = empty_params), '[V] HVU MCP read voltage', after = 'HV_SCR_V'
fxaddpar, primary_header, 'HV_MCP_I', interpol_param(hk_table, 'NIT0E0BF', date_avg, empty_params = empty_params), '[uA] HVU MCP current', after = 'HV_MCP_V'
fxaddpar, primary_header, 'HV_TEMP', interpol_param(hk_table, 'NIT0E0B5', date_avg, empty_params = empty_params), '[degC] HVU temperature', after = 'HV_MCP_I'
fxaddpar, primary_header, 'TSENSOR ', interpol_param(hk_table, 'NIT0E050', date_avg, empty_params = empty_params), '[degC] UVDA temperature'
journal, 'Header keywords were calibrated using HK parameters:'
journal, ' HVU_SCR = ' + string(fxpar(primary_header, 'HVU_SCR'), format = '(F0)')
journal, ' HVU_MCP = ' + string(fxpar(primary_header, 'HVU_MCP'), format = '(F0)')
journal, ' HV_SCR_V = ' + string(fxpar(primary_header, 'HV_SCR_V'), format = '(F0)')
journal, ' HV_MCP_V = ' + string(fxpar(primary_header, 'HV_MCP_V'), format = '(F0)')
journal, ' HV_MCP_I = ' + string(fxpar(primary_header, 'HV_MCP_I'), format = '(F0)')
journal, ' HV_TEMP = ' + string(fxpar(primary_header, 'HV_TEMP'), format = '(F0)')
journal, ' TSENSOR = ' + string(fxpar(primary_header, 'TSENSOR'), format = '(F0)')
key = fxpar(primary_header, 'MEASKIND', count = count)
if count gt 0 then begin
pol_id = fxpar(primary_header, 'POL_ID')
if key eq 0 and pol_id eq 0 then fxaddpar, primary_header, 'MEASKIND', 'Fixed pol.'
if key eq 0 and pol_id ne 0 then fxaddpar, primary_header, 'MEASKIND', 'pB'
if key eq 1 then fxaddpar, primary_header, 'MEASKIND', 'tB'
endif
key = fxpar(primary_header, 'FRAMEMOD', count = count)
if count gt 0 then begin
if key eq 0 then fxaddpar, primary_header, 'FRAMEMOD', 'Single'
if key eq 1 then fxaddpar, primary_header, 'FRAMEMOD', 'Multiple'
endif
key = fxpar(primary_header, 'VLFPFILT', count = count)
if count gt 0 then begin
pol_id = fxpar(primary_header, 'POL_ID')
if pol_id ne 0 then fxaddpar, primary_header, 'VLFPFILT', 'Not applicable' else begin
if key eq 0 then fxaddpar, primary_header, 'VLFPFILT', 'Binning'
if key eq 1 then fxaddpar, primary_header, 'VLFPFILT', 'Masking'
if key eq 2 then fxaddpar, primary_header, 'VLFPFILT', 'No filter'
endelse
endif
key = fxpar(primary_header, 'PMPSTAB', count = count)
if count gt 0 then begin
if key eq 0 then fxaddpar, primary_header, 'PMPSTAB', 'Not stable'
if key eq 1 then fxaddpar, primary_header, 'PMPSTAB', 'Stable'
endif
key = fxpar(primary_header, 'REF_ROWS', count = count)
if count gt 0 then begin
if key eq 0 then fxaddpar, primary_header, 'REF_ROWS', 'Not included'
if key eq 1 then fxaddpar, primary_header, 'REF_ROWS', 'Included'
endif
keys = ['PRESUM', 'CME_OBS', 'SUNDISK', 'CR_SEP', 'SP_NOISE', 'COMPR', 'MASKING', 'RADIAL']
foreach key_name, keys do begin
key = fxpar(primary_header, key_name, count = count)
if count gt 0 then begin
if key eq 0 then fxaddpar, primary_header, key_name, 'Disabled'
if key eq 1 then fxaddpar, primary_header, key_name, 'Enabled'
endif
endforeach
; remove unused keywords
sxdelpar, primary_header, 'WIDTH'
sxdelpar, primary_header, 'HEIGHT'
sxdelpar, primary_header, 'X_SIZE'
sxdelpar, primary_header, 'Y_SIZE'
sxdelpar, primary_header, 'Z_SIZE'
sxdelpar, primary_header, 'P_BANDS'
sxdelpar, primary_header, 'N_BANDS'
sxdelpar, primary_header, 'ORIG_X'
sxdelpar, primary_header, 'ORIG_Y'
fxaddpar, primary_header, 'HISTORY', 'L1 FITS file created on ' + date
; modify keywords for comments
old_comment = fxpar(primary_header, 'COMMENT', count = count)
if count eq 0 then old_comment = ''
sxdelpar, primary_header, 'COMMENT'
if max(old_comment.startswith('Warning: correction of the OBT for pB acquisitions was applied.')) then begin
if ~ isa(comment) then comment = !null
comment = ['Correction of the OBT for pB acquisitions was applied at L0 level.', comment]
endif
if max(old_comment.startswith('Warning: the OBT of the acquisition is missing.')) then begin
if ~ isa(comment) then comment = !null
comment = ['Warning: the OBT of the acquisition is missing.',' The generation time of the first science packet was used', ' to compute OBT_BEG. Polarimetric keywords DAC*POL*',' may be wrong.', comment]
endif
if isa(comment) then begin
for k = 0, n_elements(comment) - 1 do $
fxaddpar, primary_header, 'COMMENT', comment[k]
endif
mwrfits, data, out_file_name, primary_header, /no_comment, /create, /silent
journal, 'Fits file created:'
journal, ' file name = ' + out_file_name
; if applicable, save the data binary-table extension as it is
if isa(data_bin_table) then mwrfits, data_bin_table, 'output/' + file_name, data_extension_header, /no_comment, /silent
; build the telemetry extension
hk_extension_header = !null
fxaddpar, hk_extension_header, 'XTENSION', 'BINTABLE', 'Extension type'
fxaddpar, hk_extension_header, 'BITPIX', 8, 'Number of bits per data pixel'
fxaddpar, hk_extension_header, 'NAXIS', 2, 'Number of data axes'
fxaddpar, hk_extension_header, 'NAXIS1', 0, 'Number of pixels along axis 1'
fxaddpar, hk_extension_header, 'NAXIS2', 0, 'Number of pixels along axis 2'
fxaddpar, hk_extension_header, 'PCOUNT', 0, 'Parameter count'
fxaddpar, hk_extension_header, 'GCOUNT', 1, 'Group count'
fxaddpar, hk_extension_header, 'EXTNAME', 'House-keeping', 'Extension name'
mwrfits, hk_bin_table, out_file_name, hk_extension_header, /no_comment, /silent
journal, ' HK binary-table extension correctly added'
; unload the spice kernels
cspice_unload, kernels
journal, 'SPICE kernel files unloaded.'
; write the auxiliary information file
output = { $
file_name: out_file_name, $
log_file_name: 'output/metis_l1_prep_log.txt', $
empty_params : empty_params[uniq(empty_params)] $
}
journal, 'Exiting without errors.'
journal
exit, status = 0