''' Created on Aug 1, 2017 @author: Juan C. Espinoza ''' import os import sys import time import json import datetime import numpy try: import madrigal import madrigal.cedar except: print 'You should install "madrigal library" module if you want to read/write Madrigal data' from schainpy.model.proc.jroproc_base import Operation from schainpy.model.data.jrodata import Parameters MISSING = -32767 DEF_CATALOG = { 'principleInvestigator': 'Marco Milla', 'expPurpose': None, 'expMode': None, 'cycleTime': None, 'correlativeExp': None, 'sciRemarks': None, 'instRemarks': None } DEF_HEADER = { 'kindatDesc': None, 'analyst': 'Jicamarca User', 'comments': None, 'history': None } MNEMONICS = { 10: 'jro', 11: 'jbr', 840: 'jul', 13: 'jas', 1000: 'pbr', 1001: 'hbr', 1002: 'obr', } def load_json(obj): ''' Parse json as string instead of unicode ''' if isinstance(obj, str): obj = json.loads(obj) return {str(k): load_json(v) if isinstance(v, dict) else str(v) if isinstance(v, unicode) else v for k, v in obj.items()} class MAD2Writer(Operation): def __init__(self, **kwargs): Operation.__init__(self, **kwargs) self.dataOut = Parameters() self.path = None self.dataOut = None self.ext = '.dat' return def run(self, dataOut, path, oneDList, twoDParam='', twoDList='{}', metadata='{}', **kwargs): ''' Inputs: path - path where files will be created oneDList - json of one-dimensional parameters in record where keys are Madrigal codes (integers or mnemonics) and values the corresponding dataOut attribute e.g: { 'gdlatr': 'lat', 'gdlonr': 'lon', 'gdlat2':'lat', 'glon2':'lon'} twoDParam - independent parameter to get the number of rows e.g: heighList twoDList - json of two-dimensional parameters in record where keys are Madrigal codes (integers or mnemonics) and values the corresponding dataOut attribute if multidimensional array specify as tupple ('attr', pos) e.g: { 'gdalt': 'heightList', 'vn1p2': ('data_output', 0), 'vn2p2': ('data_output', 1), 'vn3': ('data_output', 2), 'snl': ('data_SNR', 'db') } metadata - json of madrigal metadata (kinst, kindat, catalog and header) ''' if not self.isConfig: self.setup(dataOut, path, oneDList, twoDParam, twoDList, metadata, **kwargs) self.isConfig = True self.putData() return def setup(self, dataOut, path, oneDList, twoDParam, twoDList, metadata, **kwargs): ''' Configure Operation ''' self.dataOut = dataOut self.nmodes = self.dataOut.nmodes self.path = path self.blocks = kwargs.get('blocks', None) self.counter = 0 self.oneDList = load_json(oneDList) self.twoDList = load_json(twoDList) self.twoDParam = twoDParam meta = load_json(metadata) self.kinst = meta.get('kinst') self.kindat = meta.get('kindat') self.catalog = meta.get('catalog', DEF_CATALOG) self.header = meta.get('header', DEF_HEADER) return def setFile(self): ''' Create new cedar file object ''' self.mnemonic = MNEMONICS[self.kinst] #TODO get mnemonic from madrigal date = datetime.datetime.utcfromtimestamp(self.dataOut.utctime) filename = '%s%s_%s%s' % (self.mnemonic, date.strftime('%Y%m%d_%H%M%S'), self.dataOut.mode, self.ext) self.fullname = os.path.join(self.path, filename) if os.path.isfile(self.fullname) : print "Destination path '%s' already exists. Previous file deleted. " %self.fullname os.remove(self.fullname) try: print '[Writing] creating file : %s' % (self.fullname) self.cedarObj = madrigal.cedar.MadrigalCedarFile(self.fullname, True) except ValueError, e: print '[Error]: Impossible to create a cedar object with "madrigal.cedar.MadrigalCedarFile" ' return return 1 def writeBlock(self): ''' Add data records to cedar file taking data from oneDList and twoDList attributes. Allowed parameters in: parcodes.tab ''' startTime = datetime.datetime.utcfromtimestamp(self.dataOut.utctime) endTime = startTime + datetime.timedelta(seconds=self.dataOut.paramInterval) nrows = len(getattr(self.dataOut, self.twoDParam)) rec = madrigal.cedar.MadrigalDataRecord( self.kinst, self.kindat, startTime.year, startTime.month, startTime.day, startTime.hour, startTime.minute, startTime.second, startTime.microsecond/10000, endTime.year, endTime.month, endTime.day, endTime.hour, endTime.minute, endTime.second, endTime.microsecond/10000, self.oneDList.keys(), self.twoDList.keys(), nrows ) # Setting 1d values for key in self.oneDList: rec.set1D(key, getattr(self.dataOut, self.oneDList[key])) # Setting 2d values invalid = numpy.isnan(self.dataOut.data_output) self.dataOut.data_output[invalid] = MISSING out = {} for key, value in self.twoDList.items(): if isinstance(value, str): out[key] = getattr(self.dataOut, value) elif isinstance(value, tuple): attr, x = value if isinstance(x, (int, float)): out[key] = getattr(self.dataOut, attr)[int(x)] elif x.lower()=='db': tmp = getattr(self.dataOut, attr) SNRavg = numpy.average(tmp, axis=0) out[key] = 10*numpy.log10(SNRavg) for n in range(nrows): for key in out: rec.set2D(key, n, out[key][n]) self.cedarObj.append(rec) self.cedarObj.dump() print '[Writing] Record No. {} (mode {}).'.format( self.counter, self.dataOut.mode ) def setHeader(self): ''' Create an add catalog and header to cedar file ''' header = madrigal.cedar.CatalogHeaderCreator(self.fullname) header.createCatalog(**self.catalog) header.createHeader(**self.header) header.write() def putData(self): if self.dataOut.flagNoData: return 0 if self.counter == 0: self.setFile() if self.counter <= self.dataOut.nrecords: self.writeBlock() self.counter += 1 if self.counter == self.dataOut.nrecords or self.counter == self.blocks: self.setHeader() self.counter = 0