1
|
|
|
#!/usr/bin/env python |
2
|
|
|
# -*- coding: utf-8 -*- |
3
|
|
|
|
4
|
|
|
# Copyright (c) 2017 Adam.Dybbroe |
5
|
|
|
|
6
|
|
|
# Author(s): |
7
|
|
|
|
8
|
|
|
# Adam.Dybbroe <[email protected]> |
9
|
|
|
# Thomas Lepellt, DWD |
10
|
|
|
|
11
|
|
|
# This program is free software: you can redistribute it and/or modify |
12
|
|
|
# it under the terms of the GNU General Public License as published by |
13
|
|
|
# the Free Software Foundation, either version 3 of the License, or |
14
|
|
|
# (at your option) any later version. |
15
|
|
|
|
16
|
|
|
# This program is distributed in the hope that it will be useful, |
17
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
18
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19
|
|
|
# GNU General Public License for more details. |
20
|
|
|
|
21
|
|
|
# You should have received a copy of the GNU General Public License |
22
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
23
|
|
|
|
24
|
|
|
"""Depending on the satellite sensor spectral band the upwelling infrared |
25
|
|
|
radiation in a cloudfree atmosphere will be subject to absorption by |
26
|
|
|
atmospherical molecules, mainly water vapour, Ozone, CO2 and other trace |
27
|
|
|
gases. This absorption is depending on the observational path length. Usually |
28
|
|
|
the satellite signal is subject to what is referred to as limb cooling, such |
29
|
|
|
that observed brightness temperatures under large view angles are lower than |
30
|
|
|
those under smaller view angles. |
31
|
|
|
|
32
|
|
|
This code seek to correct for this cooling, so that it is possible to get a |
33
|
|
|
more consistent looking satellite image also when using imager bands which are |
34
|
|
|
not true window channels, but rather subject to some absorption ('dirty window |
35
|
|
|
channel'). |
36
|
|
|
|
37
|
|
|
The goal is eventually to do this atmospheric correction with the use of off |
38
|
|
|
line Radiative Transfer Model simulations giving look up tables of atmospheric |
39
|
|
|
absorption for various standard atmospheres and satellite zenith angles and |
40
|
|
|
spectral bands. |
41
|
|
|
|
42
|
|
|
But, for now, we apply and old parametric method implemented at DWD since long |
43
|
|
|
ago: |
44
|
|
|
|
45
|
|
|
J.Asmus (August 2017): 'Some information to our atmospheric correction. This |
46
|
|
|
routine is very old, as Katja told. It running in DWD since around 1989 at |
47
|
|
|
first on DEC Micro VAX 4000-300, later on SGI workstation (O2) both as FORTRAN |
48
|
|
|
program, later in CineSat and now in PyTroll. This correction is very simple |
49
|
|
|
but it had to run on a Micro VAX. The idea was to get some more realistic |
50
|
|
|
temperatures in the higher latitudes. The source for this atmospheric |
51
|
|
|
correction must be a paper from the US Air Force in the 1960 or 1970 years |
52
|
|
|
(?). Unfortunaly I have no more information about the source and the colleagues |
53
|
|
|
from this time are not longer at DWD.' |
54
|
|
|
|
55
|
|
|
""" |
56
|
|
|
|
57
|
|
|
|
58
|
|
|
import numpy as np |
59
|
|
|
import logging |
60
|
|
|
|
61
|
|
|
LOG = logging.getLogger(__name__) |
62
|
|
|
|
63
|
|
|
|
64
|
|
|
class AtmosphericalCorrection(object): |
65
|
|
|
|
66
|
|
|
"""Container for the IR atmospherical correction of satellite imager IR (dirty) |
67
|
|
|
window bands |
68
|
|
|
|
69
|
|
|
""" |
70
|
|
|
|
71
|
|
|
def __init__(self, platform_name, sensor, **kwargs): |
72
|
|
|
self.platform_name = platform_name |
73
|
|
|
self.sensor = sensor |
74
|
|
|
self.coeff_filename = None |
75
|
|
|
|
76
|
|
|
if 'atmosphere' in kwargs: |
77
|
|
|
atm_type = kwargs['atmosphere'] |
78
|
|
|
else: |
79
|
|
|
atm_type = None |
80
|
|
|
|
81
|
|
|
if atm_type: |
82
|
|
|
raise AttributeError("Atmosphere type %s not supported", atm_type) |
83
|
|
|
|
84
|
|
|
LOG.info("Atmospherical correction by old DWD parametric method...") |
85
|
|
|
|
86
|
|
|
def get_correction(self, sat_zenith, bandname, data): |
87
|
|
|
""" |
88
|
|
|
Get the correction depending on satellite zenith angle and band |
89
|
|
|
""" |
90
|
|
|
|
91
|
|
|
# From band name we will eventually need the effective wavelength |
92
|
|
|
# and then use that to find the atm contribution depedning on zenith |
93
|
|
|
# angle from a LUT |
94
|
|
|
|
95
|
|
|
return viewzen_corr(data, sat_zenith) |
96
|
|
|
|
97
|
|
|
|
98
|
|
|
def viewzen_corr(data, view_zen): |
99
|
|
|
"""Apply atmospheric correction on the given *data* using the |
100
|
|
|
specified satellite zenith angles (*view_zen*). Both input data |
101
|
|
|
are given as 2-dimensional Numpy (masked) arrays, and they should |
102
|
|
|
have equal shapes. |
103
|
|
|
The *data* array will be changed in place and has to be copied before. |
104
|
|
|
""" |
105
|
|
|
def ratio(value, v_null, v_ref): |
106
|
|
|
return (value - v_null) / (v_ref - v_null) |
107
|
|
|
|
108
|
|
|
def tau0(t): |
109
|
|
|
T_0 = 210.0 |
110
|
|
|
T_REF = 320.0 |
111
|
|
|
TAU_REF = 9.85 |
112
|
|
|
return (1 + TAU_REF)**ratio(t, T_0, T_REF) - 1 |
113
|
|
|
|
114
|
|
|
def tau(t): |
115
|
|
|
T_0 = 170.0 |
116
|
|
|
T_REF = 295.0 |
117
|
|
|
TAU_REF = 1.0 |
118
|
|
|
M = 4 |
119
|
|
|
return TAU_REF * ratio(t, T_0, T_REF)**M |
120
|
|
|
|
121
|
|
|
def delta(z): |
122
|
|
|
Z_0 = 0.0 |
123
|
|
|
Z_REF = 70.0 |
124
|
|
|
DELTA_REF = 6.2 |
125
|
|
|
return (1 + DELTA_REF)**ratio(z, Z_0, Z_REF) - 1 |
126
|
|
|
|
127
|
|
|
y0, x0 = np.ma.where(view_zen == 0) |
128
|
|
|
data[y0, x0] += tau0(data[y0, x0]) |
129
|
|
|
|
130
|
|
|
y, x = np.ma.where((view_zen > 0) & (view_zen < 90) & (~data.mask)) |
131
|
|
|
data[y, x] += tau(data[y, x]) * delta(view_zen[y, x]) |
132
|
|
|
return data |
133
|
|
|
|
134
|
|
|
|
135
|
|
|
if __name__ == "__main__": |
136
|
|
|
|
137
|
|
|
this = AtmosphericalCorrection('Suomi-NPP', 'viirs') |
138
|
|
|
SHAPE = (1000, 3000) |
139
|
|
|
NDIM = SHAPE[0] * SHAPE[1] |
140
|
|
|
SATZ = np.ma.arange(NDIM).reshape(SHAPE) * 60. / float(NDIM) |
141
|
|
|
TBS = np.ma.arange(NDIM).reshape(SHAPE) * 80.0 / float(NDIM) + 220. |
142
|
|
|
atm_corr = this.get_correction(SATZ, 'M4', TBS) |
143
|
|
|
|