OB.DAAC Logo
NASA Logo
Ocean Color Science Software

ocssw V2022
modis_L1A_utils.py
Go to the documentation of this file.
1 #! /usr/bin/env python3
2 
3 
4 import os
5 import sys
6 import subprocess
7 import shutil
8 from seadasutils.ParamUtils import ParamProcessing
10 import mlp.obpg_data_file
12 import seadasutils.ProcUtils as ProcUtils
13 
14 
15 class modis_l1a:
16  """
17  This class defines the parameters and sets up the environment necessary
18  to process a MODIS L0 granule to produce a L1A granule. It is implemented
19  by the modis_L1A code. This class also contains the methods necessary to
20  run the l0_write_construct and l1agen_modis binaries.
21  """
22 
23  def __init__(self, filename=None,
24  parfile=None,
25  l1a=None,
26  nextgranule=None,
27  startnudge=10,
28  stopnudge=10,
29  satellite=None,
30  fix=True,
31  rounding=True,
32  lutver=None,
33  lutdir=None,
34  log=False,
35  verbose=True):
36  self.filename = filename
37  self.parfile = parfile
38  self.l1a = l1a
39  self.proctype = 'modisL1A'
40  self.nextgranule = nextgranule
41  self.stopnudge = stopnudge
42  self.startnudge = startnudge
43  self.sat_name = satellite
44  self.verbose = verbose
45  self.fix = fix
46  self.l0fixed = False
47  self.rounding = rounding
48  self.lutversion = lutver
49  self.lutdir = lutdir
50  self.log = log
51  self.ancdir = None
52  self.curdir = False
53  self.pcf_template = None
54  self.start = None
55  self.stop = None
56  self.length = 0
57  self.dirs = {}
58 
59  # version-specific variables
60  self.collection_id = '061'
61  self.pgeversion = '6.1.9'
62 # self.lutversion = '0'
63 
64  if self.parfile:
65  p = ParamProcessing(parfile=self.parfile)
66  p.parseParFile(prog='l1agen')
67  phash = p.params['l1agen']
68  for param in (phash.keys()):
69  if not self[param]:
70  self[param] = phash[param]
71  self.l0file = os.path.basename(self.filename)
72 
73  def __setitem__(self, index, item):
74  self.__dict__[index] = item
75 
76  def __getitem__(self, index):
77  return self.__dict__[index]
78 
79 
80  def chk(self):
81  """
82  check input parameters
83  """
84  if not os.path.exists(self.filename):
85  print("ERROR: File: " + self.filename + " does not exist.")
86  sys.exit(1)
87 
88  if self.nextgranule is not None:
89  if not os.path.exists(self.nextgranule):
90  print("ERROR: File " + self.nextgranule + " does not exist.")
91  sys.exit(1)
92  else:
93  if self.verbose and self.stopnudge != 0:
94  print("* Next L0 granule is specified, therefore setting stopnudge = 0 *")
95  self.stopnudge = 0
96 
97  def get_constructor(self):
98 
99  # rudimentary PCF file for logging, leapsec.dat
100  self.pcf_file = self.l0file + '.pcf'
101  ProcUtils.remove(self.pcf_file)
102 
103  os.environ['PGS_PC_INFO_FILE'] = self.pcf_file
104  pcf = [line for line in open(self.pcf_template, 'r')]
105  sed = open(self.pcf_file, 'w')
106  for line in pcf:
107  line = line.replace('LOGDIR', self.dirs['run'].resolve().as_posix())
108  line = line.replace('L1AFILE', os.path.basename(self.filename))
109  line = line.replace('VARDIR', os.path.join(self.dirs['var'], 'modis'))
110  sed.write(line)
111  sed.close()
112 
113  # create constructor file
114  if self.verbose:
115  print("Determining pass start and stop time...\n")
116 
117  l0cnst_write_cmd = [os.path.join(self.dirs['bin'], 'l0cnst_write_modis'), self.l0file]
118 
119  l0cnst_write_output = subprocess.run(l0cnst_write_cmd, capture_output=True, text=True, shell=False)
120  if l0cnst_write_output.returncode:
121  return l0cnst_write_output.returncode
122  else:
123  for line in l0cnst_write_output.stdout.splitlines():
124  try:
125  key, val = line.split('=')
126  except ValueError:
127  continue # line doesn't contain '='
128  if 'starttime' in key:
129  self.start = val.strip()
130  elif 'stoptime' in key:
131  self.stop = val.strip()
132  elif 'length' in key:
133  self.length = val.strip()
134  return 0
135 
136 
137  def l1a_name(self):
138  # determine output file name
140  ftype, sensor = file_typer.get_file_type()
141  stime, etime = file_typer.get_file_times()
143  ftype, sensor,
144  stime, etime)])
145  self.l1a = mlp.get_output_name_utils.get_output_name(data_files_list, 'modis_L1A', None)
146 
147  def l0(self):
148  """
149  Write L0 Constructor File
150  """
151 
152  # The L0 file and constructor file must reside in the same directory,
153  # so create a symlink to the L0 file as needed.
154 
155  if not os.path.exists(self.l0file):
156  os.symlink(self.filename, self.l0file)
157 
158  # create constructor file
159  status = self.get_constructor()
160 
161  if status != 0 and status != 3:
162  # bad error - exit now
163  print("l0cnst_write_modis: Unrecoverable error encountered while attempting to generate constructor file.")
164  sys.exit(1)
165 
166  if status == 3:
167  # recoverable? try to fix l0
168  if self.verbose:
169  print("l0cnst_write_modis: A corrupt packet or a packet of the wrong size")
170  print(" was detected while generating the constructor file.")
171  if not self.fix:
172  print("Fixing of Level-0 file is currently disabled.")
173  print("Please re-run without the '--disable-fix_L0' option.")
174  sys.exit(1)
175 
176  # 1st call to l0fix_modis: get pass times
177  if self.verbose:
178  print("Attempting to fix the Level 0 file using l0fix_modis")
179 
180  l0fixcmd = [os.path.join(self.dirs['bin'], 'l0fix_modis'), self.l0file, '-1', '-1']
181  if self.verbose:
182  print(l0fixcmd)
183  l0fix = subprocess.run(l0fixcmd, capture_output=True, text=True, shell=False)
184  if l0fix.returncode:
185  print("l0fix_modis: Unrecoverable error in l0fix_modis!")
186  sys.exit(1)
187 
188  # 2nd call to l0fix_modis: fix packets
189  for line in l0fix.stdout.splitlines():
190  if "taitime_start:" in line:
191  self.taitime_start = line.split()[1]
192  if "taitime_stop:" in line:
193  self.taitime_stop = line.split()[1]
194 
195  l0fixcmd = [os.path.join(self.dirs['bin'], 'l0fix_modis'), self.l0file, self.taitime_start, self.taitime_stop,
196  self.l0file + '.fixed']
197  if self.verbose:
198  print(l0fixcmd)
199  l0fix = subprocess.run(l0fixcmd, capture_output=True, shell=False)
200  if l0fix.returncode:
201  print("l0fix_modis: Unrecoverable error in l0fix_modis!")
202  sys.exit(1)
203  if self.verbose:
204  print("New Level 0 file successfully generated. Regenerating constructor file...")
205  self.l0file += '.fixed'
206 
207  # try again to make constructor file
208  status = self.get_constructor()
209  if status:
210  print("Failed to generate constructor file after running l0fix_modis.")
211  print("Please examine your Level 0 file to determine if it is completely corrupt.")
212  sys.exit(1)
213  else:
214  self.l0fixed = True
215 
216  # Determine pass start and stop times and duration of pass
217  if self.l1a is not None:
218  if self.verbose:
219  print("Using specified output L1A filename: %s" % self.l1a)
220  else:
221  self.l1a_name()
222 
223  # Adjust end time to nominal 5-minute boundary to ensure processing of final scan
224  if self.rounding:
225  starttime = ProcUtils.round_minutes(self.start,'t',5)
226  stoptime = ProcUtils.round_minutes(self.stop, 't',5)
227  if starttime == stoptime:
228  if self.verbose:
229  print("Short granule: extending end time to 5-min boundary")
230  stoptime = ProcUtils.round_minutes(self.stop, 't',5,rounding=1)
231  if starttime != stoptime:
232  self.stop = stoptime # don't change self.start
233 
234  # Adjust starttime, stoptime, and gransec for L0 processing
235  self.start = ProcUtils.addsecs(self.start, self.startnudge, 't')
236  self.stop = ProcUtils.addsecs(self.stop, -1 * self.stopnudge, 't')
237  self.length = ProcUtils.diffsecs(self.start, self.stop, 't')
238  self.granmin = str(float(self.length) / 60.0)
239 
240  # set output file name
241  if self.verbose:
242  print("Input Level 0:", self.filename)
243  print("Output Level 1A:", self.l1a)
244  print("Satellite:", self.sat_name)
245  print("Start Time:", self.start)
246  print("Stop Time:", self.stop)
247  print("Granule Duration:", self.length, "seconds")
248  print("")
249 
250  def run(self):
251  """
252  Run l1agen_modis (MOD_PR01)
253  """
254 
255  # if next granule is set, create temp concatenated file
256  if self.nextgranule is not None:
257  shutil.move(self.l0file, self.l0file + '.tmp')
258  cat = open(self.l0file, 'wb')
259  shutil.copyfileobj(open(self.l0file + '.tmp', 'rb'), cat)
260  shutil.copyfileobj(open(self.nextgranule, 'rb'), cat)
261  cat.close()
262 
263  if self.verbose:
264  print("Processing MODIS L0 file to L1A...")
265  status = subprocess.run(os.path.join(self.dirs['bin'], 'l1agen_modis'), shell=False).returncode
266  if self.verbose:
267  print('l1agen_modis exit status:', str(status))
268 
269  # if next granule is set, move original L0 file back to original name
270  if self.nextgranule is not None:
271  shutil.move(self.l0file + '.tmp', self.l0file)
272 
273  # clean up log files
274  if not status:
275  ProcUtils.remove(self.l0file + '.constr')
276  ProcUtils.remove(self.l0file + '.pcf')
277  base = os.path.basename(self.l0file)
278  ProcUtils.remove(os.path.join(self.dirs['run'], 'LogReport.' + base))
279  ProcUtils.remove(os.path.join(self.dirs['run'], 'LogStatus.' + base))
280  ProcUtils.remove(os.path.join(self.dirs['run'], 'LogUser.' + base))
281  ProcUtils.remove(os.path.join(self.dirs['run'], "GetAttr.temp"))
282  ProcUtils.remove(os.path.join(self.dirs['run'], "ShmMem"))
283  if self.l0fixed:
284  fixed_l0file = self.l0file.replace('.fixed','')
285  ProcUtils.remove(fixed_l0file + '.constr')
286  ProcUtils.remove(fixed_l0file + '.pcf')
287  base = os.path.basename(fixed_l0file)
288  ProcUtils.remove(os.path.join(self.dirs['run'], 'LogReport.' + base))
289  ProcUtils.remove(os.path.join(self.dirs['run'], 'LogStatus.' + base))
290  ProcUtils.remove(os.path.join(self.dirs['run'], 'LogUser.' + base))
291 
292  if os.path.dirname(self.l1a) != self.dirs['run']:
293  origfile = os.path.join(self.dirs['run'],
294  os.path.basename(self.l1a))
295  if os.path.exists(origfile):
296  shutil.move(origfile, self.l1a)
297  ProcUtils.remove('.'.join([origfile, 'met']))
298  else:
299  ProcUtils.remove('.'.join([self.l1a, 'met']))
300  # ProcUtils.remove(self.l1a + '.met')
301 
302  if os.path.islink(self.l0file):
303  os.remove(self.l0file)
304 
305  if os.path.islink(os.path.basename(self.filename)):
306  os.remove(os.path.basename(self.filename))
307 
308  if self.log is False:
309  ProcUtils.remove(self.pcf_file)
310  base = os.path.basename(self.l1a)
311  ProcUtils.remove(os.path.join(self.dirs['run'], 'LogReport.' + base))
312  ProcUtils.remove(os.path.join(self.dirs['run'], 'LogStatus.' + base))
313  ProcUtils.remove(os.path.join(self.dirs['run'], 'LogUser.' + base))
314 
315  if self.verbose:
316  print("MODIS L1A processing complete.")
317  else:
318  print("modis_l1a: ERROR: MODIS L1A processing failed.")
319  print("Please examine the LogStatus and LogUser files for more information.")
320  sys.exit(1)
list(APPEND LIBS ${PGSTK_LIBRARIES}) add_executable(atteph_info_modis atteph_info_modis.c) target_link_libraries(atteph_info_modis $
Definition: CMakeLists.txt:7
def __init__(self, filename=None, parfile=None, l1a=None, nextgranule=None, startnudge=10, stopnudge=10, satellite=None, fix=True, rounding=True, lutver=None, lutdir=None, log=False, verbose=True)
def __setitem__(self, index, item)
const char * str
Definition: l1c_msi.cpp:35
def get_output_name(data_files, target_program, clopts)