Skip to content

Commit 5b4c9d1

Browse files
Merge pull request #105 from ninahakansson/seviri_ir_calibration
SEVIRI IR calibration
2 parents a991af4 + bb8262e commit 5b4c9d1

12 files changed

+87
-9
lines changed

bin/seviri2pps.py

+7
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,19 @@
5050
help="Engine for saving netcdf files netcdf4 or h5netcdf (default).")
5151
parser.add_argument('--use-nominal-time-in-filename', action='store_true',
5252
help='Use nominal scan timestamps in output filename.')
53+
parser.add_argument('--path-to-external-ir-calibration', type=str, default=".",
54+
help='Path to external IR calibration.')
55+
parser.add_argument('--use-nominal-ir-calibration', action='store_true',
56+
help='Use nominal IR claibration.')
5357
options = parser.parse_args()
58+
if options.use_nominal_ir_calibration:
59+
options.path_to_external_ir_calibration = None
5460
process_one_scan(
5561
options.files,
5662
out_path=options.out_dir,
5763
rotate=not options.no_rotation,
5864
engine=options.nc_engine,
5965
use_nominal_time_in_filename=options.use_nominal_time_in_filename,
66+
path_to_external_ir_calibration=options.path_to_external_ir_calibration,
6067
save_azimuth_angles=options.azimuth_angles,
6168
)

level1c4pps/calibration_coefs.py

+39-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@
2121

2222
import datetime
2323
from enum import Enum
24+
import json
25+
import logging
26+
import os
27+
28+
logger = logging.getLogger("calibration")
2429

2530

2631
class CalibrationData(Enum):
@@ -55,7 +60,7 @@ class CalibrationData(Enum):
5560
SATPY_CALIB_MODE = 'Nominal'
5661

5762

58-
def get_calibration(platform, time, clip=False):
63+
def get_calibration(platform, time, clip=False, calib_ir_path=None):
5964
"""Get MODIS-intercalibrated gain and offset for specific time.
6065
6166
Args:
@@ -74,6 +79,15 @@ def get_calibration(platform, time, clip=False):
7479
time=time,
7580
clip=clip
7681
)
82+
if calib_ir_path is not None:
83+
for channel in ('IR_039', 'IR_087', 'IR_108', 'IR_120',
84+
'IR_134', 'IR_097', 'WV_062', 'WV_073'):
85+
coefs[channel] = get_ir_calibration_coeffs(
86+
calib_ir_path,
87+
platform=platform,
88+
channel=channel,
89+
time=time,
90+
)
7791
return coefs
7892

7993

@@ -106,11 +120,13 @@ def _convert_to_datetime(date_or_time):
106120

107121

108122
def _is_date(date_or_time):
123+
"""Check that we have a datetime date object."""
109124
# datetime is a subclass of date, therefore we cannot use isinstance here
110-
return type(date_or_time) == datetime.date
125+
return type(date_or_time) == datetime.date # noqa E721
111126

112127

113128
def _check_is_valid_time(time):
129+
"""Check that we have a valid time."""
114130
ref_time = CalibrationData.REF_TIME.value
115131
if time < ref_time:
116132
raise ValueError('Given time ({0}) is < reference time ({1})'.format(
@@ -154,6 +170,22 @@ def _microwatts_to_milliwatts(microwatts):
154170
return microwatts / 1000.0
155171

156172

173+
def get_ir_calibration_coeffs(ir_calib_path, platform="MSG2", channel="IR_039",
174+
time=datetime.datetime(2048, 1, 18, 12, 0)):
175+
"""Get IR calibration from EUMETSAT, modified by CMSAF."""
176+
filename = os.path.join(ir_calib_path, f"TIR_calib_{platform}_{channel}.json")
177+
logger.info(f'Using IR calibration from {filename}')
178+
with open(filename, 'r') as fhand:
179+
data = json.load(fhand)
180+
for item in data:
181+
date_s = item[0]
182+
date_i = datetime.datetime.strptime(date_s, "%Y-%m-%dT%H:%M:%S.%f")
183+
if date_i > time:
184+
break
185+
gain, offset = item[1:]
186+
return {'gain': gain, 'offset': offset}
187+
188+
157189
if __name__ == '__main__':
158190
time = datetime.datetime(2018, 1, 18, 12, 0)
159191
platform = 'MSG3'
@@ -164,4 +196,9 @@ def _microwatts_to_milliwatts(microwatts):
164196
time=time)
165197
coefs[channel] = {'gain': gain, 'offset': offset}
166198

199+
for channel in ('IR_039', 'IR_087', 'IR_108', 'IR_120',
200+
'IR_134', 'IR_097', 'WV_062', 'WV_073'):
201+
gain, offset = get_ir_calibration_coeffs(platform=platform, channel=channel,
202+
time=time)
203+
coefs[channel] = {'gain': gain, 'offset': offset}
167204
print(coefs)

level1c4pps/seviri2pps_lib.py

+16-6
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ class UnexpectedSatpyVersion(Exception):
8787

8888

8989
def load_and_calibrate(filenames, rotate,
90-
clip_calib):
90+
clip_calib, path_to_external_ir_calibration=None):
9191
"""Load and calibrate data.
9292
9393
Uses inter-calibration coefficients from Meirink et al.
@@ -109,8 +109,8 @@ def load_and_calibrate(filenames, rotate,
109109
calib_coefs = get_calibration(
110110
platform=info['platform_shortname'],
111111
time=info['start_time'],
112-
clip=clip_calib
113-
)
112+
clip=clip_calib,
113+
calib_ir_path=path_to_external_ir_calibration)
114114
scn_ = _create_scene(file_format, filenames, calib_coefs)
115115
_check_is_seviri_data(scn_)
116116
_load_bands(scn_, rotate)
@@ -563,6 +563,7 @@ def _postproc_hrit(self, parsed):
563563
def process_one_scan(tslot_files, out_path, rotate=True, engine='h5netcdf',
564564
use_nominal_time_in_filename=False,
565565
clip_calib=False,
566+
path_to_external_ir_calibration=None,
566567
save_azimuth_angles=False):
567568
"""Make level 1c files in PPS-format."""
568569
for fname in tslot_files:
@@ -573,7 +574,8 @@ def process_one_scan(tslot_files, out_path, rotate=True, engine='h5netcdf',
573574
scn_ = load_and_calibrate(
574575
tslot_files,
575576
rotate=rotate,
576-
clip_calib=clip_calib
577+
clip_calib=clip_calib,
578+
path_to_external_ir_calibration=path_to_external_ir_calibration,
577579
)
578580
if hasattr(scn_, 'start_time'):
579581
scn_.attrs['start_time'] = scn_.start_time
@@ -628,7 +630,11 @@ def process_one_scan(tslot_files, out_path, rotate=True, engine='h5netcdf',
628630
return filename
629631

630632

631-
def process_all_scans_in_dname(dname, out_path, ok_dates=None, rotate=False):
633+
def process_all_scans_in_dname(dname, out_path,
634+
ok_dates=None,
635+
rotate=False,
636+
path_to_external_ir_calibration=None,
637+
save_azimuth_angles=False):
632638
"""Make level 1c files for all files in directory dname."""
633639
parser = Parser(HRIT_FILE_PATTERN)
634640
fl_ = glob(os.path.join(dname, globify(HRIT_FILE_PATTERN)))
@@ -645,6 +651,10 @@ def process_all_scans_in_dname(dname, out_path, ok_dates=None, rotate=False):
645651
tslot_files = [f for f in fl_ if parser.parse(
646652
os.path.basename(f))['start_time'] == uqdate]
647653
try:
648-
process_one_scan(tslot_files, out_path, rotate=rotate)
654+
process_one_scan(tslot_files,
655+
out_path,
656+
rotate=rotate,
657+
path_to_external_ir_calibration=path_to_external_ir_calibration,
658+
save_azimuth_angles=save_azimuth_angles)
649659
except Exception:
650660
pass
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[["2007-06-05T00:00:00.000",0.0035676316,-0.1819492105]]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[["2007-06-05T00:00:00.000",0.0035676316,-0.1819492105]]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[["2007-06-05T00:00:00.000",0.0035676316,-0.1819492105]]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[["2007-06-05T00:00:00.000",0.0035676316,-0.1819492105]]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[["2007-06-05T00:00:00.000",0.035676316,-1.819492105],["2007-06-17T23:59:0.000",0.0035676316,-0.1819492105],["2007-06-18T01:00:00.000",0.035676316,-1.819492105]]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[["2007-06-05T00:00:00.000",0.0035676316,-0.1819492105]]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[["2007-06-05T00:00:00.000",0.0035676316,-0.1819492105]]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[["2007-06-05T00:00:00.000",0.0035676316,-0.1819492105]]

level1c4pps/tests/test_seviri2pps.py

+17-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def test_load_and_calibrate(self, mocked_scene):
7272
res = seviri2pps.load_and_calibrate(
7373
filenames,
7474
rotate=False,
75-
clip_calib=False
75+
clip_calib=False,
7676
)
7777

7878
# Compare results and expectations
@@ -613,6 +613,22 @@ def test_get_calibration(self, platform, timestamp, expected):
613613
coefs = calib.get_calibration(platform=platform, time=timestamp)
614614
self._assert_coefs_close(coefs, expected)
615615

616+
def test_get_calibration_ir_no_file(self):
617+
"""Test get ir calibration with mising json file."""
618+
with pytest.raises(FileNotFoundError):
619+
coefs = calib.get_calibration(
620+
platform="MSG2",
621+
time=dt.datetime(2007, 6, 5, 0, 0),
622+
calib_ir_path=".")
623+
624+
def test_get_calibration_ir(self):
625+
"""Test get ir calibration.."""
626+
coefs = calib.get_calibration(
627+
platform="MSG2",
628+
time=dt.datetime(2007, 6, 18, 0, 0),
629+
calib_ir_path="./level1c4pps/tests/")
630+
np.testing.assert_almost_equal(coefs['IR_120']['gain'], 0.003567, decimal=6)
631+
616632
def test_calibration_is_smooth(self):
617633
"""Test that calibration is smooth in time."""
618634
coefs1 = calib.get_calibration(

0 commit comments

Comments
 (0)