OB.DAAC Logo
NASA Logo
Ocean Color Science Software

ocssw V2022
HicoL0Reader.py
Go to the documentation of this file.
1 # -*- coding: utf-8 -*-
2 """
3 Created on Tue Feb 3 14:01:27 2015
4 Reads Hico L0 bil file.
5 Header (first 256 bytes) parsed into header dictionary.
6 Data
7 @author: EKarako:ylu:
8 """
9 import sys
10 import struct
11 import numpy as np
12 import datetime as dtime
13 from math import sqrt, atan, atan2, pi
14 import logging
15 import time
16 
17 
18 class HicoL0Reader(object):
19 
20  def __init__(self, l0FileName, parentLoggerName):
21 
22  self.data = {}
23  self.header = {}
24  self.logger = logging.getLogger('%s.HicoL0Reader' % parentLoggerName)
25  with open(l0FileName, 'rb') as fh:
26  self.__ParseFillHeaderDict(fh)
27  self.__ParseFillDataDict(fh)
28 
29  @staticmethod
30  def __BinaryCodedDec2Int(x):
31  return((x & 0xf0) >> 4) * 10 + (x & 0x0f)
32 
33  def __HicoDateTime(self):
34  def __timeWrapAround(*args):
35  hr, mn, secs = (arg for arg in args)
36  mn += secs // 60
37  secs = secs % 60
38  hr += mn // 60
39  mn = mn%60
40  return [hr, mn, secs]
41  t_start = time.time()
42  # Might have to add a safety to ensure date/time data is in allowable range.
43  centerDate = []
44  centerTime = []
45  # dateList.append(self.__BinaryCodedDec2Int(self.header['FFyearMSB']) * 100 + \
46  # self.__BinaryCodedDec2Int(self.header['FFyearLSB']))
47  year = self.header['FFyearMSB'] * 100 + self.header['FFyearLSB']
48  centerDate.append(year)
49  # dateList.append(self.__BinaryCodedDec2Int(self.header['FFmonth']))
50  centerDate.append(self.header['FFmonth'])
51  # dateList.append(self.__BinaryCodedDec2Int(self.header['FFday']))
52  centerDate.append(self.header['FFday'])
53  # timeList.append(self.__BinaryCodedDec2Int(self.header['FFhours']))
54  centerTime.append(self.header['FFhours'])
55  # timeList.append(self.__BinaryCodedDec2Int(self.header['FFmin']))
56  centerTime.append(self.header['FFmin'])
57 
58  # pps wraps at 2**16
59  if self.header['LFpps'] >= self.header['FFpps']:
60  LFpps_ = self.header['LFpps']
61  else:
62  LFpps_ = (self.header['LFpps'] + 2**16)
63  time_imageInt = LFpps_ + self.header['LFppsSub'] * 16.762e-6 - \
64  (self.header['FFpps'] + self.header['FFppsSub'] * 16.762e-6)
65 
66  # secs = self.__BinaryCodedDec2Int(self.header['FFsec']) + \
67  secs = self.header['FFsec'] + \
68  ((int(self.header['Word08'] & 0x0f) << 16) +
69  int(self.header['FFsubLSB'])) * 1.0e-6 + 0.5 * time_imageInt
70  centerTime.append(secs)
71  startDate = centerDate[:]
72  endDate = centerDate[:]
73  startTime = centerTime[:]
74  endTime = centerTime[:]
75  startTime[2] -= 0.5 * time_imageInt
76  endTime[2] += 0.5 * time_imageInt
77  startTime = __timeWrapAround(*startTime)
78  endTime = __timeWrapAround(*endTime)
79 
80  tempDate = dtime.date(centerDate[0], centerDate[1], centerDate[2])
81  iday = tempDate.timetuple().tm_yday
82  fhr = centerTime[0] + (centerTime[1] + centerTime[2] / 60) / 60
83  resDict = {'startTime': startTime, 'centerTime': centerTime,
84  'endTime': endTime, 'startDate': startDate, 'centerDate': centerDate,
85  'endDate': endDate, 'iday': iday, 'fhr': fhr}
86  tot_time = time.time() - t_start
87  self.logger.debug('time taken: %f' % tot_time)
88  return resDict
89 
90  @staticmethod
91  def __LatLonH(*arg):
92 
93  if len(arg) == 1:
94  xyz, = arg
95  x, y, z = xyz
96  elif len(arg) == 3:
97  x, y, z = arg
98  else:
99  print("!!-> Incorrect arg. # @ LatLonH")
100  sys.exit(1)
101  esa_m = 6378137.0 # earth semimajor axis (radius?) in meters
102  recFlat = 1 / 298.257223563 # reciprocal flattening
103  seminax = esa_m * (1 - recFlat) # semi-minor axis
104  fEcc_2 = 2 * recFlat * (1 - recFlat) # first eccentricity, squared
105  sEcc_2 = recFlat * (2 - recFlat) / ((1 - recFlat) ** 2) # second eccentricity, squared
106  r2 = x**2 + y**2
107  r = sqrt(r2)
108  ee2 = esa_m**2 - seminax**2
109  ff = 54 * seminax ** 2 * z**2
110  g = r2 + (1 - fEcc_2) * z ** 2 - fEcc_2*ee2
111  c = (fEcc_2**2 * ff * r2) / (g**3)
112  s = 1.0 + c + sqrt(c * (c + 2)) ** (1.0/3.0)
113  p = ff / (2.0 * (s + 1.0 / (s) + 1.0)**2 * g**2)
114  q = sqrt(1.0 + 2 * fEcc_2**2 * p)
115  ro = -(fEcc_2*p*r) / (1+q) + sqrt((esa_m**2 / 2.0) * (1+1.0/q) -
116  ((1-fEcc_2) * p * z**2) / (q * (1+q)) - p*r2/2.0)
117  tmp = (r - fEcc_2 * ro)**2
118  u = sqrt(tmp+z**2)
119  v = sqrt(tmp+(1-fEcc_2)*z**2)
120  z0 = (seminax**2 * z) / (esa_m * v)
121  h = u*(1 - seminax ** 2 / (esa_m * v))
122  phi = atan((z + sEcc_2*z0)/r)*180.0/pi
123  lambd = atan2(y, x) * 180.0 / pi
124  returnList = [phi, lambd, h]
125  return returnList
126 
127  def __ParseFillHeaderDict(self, fHandle):
128  """
129  Goes through 256-byte embedded HICO header data.
130  Note that header is big-endian while data is little-endian
131  """
132  t_start = time.time()
133  keys = ['Sync', 'Len', 'FFpre', 'FFyearMSB', 'FFyearLSB', 'FFmonth', 'FFday',
134  'FFhours', 'FFmin', 'FFsec', 'Word08', 'FFsubLSB', 'FFpps', 'FFppsSub',
135  'LFpps', 'LFppsSub', 'roiw', 'roih', 'roix', 'roiy', 'hbin', 'vbin',
136  'ROport', 'ROspeed', 'IcuSwVersion', 'CamClearMode', 'TotalFrames',
137  'Dark1', 'Scene', 'Dark2', 'ID', 'ExpTime']
138  values = struct.unpack('>4sL8b10H6b5HL', fHandle.read(56)) # 33 items
139  d1 = dict(zip(keys, values))
140  if d1['Sync'] != b'HICO':
141  sys.exit("File is not L0. Exiting...")
142  keys2 = ['FFpos', 'FFvel', 'FFquat']
143  FFpos = np.fromfile(fHandle, dtype='>f4', count=3) # big endian float array
144  FFvel = np.fromfile(fHandle, dtype='>f4', count=3)
145  FFquat = np.fromfile(fHandle, dtype='>f4', count=4)
146 
147  keys3 = ['FFgncSec', 'FFgncSubSec', 'FFgncQual', 'TotalErr']
148  values3 = struct.unpack('>L2bH', fHandle.read(8))
149 
150  keys4 = ['LFpos', 'LFvel', 'LFquat']
151  LFpos = np.fromfile(fHandle, dtype='>f4', count=3) # big endian float array
152  LFvel = np.fromfile(fHandle, dtype='>f4', count=3)
153  LFquat = np.fromfile(fHandle, dtype='>f4', count=4)
154 
155  keys5 = ['LFgncSec', 'LFgncSubSec', 'LFgncQual', 'EventFlags', 'Dark1Offset',
156  'SceneOffset', 'Dark2Offset', 'Again', 't_on', 't_dark', 't_obs', 't_ang',
157  'TriggerCount', 'EMgain', 'Dark1Tel', 'Dark1Pos', 'Dark1TriggerCount',
158  'SceneTel', 'ScenePos', 'SceneTriggerCount', 'Dark2Tel', 'Dark2Pos',
159  'Dark2TriggerCount', 't_motor', 'Spare100', 'Spare101', 'Spare102',
160  'ErrorLogCount']
161  values5 = struct.unpack('>L2bH4L7Hh2Hh2Hh6H', fHandle.read(64))
162 
163  keys6 = ['Errors']
164  Errors = np.fromfile(fHandle, dtype='>i2', count=24)
165  d2 = dict(zip(keys2, (FFpos, FFvel, FFquat)))
166  d3 = dict(zip(keys3, values3))
167  d4 = dict(zip(keys4, (LFpos, LFvel, LFquat)))
168  d5 = dict(zip(keys5, values5))
169  d6 = dict(zip(keys6, Errors))
170  self.header.update(d1)
171  self.header.update(d2)
172  self.header.update(d3)
173  self.header.update(d4)
174  self.header.update(d5)
175  self.header.update(d6)
176 
177  # BCD correction:
178  self.header['FFpre'] = self.__BinaryCodedDec2Int(self.header['FFpre'])
179  self.header['FFyearMSB'] = self.__BinaryCodedDec2Int(self.header['FFyearMSB'])
180  self.header['FFyearLSB'] = self.__BinaryCodedDec2Int(self.header['FFyearLSB'])
181  self.header['FFmonth'] = self.__BinaryCodedDec2Int(self.header['FFmonth'])
182  self.header['FFday'] = self.__BinaryCodedDec2Int(self.header['FFday'])
183  self.header['FFhours'] = self.__BinaryCodedDec2Int(self.header['FFhours'])
184  self.header['FFmin'] = self.__BinaryCodedDec2Int(self.header['FFmin'])
185  self.header['FFsec'] = self.__BinaryCodedDec2Int(self.header['FFsec'])
186  tot_time = time.time() - t_start
187  self.logger.debug('time taken: %f' % tot_time)
188  return None
189 
190  def __ParseFillDataDict(self, fHandle):
191  # this is to fill the L0 dictionary (hash)
192  t_start = time.time()
193  m2ft = 0.3048
194  dtDict = self.__HicoDateTime()
195  ffLatLonH = self.__LatLonH(self.header['FFpos'] * m2ft)
196  lfLatLonH = self.__LatLonH(self.header['LFpos'] * m2ft)
197  ffLatLonH[2] /= 1e3 # converts m to KM.
198  lfLatLonH[2] /= 1e3
199  nl = int(self.header['TotalFrames'])
200  nb = int(self.header['roih'])
201  ns = int(self.header['roiw'])
202  count = nl * nb * ns
203  pic0 = np.reshape(np.fromfile(fHandle, dtype='<i2', count=count),
204  (nl, nb, ns))
205  self.data = {"ns": ns, "nb": nb, "nl": nl, "offset": 2561,
206  "file_type": 'ENVI Standard', "data_type": 12, "interleave": 'bil',
207  "sensor_type": self.header['Sync'], "byte order": 0,
208  "exposure_time": self.header['TriggerCount'] * 1.117460459,
209  "n_predark": int(self.header['Dark1']),
210  "n_image": int(self.header['Scene']),
211  "n_postdark": int(self.header['Dark2']),
212  "start_date": dtDict['startDate'], "start_time": dtDict['startTime'],
213  "center_date": dtDict['centerDate'], "center_time": dtDict['centerTime'],
214  "end_date": dtDict['endDate'], "end_time": dtDict['endTime'],
215  "yearDay": dtDict['iday'], "floatHour": dtDict['fhr'],
216  "ScenePointingAngle": (self.header['ScenePos'] + 395) * 0.004,
217  "FFVelMag": sqrt(sum((self.header['FFvel'] * m2ft - 3) ** 2)),
218  "LFVelMag": sqrt(sum((self.header['LFvel'] * m2ft - 3) ** 2)),
219  "FFLatLonH": ffLatLonH,
220  "LFLatLonH": lfLatLonH,
221  "FFquat": self.header["FFquat"],
222  "LFquat": self.header["LFquat"],
223  "Dark1Pos": self.header["Dark1Pos"],
224  "Dark2Pos": self.header["Dark2Pos"],
225  "pic0": pic0
226  }
227  tot_time = time.time() - t_start
228  self.logger.debug('time taken: %f' % tot_time)
229  return None
def __init__(self, l0FileName, parentLoggerName)
Definition: HicoL0Reader.py:20
def __ParseFillDataDict(self, fHandle)
def __ParseFillHeaderDict(self, fHandle)