diff --git a/fix_fits_header.pro b/fix_fits_header.pro
new file mode 100644
index 0000000000000000000000000000000000000000..0e0bd371b712de4ae347d3a16c4271a8bfb2d40e
--- /dev/null
+++ b/fix_fits_header.pro
@@ -0,0 +1,13 @@
+pro fix_fits_header, filename
+    fits_info, filename, n_ext = n_ext, /silent
+    for ext = 0, n_ext do begin
+        data = readfits(filename, header, exten_no = ext, /silent)
+        for k = 0, n_elements(header) - 1 do begin
+            while header[k].matches('\/  +') do begin
+                header[k] = header[k].replace('/  ', '/ ')
+            endwhile
+        endfor
+        fits_add_checksum, header, data
+        modfits, filename, 0, header, exten_no = ext
+    endfor
+end
\ No newline at end of file
diff --git a/metis_l1_prep.pro b/metis_l1_prep.pro
index 57c881e8361aff309c46324eb00f9adef2f48a44..4df21dcfa4fe13ebc171527554613c3e51298cbb 100755
--- a/metis_l1_prep.pro
+++ b/metis_l1_prep.pro
@@ -1,588 +1,590 @@
 pro metis_l1_prep
+    ; keyword defining if the detector reference frame must be used for the output
 
-	; keyword defining if the detector reference frame must be used for the output
+    ref_detector = 1
 
-	ref_detector = 1
+    ; start the log
 
-	; start the log
+    journal, 'output/metis_l1_prep_log.txt'
 
-	journal, 'output/metis_l1_prep_log.txt'
+    ; some definitions
 
-	; some definitions
+    metis_datatype = list( $
+        '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_datatype = list( $
-		'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
-	)
+    ; read the auxiliary input file
 
-	; read the auxiliary input file
+    input = json_parse('input/contents.json', /toarray, /tostruct)
 
-	input = json_parse('input/contents.json', /toarray, /tostruct)
+    journal, 'File ' + input.file_name
 
-	journal, 'File ' + input.file_name
+    ; load the spice kernels
 
-	; load the spice kernels
+    load_spice_kernels, input.spice_kernels, kernel_list = kernel_list, kernel_version = kernel_version
 
-	load_spice_kernels, input.spice_kernels, kernel_list = kernel_list, kernel_version = kernel_version
+    journal, 'SPICE kernel files correctly loaded:'
+    journal, '  SDK version = ' + kernel_version
 
-	journal, 'SPICE kernel files correctly loaded:'
-	journal, '  SDK version = ' + kernel_version
+    ; import the calibration package index file
 
-	; import the calibration package index file
+    cal_pack = json_parse(input.cal_pack_path + '/index.json', /toarray, /tostruct)
 
-	cal_pack = json_parse(input.cal_pack_path + '/index.json', /toarray, /tostruct)
+    ; include the calibration package path into the cal_pack structure
 
-	; include the calibration package path into the cal_pack structure
+    cal_pack = create_struct('path', input.cal_pack_path + path_sep(), cal_pack)
 
-	cal_pack = create_struct('path', input.cal_pack_path + path_sep(), cal_pack)
+    journal, 'Calibration package correctly imported:'
+    journal, '  version = ' + cal_pack.version
+    journal, '  validity range = ' + string(cal_pack.validity_range.start, cal_pack.validity_range._end, format = '(A, "-", A)')
 
-	journal, 'Calibration package correctly imported:'
-	journal, '  version = ' + cal_pack.version
-	journal, '  validity range = ' + string(cal_pack.validity_range.start, cal_pack.validity_range._end, format = '(A, "-", A)')
+    ; read the primary hdu
 
-	; read the primary hdu
+    data = mrdfits(input.file_name, 0, primary_header, /silent)
 
-	data = mrdfits(input.file_name, 0, primary_header, /silent)
+    naxis = fxpar(primary_header, 'NAXIS')
+    naxis1 = fxpar(primary_header, 'NAXIS1', missing = 0)
+    naxis2 = fxpar(primary_header, 'NAXIS2', missing = 0)
 
-	naxis = fxpar(primary_header, 'NAXIS')
-	naxis1 = fxpar(primary_header, 'NAXIS1', missing = 0)
-	naxis2 = fxpar(primary_header, 'NAXIS2', missing = 0)
+    ; read the metadata extension
 
-	; read the metadata extension
+    metadata_bin_table = mrdfits(input.file_name, 'metis metadata', metadata_extension_header, /silent)
 
-	metadata_bin_table = mrdfits(input.file_name, 'metis metadata', metadata_extension_header, /silent)
+    ; identify the data product and its nominal size
 
-	; identify the data product and its nominal size
+    datatype = fxpar(metadata_extension_header, 'DATATYPE')
 
-	datatype = fxpar(metadata_extension_header, 'DATATYPE')
+    journal, 'L0 FITS file correctly read:'
+    journal, '  datatype = ' + string(datatype, format = '(I0)')
 
-	journal, 'L0 FITS file correctly read:'
-	journal, '  datatype = ' + string(datatype, format = '(I0)')
+    ; if the data product is a light curve or a pcu-event list, read the data binary hk_table
 
-	; if the data product is a light curve or a pcu-event list, read the data binary hk_table
+    if datatype gt 6 then data_bin_table = mrdfits(input.file_name, 1, data_extension_header, /silent) else data_bin_table = !null
 
-	if datatype gt 6 then data_bin_table = mrdfits(input.file_name, 1, data_extension_header, /silent) else data_bin_table = !null
+    ; if the data product is an accumulation matrix, look for the accumulation vector extension and read it if it exists
 
-	; 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 $
+        if fxpar(metadata_extension_header, 'M') eq 256 then $
+            accumul_vector = mrdfits(input.file_name, 'accumulation vector', vector_extension_header, /silent) else accumul_vector = !null
 
-	if datatype eq 2 then $
-		if fxpar(metadata_extension_header, 'M') eq 256 then $
-			accumul_vector = mrdfits(input.file_name, 'accumulation vector', vector_extension_header, /silent) else accumul_vector = !null
+    ; NOTE - the radialization extension is ignored
 
-	; NOTE - the radialization extension is ignored
+    ; read the planning data
 
-	; read the planning data
+    planning_data = json_parse(input.planning_file_name, /toarray, /tostruct)
 
-	planning_data = json_parse(input.planning_file_name, /toarray, /tostruct)
+    ; definitions for the primary header
+    ; version of the fits file
 
-	; definitions for the primary header
-	; version of the fits file
+    version = string(input.l1_version, format = '(I02)')
 
-	version = string(input.l1_version, format = '(I02)')
+    ; creation and acquisition times in utc
 
-	; creation and acquisition times in utc
+    date = date_conv(systime(/julian, /utc), 'FITS')
 
-	date = date_conv(systime(/julian, /utc), 'FITS')
+    obt_beg = fxpar(primary_header, 'OBT_BEG')
+    obt_end = fxpar(primary_header, 'OBT_END')
+    obt_avg = (obt_beg + obt_end) / 2.0d
 
-	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(obt_beg)
+    date_end = solo_obt2utc(obt_end)
+    date_avg = solo_obt2utc(obt_avg)
 
-	date_beg = solo_obt2utc(obt_beg)
-	date_end = solo_obt2utc(obt_end)
-	date_avg = solo_obt2utc(obt_avg)
+    date_beg_string = strmid(date_beg, 0, 19)
+    date_beg_string = date_beg_string.replace('-', '')
+    date_beg_string = date_beg_string.replace(':', '')
 
-	date_beg_string = strmid(date_beg, 0, 19)
-	date_beg_string = date_beg_string.replace('-', '')
-	date_beg_string = date_beg_string.replace(':', '')
+    journal, 'Calculated UTC time of start acquisition:'
+    journal, '  time = ' + date_beg
 
-	journal, 'Calculated UTC time of start acquisition:'
-	journal, '  time = ' + date_beg
+    ; name of the fits file
 
-	; name of the fits file
+    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
 
-	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
+    ; filter keyword
 
-	; filter keyword
+    case datatype of
+        0: filter = 'VL'
+        1: filter = 'UV'
+        2: filter = 'UV'
+        3: filter = 'VL'
+        4: filter = 'UV'
+        5: filter = 'VL'
+        6: filter = 'UV'
+        7: filter = 'UV'
+        8: filter = 'UV'
+        9: filter = 'VL'
+    endcase
 
-	case datatype of
-		0: filter = 'VL'
-		1: filter = 'UV'
-		2: filter = 'UV'
-		3: filter = 'VL'
-		4: filter = 'UV'
-		5: filter = 'VL'
-		6: filter = 'UV'
-		7: filter = 'UV'
-		8: filter = 'UV'
-		9: filter = 'VL'
-	endcase
+    ; choose the correct calibration-package structure based on the image filter
 
-	; choose the correct calibration-package structure based on the image filter
+    if filter eq 'VL' then channel = cal_pack.vl_channel else channel = cal_pack.uv_channel
 
-	if filter eq 'VL' then channel = cal_pack.vl_channel else channel = cal_pack.uv_channel
+    ; instrument/telescope/detector keywords
 
-	; instrument/telescope/detector keywords
+    detector = filter + 'D'
+    telescope = 'SOLO/Metis/' + detector
+    wavelnth = channel.bandpass.value[1]
+    wavemin = channel.bandpass.value[0]
+    wavemax = channel.bandpass.value[2]
+    waveband = channel.name
 
-	detector = filter + 'D'
-	telescope = 'SOLO/Metis/' + detector
-	wavelnth = channel.bandpass.value[1]
-	wavemin = channel.bandpass.value[0]
-	wavemax = channel.bandpass.value[2]
-	waveband = channel.name
-	
-	; campaign keywords
+    ; campaign keywords
 
-	if planning_data.obs_id ne 'none' then begin
-		obs_id_fields = strsplit(planning_data.obs_id, '_', /extract)
-		if obs_id_fields[2] eq '000' then sooptype = 'none' else sooptype = obs_id_fields[2]
-		if obs_id_fields[4] eq '000' then obs_type = 'none' else obs_type = obs_id_fields[4]
-	endif else begin
-		sooptype = 'none'
-		obs_type = 'none'
-	endelse
+    if planning_data.obs_id ne 'none' then begin
+        obs_id_fields = strsplit(planning_data.obs_id, '_', /extract)
+        if obs_id_fields[2] eq '000' then sooptype = 'none' else sooptype = obs_id_fields[2]
+        if obs_id_fields[4] eq '000' then obs_type = 'none' else obs_type = obs_id_fields[4]
+    endif else begin
+        sooptype = 'none'
+        obs_type = 'none'
+    endelse
 
-	; build the fits file extensions
+    ; build the fits file extensions
 
-	; join the metadata extension header to the primary header removing useless keywords
+    ; join the metadata extension header to the primary header removing useless keywords
 
-	del_tags = list( $
-		'XTENSION', $
-		'BITPIX', $
-		'NAXIS', $
-		'NAXIS1', $
-		'NAXIS2', $
-		'PCOUNT', $
-		'GCOUNT', $
-		'TFIELDS', $
-		'TTYPE1', $
-		'TFORM1', $
-		'TTYPE2', $
-		'TFORM2', $
-		'EXTNAME', $
-		'CHECKSUM', $
-		'DATASUM', $
-		'END' $
-	)
-
-	foreach item, metadata_extension_header do begin
-		tag = strmid(item, 0, 8)
-		tag = tag.trim()
-		if ~ isa(del_tags.where(tag)) then begin
-			value = fxpar(metadata_extension_header, tag, comment = comment)
-			fxaddpar, primary_header, tag, value, comment, before = 'CHECKSUM'
-		endif
-	endforeach
-
-	compr = fxpar(primary_header, 'COMPR',  missing = -1)
-	xsize = fxpar(primary_header, 'X_SIZE', missing = -1)
-	
-	if compr and xsize lt 0 then begin
-		bin_type = fxpar(primary_header, 'BIN_TYPE')
-		fxaddpar, primary_header, 'X_SIZE', 2048/2^bin_type, before = 'CHECKSUM'
-		fxaddpar, primary_header, 'Y_SIZE', 2048/2^bin_type, before = 'CHECKSUM'
-		fxaddpar, primary_header, 'Z_SIZE', 1, before = 'CHECKSUM'
-		fxaddpar, primary_header, 'P_BANDS', 0, before = 'CHECKSUM'
-		fxaddpar, primary_header, 'N_BANDS', 1, before = 'CHECKSUM'
-		fxaddpar, primary_header, 'ORIG_X', 2048, before = 'CHECKSUM'
-		fxaddpar, primary_header, 'ORIG_Y', 2048, before = 'CHECKSUM'
-		fxaddpar, primary_header, 'FIRSTROW', 0, before = 'CHECKSUM'
-		fxaddpar, primary_header, 'B0_BIN', bin_type, before = 'CHECKSUM'
-		fxaddpar, primary_header, 'B0_DQ', 0, before = 'CHECKSUM'
-		fxaddpar, primary_header, 'B0_STOP', 2048, before = 'CHECKSUM'
-		fxaddpar, primary_header, 'B1_BIN', 0, before = 'CHECKSUM'
-		fxaddpar, primary_header, 'B1_DQ', 0, before = 'CHECKSUM'
-		fxaddpar, primary_header, 'B1_STOP', 0, before = 'CHECKSUM'
-		fxaddpar, primary_header, 'B2_BIN', 0, before = 'CHECKSUM'
-		fxaddpar, primary_header, 'B2_DQ', 0, before = 'CHECKSUM'
-		fxaddpar, primary_header, 'B2_STOP', 0, before = 'CHECKSUM'
-	endif
-
-	; rebin the image if binning was applied during the acquisition and check for the data quality
-	; NOTE - this is done only for image data products
-
-	comment = !null
-	if datatype le 6 then begin
-		bin_type = fxpar(primary_header, 'B0_BIN', missing = 0)
-
-		; check for wrong binning type in fixed-polarization filtered images
-
-		pol_id = fxpar(primary_header, 'POL_ID', missing = -1)
-		vlfpfilt = fxpar(primary_header, 'VLFPFILT', missing = -1)
-		if vlfpfilt eq 0 and pol_id eq 0 and bin_type eq 0 then bin_type = 1
-
-		bin_fact = 2^bin_type ; binning factor = 1, 2, or 4
-
-		if bin_fact gt 1 then begin
-			naxis1 = naxis1/bin_fact
-			naxis2 = naxis2/bin_fact
-			data = rebin(data, naxis1, naxis2)
-
-			; evaluate the quality matrix
-
-			quality_matrix = check_quality(data, cal_pack, filter)
-
-			; correct values including binning factor only for vl and uv images and accumulation matrices
-
-			if datatype le 2 then data = long(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 rebinned:'
-			journal, '    new size = ' + string(naxis1, format = '(I0)') + '×' + string(naxis2, format = '(I0)')
-		endif else begin
-			quality_matrix = check_quality(data, cal_pack, filter)
-
-			journal, 'Data was not binned during acquistion.'
-		endelse
-
-		; read the bad-pixel map
-
-		bad_pixel_map = readfits(cal_pack.path + channel.bad_pixel_map[0].file_name, /silent)
-
-		; adjust the map based on the presence of the reference rows
-		
-		map_size = size(bad_pixel_map)
-		ref_rows = fxpar(primary_header, 'REF_ROWS', missing = -1)
-		if ref_rows ge 0 then first_row = ref_rows ? 2 : 0 else first_row = 0
-		bad_pixel_map = bad_pixel_map[*, first_row : first_row + map_size[1] - 1]
-
-		; rebin the bad-pixel map to the size of the image to be corrected
-
-		bad_pixel_map = rebin(bad_pixel_map, naxis1, naxis2) * bin_fact^2
-
-		; correct the quality matrix according to the bad-pixel map
-
-		bad_pixels = where(bad_pixel_map gt 0, count)
-		if count gt 0 then quality_matrix[bad_pixels] = 0
-	endif else quality_matrix = !null
-
-	; correct for the effective exposure time
-	; NOTE - this is done only for vl and uv images and accumulation matrices
-
-	telapse = float(obt_end - obt_beg)
-
-	dit = fxpar(metadata_extension_header, 'DIT')
-	ndit = fxpar(metadata_extension_header, 'NDIT', missing = 1)
-	ndit1 = fxpar(metadata_extension_header, 'NDIT1', missing = 1)
-	ndit2 = fxpar(metadata_extension_header, 'NDIT2', missing = 1)
-
-	if datatype le 2 then begin
-		nsumexp = ndit * ndit1 * ndit2
-		xposure = dit/1000.D0 * nsumexp
-		data = long(data) * nsumexp
-
-		if ~ isa(comment) then comment = !null
-		comment = [comment, 'Image values were corrected for the total exposure time.']
-
-		journal, 'Image values were corrected for the total exposure time:'
-		journal, '  dit = ' + string(dit, format = '(I0)')
-		journal, '  nsumexp = ' + string(nsumexp, format = '(I0)')
-	endif else begin
-		nsumexp = 1
-		xposure = dit/1000.D0
-	endelse
-
-	; adjust the primary header (it is almost the same for all data product types)
-
-	fxaddpar, primary_header, 'FILENAME', file_name
-	fxaddpar, primary_header, 'PARENT', file_basename(input.file_name), 'name of the parent file', 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, 'VERS_SW', input.sw_version
-	fxaddpar, primary_header, 'VERS_CAL', cal_pack.version, 'version of the calibration package', after = 'VERS_SW'
-	fxaddpar, primary_header, 'VERSION', version
-	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. bandpass wavelength', before = 'DATAMIN'
-	fxaddpar, primary_header, 'WAVEMAX', wavemax, '[nm] max. bandpass wavelength', 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 during observation', before = 'DATAMIN'
-	fxaddpar, primary_header, 'SOOPNAME', planning_data.soop_name, 'name of the SOOP that the data belong to', before = 'DATAMIN'
-	fxaddpar, primary_header, 'SOOPTYPE', sooptype, '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', before = 'DATAMIN'
-	fxaddpar, primary_header, 'BLANK', 0, 'data value used to mark undefined pixels', after = 'BUNIT'
-	fxaddpar, primary_header, 'DATAMIN', min(data, /nan)
-	fxaddpar, primary_header, 'DATAMAX', max(data, /nan)
-	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', before = 'HISTORY'
-
-	if datatype le 6 then begin
-		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 read out in dimension 1', before = 'COMPRESS'
-		fxaddpar, primary_header, 'PXBEG2', 1, 'first pixel read out in dimension 2', before = 'COMPRESS'
-		fxaddpar, primary_header, 'PXEND1', naxis1, 'last pixel read out in dimension 1', before = 'COMPRESS'
-		fxaddpar, primary_header, 'PXEND2', naxis2, 'last pixel read out in dimension 2', before = 'COMPRESS'
-	endif
-
-	; read the house-keeping telemetry
-
-	hk_table = json_parse(input.hk_file_name, /toarray, /tostruct)
-
-	; replace raw values with calibrated values in the primary header
-
-	empty_params = !null
-
-	if datatype eq 0 or datatype eq 3 or datatype eq 5 then begin
-		
-		; patch to fix the lack of the keyword SEQ_NUM
-		
-		pol_id = fxpar(primary_header, 'POL_ID', missing = 0)
-		if pol_id ge 1 and pol_id le 4 then begin
-			seq_num = fxpar(primary_header, 'SEQ_NUM', missing = 0)
-			if seq_num eq 0 then begin
-				obj_cnt = fxpar(primary_header, 'OBJ_CNT')
-				n_pol = fxpar(primary_header, 'N_POL', missing = 4)
-				seq_num = ((obj_cnt - 1)/n_pol + 1)
-				fxaddpar, primary_header, 'SEQ_NUM', seq_num, before = 'POL_ID'
-			endif
-		endif
-
-		; 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'
-		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)')
-	endif
-
-	if empty_params eq !null then empty_params = ''
-
-	; replace with text keyword values
-
-	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
-
-	; convert the fits header into an idl structure
-
-	header = fits_hdr2struct(primary_header)
-
-	; append wcs keywords
-
-	if datatype le 6 then begin
-		wcs = metis_wcs(header, cal_pack, ref_detector = ref_detector)
-		foreach element, wcs do fxaddpar, primary_header, element.name, element.value, element.comment, before = 'DATATYPE'
-	endif
-
-	; append solar keywords
-
-	ephemeris = solo_get_ephemeris(header, cal_pack)
-	foreach element, ephemeris do fxaddpar, primary_header, element.name, element.value, element.comment, before = 'DATATYPE'
-
-	fxaddpar, primary_header, 'HISTORY', 'WCS and solar ephemeris:'
-	fxaddpar, primary_header, 'HISTORY', '  SKD version = ' + kernel_version
-
-	; remove redundant and misleading 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'
-
-	; modify keywords for file history
-
-	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
+    del_tags = list( $
+        'XTENSION', $
+        'BITPIX', $
+        'NAXIS', $
+        'NAXIS1', $
+        'NAXIS2', $
+        'PCOUNT', $
+        'GCOUNT', $
+        'TFIELDS', $
+        'TTYPE1', $
+        'TFORM1', $
+        'TTYPE2', $
+        'TFORM2', $
+        'EXTNAME', $
+        'CHECKSUM', $
+        'DATASUM', $
+        'END' $
+        )
 
-	; rectify the image if requested
+    foreach item, metadata_extension_header do begin
+        tag = strmid(item, 0, 8)
+        tag = tag.trim()
+        if ~isa(del_tags.where(tag)) then begin
+            value = fxpar(metadata_extension_header, tag, comment = comment)
+            fxaddpar, primary_header, tag, value, comment, before = 'CHECKSUM'
+        endif
+    endforeach
+
+    compr = fxpar(primary_header, 'COMPR', missing = -1)
+    xsize = fxpar(primary_header, 'X_SIZE', missing = -1)
+
+    if compr and xsize lt 0 then begin
+        bin_type = fxpar(primary_header, 'BIN_TYPE')
+        fxaddpar, primary_header, 'X_SIZE', 2048 / 2 ^ bin_type, before = 'CHECKSUM'
+        fxaddpar, primary_header, 'Y_SIZE', 2048 / 2 ^ bin_type, before = 'CHECKSUM'
+        fxaddpar, primary_header, 'Z_SIZE', 1, before = 'CHECKSUM'
+        fxaddpar, primary_header, 'P_BANDS', 0, before = 'CHECKSUM'
+        fxaddpar, primary_header, 'N_BANDS', 1, before = 'CHECKSUM'
+        fxaddpar, primary_header, 'ORIG_X', 2048, before = 'CHECKSUM'
+        fxaddpar, primary_header, 'ORIG_Y', 2048, before = 'CHECKSUM'
+        fxaddpar, primary_header, 'FIRSTROW', 0, before = 'CHECKSUM'
+        fxaddpar, primary_header, 'B0_BIN', bin_type, before = 'CHECKSUM'
+        fxaddpar, primary_header, 'B0_DQ', 0, before = 'CHECKSUM'
+        fxaddpar, primary_header, 'B0_STOP', 2048, before = 'CHECKSUM'
+        fxaddpar, primary_header, 'B1_BIN', 0, before = 'CHECKSUM'
+        fxaddpar, primary_header, 'B1_DQ', 0, before = 'CHECKSUM'
+        fxaddpar, primary_header, 'B1_STOP', 0, before = 'CHECKSUM'
+        fxaddpar, primary_header, 'B2_BIN', 0, before = 'CHECKSUM'
+        fxaddpar, primary_header, 'B2_DQ', 0, before = 'CHECKSUM'
+        fxaddpar, primary_header, 'B2_STOP', 0, before = 'CHECKSUM'
+    endif
+
+    ; rebin the image if binning was applied during the acquisition and check for the data quality
+    ; NOTE - this is done only for image data products
+
+    comment = !null
+    if datatype le 6 then begin
+        bin_type = fxpar(primary_header, 'B0_BIN', missing = 0)
+
+        ; check for wrong binning type in fixed-polarization filtered images
+
+        pol_id = fxpar(primary_header, 'POL_ID', missing = -1)
+        vlfpfilt = fxpar(primary_header, 'VLFPFILT', missing = -1)
+        if vlfpfilt eq 0 and pol_id eq 0 and bin_type eq 0 then bin_type = 1
+
+        bin_fact = 2 ^ bin_type ; binning factor = 1, 2, or 4
+
+        if bin_fact gt 1 then begin
+            naxis1 = naxis1 / bin_fact
+            naxis2 = naxis2 / bin_fact
+            data = rebin(data, naxis1, naxis2)
+
+            ; evaluate the quality matrix
+
+            quality_matrix = check_quality(data, cal_pack, filter)
+
+            ; correct values including binning factor only for vl and uv images and accumulation matrices
+
+            if datatype le 2 then data = long(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 rebinned:'
+            journal, '    new size = ' + string(naxis1, format = '(I0)') + '×' + string(naxis2, format = '(I0)')
+        endif else begin
+            quality_matrix = check_quality(data, cal_pack, filter)
+
+            journal, 'Data was not binned during acquistion.'
+        endelse
+
+        ; read the bad-pixel map
+
+        bad_pixel_map = readfits(cal_pack.path + channel.bad_pixel_map[0].file_name, /silent)
+
+        ; adjust the map based on the presence of the reference rows
+
+        map_size = size(bad_pixel_map)
+        ref_rows = fxpar(primary_header, 'REF_ROWS', missing = -1)
+        if ref_rows ge 0 then first_row = ref_rows ? 2 : 0 else first_row = 0
+        bad_pixel_map = bad_pixel_map[*, first_row : first_row + map_size[1] - 1]
+
+        ; rebin the bad-pixel map to the size of the image to be corrected
+
+        bad_pixel_map = rebin(bad_pixel_map, naxis1, naxis2) * bin_fact ^ 2
+
+        ; correct the quality matrix according to the bad-pixel map
+
+        bad_pixels = where(bad_pixel_map gt 0, count)
+        if count gt 0 then quality_matrix[bad_pixels] = 0
+    endif else quality_matrix = !null
+
+    ; correct for the effective exposure time
+    ; NOTE - this is done only for vl and uv images and accumulation matrices
+
+    telapse = float(obt_end - obt_beg)
+
+    dit = fxpar(metadata_extension_header, 'DIT')
+    ndit = fxpar(metadata_extension_header, 'NDIT', missing = 1)
+    ndit1 = fxpar(metadata_extension_header, 'NDIT1', missing = 1)
+    ndit2 = fxpar(metadata_extension_header, 'NDIT2', missing = 1)
+
+    if datatype le 2 then begin
+        nsumexp = ndit * ndit1 * ndit2
+        xposure = dit / 1000.d0 * nsumexp
+        data = long(data) * nsumexp
+
+        if ~isa(comment) then comment = !null
+        comment = [comment, 'Image values were corrected for the total exposure time.']
+
+        journal, 'Image values were corrected for the total exposure time:'
+        journal, '  dit = ' + string(dit, format = '(I0)')
+        journal, '  nsumexp = ' + string(nsumexp, format = '(I0)')
+    endif else begin
+        nsumexp = 1
+        xposure = dit / 1000.d0
+    endelse
+
+    ; adjust the primary header (it is almost the same for all data product types)
+
+    fxaddpar, primary_header, 'FILENAME', file_name
+    fxaddpar, primary_header, 'PARENT', file_basename(input.file_name), 'name of the parent file', 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, 'VERS_SW', input.sw_version
+    fxaddpar, primary_header, 'VERS_CAL', cal_pack.version, 'version of the calibration package', after = 'VERS_SW'
+    fxaddpar, primary_header, 'VERSION', version
+    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. bandpass wavelength', before = 'DATAMIN'
+    fxaddpar, primary_header, 'WAVEMAX', wavemax, '[nm] max. bandpass wavelength', 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 during observation', before = 'DATAMIN'
+    fxaddpar, primary_header, 'SOOPNAME', planning_data.soop_name, 'name of the SOOP that the data belong to', before = 'DATAMIN'
+    fxaddpar, primary_header, 'SOOPTYPE', sooptype, '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', before = 'DATAMIN'
+    fxaddpar, primary_header, 'BLANK', 0, 'data value used to mark undefined pixels', after = 'BUNIT'
+    fxaddpar, primary_header, 'DATAMIN', min(data, /nan)
+    fxaddpar, primary_header, 'DATAMAX', max(data, /nan)
+    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', before = 'HISTORY'
+
+    if datatype le 6 then begin
+        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 read out in dimension 1', before = 'COMPRESS'
+        fxaddpar, primary_header, 'PXBEG2', 1, 'first pixel read out in dimension 2', before = 'COMPRESS'
+        fxaddpar, primary_header, 'PXEND1', naxis1, 'last pixel read out in dimension 1', before = 'COMPRESS'
+        fxaddpar, primary_header, 'PXEND2', naxis2, 'last pixel read out in dimension 2', before = 'COMPRESS'
+    endif
+
+    ; read the house-keeping telemetry
+
+    hk_table = json_parse(input.hk_file_name, /toarray, /tostruct)
+
+    ; replace raw values with calibrated values in the primary header
+
+    empty_params = !null
+
+    if datatype eq 0 or datatype eq 3 or datatype eq 5 then begin
+        ; patch to fix the lack of the keyword SEQ_NUM
+
+        pol_id = fxpar(primary_header, 'POL_ID', missing = 0)
+        if pol_id ge 1 and pol_id le 4 then begin
+            seq_num = fxpar(primary_header, 'SEQ_NUM', missing = 0)
+            if seq_num eq 0 then begin
+                obj_cnt = fxpar(primary_header, 'OBJ_CNT')
+                n_pol = fxpar(primary_header, 'N_POL', missing = 4)
+                seq_num = ((obj_cnt - 1) / n_pol + 1)
+                fxaddpar, primary_header, 'SEQ_NUM', seq_num, before = 'POL_ID'
+            endif
+        endif
+
+        ; 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'
+        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)')
+    endif
+
+    if empty_params eq !null then empty_params = ''
+
+    ; replace with text keyword values
+
+    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
+
+    ; convert the fits header into an idl structure
+
+    header = fits_hdr2struct(primary_header)
+
+    ; append wcs keywords
+
+    if datatype le 6 then begin
+        wcs = metis_wcs(header, cal_pack, ref_detector = ref_detector)
+        foreach element, wcs do fxaddpar, primary_header, element.name, element.value, element.comment, before = 'DATATYPE'
+    endif
+
+    ; append solar keywords
+
+    ephemeris = solo_get_ephemeris(header, cal_pack)
+    foreach element, ephemeris do fxaddpar, primary_header, element.name, element.value, element.comment, before = 'DATATYPE'
+
+    fxaddpar, primary_header, 'HISTORY', 'WCS and solar ephemeris:'
+    fxaddpar, primary_header, 'HISTORY', '  SKD version = ' + kernel_version
+
+    ; remove redundant and misleading 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'
+
+    ; modify keywords for file history
+
+    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
 
-	if not ref_detector then begin
-		data = metis_rectify(data, filter)
-		quality_matrix = metis_rectify(quality_matrix, filter)
-	endif
+    ; rectify the image if requested
 
-	; add checksum and datasum to the fits header and save
+    if not ref_detector then begin
+        data = metis_rectify(data, filter)
+        quality_matrix = metis_rectify(quality_matrix, filter)
+    endif
 
-	fits_add_checksum, primary_header, data
-	mwrfits, data, out_file_name, primary_header, /no_comment, /create, /silent
+    ; add checksum and datasum to the fits header and save
 
-	journal, 'Fits file created:'
-	journal, '  file name = ' + out_file_name
+    fits_add_checksum, primary_header, data
+    mwrfits, data, out_file_name, primary_header, /no_comment, /create, /silent
 
-	; if applicable, save the data binary-table extension as it is
+    journal, 'Fits file created:'
+    journal, '  file name = ' + out_file_name
 
-	if isa(data_bin_table) then begin
-		if datatype lt 9 then fits_add_checksum, data_extension_header, data_bin_table
-		mwrfits, data_bin_table, out_file_name, data_extension_header, /no_comment, /silent
-	endif
+    ; if applicable, save the data binary-table extension as it is
 
-	; save the quality matrix
+    if isa(data_bin_table) then begin
+        if datatype lt 9 then fits_add_checksum, data_extension_header, data_bin_table
+        mwrfits, data_bin_table, out_file_name, data_extension_header, /no_comment, /silent
+    endif
 
-	if datatype le 6 and isa(quality_matrix) then begin
-		quality_matrix_header = !null
-		fxaddpar, quality_matrix_header, 'PCOUNT', 0, 'parameter count'
-		fxaddpar, quality_matrix_header, 'GCOUNT', 1, 'group count'
-		fxaddpar, quality_matrix_header, 'EXTNAME', 'Quality matrix', 'extension name'
-		fits_add_checksum, quality_matrix_header, quality_matrix
-		mwrfits, quality_matrix, out_file_name, quality_matrix_header, /no_comment, /silent
+    ; save the quality matrix
 
-		journal, 'Quality-matrix extension correctly added.'
-	endif
+    if datatype le 6 and isa(quality_matrix) then begin
+        quality_matrix_header = !null
+        fxaddpar, quality_matrix_header, 'PCOUNT', 0, 'parameter count'
+        fxaddpar, quality_matrix_header, 'GCOUNT', 1, 'group count'
+        fxaddpar, quality_matrix_header, 'EXTNAME', 'Quality matrix', 'extension name'
+        fits_add_checksum, quality_matrix_header, quality_matrix
+        mwrfits, quality_matrix, out_file_name, quality_matrix_header, /no_comment, /silent
 
-	; build the telemetry extension
+        journal, 'Quality-matrix extension correctly added.'
+    endif
 
-	hk_extension_header = !null
-	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'
+    ; build the telemetry extension
 
-	hk_bin_table = make_bin_table(hk_table)
-	mwrfits, hk_bin_table, out_file_name, hk_extension_header, /no_comment, /silent
+    hk_extension_header = !null
+    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'
 
-	journal, 'HK binary-table extension correctly added.'
+    hk_bin_table = make_bin_table(hk_table)
+    mwrfits, hk_bin_table, out_file_name, hk_extension_header, /no_comment, /silent
 
-	; unload the spice kernels
+    journal, 'HK binary-table extension correctly added.'
 
-	load_spice_kernels, kernel_list = kernel_list, /unload
+    ; fix some header keywords
 
-	journal, 'SPICE kernel files unloaded.'
+    fix_fits_header, out_file_name
 
-	; write the auxiliary information file
+    ; unload the spice kernels
 
-	output = { $
-		file_name: out_file_name, $
-		l0_file_name: input.file_name, $
-		log_file_name: 'output/metis_l1_prep_log.txt', $
-		empty_params : empty_params[uniq(empty_params)]  $
- 	}
+    load_spice_kernels, kernel_list = kernel_list, /unload
 
-	json_write, output, 'output/contents.json'
+    journal, 'SPICE kernel files unloaded.'
 
-	journal, 'Output saved'
+    ; write the auxiliary information file
 
-	;close the log
+    output = { $
+        file_name: out_file_name, $
+        l0_file_name: input.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
-end
+    json_write, output, 'output/contents.json'
+
+    journal, 'Output saved'
+
+    ; close the log
+
+    journal, 'Exiting without errors.'
+    journal
+    exit, status = 0
+end
\ No newline at end of file