4 Created on Wed Jan 21 14:32:11 2015
5 Transaltion of NRL's IDL code hico_raw_to_radiance.pro
6 @author: Erdem Karako:ylu:
8 NOTES ON THE IDL=>PYTHON TRANSLATION
9 There a number of substantial dangling assignments (variables assigned to that are not
10 subsequently used -- this is apparently the cleaned-up version.)
12 Lines-1333->1359: Nothing useful, a couple of assignments to file_ and geo_history.
13 ->deal with that later.
21 import calendar
as cal
26 from datetime
import datetime
as DT
27 from datetime
import timedelta
as TDel
29 from scipy
import interpolate
36 @functools.wraps(func)
37 def wrapper(self, *args, **kwargs):
39 result =
func(self, *args, **kwargs)
40 tot_time = time.time() - start
41 self.logger.debug(
'time taken by %s: %.2f secs.' % (func.__name__, tot_time))
47 This is here because I was trying to jit it with numba. tbc...
51 for drk_line
in range(startLine, stopLine+1):
52 wmin = max(startLine, drk_line - win_size)
53 wmax = min(stopLine, drk_line + win_size)
54 nwin = wmax - wmin + 1
55 win_ind = np.arange(nwin) + wmin
56 win_ind = win_ind[np.where(win_ind != drk_line)]
57 win_mat = data[win_ind]
58 win_sd = win_mat.std(axis=0, dtype=
'f4', ddof=1)
59 offset = win_n_sd * win_sd
60 win_median = np.median(win_mat, axis=0).astype(
'f4')
61 minCrit = win_median - offset.astype(
'f4')
62 maxCrit = win_median + offset.astype(
'f4')
63 data[drk_line] = np.where((data[drk_line] < minCrit) |
64 (data[drk_line] > maxCrit),
65 win_median, data[drk_line])
70 Object to assemble the data for converting a HICO L0 file to
72 Optional keyword arguments:
73 hdrFile: header file name
74 => default: <L0 basename>.hdr
75 csvFile: csv file name
76 => default: <L0 basename>.csv
77 outFile: output file name
78 => default: <L0 basename>.nc
79 bandScaleFactorFile: as the name suggests
80 => default: Gao_scaling_Final_1_2012.txt
81 hicoFitCoeffsFile: radiometric calibration file name
82 => default: coeff_080706g10emncexp0137ls4l_it13_ftt111_smoothed_2ndcorrsm074x1_asm10_bsm20_
84 wvlFile: wavelength file name
85 => default: "HICO_Wavlns_FWHM_128_Chnls_Gao_Shift_0p84nm_v2p0_1column.txt"
86 issXVVFile: ISS data file name
87 => default: "ISS_MINUS_XVV.TXT"
88 secOrFile: second order correction coefficients file name
89 => default: "HICO_Empirical_water_scale_ftt1.11ms_1_2012.txt"
90 r2rInputParamFile: processing parameters, defaults will be applied if not
92 doCal: calibration flag
98 ocdataroot = os.environ[
'OCDATAROOT']
99 ocvarroot = os.environ[
'OCVARROOT']
100 mainLoggerName = kwds.pop(
'parentLoggerName', __name__)
101 self.
logger = logging.getLogger(
'%s.HicoL0toL1B' % mainLoggerName)
104 self.
inpDict[
"l0File"] = iArgs.l0file
105 self.
inpDict[
"hdrFile"] = iArgs.hdrfile
106 self.
inpDict[
"csvFile"] = iArgs.csvfile
107 if "bandScaleFactorFile" in kwds:
108 self.
inpDict[
"bandScaleFactorFile"] = kwds[
"bandScaleFactorFile"]
110 self.
inpDict[
"bandScaleFactorFile"] = ocdataroot + \
111 "/hico/cal/Gao_scaling_factor_Final_1_2012.txt"
112 if "hicoFitCoeffsFile" in kwds:
113 self.
inpDict[
"hicoFitCoeffsFile"] = kwds[
"hicoFitCoeffsFile"]
115 self.
inpDict[
"hicoFitCoeffsFile"] = ocdataroot + \
116 "/hico/cal/coeff_080706g10emncexp0137ls4l_" \
117 +
"it13_ftt111_smoothed_2ndcorrsm074x1_" + \
118 "asm10_bsm20_wsm12_iss_1_2012.dat"
119 if "wvlFile" in kwds:
120 self.
inpDict[
"wvlFile"] = kwds[
"wvlFile"]
122 self.
inpDict[
"wvlFile"] = ocdataroot + \
123 "/hico/cal/HICO_Wavlns_FWHM_128_Chnls_Gao" \
124 +
"_Shift_0p84nm_v2p0_1column.txt"
125 if "secOrFile" in kwds:
126 self.
inpDict[
"secOrFile"] = kwds[
"secOrFile"]
128 self.
inpDict[
"secOrFile"] = ocdataroot + \
129 "/hico/cal/HICO_Empirical_water_scale_" \
130 +
"ftt1.11ms_1_2012.txt"
131 if "issXVVFile" in kwds:
132 self.
inpDict[
"issXVVFile"] = kwds[
"issXVVFile"]
134 self.
inpDict[
"issXVVFile"] = ocvarroot+
"/hico/ISS_MINUS_XVV.TXT"
135 self.
inpDict[
"r2rInputParam"] = kwds.pop(
"r2rInputParam",
None)
136 self.
inpDict[
"doCal"] = kwds.pop(
"doCal",
True)
137 self.
inpDict[
"verbose"] = kwds.pop(
"verbose",
False)
138 self.
inpDict[
"labDark"] = kwds.pop(
"labDark",
False)
139 self.
inpDict[
"doSmooth"] = kwds.pop(
"doSmooth",
True)
140 self.
logger.info(
'inpDict content: ')
141 for k, v
in self.
inpDict.items():
142 self.
logger.info(
'%s: %s' % (k, v))
144 self.
params = dict.fromkeys([
'base_dir',
'input_dir',
'output_dir',
145 'dark_temp',
'ffTemp',
'flip_left_right',
146 'flip_cal',
'cal_mult',
'cal_mult',
147 'cal_shift',
'outputInterleave',
148 'outputScaleFactor',
'frameTransferTime',
149 'exposureTime',
'validL0Pixels',
'nb_read',
150 'nb_all',
'ns',
'nl_predark',
151 'nl_postdark',
'output_is_int',
152 'jpg_r_range',
'jpg_g_range',
153 'jpg_b_range',
'wave_um',
'fwhm',
154 'coeff_13ms',
'bScaleFactor',
155 'begin_date',
'end_date',
'begin_time',
'end_time'])
156 self.
debugDict = dict.fromkeys([
'corr_smear',
'so2',
'smoother'])
157 self.
geomDict = dict.fromkeys([
'imCenLat',
'imCenLon',
'fullLats',
158 'fullLons',
'viewz',
'viewa',
159 'sunposzen',
'sunposaz'])
160 self.
headerDict = dict.fromkeys([
'ns',
'nl',
'nb',
'offset',
'inter',
161 'data_type',
'byte_order',
162 'bad_bands',
'file_type',
163 'sensor_type',
'band_names',
164 'descrip',
'wave_len',
'fwhm',
165 'wavelength_unit',
'x_start',
166 'ystart',
'map_info',
'def_bands',
167 'z_range',
'imcenlat',
'imcenlon'])
170 'flagHdr',
'ndviEnvi',
171 'ndviHdr',
'rgbJpg',
'rgbBip',
172 'rgbHdr',
'outRad',
'outRadHdr'])
173 self.
ancDict = dict.fromkeys([
'n11',
'n12',
'n1t',
193 def __FillAncDict(self):
194 self.
logger.debug(
'filling ancDict')
196 self.
ancDict[
'n12'] = self.
L0.data[
'n_predark'] - 1
201 self.
ancDict[
'n2t'] = self.
L0.data[
'nl'] - self.
L0.data[
'n_predark'] -\
202 self.
L0.data[
'n_postdark'] - self.
ancDict[
'n11']
205 self.
ancDict[
'n31'] = self.
L0.data[
'nl'] - self.
L0.data[
'n_postdark']\
207 self.
ancDict[
'n32'] = self.
L0.data[
'nl'] - 1
209 theta_day = (2 * m.pi / 365) * (self.
L0.data[
'yearDay'] - 1)
210 d2_ratio = (1.00011 + 0.034221 * m.cos(theta_day) +
211 0.001280 * m.sin(theta_day) +
212 0.000719 * m.cos(2 * theta_day) -
213 0.000077 * m.sin(2 * theta_day))
214 self.
ancDict[
'd2_ratio'] = d2_ratio
217 def __FillOutputFileNames(self):
219 Fills dictionary containing output filenames
225 outFileNameBase = os.path.basename(self.
inpDict[
'l0File']) + radtype
239 def __GetISSOrientation(self):
241 Determines ISS orientation
244 issOrientation =
"+XVV"
245 sceneid =
int(os.path.basename(self.
inpDict[
"l0File"]).split(
'.')[5])
246 with open(self.
inpDict[
"issXVVFile"])
as isf:
247 for line
in isf.readlines()[1:]:
248 lElems = line.split(
',')
249 if sceneid >=
int(lElems[0])
and sceneid <=
int(lElems[1]):
251 issOrientation =
"-XVV"
252 self.
params[
'flip_left_right'] = flipLR
253 self.
params[
'issOrientation'] = issOrientation
255 def __SetPeriods(self):
256 '''Formatting time for writing to nc file'''
259 gnc_epoch = DT(1980, 1, 6)
260 start_delta = TDel(0, self.
L0.header[
'FFgncSec'])
261 end_delta = TDel(0, self.
L0.header[
'LFgncSec'])
262 firstFrameDT = start_delta + gnc_epoch
263 lastFrameDT = end_delta + gnc_epoch
264 self.
params[
'begin_date'] = DT.strftime(firstFrameDT, datefmt)
265 self.
params[
'end_date'] = DT.strftime(lastFrameDT, datefmt)
266 self.
params[
'begin_time'] = DT.strftime(firstFrameDT, timefmt)
267 self.
params[
'end_time'] = DT.strftime(lastFrameDT, timefmt)
270 def __SetL02L1BParams(self):
272 Populates L02L1BParams hash (as a dict).
273 In hico_L0_to_L1B_h5.bash this is equivalent to
274 * filling out {basename}.raw_param
277 ff_time = DT(self.
L0.header[
'FFyearMSB'] * 100 + self.
L0.header[
'FFyearLSB'],
278 self.
L0.header[
'FFmonth'], self.
L0.header[
'FFday'],
279 self.
L0.header[
'FFhours'], self.
L0.header[
'FFmin'], self.
L0.header[
'FFsec'])
280 timestamp = cal.timegm(ff_time.timetuple())
281 dark_time = DT.utcfromtimestamp(timestamp + offset)
284 self.
params[
'base_dir'] =
"./"
285 self.
params[
'input_dir'] =
"./"
286 self.
params[
'output_dir'] =
"./"
287 self.
params[
'dark_temp'] = dark_temp
288 self.
params[
'ffTemp'] = ff_temp
289 self.
params[
'flip_cal'] =
True
290 self.
params[
'cal_mult'] = 1.32
291 self.
params[
'cal_shift'] = 0
292 self.
params[
'outputInterleave'] =
"bil"
293 self.
params[
'outputScaleFactor'] = 50
294 self.
params[
'frameTransferTime'] = 0.00111
295 self.
params[
'exposureTime'] = 0.0137
296 self.
params[
'validL0Pixels'] = 508
297 self.
params[
'nb_read'] = 128
298 self.
params[
'nb_all'] = 170
300 self.
params[
'nl_predark'] = 200
301 self.
params[
'nl_postdark'] = 200
302 self.
params[
'out_type'] =
'uint16'
308 good = np.where(tempData != -1,
True,
False)
309 picXave = tempData[good].reshape(tempData.shape).
mean(axis=0,
314 def __LinearCongrid(arr, newdims):
318 m1 = np.cast[int](minusone)
319 ndims = len(arr.shape)
321 ofs = np.cast[int](centre) * 0.5
322 old = np.array(arr.shape)
323 newdims = np.asarray(newdims, dtype=float)
325 for i
in range(ndims):
326 base = np.arange(newdims[i])
327 dimlist.append((old[i] - m1) / (newdims[i] - m1) *
330 olddims = [np.arange(i, dtype=np.float)
for i
in list(arr.shape)]
333 mint = interpolate.interp1d(olddims[-1], arr, kind=method)
334 newa = mint(dimlist[-1])
336 for i
in range(ndims - 2, -1, -1):
337 newa = newa.transpose()
338 mint = interpolate.interp1d(olddims[i], newa, kind=method)
339 newa = mint(dimlist[i])
342 newa = newa.transpose()
346 def __RotMatrix(roll, pitch, yaw):
348 Computes a rotation matrix given roll,pitch and yaw, in that order.
349 Resulting rotation matrix shape is (3x3)
351 rotMat = np.zeros((3, 3))
352 phi = m.radians(roll)
353 theta = m.radians(pitch)
361 rotMat = [[ct * cp, ct * sp, -st],
362 [sf * st * cp - cf * sp, sf * st * sp + cf * cp, ct * sf],
363 [cf * st * cp + sf * sp, cf * st * sp - sf * cp, ct * cf]]
365 return np.array(rotMat)
368 def __RngBear2LatLonEllipsoid(latInArr, lonInArr, distArr, brngInArr):
370 Solution of the geodetic direct problem after T. Vincenty.
371 Modified Rainsford's method with Helmert's elliptical terms,
372 effective in any azimuth and at any distance short of antipodal.
374 semiMajAx is the semi-major axis of the reference ellipsoid;
375 flatFactor is the flattening of the reference elllipsoid;
376 latitudes and longitudes in randians, +ve North and East;
377 azims in radians clockwise from North.
378 Geodesic distance s assumed in units of semiMajAx
381 flatFactor = 1/298.257223563
386 glon1 = np.radians(lonInArr)
387 glat1 = np.radians(latInArr)
388 faz = np.radians(brngInArr)
390 tu = r*np.sin(glat1) / np.cos(glat1)
393 baz = np.zeros_like(faz)
395 baz = np.arctan2(tu[w], cf[w])*2
396 cu = (np.sqrt(tu * tu + 1)) ** -1
400 x = np.sqrt((1/(r**2) - 1) * c2a + 1) + 1
403 c = ((x*x/4) + 1) / c
404 d = (0.375 * x**3 - x)
405 tu = distArr / r / semiMajAx / c
416 y = ((((2*sy)**2 - 3) * y * cz * d / 6 + x) * d / 4 - cz) * sy *\
418 if np.max(
abs(y - c)) <= eps:
420 baz = cu * cy * cf - su * sy
421 c = r * np.sqrt(sa**2 + baz ** 2)
422 d = su * cy + cy * sy * cf
423 glat2 = np.arctan2(d, c)
424 c = cu * cy - su * sy * sf
425 x = np.arctan2(sy * sf, c)
426 c = ((-3 * c2a + 4) * flatFactor + 4) * c2a * flatFactor / 16
427 d = ((e * cy * c + cz) * sy * c + y) * sa
428 glon2 = glon1 + x - (1 - c) * d * flatFactor
429 baz = np.arctan2(sa, baz) + m.pi
430 lonOutArr = np.degrees(glon2)
431 latOutArr = np.degrees(glat2)
432 brngOutArr = np.degrees(baz)
434 return latOutArr, lonOutArr, brngOutArr
437 def __Quats2Euler(q, qtype=0):
439 Takes ISS Quaternions (q) in their normal order.
440 Returns angles phi, theta, psi.
441 X,Y,Z are forward in, right of orbit and down, respectively.
442 qtype0 returns Euler angle in the typical R1 R2 R3 order w/ first
443 rotation about z-axis, then y-axis, then x-axis
447 phi = m.degrees(m.atan2(2*(q[2] * q[3] + q[0] * q[1]),
448 q[3]**2 - q[2]**2 - q[1]**2 + q[0]**2))
449 theta = m.degrees(-m.asin(2 * (q[1] * q[3] - q[0] * q[2])))
450 psi = m.degrees(m.atan2(2 * (q[2] * q[1] + q[0] * q[3]),
451 -q[3]**2 - q[2]**2 + q[1]**2 + q[0]**2))
453 phi = m.degrees(m.atan2(2*(-q[1] * q[2] + q[0] * q[3]),
454 -q[3]**2 - q[2]**2 + q[1]**2 + q[0]**2))
455 theta = m.degrees(m.asin(2 * (q[1] * q[3] + q[0] * q[2])))
456 psi = m.degrees(m.atan2(2 * (-q[2] * q[3] + q[0] * q[1]),
457 q[3]**2 - q[2]**2 - q[1]**2 + q[0]**2))
458 return phi, theta, psi
461 def __Vec2ZenithBearing(vec):
464 Output: zenith and bearing angles (radians)
467 zenithRad = m.acos(vec[2])
468 bearingRad = m.atan2(vec[1], vec[0])
470 bearingRad += 2 * m.pi
471 return zenithRad, bearingRad
474 def __GetVecs(rotMat, angleRad):
476 Output 3x1 ref vector.
477 Input: * 3x3 rotation matrix, rotMat
478 * reference angle in radians, angleRad
480 dirVec = np.array([0, m.sin(angleRad), m.cos(angleRad)])
481 refVec = np.array([np.sum(rotMat[:, i] * dirVec)
for i
in range(3)])
485 def __GetDist(zenithAngleRad, height):
487 Computes distance along surface of ellipsoid (km)
490 dist = (m.asin(((earthRadiusKM + height) / earthRadiusKM) *
491 m.sin(
abs(zenithAngleRad))) -
492 abs(zenithAngleRad)) * earthRadiusKM
496 def __ValueLocate(vec, vals):
498 Equivalent to IDL's value_locate routine. Excerpt from exelis follows
499 "The VALUE_LOCATE function finds the intervals within a given monotonic
500 vector that brackets a given set of one or more search values."
501 Assumes vec and val are 1D arrays
503 return np.amax([np.where(v >= vec, np.arange(len(vec)), -1)
504 for v
in vals], axis=1)
506 def __GetCamTemp(self, csvFileName, timeData):
510 use_columns = [
"ISSTIMEYEAR",
"ISSTIMEMONTH",
"ISSTIMEDAY",
511 "ISSTIMEHOUR",
"ISSTIMEMINUTE",
"ISSTIMESECOND",
515 df = pd.read_csv(csvFileName, usecols=use_columns)
517 self.
logger.warning(
"badly formatted csv file - attempting to resolve")
518 ocdataroot = os.getenv(
'OCDATAROOT')
519 hicocaldir = os.path.join(ocdataroot,
'hico/cal')
520 col_name_file = os.path.join(hicocaldir,
'csv_columns.pkl')
522 with open(col_name_file,
'rb')
as f:
523 col_names = pickle.load(f)
524 except FileNotFoundError:
525 self.
logger.error(
"column name file not found")
528 df = pd.read_csv(csvFileName, skiprows=1, names=col_names,
530 self.
logger.info(
'csv format issue resolved')
532 self.
logger.error(
"Something is wrong with the CSV file")
535 x = df.loc[(df.ISSTIMEYEAR == (timeData.year % 100)) &
536 (df.ISSTIMEMONTH == (timeData.month)) &
537 (df.ISSTIMEDAY == (timeData.day)) &
538 (df.ISSTIMEHOUR == (timeData.hour)) &
539 (df.ISSTIMEMINUTE == (timeData.minute)) &
540 (df.ISSTIMESECOND == (timeData.second))].HICOCAMERATEMP.values
547 def __ReadWaveFile(self):
548 '''Reads wavelength file'''
549 if self.
inpDict[
'wvlFile'] ==
'DEFAULT':
550 nb = self.
L0.data[
'nb']
557 self.
inpDict[
'wvlFile'] =
'lambda_b (0<=b<=' +
str(nb) +
'):' +\
558 str(wc1) +
'+' +
str(wc2) +
'*b'
559 self.
params[
'wave_um'] = wc1 + wc2 * np.ones(nb)
564 def __ReadCalFile(self):
566 READS hicoFitCoeffs FILE
568 if sys.byteorder ==
'little':
572 tempArray = np.fromfile(self.
inpDict[
'hicoFitCoeffsFile'], dtype=dt)
573 coeff_13ms = tempArray.reshape(self.
params[
'nb_read'], self.
params[
'ns'])
574 if self.
L0.data[
'nb'] == 128:
575 exposure_time = self.
L0.data[
'exposure_time'] / 1e6
576 et_ratio = 13e-3 / exposure_time
577 coeff_13ms *= et_ratio
578 cal_sh = self.
params[
'cal_shift']
579 if self.
params[
'flip_cal']:
580 coeff_13ms = np.fliplr(coeff_13ms)
584 coeff_13ms[:, 0:512 + cal_sh] = hhold[:, 0-cal_sh:512]
585 coeff_13ms[:, 512 + cal_sh:512] = 0
587 coeff_13ms[:, cal_sh:512] = hhold[0:512 - cal_sh, :]
588 coeff_13ms[:, 0:cal_sh] = 0.0
589 coeff_13ms = np.expand_dims(coeff_13ms, axis=0)
590 self.
params[
'coeff_13ms'] = coeff_13ms
593 def __ReadBandScaleFile(self):
595 READS bandScaleFacor FILE
597 bScaleFactor = np.genfromtxt(self.
inpDict[
'bandScaleFactorFile'])
598 bScaleFactor = np.expand_dims(bScaleFactor[:, 1], axis=0)
599 bScaleFactor = np.expand_dims(bScaleFactor, axis=-1)
600 self.
params[
'bScaleFactor'] = bScaleFactor
605 Returns angle of slit on the ground, returned in degrees
607 msAlt = (self.
L0.data[
'FFLatLonH'][2] + self.
L0.data[
'LFLatLonH'][2]
609 msLatDeg = (self.
L0.data[
'FFLatLonH'][0] +
610 self.
L0.data[
'LFLatLonH'][0]) * 0.5
611 msLatDeg = min([max([msLatDeg, -51.6]), 51.6])
612 gammaRad = m.acos(m.cos(m.radians(51.6)) / m.cos(m.radians(msLatDeg)))
613 if self.
L0.data[
'LFLatLonH'][0] < self.
L0.data[
'FFLatLonH'][0]:
615 gammaDeg = m.degrees(gammaRad)
616 return msAlt, msLatDeg, gammaDeg
619 def _DoDarkCorrection(self):
621 hico_to_rad_kwp_truncate.pro
623 Nested function implemented to avoid nest loop repetitions (thanks NRL!)
624 No closure: nested function not returned by calling function.
625 Also pic0 & others are already in scope but note "nonlocal" declaration
626 allowing changes in pic0.
627 NOTE: THIS IS USED ONLY BY SMEAR CORRECTION FUNCTION
635 intData = self.
L0.data[
'pic0']
636 dark1Pos, dark2Pos = self.
L0.data[
'Dark1Pos'], self.
L0.data[
'Dark2Pos']
637 darkTemp, fftemp = self.
params[
'dark_temp'], self.
params[
'ffTemp']
638 prePostDarkOK, pic13ave, ts, = 0, 0, 41
639 fit20 = np.empty((n2t, 1, 1))
640 fit20[:, 0, 0] = np.log(1 + np.arange(n2t) / ts)
641 f13av = (1 + ts / n1t) * np.log(1 + n1t / ts) - 1
643 if fftemp >= 10.0
and fftemp < 24.0:
645 elif fftemp >= 24.0
and fftemp < 27.5:
646 dark_b_coef = -0.6286 * fftemp + 26.2857
647 elif fftemp >= 27.5
and fftemp < 33.0:
648 dark_b_coef = 0.7273 * fftemp - 11.0
649 elif fftemp >= 33.0
and fftemp < 50.0:
650 dark_b_coef = 0.2143 * fftemp + 5.9286
652 self.
logger.debug(
'intData after n11-: %s' % intData.dtype)
654 self.
logger.debug(
'intData after n31-: %s' % intData.dtype)
661 if prePostDarkOK == 3:
663 elif prePostDarkOK == 2:
664 pic13ave *= (0.970 + 0.0036 * (darkTemp - 23))
665 elif prePostDarkOK == 1:
666 pic13ave *= (1.032 - 0.0040 * (darkTemp - 23))
667 b = dark_b_coef + 0.9 * (pic13ave - 221) / (285 - 221)
670 self.
floatData = intData[n21:n22+1] - (pic13ave - b * f13av + 1.2 + b * fit20.astype(
'f4'))
672 self.
logger.debug(
'floatData after drk corr.: %s' % self.
floatData.dtype)
675 def _DoSmearCorrection(self):
676 nb = self.
L0.data[
'nb']
677 ns = self.
L0.data[
'ns']
678 exposureTime = self.
params[
'exposureTime']
679 frameTransferTime = self.
params[
'frameTransferTime']
680 stable_integration_time = np.array(exposureTime - frameTransferTime, dtype=
'f2')
681 delta_t = np.array(frameTransferTime / 511, dtype=
'f2')
682 frac_eqB6 = (frameTransferTime + delta_t) / (stable_integration_time - delta_t)
684 tot_count = (self.
floatData.sum(axis=1) +
685 self.
floatData[:, -1, :] * (171 - nb)) * \
688 tot_count = (self.
floatData.sum(axis=1) +
689 self.
floatData[:, -1, :] * (512 - nb)) * \
691 self.
floatData *= (1 + frac_eqB6.astype(
'f2'))
692 self.
floatData -= tot_count[:, np.newaxis, :].astype(
'f2')
693 self.
logger.debug(
'floatData after smear correction: %s' % self.
floatData.dtype)
696 def __MakeFWHM(self):
697 nb = self.
L0.data[
'nb']
709 fwhm = np.ones(nb) * fwc
710 self.
params[
'fwhm'] = fwhm * 1000
713 def _DoGaussianSmoothing(self):
714 ''' Two steps 1-prepare parameters for gaussian smoothing
715 2-perform gaussian smoothing
717 nb = self.
L0.data[
'nb']
720 fwhm_10nm = np.array([0.0001497, 0.0141444, 0.2166629, 0.5380859])
721 fwhm_10nm = np.concatenate((fwhm_10nm, -np.sort(-fwhm_10nm[:-1])))
722 fwhm_20nm = np.array([0.0070725, 0.0347489, 0.1083365, 0.2143261, 0.2690555])
723 fwhm_20nm = np.concatenate((fwhm_20nm, -np.sort(-fwhm_20nm[:-1])))
725 fwhm_10nm /= fwhm_10nm.sum()
726 fwhm_20nm /= fwhm_20nm.sum()
727 smoother = np.zeros((nb, nb))
728 np.fill_diagonal(smoother, 1)
729 for i
in range(3, 69):
730 smoother[i, i-3:i+4] = fwhm_10nm
731 for i
in range(69, nb-4):
732 smoother[i, i-4:i+5] = fwhm_20nm
733 for li
in range(n2t):
738 def _Do2ndOrdCorrection(self):
740 Populates a matrix that holds linear interp. weights combined with scale factors
741 to subsequently allow interpolation and 2nd order calcs. to be done in a single
742 matrix multiplication.
744 nb = self.
L0.data[
'nb']
748 wl = self.
params[
'wave_um']
750 sc_1 = 0.131 * (0.86 - wl)
751 sc_2 = 0.113 * (wl - 0.86)
752 scale = np.where(sc_1 > sc_2, sc_1, sc_2)
753 scale[wl < 2 * wl[0]] = 0
756 s2data = np.loadtxt(self.
inpDict[
'secOrFile'], skiprows=1)
760 scale = np.interp(wl, s2data[:, 0], s2data[:, 2])
761 so2 = np.diagflat([1]*nb).astype(
'f8')
764 weight = (wave_half[i]-wl[maP[i]])/(wl[maP[i] + 1] - wl[maP[i]])
765 so2[i, maP[i]:maP[i]+2] = -np.array([(1 - weight), weight]) * scale[i]
766 if so2.sum() != so2.shape[1]:
767 for li
in range(n2t):
772 def _ApplyMasks(self):
773 '''Called by _DoCalMaskAndScale()'''
774 ns = self.
L0.data[
'ns']
776 if bad_sat_pix.any():
782 right_mask = self.
params[
'validL0Pixels']
786 def _FlipDataArray(self):
787 ''' hack to use numpy's fliplr method on a 3D array. '''
793 def _DoCalMaskAndScale(self):
794 '''Calibration changes depending on number of bands and doCal option.
795 When doCal=False but nb=384, data is re-scaled.
797 cal_mult = self.
params[
'cal_mult']
798 gao_scale = self.
params[
'bScaleFactor']
799 out_scale = self.
params[
'outputScaleFactor']
802 if self.
params[
'flip_left_right']:
811 self.
logger.info(
"Dark correction done")
813 self.
logger.info(
"Bad data indexed")
815 self.
logger.info(
"Smear correction done")
817 self.
logger.info(
"Second order correction done")
820 self.
logger.info(
"Gaussian smoothing done")
821 if self.
inpDict[
'doCal']
and self.
L0.data[
'nb'] == 128:
823 self.
logger.info(
"Calibration/Masking/Scaling done")
824 elif not self.
inpDict[
'doCal']
and self.
L0.data[
'nb'] == 384:
826 self.
logger.info(
"Raw to radiance conversion completed")
830 def __GetSunAngles(self):
834 iday: year's day number
835 hr: GMT time of day in real hours, e.g. 4:30PM = 16.5
838 solarAngles {ndarray}, where
839 solarAngles[0] = solar zenith angle in degrees
840 solarAngles[1] = solar azimuth angle in degrees clockwise from North
842 iday, hr = self.
L0.data[
'yearDay'], self.
L0.data[
'floatHour']
845 thez = 360*(iday - 1) / 365
846 rthez = m.radians(thez)
847 sdec = 0.396372 - 22.91327 * m.cos(rthez) + 4.02543 * m.sin(rthez) - \
848 0.387205 * m.cos(2 * rthez) + 0.051967 * m.sin(2 * rthez) - \
849 0.154527 * m.cos(3 * rthez) + 0.084798 * m.sin(3 * rthez)
850 rsdec = m.radians(sdec)
852 tc = 0.004297 + 0.107029 * m.cos(rthez) - 1.837877 * m.sin(rthez) - \
853 0.837378 * m.cos(2 * rthez) - 2.342824 * m.sin(2 * rthez)
854 xha = (hr - 12) * 15 + xlon + tc
855 xha[xha > 180] -= 360
856 xha[xha < -180] += 360
857 rlat = np.deg2rad(ylat)
859 rha = np.radians(xha)
861 costmp = np.sin(rlat) * np.sin(rsdec) + np.cos(rlat) * np.cos(rsdec) * np.cos(rha)
863 costmp[costmp > 1] = 1
864 costmp[costmp < -1] = -1
865 rsunz = np.arccos(costmp)
866 sunz = np.rad2deg(rsunz)
868 sna = m.cos(rsdec) * np.sin(rha)
869 csa = np.sin(rlat) * np.cos(rha) * m.cos(rsdec) - m.sin(rsdec) * np.cos(rlat)
870 rsuna = np.arctan2(sna, csa) + m.pi
871 suna = np.rad2deg(rsuna)
876 def __GetFirstLastFrameLatsLons(self):
880 latsFF,latsLF: first and last frame latitude
881 lonsFF,lonsLF: first and last frame longitude
883 t_start = time.time()
884 ALPHA_L = m.radians(-3.461515)
885 ALPHA_R = m.radians(3.461514)
887 mean_sens_alt, mean_sens_lat, gammaDeg = self.
__MSALGam()
889 firstFramePhi, firstFrameTheta, firstFramePsi = self.
__Quats2Euler(self.
L0.header[
'FFquat'])
890 lastFramePhi, lastFrameTheta, lastFramePsi = self.
__Quats2Euler(self.
L0.header[
'LFquat'])
891 if (
abs(firstFramePsi) <= 20)
and (
abs(lastFramePsi) <= 20):
893 elif (
abs(firstFramePsi) >= 160)
and (
abs(lastFramePsi) >= 160):
896 if self.
params[
'issOrientation'] != hofq:
897 msg =
'ISS orientation in input file does not agree with the value \
898 derived from the quaternions in the L0 file...'
901 firstFrameRotMat = self.
__RotMatrix(firstFramePhi, firstFrameTheta, firstFramePsi)
902 lastFrameRotMat = self.
__RotMatrix(lastFramePhi, lastFrameTheta, lastFramePsi)
903 centerAngleDeg = self.
L0.data[
'ScenePointingAngle'] - 60.0
904 if self.
params[
'issOrientation'] ==
'-XVV':
905 centerAngleDeg *= -1.0
906 centerAngleRad = m.radians(centerAngleDeg)
908 northAngleRad = centerAngleRad + ALPHA_L
909 southAngleRad = centerAngleRad + ALPHA_R
910 vecNorthFF = self.
__GetVecs(firstFrameRotMat, northAngleRad)
911 vecCenterFF = self.
__GetVecs(firstFrameRotMat, centerAngleRad)
912 vecSouthFF = self.
__GetVecs(firstFrameRotMat, southAngleRad)
913 vecNorthLF = self.
__GetVecs(lastFrameRotMat, northAngleRad)
914 vecCenterLF = self.
__GetVecs(lastFrameRotMat, centerAngleRad)
915 vecSouthLF = self.
__GetVecs(lastFrameRotMat, southAngleRad)
923 if self.
params[
'issOrientation'] ==
'+XVV':
924 distNorthFF = self.
__GetDist(zenithNorthFF, self.
L0.data[
'FFLatLonH'][2])
925 distNorthLF = self.
__GetDist(zenithNorthLF, self.
L0.data[
'FFLatLonH'][2])
926 distSouthFF = self.
__GetDist(zenithSouthFF, self.
L0.data[
'FFLatLonH'][2])
927 distSouthLF = self.
__GetDist(zenithSouthLF, self.
L0.data[
'FFLatLonH'][2])
929 distNorthFF = self.
__GetDist(zenithSouthFF, self.
L0.data[
'FFLatLonH'][2])
930 distNorthLF = self.
__GetDist(zenithSouthLF, self.
L0.data[
'FFLatLonH'][2])
931 distSouthFF = self.
__GetDist(zenithSouthFF, self.
L0.data[
'FFLatLonH'][2])
932 distSouthLF = self.
__GetDist(zenithSouthLF, self.
L0.data[
'FFLatLonH'][2])
933 bearingNorthFF, bearingSouthFF = bearingSouthFF, bearingNorthFF
934 bearingNorthLF, bearingSouthLF = bearingSouthLF, bearingNorthLF
935 distCenterFF = self.
__GetDist(zenithCenterFF, self.
L0.data[
'FFLatLonH'][2])
936 distCenterLF = self.
__GetDist(zenithCenterLF, self.
L0.data[
'FFLatLonH'][2])
937 bearingNorthFF += 90 - gammaDeg
938 bearingSouthFF += 90 - gammaDeg
939 bearingCenterFF += 90 - gammaDeg
940 bearingNorthLF += 90 - gammaDeg
941 bearingSouthLF += 90 - gammaDeg
942 bearingCenterLF += 90 - gammaDeg
945 np.repeat(self.
L0.data[
'FFLatLonH'][1],
947 np.array([distNorthFF, distCenterFF,
949 np.array([bearingNorthFF,
952 lonsFF[lonsFF < -180] += 360
953 lonsFF[lonsFF >= 180] -= 360
956 np.repeat(self.
L0.data[
'LFLatLonH'][1],
958 np.array([distNorthLF, distCenterLF,
960 np.array([bearingNorthLF,
963 lonsLF[lonsLF < -180] += 360
964 lonsLF[lonsLF >= 180] -= 360
965 angleDict = {
'latsFF': latsFF,
'latsLF': latsLF,
966 'lonsFF': lonsFF,
'lonsLF': lonsLF,
967 'centerAngleDeg': centerAngleDeg,
'gammaDeg': gammaDeg}
971 def __GetLonsLats(self, latsFF, latsLF, lonsFF, lonsLF):
972 nl_predark = self.
L0.data[
'n_predark']
973 nl_postdark = self.
L0.data[
'n_postdark']
974 nll = self.
L0.data[
'nl']
975 ns = self.
L0.data[
'ns']
976 nl = nll - nl_predark - nl_postdark
978 n22 = nl + nl_predark - 1
981 imCenLat = (latsFF[1] + latsLF[1]) * 0.5
982 if (np.abs(lonsLF[1] - lonsFF[1]) < 100):
983 imCenLon = (lonsLF[1] - lonsFF[1]) * 0.5
985 imCenLon = ((np.max([lonsFF[1], lonsLF[1]]) - 360) +
986 np.min(lonsLF[1], lonsFF[1]) * 0.5)
989 self.
geomDict[
'imCenLon'] = imCenLon
990 self.
geomDict[
'imCenLat'] = imCenLat
991 fullLats = np.vstack((latsFF, latsLF))
994 ffeasthem, = np.where(((lonsFF > 0) & (lonsFF <= 180)))
995 ffwesthem, = np.where(((lonsFF <= 0) | (lonsFF > 180)))
996 lfwesthem, = np.where(((lonsLF > -180) & (lonsLF <= 0)))
997 nffe, nffw, nlfw = ffeasthem.size, ffwesthem.size, lfwesthem.size
998 cross_date_line =
False
999 if (nffe > 0) & (nlfw > 0):
1000 lonsLF[lfwesthem] += 360
1001 cross_date_line =
True
1003 lonsFF[ffwesthem] += 360
1004 fullLons = np.vstack((lonsFF, lonsLF))
1007 fullLons[fullLons > 180] -= 360
1008 if ((self.
params[
'issOrientation'] ==
'+XVV') & (self.
params[
'flip_left_right'])) | \
1009 ((self.
params[
'issOrientation'] ==
'-XVV') & (
not self.
params[
'flip_left_right'])):
1010 fullLats = np.transpose(np.rot90(fullLats, 1))
1011 fullLons = np.transpose(np.rot90(fullLons, 1))
1012 self.
geomDict[
'fullLats'] = fullLats.T
1013 self.
geomDict[
'fullLons'] = fullLons.T
1016 def __GetViewAngles(self, centAngDeg, gammaDeg):
1018 Fill geomDict viewA and viewZ and viewAZ
1021 viewaz = 180 - gammaDeg
1022 elif centAngDeg > 0:
1023 viewaz = 360 - gammaDeg
1028 pixels = (np.arange(self.
L0.data[
'ns']) - 256.5) / 255.5
1029 viewl0 = 3.487 * pixels - 0.0035 * pixels ** 3
1030 spa = self.
L0.data[
'ScenePointingAngle']
1031 vl = (spa + viewl0 - 60)
1032 if self.
params[
'issOrientation'] !=
'+XVV':
1035 azNlocs = np.where(vl <= 0,
True,
False)
1037 viewa_line = np.zeros((self.
L0.data[
'ns'], 1))
1038 viewa_line[azNlocs] = 180 - gammaDeg
1039 viewa_line[azSlocs] = 360 - gammaDeg
1041 viewz = np.tile(np.reshape(viewl, (viewl.shape[0], 1)), (1, self.
ancDict[
'n2t'] + 3))
1042 viewa = np.tile(viewa_line, (1, self.
ancDict[
'n2t'] + 3))
1043 if self.
params[
'flip_left_right']:
1044 viewa = np.transpose(np.rot90(viewa, 1))
1045 viewz = np.transpose(np.rot90(viewz, 1))
1050 def __FillGeomDict(self):
1052 Fills geomDict, calls four auxiliaries functions;
1053 __GetFirstLastFrameLatsLon()
1054 __GetLonsLats() -> fills imcenlon/lat and fullLons/lats
1055 __GetSunAngles() -> fills sola/solz
1056 __GetViewAngles() -> fills viewaz/viewa/viewz
1060 angDict[
'lonsFF'], angDict[
'lonsLF'])
1065 def _StoreBadData(self):
1066 """Computes and stores bad, dropped and saturated pixels"""
1070 ns = self.
L0.data[
'ns']
1071 nb = self.
L0.data[
'nb']
1072 self.
badDataDict[
'bad_pixs'] = np.zeros((n2t+3, nb, ns), dtype=bool)
1073 self.
badDataDict[
'sat_pixs'] = np.zeros((n2t+3, nb, ns), dtype=bool)
1074 intData = self.
L0.data[
'pic0'][n21:n22+1]
1075 sat_pix = np.where(intData == 16383,
True,
False)
1076 bad_pix = np.where(intData == -1,
True,
False)
1085 Fills bad data flags array. Flags array is then saved to file.
1086 Flagbits used are 0, 1, 2, 4, 5, 6; also 7 if doCal option is on.
1087 Bit 3, NAVWARN, is set by default
1090 flags = np.ones((self.
ancDict[
'n2t']+3, self.
L0.data[
'ns']), dtype=
'int8') * 4
1091 bad_sunzen = np.where(self.
geomDict[
'sunposzen'] > 88,
True,
False)
1092 if bad_sunzen.any():
1093 self.
ancDict[
'rhot_nir'][bad_sunzen] *= m.pi
1094 self.
ancDict[
'rhot_red'][bad_sunzen] *= m.pi
1095 if not bad_sunzen.all():
1096 goodsz = np.logical_not(bad_sunzen)
1097 self.
ancDict[
'rhot_nir'][goodsz] *= (m.pi *
1099 (self.
geomDict[
'sunposzen'][goodsz])))
1100 self.
ancDict[
'rhot_red'][goodsz] *= (m.pi *
1102 (self.
geomDict[
'sunposzen'][goodsz])))
1103 ptr = np.where(((self.
geomDict[
'fullLats'] > 90) |
1105 (self.
geomDict[
'fullLons'] < -180)),
True,
False)
1106 flags[ptr] = flags[ptr] | 2
1107 ptr = np.where(self.
geomDict[
'viewz'] > 60,
True,
False)
1108 flags[ptr] = flags[ptr] | 8
1109 ptr = np.where(self.
geomDict[
'sunposzen'] > 75,
True,
False)
1110 flags[ptr] = flags[ptr] | 16
1111 ptr = np.where(self.
badDataDict[
'sat_pixs'] > 0,
True,
False)
1112 flags[ptr] = flags[ptr] | 32
1113 ptr = np.where(self.
badDataDict[
'bad_pixs'] > 0,
True,
False)
1114 flags[ptr] = flags[ptr] | 64
1116 if self.
inpDict[
'doCal'] & self.
L0.data[
'nb'] == 128:
1117 ptr = np.where((self.
ancDict[
'rhot_nir'] > 0.02),
True,
False)
1118 flags[ptr] = flags[ptr] | 1
1119 pos_red = np.where(self.
ancDict[
'rhot_red'] > 0)
1120 ratio = np.zeros((self.
L0.data[
'ns'], self.
ancDict[
'n2t']+3))
1121 ratio[pos_red] = self.
ancDict[
'rhot_nir'][pos_red] / self.
ancDict[
'rhot_red'][pos_red]
1122 ptr = np.where((((self.
ancDict[
'rhot_red'] > 0.5) & (self.
ancDict[
'rhot_nir'] > 0.5))
1123 | ((ratio > 0.8) & (ratio < 1.1))),
True,
False)
1124 flags[ptr] = flags[ptr] | 128
1125 ncgrp[
'scan_quality_flags'][:] = flags.T
1130 Writes _rad.bil (and _rad.hdr files?).
1132 Steps: assess whether to export as bil or bip and transpose accordingly,
1135 Note: The data array, initially, has dims nl x nb x ns, where
1136 nl = number of lines, nb = number of bands, ns = number of samples.
1138 nlsub = self.
ancDict[
'n2t'] + 3
1139 nb = self.
L0.data[
'nb']
1140 ns = self.
L0.data[
'ns']
1141 outArray = np.zeros((nlsub, ns, nb))
1149 lt.wavelengths = self.
params[
"wave_um"].astype(
'f4') * 1000
1150 lt.fwhm = self.
params[
'fwhm'].astype(
'f4')
1153 periodGrp.Beginning_Date = self.
params[
'begin_date']
1154 periodGrp.Ending_Date = self.
params[
'end_date']
1155 periodGrp.Beginning_Time = self.
params[
'begin_time']
1156 periodGrp.Ending_Time = self.
params[
'end_time']