OB.DAAC Logo
NASA Logo
Ocean Color Science Software

ocssw V2022
cproc_hico.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 '''
3 Created on Nov 19, 2015
4 
5 @author: rhealy
6 '''
7 from netCDF4 import Dataset
8 
9 class hico_geo():
10  def __init__(self,ifile,earth_orient_file,leap_sec_file,boresight):
11  from hico.hicoLonLatHdr import hicoLonLatHdr
12  from hico.astreduc import frac2DegMinSec
13  import hico.bore_sight as bore
14  import hico.read_pos_vel_quat as rquat
15  import numpy as np
16  import os,sys
17  import struct
18  import hico.astreduc as astreduc
19 
20  self.input_file = ifile
21  self.earth_orient_file = earth_orient_file
22  self.leap_sec_file = leap_sec_file
23  self.boresight = boresight
24  lonHemi = {1:'E',0:'W'}
25  latHemi = {1:'N',0:'S'}
26  direction = {1:'ascending',0:'descending'}
27  byteorder = {'little':0, 'big':1}
28 
29  rad2Deg = 180.0/np.pi
30  deg2Rad = np.pi/180.0
31  theta_stowed=-60.
32 
33  try:
34  ocvarroot = os.environ['OCVARROOT']
35  except KeyError:
36  print("OCSSW environement variable OCVARROOT not set.")
37  print("Typically it is set to $OCSSW/run/var.")
38  sys.exit()
39 
40 
41  bs = np.zeros((3))
42  bs[0] = boresight[0]*deg2Rad
43  bs[1] = boresight[1]*deg2Rad
44  bs[2] = boresight[2]*deg2Rad
45  r_hico_bs = bore.bore_sight(bs)
46 
47  (quat_info, quat, pvq_data) = rquat.read_pos_vel_quat(ifile)
48  nsamples = np.int(quat_info['Requested number of pixels'])
49  self.iss_orientation = quat_info['ISS orientation']
50  # print('The CSV file is...' + quat_info['Source CSV file'])
51  # print('Theta = ' + quat_info['Theta (degrees from stowed position)'])
52  # print('The CSV file is...' + getattr(quat_info,'Source CSV file'))
53  # print('Distance Unit =' + getattr(quat, 'Distance Unit'))
54 
55  filein = "{}/hico/{}".format(ocvarroot,'nutation.dat')
56  # print(filein)
57  nut_data = astreduc.initReduc(filein)
58  astrodate = astreduc.get_astrodate(pvq_data['SecondsSinceEpoch'][0])
59 
60  yyyy = astrodate.year% 100
61 
62  try:
63  pmx,pmy,dut1,loda = astreduc.getEDFData(earth_orient_file,yyyy,astrodate.month,astrodate.day)
64  # print(pmx,pmy,dut1,loda)
65  except ValueError:
66  print("Earth Data File does not have year={},month={},day={}".format(astrodate.year,astrodate.month,astrodate.day))
67  sys.exit()
68 
69  try:
70  # print("Date={}/{}/{} {}:{}:{}".format(astrodate.year,astrodate.month,astrodate.day,astrodate.hour,astrodate.minute,astrodate.second))
71  lsdat = astreduc.getLSData(leap_sec_file,astrodate.year,astrodate.month,astrodate.day)
72  # print('lsdat=' + str(lsdat))
73  except ValueError:
74  print("Leap Sec File does not have year={},month={},day={}".format(astrodate.year,astrodate.month,astrodate.day))
75  sys.exit()
76 
77  # Define the rotation from HICO frame of reference to ISS frame of reference
78  # NOTE: This assumes perfect alignment.
79 
80  r_hico_to_iss = np.zeros((3,3))
81  r_hico_to_iss[0][0] = -1.0
82  r_hico_to_iss[1][1] = -1.0
83  r_hico_to_iss[2][2] = 1.0
84  # include bore sight offsets
85 
86  r_hico_to_iss = np.dot(r_hico_to_iss,r_hico_bs)
87  #!!!! BEGIN HICO pointing angles:
88  # I believe the 1/2 FOV is 3.461515 degrees (from Mike C?)
89  # A document by Bob Lucke states that the relationship is as follows;
90  # this was in MJM's HICO email folder in an email from Bill Snyder.
91  # UNITS ARE DEGREES
92 
93  if quat_info['ISS orientation'] == '+XVV':
94  imult = 1
95  else:
96  imult = -1
97  theta = np.zeros(nsamples)
98 
99  for i in range(0,nsamples):
100  frac = imult*(i-255.5)/255.5
101  theta[i] = ( -3.487 + 0.035 * frac**2)*frac
102 
103  theta = (theta + theta_stowed + float(quat_info['Theta (degrees from stowed position)']))*deg2Rad
104  # Now theta is in radians and accounts for angle from stowed position
105  # Now I need to convert to pointing angles in HICO's frame of reference.
106  # Note that HICO is in the YZ plane of HICO. The stowed angle (+ is right
107  # hand rule about X, measured from +Z) is in the +Y+Z quadrant, even
108  # for negative values of theta...
109 
110  svec = np.zeros((3,nsamples))
111 
112  svec[0,:] = 0.0
113  svec[1,:] = -np.sin(theta[:])
114  svec[2,:] = np.cos(theta[:])
115 
116  pm = astreduc.polarm(pmx,pmy)
117 
118  #print(pm)
119 
120  r_iss = np.array((3),dtype=float)
121  q_iss = np.array((4),dtype=float)
122  fout = quat_info['Expected Lon/Lat/View angle filename']
123  fheader = fout.split('.bil')[0] + '.hdr'
124  # fbil=open(fout,"wb")
125  self.lines = pvq_data['SecondsSinceEpoch'].size
126 
127  #Setup the HICO header file using the hicoLonLatHdr class
128  hicoHeader = hicoLonLatHdr(filename=fheader, \
129  samples=nsamples, \
130  lines=self.lines, \
131  description='HICO geometry file, with calculated positions, and solar & view geometry, on the ground', \
132  sensor_type='HICO-ISS' \
133  )
134  hicoHeader.set_variable('data type',5)
135  hicoHeader.set_variable('interleave','bil')
136  hicoHeader.set_variable('wavelength units','{ degrees, degrees, degrees, degrees, degrees, degrees }')
137  hicoHeader.set_variable('band names','{ longitude, latitude, view zenith, view azimuth, sol zenith, sol azimuth }')
138  hicoHeader.set_variable('byte order',byteorder[sys.byteorder])
139  hicoHeader.set_variable('geometry_deltaUT1_s',dut1)
140  hicoHeader.set_variable('geometry_deltaT_s',lsdat)
141  hicoHeader.set_variable('geometry_xp_rad',pmx)
142  hicoHeader.set_variable('geometry_yp_rad',pmy)
143  hicoHeader.set_variable('geometry_LOD',loda)
144  hicoHeader.set_variable('geometry_bore_sight_params','{ ' + ', '.join( str(v) for v in bs) + ' }')
145 
146  self.lon = np.zeros((self.lines,nsamples))
147  self.lat = np.zeros((self.lines,nsamples))
148  self.viewzen = np.zeros((self.lines,nsamples))
149  self.viewaz = np.zeros((self.lines,nsamples))
150  self.solarzen = np.zeros((self.lines,nsamples))
151  self.solaraz = np.zeros((self.lines,nsamples))
152 
153  for i in range(0,self.lines):
154  # for i in range(0,1):
155 
156  r_iss = np.multiply([pvq_data['ISSPOSX'][i],pvq_data['ISSPOSY'][i],pvq_data['ISSPOSZ'][i] ],0.3048e0) # convert to meeters
157  q_iss = [ pvq_data['ISSQS'][i],pvq_data['ISSQX'][i],pvq_data['ISSQY'][i],pvq_data['ISSQZ'][i] ]
158  rot_body = astreduc.quat_to_rot(q_iss)
159  astrodate = astreduc.get_astrodate(pvq_data['SecondsSinceEpoch'][i])
160  hico_jdate = astreduc.UT2time(astrodate.year,astrodate.month,astrodate.day,astrodate.hour,astrodate.minute,astrodate.second,dut1,lsdat)
161  #print(hico_jdate.jdut1,hico_jdate.ttdb)
162  prec = astreduc.precession(hico_jdate.ttdb)
163  DeltaPsi, TrueEps, MeanEps, Omega, nut = astreduc.nutation(hico_jdate.ttdb,nut_data)
164  st,stdot,OmegaEarth = astreduc.sidereal(hico_jdate.jdut1,DeltaPsi,TrueEps,Omega,loda,2)
165 
166  t_eci_to_ecef=np.dot(pm,np.dot(st,np.dot(nut,prec)))
167  # for attitudes from HICO frame to ECEF
168  t_hico_to_ecef=np.dot(t_eci_to_ecef,np.dot(rot_body,r_hico_to_iss))
169  # The position does not depend on the attitude transformation
170  r_ecef=np.dot(t_eci_to_ecef,r_iss)
171  # Here, v_ecef are the 512 pointing vectors.
172  # v_ecef=matmul(t_hico_to_ecef,v_hico) ! v_hico=[3,NS]
173  v_ecef=np.dot(t_hico_to_ecef,svec)
174  llh,view_zen,view_az = astreduc.wgs84_intercept(r_ecef,v_ecef)
175 
176  sol_zen_az=rad2Deg*astreduc.solar_geometry(astrodate.year,astrodate.month,astrodate.day,astrodate.hour,astrodate.minute,astrodate.second,llh[1,:],-llh[0,:])
177 
178  self.lon[i,:] = np.array(llh[0,:])
179  self.lat[i,:] = np.array(llh[1,:])
180  self.viewzen[i,:] = np.array(view_zen,dtype='f8')
181  view_az[view_az>180] -= 360
182  self.viewaz[i,:] = np.array(view_az,dtype='f8')
183  self.solarzen[i,:] = np.array(sol_zen_az[0,:],dtype='f8')
184  sol_zen_az[1,:][sol_zen_az[1,:]>180] -= 360
185  self.solaraz[i,:] = np.array(sol_zen_az[1,:],dtype='f8')
186  # bilout = np.concatenate((lon_out,lat_out))
187  # bilout = np.concatenate((bilout,viewzen))
188  # bilout = np.concatenate((bilout,viewaz))
189  # bilout = np.concatenate((bilout,solarzen))
190  # bilout = np.concatenate((bilout,solaraz ))
191  # myfmt='d'*len(bilout)
192  # binout = struct.pack(myfmt,*bilout)
193  # fbil.write(binout)
194 
195  # Below are some items for output header, grabbed near center of line
196  # Populate the HICO header for output to the header file
197  if i == self.lines/2-1:
198  hicoHeader.set_variable('date', '{ ' + str(astrodate.year)+','+str(astrodate.month)+','+str(astrodate.day) + ' }')
199  hicoHeader.set_variable('time', '{ ' + str(astrodate.hour)+','+str(astrodate.minute)+','+str(astrodate.second) + ' }')
200  hicoHeader.set_variable('image_center_long','{ ' + ', '.join( str(v) for v in frac2DegMinSec(np.abs(self.lon[i,254]),0.,0.)) + ' }')
201  hicoHeader.set_variable('image_center_lat', '{ ' + ', '.join( str(v) for v in frac2DegMinSec(np.abs(self.lat[i,254]),0.,0.)) + ' }')
202  hicoHeader.set_variable('image_center_long_hem', lonHemi[(self.lon[i,254]>=0)])
203  hicoHeader.set_variable('image_center_lat_hem', latHemi[(self.lat[i,254]>=0)])
204  hicoHeader.set_variable('image_center_zenith_ang','{ ' + ', '.join( str(v) for v in frac2DegMinSec((self.viewzen[i,254]+self.viewzen[i,255])/2,0.,0.)) + ' }')
205  hicoHeader.set_variable('image_center_azimuth_ang','{ ' + ', '.join( str(v) for v in frac2DegMinSec((self.viewaz[i,254]+self.viewaz[i,255])/2,0.,0.)) + ' }')
206  hicoHeader.set_variable('geometry_ISS_Z_direction',direction[(pvq_data['ISSVELZ'][i]>=0)]) #! a bit crude, based on ECI velocity
207  r_iss_new = np.tile(r_iss[np.newaxis],1).T
208  llh = astreduc.ecef2latlon(r_iss_new)
209  hicoHeader.set_variable('sensor_altitude',llh[2,0]/1000.)
210 
211  # fbil.close()
212 
213  # Create the history for output into the header file using the information from the
214  # pos_vel_quat input csv file and then write out the header file.
215 
216  hicoHeader.createHistory(quat_info)
217  hicoHeader.writeHicoENVIHeaderFile()
218 
219  def write_geo_nc(self,nc_grp,calib_grp):
220 
221  nc_grp['sensor_zenith'] [:,:] = self.viewzen [:,:].astype('f4')
222  nc_grp['sensor_azimuth'][:,:] = self.viewaz [:,:].astype('f4')
223  nc_grp['solar_zenith'] [:,:] = self.solarzen[:,:].astype('f4')
224  nc_grp['solar_azimuth'] [:,:] = self.solaraz [:,:].astype('f4')
225  nc_grp['longitude'] [:,:] = self.lon [:,:].astype('f4')
226  nc_grp['latitude'] [:,:] = self.lat [:,:].astype('f4')
227 
228  calib_grp.hico_orientation_from_quaternion = self.iss_orientation
229 
230 def run_geo():
231  import argparse
232 
233  parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, description='''\
234  Generate a HICO geometry file.
235 
236  ''', add_help=True)
237  parser.add_argument('-ifile', nargs=1, type=str, help=' iss*pos_vel_quat.csv input file (Must be a CSV file) ')
238  parser.add_argument('-lfile', nargs=1, type=str, help=' leapsec file ')
239  parser.add_argument('-efile', nargs=1, type=str, help=' earth-orient file ')
240  parser.add_argument('-boresight', nargs=3, type=float, default=([0, 0, 0]), help=('Process Bore Sight Parameters'))
241 
242  args = parser.parse_args('-ifile iss.2013067.0308.063527.L0.12933.20130308205743.hico_pos_vel_quat.csv -efile /home/rhealy/ocsswn/run/var/hico/finals.data -lfile /home/rhealy/ocsswn/run/var/modis/leapsec.dat -boresight -0.9957 0.0268 -0.0128'.split())
243 
244  ifile = ''.join(args.ifile)
245  earth_orient_file = ''.join(args.efile)
246  leap_sec_file = ''.join(args.lfile)
247  boresight = args.boresight
248  rootgrp = Dataset('test_hico.nc','w')
249  rootgrp.createDimension('scan_lines', 2000)
250  rootgrp.createDimension('samples', 512)
251  #
252  # L2gen is expecting these variables under group "navigation" for l2gen
253  #
254  navigation = rootgrp.createGroup("navigation")
255 
256  senz = navigation.createVariable('sensor_zenith','f4',('scan_lines','samples',))
257  solz = navigation.createVariable('solar_zenith','f4',('scan_lines','samples',))
258  sena = navigation.createVariable('sensor_azimuth','f4',('scan_lines','samples',))
259  sola = navigation.createVariable('solar_azimuth','f4',('scan_lines','samples',))
260  lon = navigation.createVariable('longitude','f4',('scan_lines','samples',))
261  lat = navigation.createVariable('latitude','f4',('scan_lines','samples',))
262  senz.units = 'degrees'
263  senz.valid_min = 0
264  senz.valid_max = 180
265  senz.long_name = 'sensor zenith'
266  solz.units = 'degrees'
267  solz.valid_min = 0
268  solz.valid_max = 180
269  solz.long_name = 'solar zenith'
270  sena.units = 'degrees'
271  sena.valid_min = -180
272  sena.valid_max = 180
273  sena.long_name = 'sensor azimuth'
274  sola.units = 'degrees'
275  sola.valid_min = -180
276  sola.valid_max = 180
277  sola.long_name = 'solar azimuth'
278  lon.units = 'degrees'
279  lon.valid_min = -180
280  lon.valid_max = 180
281  lon.long_name = 'longitude'
282  lat.units = 'degrees'
283  lat.valid_min = -180
284  lat.valid_max = 180
285  lat.long_name = 'latitude'
286  # orientation needs to be set in group = "metadata/HICO/Calibration for l2gen"
287  metadata = rootgrp.createGroup("metadata")
288  hico = metadata.createGroup("HICO")
289  calibration = hico.createGroup("Calibration")
290  hicogeo = hico_geo(ifile,earth_orient_file,leap_sec_file,boresight)
291  hicogeo.write_geo_nc(navigation,calibration)
292  rootgrp.close()
293  print("Done")
294 
295 if __name__ == '__main__': run_geo()
def __init__(self, ifile, earth_orient_file, leap_sec_file, boresight)
Definition: cproc_hico.py:10
def write_geo_nc(self, nc_grp, calib_grp)
Definition: cproc_hico.py:219
def frac2DegMinSec(fracd, fracm, fracs)
Definition: astreduc.py:1135
const char * str
Definition: l1c_msi.cpp:35