Skip to content

Commit

Permalink
Merge pull request #1401 from simonrp84/slstr_viscal
Browse files Browse the repository at this point in the history
Add function to the SLSTR L1 reader to enable correction of VIS radiances.
  • Loading branch information
mraspaud committed Nov 16, 2020
2 parents 02d0d9b + 08b1306 commit e665071
Show file tree
Hide file tree
Showing 3 changed files with 302 additions and 121 deletions.
78 changes: 67 additions & 11 deletions satpy/readers/slstr_l1b.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# satpy. If not, see <http://www.gnu.org/licenses/>.
"""SLSTR L1b reader."""

import warnings
import logging
import os
import re
Expand All @@ -35,6 +36,28 @@
PLATFORM_NAMES = {'S3A': 'Sentinel-3A',
'S3B': 'Sentinel-3B'}

# These are the default channel adjustment factors.
# Defined in the product notice: S3.PN-SLSTR-L1.06
# https://www.eumetsat.int/website/wcm/idc/idcplg?IdcService=GET_FILE&dDocName=PDF_S3A_PN_SLSTR_L1_06&RevisionSelectionMethod=LatestReleased&Rendition=Web
CHANCALIB_FACTORS = {'S1_nadir': 1.0,
'S2_nadir': 1.0,
'S3_nadir': 1.0,
'S4_nadir': 1.0,
'S5_nadir': 1.12,
'S6_nadir': 1.2,
'S7_nadir': 1.0,
'S8_nadir': 1.0,
'S9_nadir': 1.0,
'S1_oblique': 1.0,
'S2_oblique': 1.0,
'S3_oblique': 1.0,
'S4_oblique': 1.0,
'S5_oblique': 1.15,
'S6_oblique': 1.26,
'S7_oblique': 1.0,
'S8_oblique': 1.0,
'S9_oblique': 1.0, }


class NCSLSTRGeo(BaseFileHandler):
"""Filehandler for geo info."""
Expand Down Expand Up @@ -80,9 +103,26 @@ def end_time(self):


class NCSLSTR1B(BaseFileHandler):
"""Filehandler for l1 SLSTR data."""

def __init__(self, filename, filename_info, filetype_info):
"""Filehandler for l1 SLSTR data.
By default, the calibration factors recommended by EUMETSAT are applied.
This is required as the SLSTR VIS channels are producing slightly incorrect
radiances that require adjustment.
Satpy uses the radiance corrections in S3.PN-SLSTR-L1.06, checked 26/10/2020.
User-supplied coefficients can be passed via the `user_calibration` kwarg
This should be a dict of channel names (such as `S1_nadir`, `S8_oblique`).
For example::
calib_dict = {'S1_nadir': 1.12}
scene = satpy.Scene(filenames,
reader='slstr-l1b',
reader_kwargs={'user_calib':: calib_dict})
Will multiply S1 nadir radiances by 1.12.
"""

def __init__(self, filename, filename_info, filetype_info,
user_calibration=None):
"""Initialize the SLSTR l1 data filehandler."""
super(NCSLSTR1B, self).__init__(filename, filename_info,
filetype_info)
Expand Down Expand Up @@ -113,6 +153,27 @@ def __init__(self, filename, filename_info, filetype_info):

self.platform_name = PLATFORM_NAMES[filename_info['mission_id']]
self.sensor = 'slstr'
if isinstance(user_calibration, dict):
self.usercalib = user_calibration
else:
self.usercalib = None

def _apply_radiance_adjustment(self, radiances):
"""Adjust SLSTR radiances with default or user supplied values."""
chan_name = self.channel + '_' + self.view
adjust_fac = None
if self.usercalib is not None:
# If user supplied adjustment, use it.
if chan_name in self.usercalib:
adjust_fac = self.usercalib[chan_name]
if adjust_fac is None:
if chan_name in CHANCALIB_FACTORS:
adjust_fac = CHANCALIB_FACTORS[chan_name]
else:
warnings.warn("Warning: No radiance adjustment supplied " +
"for channel " + chan_name)
return radiances
return radiances * adjust_fac

@staticmethod
def _cal_rad(rad, didx, solar_flux=None):
Expand All @@ -124,28 +185,23 @@ def _cal_rad(rad, didx, solar_flux=None):
def get_dataset(self, key, info):
"""Load a dataset."""
if (self.channel not in key['name'] or
self.stripe != key['stripe'].name or
self.stripe != key['stripe'].name or
self.view != key['view'].name):
return

logger.debug('Reading %s.', key['name'])
if key['calibration'] == 'brightness_temperature':
variable = self.nc['{}_BT_{}{}'.format(self.channel, self.stripe, self.view[0])]
else:
variable = self.nc['{}_radiance_{}{}'.format(self.channel, self.stripe, self.view[0])]

radiances = variable
radiances = self._apply_radiance_adjustment(variable)
units = variable.attrs['units']

if key['calibration'] == 'reflectance':
# TODO take into account sun-earth distance
solar_flux = self.cal[re.sub('_[^_]*$', '', key['name']) + '_solar_irradiances']
d_index = self.indices['detector_{}{}'.format(self.stripe, self.view[0])]
idx = 0 if self.view[0] == 'n' else 1 # 0: Nadir view, 1: oblique (check).

idx = 0 if self.view[0] == 'n' else 1 # 0: Nadir view, 1: oblique (check).
radiances.data = da.map_blocks(
self._cal_rad, radiances.data, d_index.data, solar_flux=solar_flux[:, idx].values)

radiances *= np.pi * 100
units = '%'

Expand Down
110 changes: 0 additions & 110 deletions satpy/tests/reader_tests/test_nc_slstr.py

This file was deleted.

0 comments on commit e665071

Please sign in to comment.