OB.DAAC Logo
NASA Logo
Ocean Color Science Software

ocssw V2022
HicoL0toL1B.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3 """
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:
7 
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.)
11 
12  Lines-1333->1359: Nothing useful, a couple of assignments to file_ and geo_history.
13  ->deal with that later.
14 
15 """
16 import os
17 import sys
18 import time
19 import functools
20 import math as m
21 import calendar as cal
22 import logging
23 import pickle
24 
25 import pandas as pd
26 from datetime import datetime as DT
27 from datetime import timedelta as TDel
28 import numpy as np
29 from scipy import interpolate
30 
31 from hico.HicoL0Reader import HicoL0Reader
32 
33 
34 
35 def TimeIt(func):
36  @functools.wraps(func)
37  def wrapper(self, *args, **kwargs):
38  start = time.time()
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))
42  return result
43  return wrapper
44 
45 def LoopDrkLine(data, startLine, stopLine):
46  '''
47  This is here because I was trying to jit it with numba. tbc...
48  '''
49  win_size = 5
50  win_n_sd = 3.0
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])
66 
67 
69  """
70  Object to assemble the data for converting a HICO L0 file to
71  L1b format.
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_
83  wsm12_iss_1_2012.dat
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
91  supplied
92  doCal: calibration flag
93  => default = true.
94 
95  """
96  @TimeIt
97  def __init__(self, iArgs, **kwds):
98  ocdataroot = os.environ['OCDATAROOT']
99  ocvarroot = os.environ['OCVARROOT']
100  mainLoggerName = kwds.pop('parentLoggerName', __name__)
101  self.logger = logging.getLogger('%s.HicoL0toL1B' % mainLoggerName)
102  self.inpDict = {}
103  self.fNamesDict = {}
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"]
109  else:
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"]
114  else:
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"]
121  else:
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"]
127  else:
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"]
133  else:
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))
143  # populate HicoBil object
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'])
168  self.badDataDict = dict.fromkeys(['bad_pixs', 'sat_pixs'])
169  self.outFilNamesDict = dict.fromkeys(['geomEnvi', 'geomHdr', 'flagEnvi',
170  'flagHdr', 'ndviEnvi',
171  'ndviHdr', 'rgbJpg', 'rgbBip',
172  'rgbHdr', 'outRad', 'outRadHdr'])
173  self.ancDict = dict.fromkeys(['n11', 'n12', 'n1t',
174  'n21', 'n22', 'n2t',
175  'n31', 'n32', 'n3t',
176  'd2_ratio']) # , 'rhot_red', 'rhot_nir'])
177  self.L0 = HicoL0Reader(iArgs.l0file, self.logger.name)
178  self.__FillOutputFileNames()
179  self.__ReadWaveFile()
180  self.__SetL02L1BParams()
181  self.__GetISSOrientation()
182  if self.inpDict['doCal']:
183  self.__ReadCalFile()
184  self.__ReadBandScaleFile()
185  self.__FillAncDict()
186  self.__SetPeriods()
187  self.__FillGeomDict()
188  n21 = self.ancDict['n21']
189  n22p = self.ancDict['n22'] + 1
190  self.floatData = self.L0.data['pic0'][n21:n22p].astype('f4')
191 
192  @TimeIt
193  def __FillAncDict(self):
194  self.logger.debug('filling ancDict')
195  self.ancDict['n11'] = 3
196  self.ancDict['n12'] = self.L0.data['n_predark'] - 1
197  self.ancDict['n1t'] = self.ancDict['n12'] - self.ancDict['n11'] + 1
198  self.ancDict['n20'] = self.ancDict['n12'] + 1 # start of HICO L1B image
199  # start of non zeroed out HICO L1B image
200  self.ancDict['n21'] = self.L0.data['n_predark'] + self.ancDict['n11']
201  self.ancDict['n2t'] = self.L0.data['nl'] - self.L0.data['n_predark'] -\
202  self.L0.data['n_postdark'] - self.ancDict['n11']
203  # end of HICO L1B image
204  self.ancDict['n22'] = self.ancDict['n2t'] + self.ancDict['n21'] - 1
205  self.ancDict['n31'] = self.L0.data['nl'] - self.L0.data['n_postdark']\
206  + self.ancDict['n11']
207  self.ancDict['n32'] = self.L0.data['nl'] - 1
208  self.ancDict['n3t'] = self.ancDict['n32'] - self.ancDict['n31'] + 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
215 
216  @TimeIt
217  def __FillOutputFileNames(self):
218  '''
219  Fills dictionary containing output filenames
220  '''
221  if self.inpDict['doCal']:
222  radtype = '_rad'
223  else:
224  radtype = '_nocal'
225  outFileNameBase = os.path.basename(self.inpDict['l0File']) + radtype
226  self.outFilNamesDict['geomEnvi'] = outFileNameBase + '_geom.bil'
227  self.outFilNamesDict['geomHdr'] = outFileNameBase + '_geom.hdr'
228  self.outFilNamesDict['outRad'] = outFileNameBase + '.bil'
229  self.outFilNamesDict['outRadHdr'] = outFileNameBase + '.hdr'
230  self.outFilNamesDict['flagEnvi'] = outFileNameBase + '_flag.bip'
231  self.outFilNamesDict['flagHdr'] = outFileNameBase + '_flag.hdr'
232  self.outFilNamesDict['ndviEnvi'] = outFileNameBase + '_ndvi.bip'
233  self.outFilNamesDict['ndviHdr'] = outFileNameBase + '_ndvi.hdr'
234  self.outFilNamesDict['rgbBip'] = outFileNameBase + '_rgb.bip'
235  self.outFilNamesDict['rgbHdr'] = outFileNameBase + '_rgb.hdr'
236  self.outFilNamesDict['rgbJpg'] = outFileNameBase + '_rgb.jpg'
237 
238  @TimeIt
239  def __GetISSOrientation(self):
240  """
241  Determines ISS orientation
242  """
243  flipLR = True
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]):
250  flipLR = False
251  issOrientation = "-XVV"
252  self.params['flip_left_right'] = flipLR
253  self.params['issOrientation'] = issOrientation
254 
255  def __SetPeriods(self):
256  '''Formatting time for writing to nc file'''
257  datefmt = '%Y%m%d'
258  timefmt = '%H%M%S'
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)
268 
269  @TimeIt
270  def __SetL02L1BParams(self):
271  """
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
275  """
276  offset = -300
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)
282  ff_temp = self.__GetCamTemp(self.inpDict["csvFile"], ff_time)
283  dark_temp = self.__GetCamTemp(self.inpDict["csvFile"], dark_time)
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 # out_scale
294  self.params['frameTransferTime'] = 0.00111
295  self.params['exposureTime'] = 0.0137
296  self.params['validL0Pixels'] = 508
297  self.params['nb_read'] = 128 # nb
298  self.params['nb_all'] = 170 # nbb
299  self.params['ns'] = 512
300  self.params['nl_predark'] = 200
301  self.params['nl_postdark'] = 200
302  self.params['out_type'] = 'uint16'
303  self.__MakeFWHM()
304 
305  # %% STATICMETHOD HELPER FUNCTIONS
306  @staticmethod
307  def GetLineAveragedPic(tempData):
308  good = np.where(tempData != -1, True, False)
309  picXave = tempData[good].reshape(tempData.shape).mean(axis=0,
310  dtype='f4')
311  return picXave
312 
313  @staticmethod
314  def __LinearCongrid(arr, newdims):
315  method = 'linear'
316  minusone = True
317  centre = False
318  m1 = np.cast[int](minusone)
319  ndims = len(arr.shape)
320  dimlist = []
321  ofs = np.cast[int](centre) * 0.5
322  old = np.array(arr.shape)
323  newdims = np.asarray(newdims, dtype=float)
324  # calculate new dims
325  for i in range(ndims):
326  base = np.arange(newdims[i])
327  dimlist.append((old[i] - m1) / (newdims[i] - m1) *
328  (base + ofs) - ofs)
329  # specify old dims
330  olddims = [np.arange(i, dtype=np.float) for i in list(arr.shape)]
331 
332  # first interpolation - for ndims = any
333  mint = interpolate.interp1d(olddims[-1], arr, kind=method)
334  newa = mint(dimlist[-1])
335  # trorder = [ndims - 1] + range(ndims - 1)
336  for i in range(ndims - 2, -1, -1):
337  newa = newa.transpose() # trorder)
338  mint = interpolate.interp1d(olddims[i], newa, kind=method)
339  newa = mint(dimlist[i])
340  if ndims > 1:
341  # need one more transpose to return to original dimensions
342  newa = newa.transpose() # trorder)
343  return newa
344 
345  @staticmethod
346  def __RotMatrix(roll, pitch, yaw):
347  """
348  Computes a rotation matrix given roll,pitch and yaw, in that order.
349  Resulting rotation matrix shape is (3x3)
350  """
351  rotMat = np.zeros((3, 3))
352  phi = m.radians(roll)
353  theta = m.radians(pitch)
354  psi = m.radians(yaw)
355  ct = np.cos(theta)
356  st = np.sin(theta)
357  cp = np.cos(psi)
358  sp = np.sin(psi)
359  cf = np.cos(phi)
360  sf = np.sin(phi)
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]]
364 
365  return np.array(rotMat)
366 
367  @staticmethod
368  def __RngBear2LatLonEllipsoid(latInArr, lonInArr, distArr, brngInArr):
369  """
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.
373 
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
379  """
380  # WGS84 constants (www.wgs84.com)
381  flatFactor = 1/298.257223563
382  semiMajAx = 6378137
383 
384  # Initial Values
385  eps = 0.5e-13 # tolerance
386  glon1 = np.radians(lonInArr)
387  glat1 = np.radians(latInArr)
388  faz = np.radians(brngInArr)
389  r = 1 - flatFactor
390  tu = r*np.sin(glat1) / np.cos(glat1)
391  sf = np.sin(faz)
392  cf = np.cos(faz)
393  baz = np.zeros_like(faz)
394  w = cf != 0
395  baz = np.arctan2(tu[w], cf[w])*2
396  cu = (np.sqrt(tu * tu + 1)) ** -1
397  su = tu*cu
398  sa = cu*sf
399  c2a = -sa * sa + 1
400  x = np.sqrt((1/(r**2) - 1) * c2a + 1) + 1
401  x = (x - 2) / x
402  c = 1 - x
403  c = ((x*x/4) + 1) / c
404  d = (0.375 * x**3 - x)
405  tu = distArr / r / semiMajAx / c
406  y = tu
407 
408  while True:
409  sy = np.sin(y)
410  cy = np.cos(y)
411  cz = np.cos(baz + y)
412  e = 2 * cz ** 2 - 1
413  c = y
414  x = e * cy
415  y = 2 * e - 1
416  y = ((((2*sy)**2 - 3) * y * cz * d / 6 + x) * d / 4 - cz) * sy *\
417  d + tu
418  if np.max(abs(y - c)) <= eps:
419  break
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)
433 
434  return latOutArr, lonOutArr, brngOutArr
435 
436  @staticmethod
437  def __Quats2Euler(q, qtype=0):
438  '''
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
444 
445  '''
446  if qtype == 0:
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))
452  else:
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
459 
460  @staticmethod
461  def __Vec2ZenithBearing(vec):
462  """"
463  Input: vec[x,y,z]
464  Output: zenith and bearing angles (radians)
465 
466  """
467  zenithRad = m.acos(vec[2])
468  bearingRad = m.atan2(vec[1], vec[0])
469  if bearingRad < 0:
470  bearingRad += 2 * m.pi
471  return zenithRad, bearingRad
472 
473  @staticmethod
474  def __GetVecs(rotMat, angleRad):
475  """
476  Output 3x1 ref vector.
477  Input: * 3x3 rotation matrix, rotMat
478  * reference angle in radians, angleRad
479  """
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)])
482  return refVec
483 
484  @staticmethod
485  def __GetDist(zenithAngleRad, height):
486  """
487  Computes distance along surface of ellipsoid (km)
488  """
489  earthRadiusKM = 6378
490  dist = (m.asin(((earthRadiusKM + height) / earthRadiusKM) *
491  m.sin(abs(zenithAngleRad))) -
492  abs(zenithAngleRad)) * earthRadiusKM
493  return dist
494 
495  @staticmethod
496  def __ValueLocate(vec, vals):
497  '''
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
502  '''
503  return np.amax([np.where(v >= vec, np.arange(len(vec)), -1)
504  for v in vals], axis=1)
505 
506  def __GetCamTemp(self, csvFileName, timeData):
507  """
508  READS CSV FILE
509  """
510  use_columns = ["ISSTIMEYEAR", "ISSTIMEMONTH", "ISSTIMEDAY",
511  "ISSTIMEHOUR", "ISSTIMEMINUTE", "ISSTIMESECOND",
512  "HICOCAMERATEMP"]
513  badval = -1
514  try:
515  df = pd.read_csv(csvFileName, usecols=use_columns)
516  except ValueError:
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')
521  try:
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")
526  sys.exit(status=1)
527  try:
528  df = pd.read_csv(csvFileName, skiprows=1, names=col_names,
529  usecols=use_columns)
530  self.logger.info('csv format issue resolved')
531  except ValueError:
532  self.logger.error("Something is wrong with the CSV file")
533  sys.exit(status=1)
534 
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
541  if x.size == 1:
542  return x[0]
543  else:
544  return badval
545 
546  @TimeIt
547  def __ReadWaveFile(self):
548  '''Reads wavelength file'''
549  if self.inpDict['wvlFile'] == 'DEFAULT':
550  nb = self.L0.data['nb']
551  if nb == 128:
552  wc1 = 0.353428
553  wc2 = 0.005728
554  else:
555  wc1 = 0.3515095
556  wc2 = 0.0019095
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)
560  else:
561  self.params['wave_um'] = np.loadtxt(self.inpDict['wvlFile'])
562 
563  @TimeIt
564  def __ReadCalFile(self):
565  """
566  READS hicoFitCoeffs FILE
567  """
568  if sys.byteorder == 'little':
569  dt = np.dtype('<f')
570  else:
571  dt = np.dtype('>f')
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)
581  if cal_sh != 0:
582  hhold = coeff_13ms
583  if cal_sh < 0:
584  coeff_13ms[:, 0:512 + cal_sh] = hhold[:, 0-cal_sh:512]
585  coeff_13ms[:, 512 + cal_sh:512] = 0
586  else:
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
591 
592  @TimeIt
593  def __ReadBandScaleFile(self):
594  """
595  READS bandScaleFacor FILE
596  """
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
601 
602  @TimeIt
603  def __MSALGam(self):
604  """
605  Returns angle of slit on the ground, returned in degrees
606  """
607  msAlt = (self.L0.data['FFLatLonH'][2] + self.L0.data['LFLatLonH'][2]
608  ) * 0.5
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]:
614  gammaRad *= -1
615  gammaDeg = m.degrees(gammaRad)
616  return msAlt, msLatDeg, gammaDeg
617 
618  @TimeIt
619  def _DoDarkCorrection(self):
620  """
621  hico_to_rad_kwp_truncate.pro
622  lines1760 - 1932
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
628  """
629  # nl, ns, nb = self.L0.data['nl'], self.L0.data['ns'], self.L0.data['nb']
630  n11, n12 = self.ancDict['n11'], self.ancDict['n12']
631  n21, n22 = self.ancDict['n21'], self.ancDict['n22']
632  n1t, n2t = self.ancDict['n1t'], self.ancDict['n2t']
633  n31, n32 = self.ancDict['n31'], self.ancDict['n32']
634  # theData = self.theData
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
642  dark_b_coef = 11.2
643  if fftemp >= 10.0 and fftemp < 24.0:
644  dark_b_coef = 11.15
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
651  LoopDrkLine(intData, startLine=n11, stopLine=n12) # modifies theData
652  self.logger.debug('intData after n11-: %s' % intData.dtype)
653  LoopDrkLine(intData, startLine=n31, stopLine=n32) # modifies theData
654  self.logger.debug('intData after n31-: %s' % intData.dtype)
655  if dark1Pos == -395:
656  pic13ave += self.GetLineAveragedPic(intData[n11:n12+1])
657  prePostDarkOK += 1
658  if dark2Pos == -395:
659  pic13ave += self.GetLineAveragedPic(intData[n31:n32+1])
660  prePostDarkOK += 2
661  if prePostDarkOK == 3:
662  pic13ave /= 2
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)
668  # self.floatData += pic13ave - b * f13av + 1.2
669  # a2 = pic13ave - b * f13av + 1.2
670  self.floatData = intData[n21:n22+1] - (pic13ave - b * f13av + 1.2 + b * fit20.astype('f4'))
671  # self.floatData += b[np.newaxis, :, :] * fit20.astype('f4')
672  self.logger.debug('floatData after drk corr.: %s' % self.floatData.dtype)
673 
674  @TimeIt
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)
683  if nb == 128:
684  tot_count = (self.floatData.sum(axis=1) +
685  self.floatData[:, -1, :] * (171 - nb)) * \
686  (3/ns) * frac_eqB6
687  else:
688  tot_count = (self.floatData.sum(axis=1) +
689  self.floatData[:, -1, :] * (512 - nb)) * \
690  (1/ns) * frac_eqB6
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)
694 
695  @TimeIt
696  def __MakeFWHM(self):
697  nb = self.L0.data['nb']
698  fwhm = np.empty(nb)
699  if self.inpDict['doSmooth']:
700  fwhm[:3] = 0.005
701  fwhm[3:69] = 0.01
702  fwhm[69:nb-4] = 0.02
703  fwhm[nb-4:] = 0.005
704  else:
705  if nb == 128:
706  fwc = 0.005
707  elif nb == 384:
708  fwc = 0.00167
709  fwhm = np.ones(nb) * fwc
710  self.params['fwhm'] = fwhm * 1000
711 
712  @TimeIt
713  def _DoGaussianSmoothing(self):
714  ''' Two steps 1-prepare parameters for gaussian smoothing
715  2-perform gaussian smoothing
716  '''
717  nb = self.L0.data['nb']
718  n2t = self.ancDict['n2t']
719  # prepare gaussian smoothing
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])))
724  # normalize
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 # diff indexing order than idl
731  for i in range(69, nb-4):
732  smoother[i, i-4:i+5] = fwhm_20nm # diff indexing order than idl
733  for li in range(n2t):
734  self.floatData[li] = smoother.dot(self.floatData[li])
735  self.logger.debug("floatData: %s" % self.floatData.dtype)
736 
737  @TimeIt
738  def _Do2ndOrdCorrection(self):
739  """
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.
743  """
744  nb = self.L0.data['nb']
745  n2t = self.ancDict['n2t']
746  # n21 = self.ancDict['n21']
747  # n22 = self.ancDict['n22']
748  wl = self.params['wave_um']
749  wave_half = wl / 2
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
754  maP = self.__ValueLocate(wl, wave_half)
755  test = maP != -1
756  s2data = np.loadtxt(self.inpDict['secOrFile'], skiprows=1)
757  if nb == 128:
758  scale = s2data[:, 2]
759  else:
760  scale = np.interp(wl, s2data[:, 0], s2data[:, 2])
761  so2 = np.diagflat([1]*nb).astype('f8')
762  for i in range(nb):
763  if test[i]:
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):
768  # self.floatData[li] = so2.dot(self.floatData[li])
769  self.floatData[li] = so2.dot(self.floatData[li])
770 
771  @TimeIt
772  def _ApplyMasks(self):
773  '''Called by _DoCalMaskAndScale()'''
774  ns = self.L0.data['ns']
775  bad_sat_pix = self.badDataDict['bad_pixs'][3:] + self.badDataDict['sat_pixs'][3:]
776  if bad_sat_pix.any():
777  self.floatData[bad_sat_pix] = 0
778  #if self.params['validL0Pixels'][0] > 0:
779  #left_mask = self.params['validL0Pixels'][0]
780  #self.floatData[:, :, :left_mask] = 0
781  #if self.params['validL0Pixels'] < (ns - 1):
782  right_mask = self.params['validL0Pixels']
783  self.floatData[:, :, right_mask:ns] = 0
784 
785  @TimeIt
786  def _FlipDataArray(self):
787  ''' hack to use numpy's fliplr method on a 3D array. '''
788  self.floatData = np.rollaxis(self.floatData, 0, 3)
789  self.floatData = np.fliplr(self.floatData)
790  self.floatData = np.rollaxis(self.floatData, 2, 0)
791 
792  @TimeIt
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.
796  '''
797  cal_mult = self.params['cal_mult']
798  gao_scale = self.params['bScaleFactor']
799  out_scale = self.params['outputScaleFactor']
800  self.floatData *= self.params['coeff_13ms']
801  self._ApplyMasks()
802  if self.params['flip_left_right']:
803  self._FlipDataArray()
804  self.floatData *= cal_mult
805  self.floatData *= gao_scale
806  return None
807 
808  @TimeIt
809  def ConvertRaw2Rad(self):
810  self._DoDarkCorrection() # do darkSubtraction
811  self.logger.info("Dark correction done")
812  self._StoreBadData()
813  self.logger.info("Bad data indexed")
814  self._DoSmearCorrection()
815  self.logger.info("Smear correction done")
816  self._Do2ndOrdCorrection()
817  self.logger.info("Second order correction done")
818  if self.inpDict['doSmooth']:
819  self._DoGaussianSmoothing()
820  self.logger.info("Gaussian smoothing done")
821  if self.inpDict['doCal'] and self.L0.data['nb'] == 128:
822  self._DoCalMaskAndScale()
823  self.logger.info("Calibration/Masking/Scaling done")
824  elif not self.inpDict['doCal'] and self.L0.data['nb'] == 384:
825  self.floatData /= self.params['outputScaleFactor']
826  self.logger.info("Raw to radiance conversion completed")
827 
828  # GEOM Helper files
829  @TimeIt
830  def __GetSunAngles(self):
831  """
832  Inputs
833  -------
834  iday: year's day number
835  hr: GMT time of day in real hours, e.g. 4:30PM = 16.5
836  Returns
837  -------
838  solarAngles {ndarray}, where
839  solarAngles[0] = solar zenith angle in degrees
840  solarAngles[1] = solar azimuth angle in degrees clockwise from North
841  """
842  iday, hr = self.L0.data['yearDay'], self.L0.data['floatHour']
843  xlon, ylat = self.geomDict['fullLons'], self.geomDict['fullLats']
844  # Compute solar declination angle
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)
851  # Time correction for solar hour angle, and solar hour angle
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)
858  # rlon = np.deg2rad(xlon)
859  rha = np.radians(xha)
860  # Sun zenith
861  costmp = np.sin(rlat) * np.sin(rsdec) + np.cos(rlat) * np.cos(rsdec) * np.cos(rha)
862  # eps = abs(costmp)
863  costmp[costmp > 1] = 1
864  costmp[costmp < -1] = -1
865  rsunz = np.arccos(costmp)
866  sunz = np.rad2deg(rsunz)
867  # Sun azimuth
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)
872  self.geomDict['sunposzen'] = sunz
873  self.geomDict['sunposaz'] = suna
874 
875  @TimeIt
876  def __GetFirstLastFrameLatsLons(self):
877  """
878  Returns
879  -------
880  latsFF,latsLF: first and last frame latitude
881  lonsFF,lonsLF: first and last frame longitude
882  """
883  t_start = time.time()
884  ALPHA_L = m.radians(-3.461515)
885  ALPHA_R = m.radians(3.461514)
886  # sensor location
887  mean_sens_alt, mean_sens_lat, gammaDeg = self.__MSALGam()
888  # quaternions to euler
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):
892  hofq = '+XVV'
893  elif (abs(firstFramePsi) >= 160) and (abs(lastFramePsi) >= 160):
894  hofq = '-XVV'
895 
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...'
899  self.logger.warning(msg)
900  # build (3x3) rotation matrices for first & last frames
901  firstFrameRotMat = self.__RotMatrix(firstFramePhi, firstFrameTheta, firstFramePsi)
902  lastFrameRotMat = self.__RotMatrix(lastFramePhi, lastFrameTheta, lastFramePsi)
903  centerAngleDeg = self.L0.data['ScenePointingAngle'] - 60.0 # signed angle from nadir
904  if self.params['issOrientation'] == '-XVV':
905  centerAngleDeg *= -1.0
906  centerAngleRad = m.radians(centerAngleDeg)
907  # north side is always to left of path in +XVV
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)
916  zenithNorthFF, bearingNorthFF = self.__Vec2ZenithBearing(vecNorthFF)
917  zenithCenterFF, bearingCenterFF = self.__Vec2ZenithBearing(vecCenterFF)
918  zenithSouthFF, bearingSouthFF = self.__Vec2ZenithBearing(vecSouthFF)
919  zenithNorthLF, bearingNorthLF = self.__Vec2ZenithBearing(vecNorthLF)
920  zenithCenterLF, bearingCenterLF = self.__Vec2ZenithBearing(vecCenterLF)
921  zenithSouthLF, bearingSouthLF = self.__Vec2ZenithBearing(vecSouthLF)
922 
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])
928  else:
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
943  latsFF, lonsFF, _ = self.__RngBear2LatLonEllipsoid(np.repeat(self.L0.data['FFLatLonH'][0],
944  3),
945  np.repeat(self.L0.data['FFLatLonH'][1],
946  3),
947  np.array([distNorthFF, distCenterFF,
948  distSouthFF]),
949  np.array([bearingNorthFF,
950  bearingCenterFF,
951  bearingSouthFF]))
952  lonsFF[lonsFF < -180] += 360
953  lonsFF[lonsFF >= 180] -= 360
954  latsLF, lonsLF, _ = self.__RngBear2LatLonEllipsoid(np.repeat(self.L0.data['LFLatLonH'][0],
955  3),
956  np.repeat(self.L0.data['LFLatLonH'][1],
957  3),
958  np.array([distNorthLF, distCenterLF,
959  distSouthLF]),
960  np.array([bearingNorthLF,
961  bearingCenterLF,
962  bearingSouthLF]))
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}
968  return angleDict
969 
970  @TimeIt
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
977  n21 = nl_predark + 3
978  n22 = nl + nl_predark - 1
979  n2t = n22 - n21 + 1
980  # Deal with lats/lons first
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
984  else:
985  imCenLon = ((np.max([lonsFF[1], lonsLF[1]]) - 360) +
986  np.min(lonsLF[1], lonsFF[1]) * 0.5)
987  if imCenLon < -180:
988  imCenLon += 360
989  self.geomDict['imCenLon'] = imCenLon
990  self.geomDict['imCenLat'] = imCenLat
991  fullLats = np.vstack((latsFF, latsLF))
992  fullLats = self.__LinearCongrid(fullLats, (ns, n2t+3))
993 
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
1002  if nffw > 0:
1003  lonsFF[ffwesthem] += 360
1004  fullLons = np.vstack((lonsFF, lonsLF))
1005  fullLons = self.__LinearCongrid(fullLons, (ns, n2t+3))
1006  if cross_date_line:
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
1014 
1015  @TimeIt
1016  def __GetViewAngles(self, centAngDeg, gammaDeg):
1017  '''
1018  Fill geomDict viewA and viewZ and viewAZ
1019  '''
1020  if centAngDeg < 0:
1021  viewaz = 180 - gammaDeg
1022  elif centAngDeg > 0:
1023  viewaz = 360 - gammaDeg
1024  else:
1025  viewaz = 0
1026  viewaz %= 360
1027  self.geomDict['viewaz'] = viewaz
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':
1033  vl = -vl
1034  viewl = np.abs(vl)
1035  azNlocs = np.where(vl <= 0, True, False)
1036  azSlocs = ~azNlocs
1037  viewa_line = np.zeros((self.L0.data['ns'], 1))
1038  viewa_line[azNlocs] = 180 - gammaDeg
1039  viewa_line[azSlocs] = 360 - gammaDeg
1040  viewa_line %= 360
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))
1046  self.geomDict['viewa'] = viewa.T
1047  self.geomDict['viewz'] = viewz.T
1048 
1049  @TimeIt
1050  def __FillGeomDict(self):
1051  '''
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
1057  '''
1058  angDict = self.__GetFirstLastFrameLatsLons()
1059  self.__GetLonsLats(angDict['latsFF'], angDict['latsLF'],
1060  angDict['lonsFF'], angDict['lonsLF'])
1061  self.__GetSunAngles()
1062  self.__GetViewAngles(angDict['centerAngleDeg'], angDict['gammaDeg'])
1063 
1064  @TimeIt
1065  def _StoreBadData(self):
1066  """Computes and stores bad, dropped and saturated pixels"""
1067  n21 = self.ancDict['n21']
1068  n22 = self.ancDict['n22']
1069  n2t = self.ancDict['n2t']
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] # ref to subset of theData
1075  sat_pix = np.where(intData == 16383, True, False)
1076  bad_pix = np.where(intData == -1, True, False)
1077  if bad_pix.any():
1078  self.badDataDict['bad_pixs'][3:] = bad_pix
1079  if sat_pix.any():
1080  self.badDataDict['sat_pixs'][3:] = sat_pix
1081 
1082  @TimeIt
1083  def FillWriteFlags(self, ncgrp):
1084  '''
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
1088 
1089  '''
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 *
1098  np.cos(np.deg2rad
1099  (self.geomDict['sunposzen'][goodsz])))
1100  self.ancDict['rhot_red'][goodsz] *= (m.pi *
1101  np.cos(np.deg2rad
1102  (self.geomDict['sunposzen'][goodsz])))
1103  ptr = np.where(((self.geomDict['fullLats'] > 90) |
1104  (self.geomDict['fullLats'] < -90) | (self.geomDict['fullLons'] > 180) |
1105  (self.geomDict['fullLons'] < -180)), True, False)
1106  flags[ptr] = flags[ptr] | 2 # bit 2 is navfail
1107  ptr = np.where(self.geomDict['viewz'] > 60, True, False)
1108  flags[ptr] = flags[ptr] | 8 # bit 4 (?) is hisatzen
1109  ptr = np.where(self.geomDict['sunposzen'] > 75, True, False)
1110  flags[ptr] = flags[ptr] | 16 # bit 5 (??) is hisolzen
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
1115  flags.tofile(self.outFilNamesDict['flagEnvi'])
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 # bit 1 is land
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
1126 
1127  @TimeIt
1128  def WriteRadFile(self, prodGrp, periodGrp):
1129  '''
1130  Writes _rad.bil (and _rad.hdr files?).
1131 
1132  Steps: assess whether to export as bil or bip and transpose accordingly,
1133  extract data
1134  write data
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.
1137  '''
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))
1142  outArray[3:] = self.floatData.transpose(0, 2, 1)
1143  # outArray dim is (nl x nb x ns)
1144  # we want dims nl x ns x nb
1145  lt = prodGrp['Lt']
1146  lt[:] = outArray
1147  # fwhm = prodGrp['fwhm']
1148  # fwhm[:] = self.params["fwhm"]
1149  lt.wavelengths = self.params["wave_um"].astype('f4') * 1000 # switch to nm
1150  lt.fwhm = self.params['fwhm'].astype('f4')
1151  # wavelengths = prodGrp['wavelengths']
1152  # wavelengths[:] = self.params["wave_um"] * 1000 # switch to nm
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']
def TimeIt(func)
Definition: HicoL0toL1B.py:35
def __GetCamTemp(self, csvFileName, timeData)
Definition: HicoL0toL1B.py:506
list(APPEND LIBS ${PGSTK_LIBRARIES}) add_executable(atteph_info_modis atteph_info_modis.c) target_link_libraries(atteph_info_modis $
Definition: CMakeLists.txt:7
float mean(float *xs, int sample_size)
Definition: numerical.c:81
def __GetDist(zenithAngleRad, height)
Definition: HicoL0toL1B.py:485
def __GetViewAngles(self, centAngDeg, gammaDeg)
def FillWriteFlags(self, ncgrp)
def __RotMatrix(roll, pitch, yaw)
Definition: HicoL0toL1B.py:346
def LoopDrkLine(data, startLine, stopLine)
Definition: HicoL0toL1B.py:45
def __Quats2Euler(q, qtype=0)
Definition: HicoL0toL1B.py:437
def GetLineAveragedPic(tempData)
Definition: HicoL0toL1B.py:307
def WriteRadFile(self, prodGrp, periodGrp)
subroutine func(x, conec, n, bconecno, bn, units, u, inno, i, outno, o, Input, Targ, p, sqerr)
Definition: ffnet.f:287
def __init__(self, iArgs, **kwds)
Definition: HicoL0toL1B.py:97
def __RngBear2LatLonEllipsoid(latInArr, lonInArr, distArr, brngInArr)
Definition: HicoL0toL1B.py:368
def __ValueLocate(vec, vals)
Definition: HicoL0toL1B.py:496
def __GetLonsLats(self, latsFF, latsLF, lonsFF, lonsLF)
Definition: HicoL0toL1B.py:971
const char * str
Definition: l1c_msi.cpp:35
def __GetVecs(rotMat, angleRad)
Definition: HicoL0toL1B.py:474
def __LinearCongrid(arr, newdims)
Definition: HicoL0toL1B.py:314
#define abs(a)
Definition: misc.h:90
void transpose(float *in[], float *out[])
Definition: get_zenaz.c:97