def test_node(check_gamit_tables=None, software_sync=()): # test node: function that makes sure that all required packages and tools are present in the nodes import traceback import platform import os import sys def check_tab_file(tabfile, date): if os.path.isfile(tabfile): # file exists, check contents with open(tabfile, 'rt', encoding='utf-8', errors='ignore') as f: lines = f.readlines() tabdate = pyDate.Date(mjd=lines[-1].split()[0]) if tabdate < date: return ' -- %s: Last entry in %s is %s but processing %s' \ % (platform.node(), tabfile, tabdate.yyyyddd(), date.yyyyddd()) return [] else: return ' -- %s: Could not find file %s' % (platform.node(), tabfile) # BEFORE ANYTHING! check the python version version = sys.version_info # if version.major > 2 or version.minor < 7 or (version.micro < 12 and version.minor <= 7): # return ' -- %s: Incorrect Python version: %i.%i.%i. Recommended version >= 2.7.12' \ # % (platform.node(), version.major, version.minor, version.micro) if version.major < 3: return ' -- %s: Incorrect Python version: %i.%i.%i. Recommended version >= 3.0.0' \ % (platform.node(), version.major, version.minor, version.micro) # start importing the modules needed try: import shutil import datetime import time import uuid import traceback # deps import numpy import pg import dirsync # app import pyRinex import dbConnection import pyStationInfo import pyArchiveStruct import pyPPP import pyBrdc import pyOptions import Utils import pyOTL import pySp3 import pyETM import pyRunWithRetry import pyDate except: return ' -- %s: Problem found while importing modules:\n%s' % ( platform.node(), traceback.format_exc()) try: if len(software_sync) > 0: # synchronize directories listed in the src and dst arguments from dirsync import sync for source_dest in software_sync: if isinstance(source_dest, str) and ',' in source_dest: s = source_dest.split(',')[0].strip() d = source_dest.split(',')[1].strip() print(' -- Synchronizing %s -> %s' % (s, d)) updated = sync(s, d, 'sync', purge=True, create=True) for f in updated: print(' -- Updated %s' % f) except: return ' -- %s: Problem found while synchronizing software:\n%s ' % ( platform.node(), traceback.format_exc()) # continue with a test SQL connection # make sure that the gnss_data.cfg is present try: cnn = dbConnection.Cnn('gnss_data.cfg') q = cnn.query('SELECT count(*) FROM networks') if int(pg.version[0]) < 5: return ' -- %s: Incorrect PyGreSQL version!: %s' % ( platform.node(), pg.version) except: return ' -- %s: Problem found while connecting to postgres:\n%s ' % ( platform.node(), traceback.format_exc()) # make sure we can create the production folder try: test_dir = os.path.join('production/node_test') if not os.path.exists(test_dir): os.makedirs(test_dir) except: return ' -- %s: Could not create production folder:\n%s ' % ( platform.node(), traceback.format_exc()) # test try: Config = pyOptions.ReadOptions('gnss_data.cfg') # check that all paths exist and can be reached if not os.path.exists(Config.archive_path): return ' -- %s: Could not reach archive path %s' % ( platform.node(), Config.archive_path) if not os.path.exists(Config.repository): return ' -- %s: Could not reach repository path %s' % ( platform.node(), Config.repository) # pick a test date to replace any possible parameters in the config file date = pyDate.Date(year=2010, doy=1) except: return ' -- %s: Problem while reading config file and/or testing archive access:\n%s' \ % (platform.node(), traceback.format_exc()) try: brdc = pyBrdc.GetBrdcOrbits(Config.brdc_path, date, test_dir) except: return ' -- %s: Problem while testing the broadcast ephemeris archive (%s) access:\n%s' \ % (platform.node(), Config.brdc_path, traceback.format_exc()) try: sp3 = pySp3.GetSp3Orbits(Config.sp3_path, date, Config.sp3types, test_dir) except: return ' -- %s: Problem while testing the sp3 orbits archive (%s) access:\n%s' \ % (platform.node(), Config.sp3_path, traceback.format_exc()) # check that all executables and GAMIT bins are in the path for prg in ('crz2rnx', 'crx2rnx', 'rnx2crx', 'rnx2crz', 'gfzrnx_lx', 'svdiff', 'svpos', 'tform', 'sh_rx2apr', 'doy', 'sed', 'compress'): with pyRunWithRetry.command('which ' + prg) as run: run.run() if run.stdout == '': return ' -- %s: Could not find path to %s' % (platform.node(), prg) # check grdtab and ppp from the config file for opt in ('grdtab', 'otlgrid', 'ppp_exe'): path = Config.options[opt] if not os.path.isfile(path): return ' -- %s: Could not find %s in %s' % (platform.node(), opt, path) ppp_path = Config.options['ppp_path'] for f in ('gpsppp.stc', 'gpsppp.svb_gps_yrly', 'gpsppp.flt', 'gpsppp.stc', 'gpsppp.met'): if not os.path.isfile(os.path.join(ppp_path, f)): return ' -- %s: Could not find %s in %s' % (platform.node(), f, ppp_path) for frame in Config.options['frames']: if not os.path.isfile(frame['atx']): return ' -- %s: Could not find atx in %s' % (platform.node(), frame['atx']) if check_gamit_tables is not None: # check the gamit tables if not none date = check_gamit_tables[0] eop = check_gamit_tables[1] gg = os.path.expanduser('~/gg') tables = os.path.expanduser('~/gg/tables') if not os.path.isdir(gg): return ' -- %s: Could not GAMIT installation dir (gg)' % ( platform.node()) elif not os.path.isdir(tables): return ' -- %s: Could not GAMIT tables dir (gg)' % ( platform.node()) # DDG: deprecated -> GAMIT now uses a single nbody file (binary) # for t_name in ('luntab.' + date.yyyy() + '.J2000', # 'soltab.' + date.yyyy() + '.J2000', # 'ut1.' + eop, # # leapseconds # # vmf1 # 'pole.' + eop # ): # result = check_tab_file(os.path.join(tables, t_name), date) # if result: # return result # fes_cmc consistency return ' -- %s: Test passed!' % platform.node()
def execute_ppp(rinexinfo, args, stnm, options, sp3types, sp3altrn, brdc_path, erase, apply_met=True, decimate=True): # put the correct APR coordinates in the header. # stninfo = pyStationInfo.StationInfo(None, allow_empty=True) stninfo = dict() brdc = pyBrdc.GetBrdcOrbits(brdc_path, rinexinfo.date, rinexinfo.rootdir) try: # inflate the chi**2 limit rinexinfo.purge_comments() rinexinfo.auto_coord(brdc=brdc, chi_limit=1000) rinexinfo.normalize_header( stninfo) # empty dict: only applies the coordinate change except pyRinex.pyRinexException as e: print str(e) if args.load_rinex: rinexinfo.compress_local_copyto('./') print 'RINEX created in current directory.' return otl_coeff = '' try: if args.ocean_loading or args.insert_sql: # get a first ppp coordinate ppp = pyPPP.RunPPP(rinexinfo, '', options, sp3types, sp3altrn, 0, strict=False, apply_met=False, kinematic=False, clock_interpolation=True) ppp.exec_ppp() # use it to get the OTL (when the auto_coord is very bad, PPP doesn't like the resulting OTL). otl = pyOTL.OceanLoading(stnm, options['grdtab'], options['otlgrid'], ppp.x, ppp.y, ppp.z) otl_coeff = otl.calculate_otl_coeff() # run again, with OTL ppp = pyPPP.RunPPP(rinexinfo, otl_coeff, options, sp3types, sp3altrn, 0, strict=False, apply_met=apply_met, kinematic=False, clock_interpolation=True, erase=erase, decimate=decimate) else: ppp = pyPPP.RunPPP(rinexinfo, '', options, sp3types, sp3altrn, 0, strict=False, apply_met=apply_met, kinematic=False, clock_interpolation=True, erase=erase, decimate=decimate) ppp.exec_ppp() if not ppp.check_phase_center(ppp.proc_parameters): print 'WARNING: phase center parameters not found for declared antenna!' if not args.insert_sql: print '%s %10.5f %13.4f %13.4f %13.4f %14.9f %14.9f %8.3f' % ( stnm, rinexinfo.date.fyear, ppp.x, ppp.y, ppp.z, ppp.lat[0], ppp.lon[0], ppp.h[0]) else: print 'INSERT INTO stations ("NetworkCode", "StationCode", "auto_x", "auto_y", "auto_z", ' \ '"Harpos_coeff_otl", lat, lon, height) VALUES ' \ '(\'???\', \'%s\', %.4f, %.4f, %.4f, \'%s\', %.8f, %.8f, %.3f)' \ % (stnm, ppp.x, ppp.y, ppp.z, otl_coeff, ppp.lat[0], ppp.lon[0], ppp.h[0]) if args.find: cnn = dbConnection.Cnn('gnss_data.cfg') Result, match, closest_stn = ppp.verify_spatial_coherence( cnn, stnm) if Result: print 'Found matching station: %s.%s' % ( match[0]['NetworkCode'], match[0]['StationCode']) elif not Result and len(match) == 1: print '%s matches the coordinate of %s.%s (distance = %8.3f m) but the filename indicates it is %s' \ % (rinexinfo.rinex, match[0]['NetworkCode'], match[0]['StationCode'], float(match[0]['distance']), stnm) elif not Result and len(match) > 0: print 'Solution for RINEX (%s %s) did not match a unique station location (and station code) ' \ 'within 10 km. Possible cantidate(s): %s' \ % (rinexinfo.rinex, rinexinfo.date.yyyyddd(), ', '.join(['%s.%s: %.3f m' % (m['NetworkCode'], m['StationCode'], m['distance']) for m in match])) elif not Result and len(match) == 0 and len(closest_stn) > 0: print 'No matches found. Closest station: %s.%s. (distance = %8.3f m)' \ % (closest_stn[0]['NetworkCode'], closest_stn[0]['StationCode'], closest_stn[0]['distance']) except pyPPP.pyRunPPPException as e: print 'Exception in PPP: ' + str(e) except pyRinex.pyRinexException as e: print 'Exception in pyRinex: ' + str(e)
def start(self, dirname, year, doy, dry_run=False): monitor_open = False try: # copy the folder created by GamitSession in the solution_pwd to the remote_pwd (pwd) try: if not os.path.exists(os.path.dirname(self.pwd)): os.makedirs(os.path.dirname(self.pwd)) except OSError: # racing condition having several processes trying to create the same folder # if OSError occurs, ignore and continue pass # if the local folder exists (due to previous incomplete processing, erase it). if os.path.exists(self.pwd): shutil.rmtree(self.pwd) # ready to copy the shared solution_dir to pwd shutil.copytree(self.solution_pwd, self.pwd, symlinks=True) with open(os.path.join(self.pwd, 'monitor.log'), 'a') as monitor: monitor_open = True monitor.write( datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ' -> %s %i %i executing on %s\n' % (dirname, year, doy, platform.node())) monitor.write( datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ' -> fetching orbits\n') try: Sp3 = pySp3.GetSp3Orbits(self.orbits['sp3_path'], self.date, self.orbits['sp3types'], self.pwd_igs, True) # type: pySp3.GetSp3Orbits except pySp3.pySp3Exception: monitor.write( datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ' -> could not find principal orbits, fetching alternative\n' ) # try alternative orbits if self.options['sp3altrn']: Sp3 = pySp3.GetSp3Orbits( self.orbits['sp3_path'], self.date, self.orbits['sp3altrn'], self.pwd_igs, True) # type: pySp3.GetSp3Orbits else: raise if Sp3.type != 'igs': # rename file shutil.copyfile(Sp3.file_path, Sp3.file_path.replace(Sp3.type, 'igs')) monitor.write( datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ' -> fetching broadcast orbits\n') pyBrdc.GetBrdcOrbits( self.orbits['brdc_path'], self.date, self.pwd_brdc, no_cleanup=True) # type: pyBrdc.GetBrdcOrbits for rinex in self.params['rinex']: monitor.write( datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ' -> fetching rinex for %s.%s %s %s\n' % (rinex['NetworkCode'], rinex['StationCode'], rinex['StationAlias'], '{:10.6f} {:11.6f}'.format( rinex['lat'], rinex['lon']))) try: with pyRinex.ReadRinex( rinex['NetworkCode'], rinex['StationCode'], rinex['source'], False) as Rinex: # type: pyRinex.ReadRinex # WARNING! some multiday RINEX were generating conflicts because the RINEX has a name, say, # tuc12302.10o and the program wants to rename it as tuc12030.10o but because it's a # multiday file, during __init__ it's already split and renamed as tuc12300.10o and # additional folders are generated with the information for each file. Therefore, find # the rinex that corresponds to the date being processed and use that one instead of the # original file. These files are not allowed by pyArchiveService, but the "start point" of # the database (i.e. the files already in the folders read by pyScanArchive) has such # problems. # figure out if this station has been affected by an earthquake # if so, window the data if rinex['jump'] is not None: monitor.write( ' -> RINEX file has been windowed: ETM detected jump on ' + rinex['jump'].datetime().strftime( '%Y-%m-%d %H:%M:%S') + '\n') if Rinex.multiday: # find the rinex that corresponds to the session being processed for Rnx in Rinex.multiday_rnx_list: if Rnx.date == self.date: Rnx.rename(rinex['destiny']) if rinex['jump'] is not None: self.window_rinex( Rnx, rinex['jump']) # before creating local copy, decimate file Rnx.decimate(30) Rnx.purge_comments() Rnx.compress_local_copyto( self.pwd_rinex) break else: Rinex.rename(rinex['destiny']) if rinex['jump'] is not None: self.window_rinex(Rinex, rinex['jump']) # before creating local copy, decimate file Rinex.decimate(30) Rinex.purge_comments() Rinex.compress_local_copyto(self.pwd_rinex) except (OSError, IOError): monitor.write( datetime.datetime.now().strftime( '%Y-%m-%d %H:%M:%S') + ' -> An error occurred while trying to copy ' + rinex['source'] + ' to ' + rinex['destiny'] + ': File skipped.\n') except (pyRinex.pyRinexException, Exception) as e: monitor.write( datetime.datetime.now().strftime( '%Y-%m-%d %H:%M:%S') + ' -> An error occurred while trying to copy ' + rinex['source'] + ': ' + str(e) + '\n') monitor.write( datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ' -> executing GAMIT\n') # create the run script self.create_replace_links() self.create_run_script() self.create_finish_script() # run the script to replace the links of the tables directory self.p = subprocess.Popen( 'find ./tables ! -name "otl.grid" -type l -exec ./replace_links.sh {} +', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=self.pwd) _, _ = self.p.communicate() # now execute the run script if not dry_run: self.p = subprocess.Popen('./run.sh', shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=self.pwd) self.stdout, self.stderr = self.p.communicate() self.p = subprocess.Popen('./finish.sh', shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=self.pwd) self.stdout, self.stderr = self.p.communicate() # check for any fatals self.p = subprocess.Popen('grep -q \'FATAL\' monitor.log', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=self.pwd) _, _ = self.p.communicate() if self.p.returncode == 0: self.success = False else: self.success = True # output statistics to the parent to display result = self.parse_monitor(self.success) with open(os.path.join(self.pwd, 'monitor.log'), 'a') as monitor: monitor.write( datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ' -> return to Parallel.GAMIT\n') # no matter the result of the processing, move folder to final destination if not dry_run: self.finish() return result except Exception: msg = traceback.format_exc() + '\nProcessing %s date %s on node %s' \ % (self.params['NetName'], self.date.yyyyddd(), platform.node()) # DDG: do not attempt to write to monitor.log or do any file operations (maybe permission problem) # problem might occur during copytree or rmtree or some other operation before opening monitor.log if monitor_open: with open(os.path.join(self.pwd, 'monitor.log'), 'a') as monitor: monitor.write( datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ' -> ERROR in pyGamitTask.start()\n%s' % msg) # the solution folder exists because it was created by GamitSession to start the processing. # erase it to upload the result if os.path.exists(self.solution_pwd): shutil.rmtree(self.solution_pwd) # execute final error step: copy to self.solution_pwd shutil.copytree(self.pwd, self.solution_pwd, symlinks=True) # remove the remote pwd shutil.rmtree(self.pwd) # output statistics to the parent to display result = self.parse_monitor(False) else: result = { 'session': '%s %s' % (self.date.yyyyddd(), self.params['DirName']), 'Project': self.params['NetName'], 'subnet': self.params['subnet'], 'Year': self.date.year, 'DOY': self.date.doy, 'FYear': self.date.fyear, 'wl': 0, 'nl': 0, 'nrms': 0, 'relaxed_constrains': '', 'max_overconstrained': '', 'node': platform.node(), 'execution_time': 0, 'execution_date': 0, 'missing': '', 'success': False, 'fatals': [] } result['error'] = msg # return useful information to the main node return result
def process_crinex_file(crinez, filename, data_rejected, data_retry): # create a uuid temporary folder in case we cannot read the year and doy from the file (and gets rejected) reject_folder = os.path.join(data_rejected, str(uuid.uuid4())) try: cnn = dbConnection.Cnn("gnss_data.cfg") Config = pyOptions.ReadOptions("gnss_data.cfg") archive = pyArchiveStruct.RinexStruct(cnn) # apply local configuration (path to repo) in the executing node crinez = os.path.join(Config.repository_data_in, crinez) except Exception: return traceback.format_exc() + ' while opening the database to process file ' + \ crinez + ' node ' + platform.node(), None # assume a default networkcode NetworkCode = 'rnx' # get the station code year and doy from the filename fileparts = archive.parse_crinex_filename(filename) if fileparts: StationCode = fileparts[0].lower() doy = int(fileparts[1]) year = int(Utils.get_norm_year_str(fileparts[3])) else: event = pyEvents.Event( Description='Could not read the station code, year or doy for file ' + crinez, EventType='error') error_handle(cnn, event, crinez, reject_folder, filename, no_db_log=True) return event['Description'], None # we can now make better reject and retry folders reject_folder = os.path.join( data_rejected, '%reason%/' + Utils.get_norm_year_str(year) + '/' + Utils.get_norm_doy_str(doy)) retry_folder = os.path.join( data_retry, '%reason%/' + Utils.get_norm_year_str(year) + '/' + Utils.get_norm_doy_str(doy)) try: # main try except block with pyRinex.ReadRinex(NetworkCode, StationCode, crinez) as rinexinfo: # type: pyRinex.ReadRinex # STOP! see if rinexinfo is a multiday rinex file if not verify_rinex_multiday(cnn, rinexinfo, Config): # was a multiday rinex. verify_rinex_date_multiday took care of it return None, None # DDG: we don't use otl coefficients because we need an approximated coordinate # we therefore just calculate the first coordinate without otl # NOTICE that we have to trust the information coming in the RINEX header (receiver type, antenna type, etc) # we don't have station info data! Still, good enough # the final PPP coordinate will be calculated by pyScanArchive on a different process # make sure that the file has the appropriate coordinates in the header for PPP. # put the correct APR coordinates in the header. # ppp didn't work, try using sh_rx2apr brdc = pyBrdc.GetBrdcOrbits(Config.brdc_path, rinexinfo.date, rinexinfo.rootdir) # inflate the chi**2 limit to make sure it will pass (even if we get a crappy coordinate) try: rinexinfo.auto_coord(brdc, chi_limit=1000) # normalize header to add the APR coordinate # empty dict since nothing extra to change (other than the APR coordinate) rinexinfo.normalize_header(dict()) except pyRinex.pyRinexExceptionNoAutoCoord: # could not determine an autonomous coordinate, try PPP anyways. 50% chance it will work pass with pyPPP.RunPPP( rinexinfo, '', Config.options, Config.sp3types, Config.sp3altrn, rinexinfo.antOffset, strict=False, apply_met=False, clock_interpolation=True) as ppp: # type: pyPPP.RunPPP try: ppp.exec_ppp() except pyPPP.pyRunPPPException as ePPP: # inflate the chi**2 limit to make sure it will pass (even if we get a crappy coordinate) # if coordinate is TOO bad it will get kicked off by the unreasonable geodetic height try: auto_coords_xyz, auto_coords_lla = rinexinfo.auto_coord( brdc, chi_limit=1000) except pyRinex.pyRinexExceptionNoAutoCoord as e: # catch pyRinexExceptionNoAutoCoord and convert it into a pyRunPPPException raise pyPPP.pyRunPPPException( 'Both PPP and sh_rx2apr failed to obtain a coordinate for %s.\n' 'The file has been moved into the rejection folder. ' 'Summary PPP file and error (if exists) follows:\n%s\n\n' 'ERROR section:\n%s\npyRinex.auto_coord error follows:\n%s' % (crinez.replace(Config.repository_data_in, ''), ppp.summary, str(ePPP).strip(), str(e).strip())) # DDG: this is correct - auto_coord returns a numpy array (calculated in ecef2lla), # so ppp.lat = auto_coords_lla is consistent. ppp.lat = auto_coords_lla[0] ppp.lon = auto_coords_lla[1] ppp.h = auto_coords_lla[2] ppp.x = auto_coords_xyz[0] ppp.y = auto_coords_xyz[1] ppp.z = auto_coords_xyz[2] # check for unreasonable heights if ppp.h[0] > 9000 or ppp.h[0] < -400: raise pyRinex.pyRinexException( os.path.relpath(crinez, Config.repository_data_in) + ' : unreasonable geodetic height (%.3f). ' 'RINEX file will not enter the archive.' % (ppp.h[0])) Result, match, _ = ppp.verify_spatial_coherence( cnn, StationCode) if Result: # insert: there is only 1 match with the same StationCode. rinexinfo.rename(NetworkCode=match[0]['NetworkCode']) insert_data(cnn, archive, rinexinfo) else: if len(match) == 1: error = "%s matches the coordinate of %s.%s (distance = %8.3f m) but the filename " \ "indicates it is %s. Please verify that this file belongs to %s.%s, rename it and " \ "try again. The file was moved to the retry folder. " \ "Rename script and pSQL sentence follows:\n" \ "BASH# mv %s %s\n" \ "PSQL# INSERT INTO stations (\"NetworkCode\", \"StationCode\", \"auto_x\", " \ "\"auto_y\", \"auto_z\", \"lat\", \"lon\", \"height\") VALUES " \ "('???','%s', %12.3f, %12.3f, %12.3f, " \ "%10.6f, %10.6f, %8.3f)\n" \ % (os.path.relpath(crinez, Config.repository_data_in), match[0]['NetworkCode'], match[0]['StationCode'], float(match[0]['distance']), StationCode, match[0]['NetworkCode'], match[0]['StationCode'], os.path.join(retry_folder, filename), os.path.join(retry_folder, filename.replace(StationCode, match[0]['StationCode'])), StationCode, ppp.x, ppp.y, ppp.z, ppp.lat[0], ppp.lon[0], ppp.h[0]) raise pyPPP.pyRunPPPExceptionCoordConflict(error) elif len(match) > 1: # a number of things could have happened: # 1) wrong station code, and more than one matching stations # (that do not match the station code, of course) # see rms.lhcl 2007 113 -> matches rms.igm0: 34.293 m, rms.igm1: 40.604 m, rms.byns: 4.819 m # 2) no entry in the database for this solution -> add a lock and populate the exit args # no match, but we have some candidates error = "Solution for RINEX in repository (%s %s) did not match a unique station location " \ "(and station code) within 5 km. Possible cantidate(s): %s. This file has been moved " \ "to data_in_retry. pSQL sentence follows:\n" \ "PSQL# INSERT INTO stations (\"NetworkCode\", \"StationCode\", \"auto_x\", " \ "\"auto_y\", \"auto_z\", \"lat\", \"lon\", \"height\") VALUES " \ "('???','%s', %12.3f, %12.3f, %12.3f, %10.6f, %10.6f, %8.3f)\n" \ % (os.path.relpath(crinez, Config.repository_data_in), rinexinfo.date.yyyyddd(), ', '.join(['%s.%s: %.3f m' % (m['NetworkCode'], m['StationCode'], m['distance']) for m in match]), StationCode, ppp.x, ppp.y, ppp.z, ppp.lat[0], ppp.lon[0], ppp.h[0]) raise pyPPP.pyRunPPPExceptionCoordConflict(error) else: # only found a station removing the distance limit (could be thousands of km away!) # The user will have to add the metadata to the database before the file can be added, # but in principle no problem was detected by the process. This file will stay in this folder # so that it gets analyzed again but a "lock" will be added to the file that will have to be # removed before the service analyzes again. # if the user inserted the station by then, it will get moved to the appropriate place. # we return all the relevant metadata to ease the insert of the station in the database otl = pyOTL.OceanLoading(StationCode, Config.options['grdtab'], Config.options['otlgrid']) # use the ppp coordinates to calculate the otl coeff = otl.calculate_otl_coeff(x=ppp.x, y=ppp.y, z=ppp.z) # add the file to the locks table so that it doesn't get processed over and over # this will be removed by user so that the file gets reprocessed once all the metadata is ready cnn.insert('locks', filename=os.path.relpath( crinez, Config.repository_data_in)) return None, [ StationCode, (ppp.x, ppp.y, ppp.z), coeff, (ppp.lat[0], ppp.lon[0], ppp.h[0]), crinez ] except (pyRinex.pyRinexExceptionBadFile, pyRinex.pyRinexExceptionSingleEpoch, pyRinex.pyRinexExceptionNoAutoCoord) \ as e: reject_folder = reject_folder.replace('%reason%', 'bad_rinex') # add more verbose output e.event['Description'] = e.event['Description'] + '\n' + os.path.relpath(crinez, Config.repository_data_in) + \ ': (file moved to ' + reject_folder + ')' e.event['StationCode'] = StationCode e.event['NetworkCode'] = '???' e.event['Year'] = year e.event['DOY'] = doy # error, move the file to rejected folder error_handle(cnn, e.event, crinez, reject_folder, filename) return None, None except pyRinex.pyRinexException as e: retry_folder = retry_folder.replace('%reason%', 'rinex_issues') # add more verbose output e.event['Description'] = e.event['Description'] + '\n' + os.path.relpath(crinez, Config.repository_data_in) + \ ': (file moved to ' + retry_folder + ')' e.event['StationCode'] = StationCode e.event['NetworkCode'] = '???' e.event['Year'] = year e.event['DOY'] = doy # error, move the file to rejected folder error_handle(cnn, e.event, crinez, retry_folder, filename) return None, None except pyPPP.pyRunPPPExceptionCoordConflict as e: retry_folder = retry_folder.replace('%reason%', 'coord_conflicts') e.event['Description'] = e.event['Description'].replace( '%reason%', 'coord_conflicts') e.event['StationCode'] = StationCode e.event['NetworkCode'] = '???' e.event['Year'] = year e.event['DOY'] = doy error_handle(cnn, e.event, crinez, retry_folder, filename) return None, None except pyPPP.pyRunPPPException as e: reject_folder = reject_folder.replace('%reason%', 'no_ppp_solution') e.event['StationCode'] = StationCode e.event['NetworkCode'] = '???' e.event['Year'] = year e.event['DOY'] = doy error_handle(cnn, e.event, crinez, reject_folder, filename) return None, None except pyStationInfo.pyStationInfoException as e: retry_folder = retry_folder.replace('%reason%', 'station_info_exception') e.event['Description'] = e.event['Description'] + '. The file will stay in the repository and will be ' \ 'processed during the next cycle of pyArchiveService.' e.event['StationCode'] = StationCode e.event['NetworkCode'] = '???' e.event['Year'] = year e.event['DOY'] = doy error_handle(cnn, e.event, crinez, retry_folder, filename) return None, None except pyOTL.pyOTLException as e: retry_folder = retry_folder.replace('%reason%', 'otl_exception') e.event['Description'] = e.event['Description'] + ' while calculating OTL for %s. ' \ 'The file has been moved into the retry folder.' \ % os.path.relpath(crinez, Config.repository_data_in) e.event['StationCode'] = StationCode e.event['NetworkCode'] = '???' e.event['Year'] = year e.event['DOY'] = doy error_handle(cnn, e.event, crinez, retry_folder, filename) return None, None except pyProducts.pyProductsExceptionUnreasonableDate as e: # a bad RINEX file requested an orbit for a date < 0 or > now() reject_folder = reject_folder.replace('%reason%', 'bad_rinex') e.event['Description'] = e.event['Description'] + ' during %s. The file has been moved to the rejected ' \ 'folder. Most likely bad RINEX header/data.' \ % os.path.relpath(crinez, Config.repository_data_in) e.event['StationCode'] = StationCode e.event['NetworkCode'] = '???' e.event['Year'] = year e.event['DOY'] = doy error_handle(cnn, e.event, crinez, reject_folder, filename) return None, None except pyProducts.pyProductsException as e: # if PPP fails and ArchiveService tries to run sh_rnx2apr and it doesn't find the orbits, send to retry retry_folder = retry_folder.replace('%reason%', 'sp3_exception') e.event['Description'] = e.event['Description'] + ': %s. Check the brdc/sp3/clk files and also check that ' \ 'the RINEX data is not corrupt.' \ % os.path.relpath(crinez, Config.repository_data_in) e.event['StationCode'] = StationCode e.event['NetworkCode'] = '???' e.event['Year'] = year e.event['DOY'] = doy error_handle(cnn, e.event, crinez, retry_folder, filename) return None, None except dbConnection.dbErrInsert as e: reject_folder = reject_folder.replace('%reason%', 'duplicate_insert') # insert duplicate values: two parallel processes tried to insert different filenames # (or the same) of the same station to the db: move it to the rejected folder. # The user might want to retry later. Log it in events # this case should be very rare event = pyEvents.Event( Description='Duplicate rinex insertion attempted while processing ' + os.path.relpath(crinez, Config.repository_data_in) + ' : (file moved to rejected folder)\n' + str(e), EventType='warn', StationCode=StationCode, NetworkCode='???', Year=year, DOY=doy) error_handle(cnn, event, crinez, reject_folder, filename) return None, None except Exception: retry_folder = retry_folder.replace('%reason%', 'general_exception') event = pyEvents.Event( Description=traceback.format_exc() + ' processing: ' + os.path.relpath(crinez, Config.repository_data_in) + ' in node ' + platform.node() + ' (file moved to retry folder)', EventType='error') error_handle(cnn, event, crinez, retry_folder, filename, no_db_log=True) return event['Description'], None return None, None
def execute_ppp(rinexinfo, args, stnm, options, sp3types, sp3altrn, brdc_path, erase, apply_met=True, decimate=True, fix_coordinate=None, solve_troposphere=105, copy_results=None, backward_substitution=False, elevation_mask=5): # put the correct APR coordinates in the header. # stninfo = pyStationInfo.StationInfo(None, allow_empty=True) brdc = pyBrdc.GetBrdcOrbits(brdc_path, rinexinfo.date, rinexinfo.rootdir) try: # inflate the chi**2 limit rinexinfo.purge_comments() rinexinfo.auto_coord(brdc=brdc, chi_limit=1000) stninfo = {} rinexinfo.normalize_header( stninfo) # empty dict: only applies the coordinate change except pyRinex.pyRinexException as e: print(str(e)) if args.load_rinex: rinexinfo.compress_local_copyto('./') print('RINEX created in current directory.') return try: otl_coeff = '' if args.ocean_loading or args.insert_sql: # get a first ppp coordinate ppp = pyPPP.RunPPP(rinexinfo, '', options, sp3types, sp3altrn, 0, strict=False, apply_met=False, kinematic=False, clock_interpolation=True) ppp.exec_ppp() # use it to get the OTL (when the auto_coord is very bad, PPP doesn't like the resulting OTL). otl = pyOTL.OceanLoading(stnm, options['grdtab'], options['otlgrid'], ppp.x, ppp.y, ppp.z) otl_coeff = otl.calculate_otl_coeff() # run again, now with OTL coeff: # determine if need to solve for coordinates or not x = y = z = 0 if fix_coordinate is not None: if len(fix_coordinate) > 1: x = float(fix_coordinate[0]) y = float(fix_coordinate[1]) z = float(fix_coordinate[2]) else: # read from file cstr = file_readlines(fix_coordinate[0]) xyz = re.findall( r'%s (-?\d+\.\d+)\s+(-?\d+\.\d+)\s+(-?\d+\.\d+)' % rinexinfo.StationCode, ''.join(cstr), re.IGNORECASE) if len(xyz): x = float(xyz[0][0]) y = float(xyz[0][1]) z = float(xyz[0][2]) else: print( 'WARNING: coordinate fixing invoked but could not find %s in list of coordinates -> ' 'unfixing station coordinate in PPP' % rinexinfo.StationCode) fix_coordinate = False print('%14.4f %14.4f %14.4f' % (x, y, z)) ppp = pyPPP.RunPPP( rinexinfo, otl_coeff, options, sp3types, sp3altrn, 0, strict=False, apply_met=apply_met, kinematic=False, clock_interpolation=True, erase=erase, decimate=decimate, solve_coordinates=True if not fix_coordinate else False, solve_troposphere=solve_troposphere, back_substitution=backward_substitution, elev_mask=elevation_mask, x=x, y=y, z=z) ppp.exec_ppp() if not ppp.check_phase_center(ppp.proc_parameters): print( 'WARNING: phase center parameters not found for declared antenna!' ) if not args.insert_sql: print( '%s %10.5f %13.4f %13.4f %13.4f %14.9f %14.9f %8.3f %8.3f %8.3f %8.3f %8.3f %8.3f' % (stnm, rinexinfo.date.fyear, ppp.x, ppp.y, ppp.z, ppp.lat[0], ppp.lon[0], ppp.h[0], ppp.clock_phase, ppp.clock_phase_sigma, ppp.phase_drift, ppp.phase_drift_sigma, ppp.clock_rms)) else: print('INSERT INTO stations ("NetworkCode", "StationCode", "auto_x", "auto_y", "auto_z", ' \ '"Harpos_coeff_otl", lat, lon, height) VALUES ' \ '(\'???\', \'%s\', %.4f, %.4f, %.4f, \'%s\', %.8f, %.8f, %.3f)' \ % (stnm, ppp.x, ppp.y, ppp.z, otl_coeff, ppp.lat[0], ppp.lon[0], ppp.h[0])) if args.find: cnn = dbConnection.Cnn('gnss_data.cfg') Result, match, closest_stn = ppp.verify_spatial_coherence( cnn, stnm) if Result: print('Found matching station: %s.%s' % (match[0]['NetworkCode'], match[0]['StationCode'])) elif len(match) == 1: print('%s matches the coordinate of %s.%s (distance = %8.3f m) but the filename indicates it is %s' \ % (rinexinfo.rinex, match[0]['NetworkCode'], match[0]['StationCode'], float(match[0]['distance']), stnm)) elif len(match) > 0: print('Solution for RINEX (%s %s) did not match a unique station location (and station code) ' \ 'within 10 km. Possible cantidate(s): %s' \ % (rinexinfo.rinex, rinexinfo.date.yyyyddd(), ', '.join(['%s.%s: %.3f m' % (m['NetworkCode'], m['StationCode'], m['distance']) for m in match]))) elif len(match) == 0 and len(closest_stn) > 0: print('No matches found. Closest station: %s.%s. (distance = %8.3f m)' \ % (closest_stn[0]['NetworkCode'], closest_stn[0]['StationCode'], closest_stn[0]['distance'])) if copy_results: copy_results = copy_results[0] try: fpath = os.path.join(copy_results, rinexinfo.StationCode) if not os.path.exists(fpath): os.makedirs(fpath) shutil.copyfile( ppp.path_res_file, os.path.join(fpath, os.path.basename(ppp.path_res_file))) shutil.copyfile( ppp.path_pos_file, os.path.join(fpath, os.path.basename(ppp.path_pos_file))) shutil.copyfile( ppp.path_ses_file, os.path.join(fpath, os.path.basename(ppp.path_ses_file))) shutil.copyfile( ppp.path_sum_file, os.path.join(fpath, os.path.basename(ppp.path_sum_file))) shutil.copyfile( os.path.join(ppp.rootdir, 'commands.cmd'), os.path.join(fpath, os.path.basename(ppp.path_sum_file) + '.cmd')) except Exception as e: print( 'WARNING: There was a problem copying results to %s: %s' % (copy_results, str(e))) except pyPPP.pyRunPPPException as e: print('Exception in PPP: ' + str(e)) except pyRinex.pyRinexException as e: print('Exception in pyRinex: ' + str(e))