jrodata.py
1074 lines
| 28.9 KiB
| text/x-python
|
PythonLexer
r1338 | # Copyright (c) 2012-2020 Jicamarca Radio Observatory | |||
# All rights reserved. | ||||
# | ||||
# Distributed under the terms of the BSD 3-clause license. | ||||
"""Definition of diferent Data objects for different types of data | ||||
|
r487 | |||
r1338 | Here you will find the diferent data objects for the different types | |||
of data, this data objects must be used as dataIn or dataOut objects in | ||||
processing units and operations. Currently the supported data objects are: | ||||
Voltage, Spectra, SpectraHeis, Fits, Correlation and Parameters | ||||
""" | ||||
|
r487 | |||
import copy | ||||
import numpy | ||||
import datetime | ||||
|
r1187 | import json | ||
|
r487 | |||
|
r1285 | import schainpy.admin | ||
|
r1187 | from schainpy.utils import log | ||
|
r1167 | from .jroheaderIO import SystemHeader, RadarControllerHeader | ||
|
r1286 | from schainpy.model.data import _noise | ||
|
r878 | |||
|
r487 | |||
def getNumpyDtype(dataTypeCode): | ||||
r889 | ||||
|
r487 | if dataTypeCode == 0: | ||
|
r1092 | numpyDtype = numpy.dtype([('real', '<i1'), ('imag', '<i1')]) | ||
|
r487 | elif dataTypeCode == 1: | ||
|
r1092 | numpyDtype = numpy.dtype([('real', '<i2'), ('imag', '<i2')]) | ||
|
r487 | elif dataTypeCode == 2: | ||
|
r1092 | numpyDtype = numpy.dtype([('real', '<i4'), ('imag', '<i4')]) | ||
|
r487 | elif dataTypeCode == 3: | ||
|
r1092 | numpyDtype = numpy.dtype([('real', '<i8'), ('imag', '<i8')]) | ||
|
r487 | elif dataTypeCode == 4: | ||
|
r1092 | numpyDtype = numpy.dtype([('real', '<f4'), ('imag', '<f4')]) | ||
|
r487 | elif dataTypeCode == 5: | ||
|
r1092 | numpyDtype = numpy.dtype([('real', '<f8'), ('imag', '<f8')]) | ||
|
r487 | else: | ||
|
r1167 | raise ValueError('dataTypeCode was not defined') | ||
r889 | ||||
|
r487 | return numpyDtype | ||
|
r1092 | |||
|
r487 | def getDataTypeCode(numpyDtype): | ||
r889 | ||||
|
r1092 | if numpyDtype == numpy.dtype([('real', '<i1'), ('imag', '<i1')]): | ||
|
r487 | datatype = 0 | ||
|
r1092 | elif numpyDtype == numpy.dtype([('real', '<i2'), ('imag', '<i2')]): | ||
|
r487 | datatype = 1 | ||
|
r1092 | elif numpyDtype == numpy.dtype([('real', '<i4'), ('imag', '<i4')]): | ||
|
r487 | datatype = 2 | ||
|
r1092 | elif numpyDtype == numpy.dtype([('real', '<i8'), ('imag', '<i8')]): | ||
|
r487 | datatype = 3 | ||
|
r1092 | elif numpyDtype == numpy.dtype([('real', '<f4'), ('imag', '<f4')]): | ||
|
r487 | datatype = 4 | ||
|
r1092 | elif numpyDtype == numpy.dtype([('real', '<f8'), ('imag', '<f8')]): | ||
|
r487 | datatype = 5 | ||
else: | ||||
datatype = None | ||||
r889 | ||||
|
r487 | return datatype | ||
|
r1092 | |||
|
r487 | def hildebrand_sekhon(data, navg): | ||
|
r568 | """ | ||
r889 | This method is for the objective determination of the noise level in Doppler spectra. This | |||
implementation technique is based on the fact that the standard deviation of the spectral | ||||
|
r568 | densities is equal to the mean spectral density for white Gaussian noise | ||
r889 | ||||
|
r568 | Inputs: | ||
Data : heights | ||||
navg : numbers of averages | ||||
r889 | ||||
|
r568 | Return: | ||
|
r1176 | mean : noise's level | ||
|
r568 | """ | ||
r889 | ||||
|
r1179 | sortdata = numpy.sort(data, axis=None) | ||
|
r1286 | ''' | ||
|
r1179 | lenOfData = len(sortdata) | ||
nums_min = lenOfData*0.2 | ||||
if nums_min <= 5: | ||||
nums_min = 5 | ||||
sump = 0. | ||||
sumq = 0. | ||||
j = 0 | ||||
cont = 1 | ||||
|
r1187 | while((cont == 1)and(j < lenOfData)): | ||
|
r1179 | |||
sump += sortdata[j] | ||||
sumq += sortdata[j]**2 | ||||
if j > nums_min: | ||||
rtest = float(j)/(j-1) + 1.0/navg | ||||
if ((sumq*j) > (rtest*sump**2)): | ||||
j = j - 1 | ||||
|
r1187 | sump = sump - sortdata[j] | ||
sumq = sumq - sortdata[j]**2 | ||||
|
r1179 | cont = 0 | ||
j += 1 | ||||
|
r1187 | lnoise = sump / j | ||
|
r1286 | ''' | ||
return _noise.hildebrand_sekhon(sortdata, navg) | ||||
|
r487 | |||
|
r501 | class Beam: | ||
r889 | ||||
|
r501 | def __init__(self): | ||
self.codeList = [] | ||||
self.azimuthList = [] | ||||
r889 | self.zenithList = [] | |||
|
r501 | |||
|
r1092 | |||
r1387 | ||||
|
r487 | class GenericData(object): | ||
r889 | ||||
|
r487 | flagNoData = True | ||
r889 | ||||
|
r487 | def copy(self, inputObj=None): | ||
r889 | ||||
|
r487 | if inputObj == None: | ||
return copy.deepcopy(self) | ||||
|
r1167 | for key in list(inputObj.__dict__.keys()): | ||
r889 | ||||
|
r757 | attribute = inputObj.__dict__[key] | ||
r889 | ||||
|
r1092 | # If this attribute is a tuple or list | ||
|
r757 | if type(inputObj.__dict__[key]) in (tuple, list): | ||
self.__dict__[key] = attribute[:] | ||||
continue | ||||
r889 | ||||
|
r1092 | # If this attribute is another object or instance | ||
|
r757 | if hasattr(attribute, '__dict__'): | ||
self.__dict__[key] = attribute.copy() | ||||
continue | ||||
r889 | ||||
|
r487 | self.__dict__[key] = inputObj.__dict__[key] | ||
def deepcopy(self): | ||||
r889 | ||||
|
r487 | return copy.deepcopy(self) | ||
r889 | ||||
|
r487 | def isEmpty(self): | ||
r889 | ||||
|
r487 | return self.flagNoData | ||
r889 | ||||
|
r1285 | def isReady(self): | ||
|
r1092 | |||
|
r1285 | return not self.flagNoData | ||
r889 | ||||
|
r1092 | |||
|
r487 | class JROData(GenericData): | ||
systemHeaderObj = SystemHeader() | ||||
radarControllerHeaderObj = RadarControllerHeader() | ||||
type = None | ||||
|
r1092 | datatype = None # dtype but in string | ||
|
r487 | nProfiles = None | ||
heightList = None | ||||
channelList = None | ||||
|
r568 | flagDiscontinuousBlock = False | ||
|
r487 | useLocalTime = False | ||
utctime = None | ||||
timeZone = None | ||||
dstFlag = None | ||||
errorCount = None | ||||
blocksize = None | ||||
|
r1092 | flagDecodeData = False # asumo q la data no esta decodificada | ||
flagDeflipData = False # asumo q la data no esta sin flip | ||||
|
r487 | flagShiftFFT = False | ||
nCohInt = None | ||||
windowOfFilter = 1 | ||||
C = 3e8 | ||||
frequency = 49.92e6 | ||||
realtime = False | ||||
beacon_heiIndexList = None | ||||
last_block = None | ||||
blocknow = None | ||||
|
r499 | azimuth = None | ||
zenith = None | ||||
|
r501 | beam = Beam() | ||
|
r534 | profileIndex = None | ||
|
r1187 | error = None | ||
data = None | ||||
|
r1188 | nmodes = None | ||
r1338 | metadata_list = ['heightList', 'timeZone', 'type'] | |||
r1387 | codeList = None | |||
azimuthList = None | ||||
elevationList = None | ||||
|
r1176 | |||
def __str__(self): | ||||
r1338 | return '{} - {}'.format(self.type, self.datatime()) | |||
|
r1176 | |||
|
r487 | def getNoise(self): | ||
r889 | ||||
|
r684 | raise NotImplementedError | ||
r889 | ||||
r1338 | @property | |||
def nChannels(self): | ||||
r889 | ||||
|
r487 | return len(self.channelList) | ||
r889 | ||||
r1338 | @property | |||
def channelIndexList(self): | ||||
r889 | ||||
|
r1167 | return list(range(self.nChannels)) | ||
r889 | ||||
r1338 | @property | |||
def nHeights(self): | ||||
r889 | ||||
|
r487 | return len(self.heightList) | ||
r889 | ||||
|
r765 | def getDeltaH(self): | ||
r889 | ||||
r1338 | return self.heightList[1] - self.heightList[0] | |||
r889 | ||||
r1338 | @property | |||
def ltctime(self): | ||||
r889 | ||||
|
r487 | if self.useLocalTime: | ||
|
r1092 | return self.utctime - self.timeZone * 60 | ||
r889 | ||||
|
r487 | return self.utctime | ||
r889 | ||||
r1338 | @property | |||
def datatime(self): | ||||
r889 | ||||
|
r487 | datatimeValue = datetime.datetime.utcfromtimestamp(self.ltctime) | ||
return datatimeValue | ||||
r889 | ||||
|
r487 | def getTimeRange(self): | ||
r889 | ||||
|
r487 | datatime = [] | ||
r889 | ||||
|
r487 | datatime.append(self.ltctime) | ||
|
r1092 | datatime.append(self.ltctime + self.timeInterval + 1) | ||
r889 | ||||
|
r487 | datatime = numpy.array(datatime) | ||
r889 | ||||
|
r487 | return datatime | ||
r889 | ||||
|
r765 | def getFmaxTimeResponse(self): | ||
r889 | ||||
|
r1092 | period = (10**-6) * self.getDeltaH() / (0.15) | ||
r889 | ||||
|
r1092 | PRF = 1. / (period * self.nCohInt) | ||
r889 | ||||
|
r765 | fmax = PRF | ||
r889 | ||||
|
r765 | return fmax | ||
r889 | ||||
|
r487 | def getFmax(self): | ||
|
r1092 | PRF = 1. / (self.ippSeconds * self.nCohInt) | ||
r889 | ||||
|
r765 | fmax = PRF | ||
|
r487 | return fmax | ||
r889 | ||||
|
r487 | def getVmax(self): | ||
r889 | ||||
|
r1092 | _lambda = self.C / self.frequency | ||
r889 | ||||
|
r1092 | vmax = self.getFmax() * _lambda / 2 | ||
r889 | ||||
|
r487 | return vmax | ||
r889 | ||||
r1338 | @property | |||
def ippSeconds(self): | ||||
|
r487 | ''' | ||
''' | ||||
return self.radarControllerHeaderObj.ippSeconds | ||||
r889 | ||||
r1338 | @ippSeconds.setter | |||
def ippSeconds(self, ippSeconds): | ||||
|
r487 | ''' | ||
''' | ||||
self.radarControllerHeaderObj.ippSeconds = ippSeconds | ||||
r889 | ||||
r1338 | @property | |||
def code(self): | ||||
|
r487 | ''' | ||
''' | ||||
r1338 | return self.radarControllerHeaderObj.code | |||
r889 | ||||
r1338 | @code.setter | |||
def code(self, code): | ||||
|
r487 | ''' | ||
''' | ||||
r1338 | self.radarControllerHeaderObj.code = code | |||
r889 | ||||
r1338 | @property | |||
r1340 | def nCode(self): | |||
|
r568 | ''' | ||
''' | ||||
r1338 | return self.radarControllerHeaderObj.nCode | |||
r889 | ||||
r1340 | @nCode.setter | |||
def nCode(self, ncode): | ||||
|
r568 | ''' | ||
''' | ||||
r1338 | self.radarControllerHeaderObj.nCode = ncode | |||
|
r568 | |||
r1338 | @property | |||
r1340 | def nBaud(self): | |||
|
r568 | ''' | ||
''' | ||||
r1338 | return self.radarControllerHeaderObj.nBaud | |||
r889 | ||||
r1340 | @nBaud.setter | |||
def nBaud(self, nbaud): | ||||
|
r568 | ''' | ||
''' | ||||
r1338 | self.radarControllerHeaderObj.nBaud = nbaud | |||
r889 | ||||
r1338 | @property | |||
def ipp(self): | ||||
|
r568 | ''' | ||
''' | ||||
r1338 | return self.radarControllerHeaderObj.ipp | |||
r889 | ||||
r1338 | @ipp.setter | |||
def ipp(self, ipp): | ||||
|
r568 | ''' | ||
''' | ||||
r1338 | self.radarControllerHeaderObj.ipp = ipp | |||
r889 | ||||
r1338 | @property | |||
def metadata(self): | ||||
''' | ||||
''' | ||||
r889 | ||||
r1338 | return {attr: getattr(self, attr) for attr in self.metadata_list} | |||
r889 | ||||
|
r1092 | |||
|
r487 | class Voltage(JROData): | ||
r889 | ||||
r1315 | dataPP_POW = None | |||
dataPP_DOP = None | ||||
dataPP_WIDTH = None | ||||
dataPP_SNR = None | ||||
r889 | ||||
|
r487 | def __init__(self): | ||
''' | ||||
Constructor | ||||
''' | ||||
r889 | ||||
|
r566 | self.useLocalTime = True | ||
|
r487 | self.radarControllerHeaderObj = RadarControllerHeader() | ||
self.systemHeaderObj = SystemHeader() | ||||
self.type = "Voltage" | ||||
self.data = None | ||||
self.nProfiles = None | ||||
|
r1191 | self.heightList = None | ||
|
r487 | self.channelList = None | ||
self.flagNoData = True | ||||
|
r568 | self.flagDiscontinuousBlock = False | ||
|
r487 | self.utctime = None | ||
|
r1326 | self.timeZone = 0 | ||
|
r487 | self.dstFlag = None | ||
self.errorCount = None | ||||
self.nCohInt = None | ||||
self.blocksize = None | ||||
|
r1310 | self.flagCohInt = False | ||
|
r1092 | self.flagDecodeData = False # asumo q la data no esta decodificada | ||
self.flagDeflipData = False # asumo q la data no esta sin flip | ||||
|
r487 | self.flagShiftFFT = False | ||
|
r1092 | self.flagDataAsBlock = False # Asumo que la data es leida perfil a perfil | ||
|
r534 | self.profileIndex = 0 | ||
r1370 | self.metadata_list = ['type', 'heightList', 'timeZone', 'nProfiles', 'channelList', 'nCohInt', | |||
r1340 | 'code', 'nCode', 'nBaud', 'ippSeconds', 'ipp'] | |||
r889 | ||||
|
r1092 | def getNoisebyHildebrand(self, channel=None): | ||
|
r487 | """ | ||
Determino el nivel de ruido usando el metodo Hildebrand-Sekhon | ||||
r889 | ||||
|
r487 | Return: | ||
noiselevel | ||||
""" | ||||
|
r568 | if channel != None: | ||
data = self.data[channel] | ||||
nChannels = 1 | ||||
else: | ||||
data = self.data | ||||
nChannels = self.nChannels | ||||
r889 | ||||
|
r568 | noise = numpy.zeros(nChannels) | ||
power = data * numpy.conjugate(data) | ||||
r889 | ||||
|
r568 | for thisChannel in range(nChannels): | ||
if nChannels == 1: | ||||
daux = power[:].real | ||||
else: | ||||
|
r1092 | daux = power[thisChannel, :].real | ||
|
r568 | noise[thisChannel] = hildebrand_sekhon(daux, self.nCohInt) | ||
r889 | ||||
|
r568 | return noise | ||
r889 | ||||
|
r1092 | def getNoise(self, type=1, channel=None): | ||
r889 | ||||
|
r487 | if type == 1: | ||
|
r568 | noise = self.getNoisebyHildebrand(channel) | ||
r889 | ||||
|
r720 | return noise | ||
r889 | ||||
|
r1092 | def getPower(self, channel=None): | ||
r889 | ||||
|
r568 | if channel != None: | ||
data = self.data[channel] | ||||
else: | ||||
data = self.data | ||||
r889 | ||||
|
r568 | power = data * numpy.conjugate(data) | ||
|
r1092 | powerdB = 10 * numpy.log10(power.real) | ||
|
r850 | powerdB = numpy.squeeze(powerdB) | ||
r889 | ||||
|
r850 | return powerdB | ||
r889 | ||||
r1338 | @property | |||
def timeInterval(self): | ||||
r889 | ||||
r1338 | return self.ippSeconds * self.nCohInt | |||
r889 | ||||
|
r526 | noise = property(getNoise, "I'm the 'nHeights' property.") | ||
r889 | ||||
|
r1092 | |||
|
r487 | class Spectra(JROData): | ||
r889 | ||||
|
r487 | def __init__(self): | ||
''' | ||||
Constructor | ||||
''' | ||||
r889 | ||||
r1364 | self.data_dc = None | |||
self.data_spc = None | ||||
self.data_cspc = None | ||||
|
r566 | self.useLocalTime = True | ||
|
r487 | self.radarControllerHeaderObj = RadarControllerHeader() | ||
self.systemHeaderObj = SystemHeader() | ||||
self.type = "Spectra" | ||||
|
r1326 | self.timeZone = 0 | ||
|
r487 | self.nProfiles = None | ||
self.heightList = None | ||||
self.channelList = None | ||||
self.pairsList = None | ||||
self.flagNoData = True | ||||
|
r568 | self.flagDiscontinuousBlock = False | ||
|
r487 | self.utctime = None | ||
self.nCohInt = None | ||||
self.nIncohInt = None | ||||
self.blocksize = None | ||||
self.nFFTPoints = None | ||||
self.wavelength = None | ||||
|
r1092 | self.flagDecodeData = False # asumo q la data no esta decodificada | ||
self.flagDeflipData = False # asumo q la data no esta sin flip | ||||
|
r487 | self.flagShiftFFT = False | ||
self.ippFactor = 1 | ||||
self.beacon_heiIndexList = [] | ||||
self.noise_estimation = None | ||||
r1370 | self.metadata_list = ['type', 'heightList', 'timeZone', 'pairsList', 'channelList', 'nCohInt', | |||
r1340 | 'code', 'nCode', 'nBaud', 'ippSeconds', 'ipp','nIncohInt', 'nFFTPoints', 'nProfiles'] | |||
r889 | ||||
r1387 | ||||
|
r568 | def getNoisebyHildebrand(self, xmin_index=None, xmax_index=None, ymin_index=None, ymax_index=None): | ||
|
r487 | """ | ||
Determino el nivel de ruido usando el metodo Hildebrand-Sekhon | ||||
r889 | ||||
|
r487 | Return: | ||
noiselevel | ||||
""" | ||||
r889 | ||||
|
r487 | noise = numpy.zeros(self.nChannels) | ||
r889 | ||||
|
r487 | for channel in range(self.nChannels): | ||
|
r1092 | daux = self.data_spc[channel, | ||
xmin_index:xmax_index, ymin_index:ymax_index] | ||||
|
r487 | noise[channel] = hildebrand_sekhon(daux, self.nIncohInt) | ||
r889 | ||||
return noise | ||||
|
r568 | def getNoise(self, xmin_index=None, xmax_index=None, ymin_index=None, ymax_index=None): | ||
r889 | ||||
|
r729 | if self.noise_estimation is not None: | ||
|
r1092 | # this was estimated by getNoise Operation defined in jroproc_spectra.py | ||
return self.noise_estimation | ||||
|
r487 | else: | ||
|
r1092 | noise = self.getNoisebyHildebrand( | ||
xmin_index, xmax_index, ymin_index, ymax_index) | ||||
|
r487 | return noise | ||
r889 | ||||
|
r765 | def getFreqRangeTimeResponse(self, extrapoints=0): | ||
r889 | ||||
|
r1092 | deltafreq = self.getFmaxTimeResponse() / (self.nFFTPoints * self.ippFactor) | ||
|
r1285 | freqrange = deltafreq * (numpy.arange(self.nFFTPoints + extrapoints) - self.nFFTPoints / 2.) - deltafreq / 2 | ||
r889 | ||||
|
r771 | return freqrange | ||
r889 | ||||
|
r771 | def getAcfRange(self, extrapoints=0): | ||
r889 | ||||
|
r1092 | deltafreq = 10. / (self.getFmax() / (self.nFFTPoints * self.ippFactor)) | ||
|
r1205 | freqrange = deltafreq * (numpy.arange(self.nFFTPoints + extrapoints) -self.nFFTPoints / 2.) - deltafreq / 2 | ||
r889 | ||||
|
r765 | return freqrange | ||
r889 | ||||
|
r487 | def getFreqRange(self, extrapoints=0): | ||
r889 | ||||
|
r1092 | deltafreq = self.getFmax() / (self.nFFTPoints * self.ippFactor) | ||
|
r1205 | freqrange = deltafreq * (numpy.arange(self.nFFTPoints + extrapoints) -self.nFFTPoints / 2.) - deltafreq / 2 | ||
r889 | ||||
|
r487 | return freqrange | ||
def getVelRange(self, extrapoints=0): | ||||
r889 | ||||
|
r1092 | deltav = self.getVmax() / (self.nFFTPoints * self.ippFactor) | ||
|
r1205 | velrange = deltav * (numpy.arange(self.nFFTPoints + extrapoints) - self.nFFTPoints / 2.) | ||
r1279 | ||||
|
r1188 | if self.nmodes: | ||
return velrange/self.nmodes | ||||
else: | ||||
return velrange | ||||
r889 | ||||
r1338 | @property | |||
def nPairs(self): | ||||
r889 | ||||
|
r487 | return len(self.pairsList) | ||
r889 | ||||
r1338 | @property | |||
def pairsIndexList(self): | ||||
r889 | ||||
|
r1167 | return list(range(self.nPairs)) | ||
r889 | ||||
r1338 | @property | |||
def normFactor(self): | ||||
r889 | ||||
|
r487 | pwcode = 1 | ||
r889 | ||||
|
r487 | if self.flagDecodeData: | ||
pwcode = numpy.sum(self.code[0]**2) | ||||
#normFactor = min(self.nFFTPoints,self.nProfiles)*self.nIncohInt*self.nCohInt*pwcode*self.windowOfFilter | ||||
|
r1205 | normFactor = self.nProfiles * self.nIncohInt * self.nCohInt * pwcode * self.windowOfFilter | ||
r889 | ||||
|
r487 | return normFactor | ||
r889 | ||||
r1338 | @property | |||
def flag_cspc(self): | ||||
r889 | ||||
|
r611 | if self.data_cspc is None: | ||
|
r487 | return True | ||
r889 | ||||
|
r487 | return False | ||
r889 | ||||
r1338 | @property | |||
def flag_dc(self): | ||||
r889 | ||||
|
r611 | if self.data_dc is None: | ||
|
r487 | return True | ||
r889 | ||||
|
r487 | return False | ||
r889 | ||||
r1338 | @property | |||
def timeInterval(self): | ||||
r889 | ||||
|
r1205 | timeInterval = self.ippSeconds * self.nCohInt * self.nIncohInt * self.nProfiles * self.ippFactor | ||
|
r1211 | if self.nmodes: | ||
return self.nmodes*timeInterval | ||||
else: | ||||
return timeInterval | ||||
r889 | ||||
|
r832 | def getPower(self): | ||
r889 | ||||
|
r832 | factor = self.normFactor | ||
|
r1092 | z = self.data_spc / factor | ||
r889 | z = numpy.where(numpy.isfinite(z), z, numpy.NAN) | |||
|
r832 | avg = numpy.average(z, axis=1) | ||
r889 | ||||
|
r1092 | return 10 * numpy.log10(avg) | ||
r889 | ||||
def getCoherence(self, pairsList=None, phase=False): | ||||
z = [] | ||||
if pairsList is None: | ||||
pairsIndexList = self.pairsIndexList | ||||
else: | ||||
pairsIndexList = [] | ||||
for pair in pairsList: | ||||
if pair not in self.pairsList: | ||||
|
r1167 | raise ValueError("Pair %s is not in dataOut.pairsList" % ( | ||
pair)) | ||||
|
r1092 | pairsIndexList.append(self.pairsList.index(pair)) | ||
r889 | for i in range(len(pairsIndexList)): | |||
pair = self.pairsList[pairsIndexList[i]] | ||||
|
r1205 | ccf = numpy.average(self.data_cspc[pairsIndexList[i], :, :], axis=0) | ||
r889 | powa = numpy.average(self.data_spc[pair[0], :, :], axis=0) | |||
powb = numpy.average(self.data_spc[pair[1], :, :], axis=0) | ||||
|
r1092 | avgcoherenceComplex = ccf / numpy.sqrt(powa * powb) | ||
r889 | if phase: | |||
data = numpy.arctan2(avgcoherenceComplex.imag, | ||||
|
r1092 | avgcoherenceComplex.real) * 180 / numpy.pi | ||
r889 | else: | |||
data = numpy.abs(avgcoherenceComplex) | ||||
z.append(data) | ||||
return numpy.array(z) | ||||
|
r624 | def setValue(self, value): | ||
r889 | ||||
|
r1167 | print("This property should not be initialized") | ||
r889 | ||||
|
r624 | return | ||
r889 | ||||
|
r624 | noise = property(getNoise, setValue, "I'm the 'nHeights' property.") | ||
|
r1092 | |||
r889 | ||||
|
r487 | class SpectraHeis(Spectra): | ||
r889 | ||||
|
r487 | def __init__(self): | ||
r889 | ||||
|
r487 | self.radarControllerHeaderObj = RadarControllerHeader() | ||
self.systemHeaderObj = SystemHeader() | ||||
self.type = "SpectraHeis" | ||||
self.nProfiles = None | ||||
self.heightList = None | ||||
self.channelList = None | ||||
self.flagNoData = True | ||||
|
r568 | self.flagDiscontinuousBlock = False | ||
|
r487 | self.utctime = None | ||
self.blocksize = None | ||||
|
r534 | self.profileIndex = 0 | ||
|
r587 | self.nCohInt = 1 | ||
self.nIncohInt = 1 | ||||
r889 | ||||
r1338 | @property | |||
def normFactor(self): | ||||
|
r496 | pwcode = 1 | ||
if self.flagDecodeData: | ||||
pwcode = numpy.sum(self.code[0]**2) | ||||
r889 | ||||
|
r1092 | normFactor = self.nIncohInt * self.nCohInt * pwcode | ||
r889 | ||||
|
r496 | return normFactor | ||
r889 | ||||
r1338 | @property | |||
def timeInterval(self): | ||||
r889 | ||||
r1338 | return self.ippSeconds * self.nCohInt * self.nIncohInt | |||
|
r487 | |||
|
r1092 | |||
|
r587 | class Fits(JROData): | ||
r889 | ||||
|
r487 | def __init__(self): | ||
r889 | ||||
|
r487 | self.type = "Fits" | ||
self.nProfiles = None | ||||
self.heightList = None | ||||
self.channelList = None | ||||
self.flagNoData = True | ||||
self.utctime = None | ||||
|
r587 | self.nCohInt = 1 | ||
self.nIncohInt = 1 | ||||
|
r487 | self.useLocalTime = True | ||
|
r534 | self.profileIndex = 0 | ||
|
r1326 | self.timeZone = 0 | ||
r889 | ||||
|
r487 | def getTimeRange(self): | ||
r889 | ||||
|
r487 | datatime = [] | ||
r889 | ||||
|
r487 | datatime.append(self.ltctime) | ||
datatime.append(self.ltctime + self.timeInterval) | ||||
r889 | ||||
|
r487 | datatime = numpy.array(datatime) | ||
r889 | ||||
|
r487 | return datatime | ||
r889 | ||||
|
r487 | def getChannelIndexList(self): | ||
r889 | ||||
|
r1167 | return list(range(self.nChannels)) | ||
r889 | ||||
|
r1092 | def getNoise(self, type=1): | ||
r889 | ||||
|
r487 | if type == 1: | ||
noise = self.getNoisebyHildebrand() | ||||
r889 | ||||
|
r487 | if type == 2: | ||
noise = self.getNoisebySort() | ||||
r889 | ||||
|
r487 | if type == 3: | ||
noise = self.getNoisebyWindow() | ||||
r889 | ||||
|
r487 | return noise | ||
r889 | ||||
r1338 | @property | |||
def timeInterval(self): | ||||
r889 | ||||
|
r587 | timeInterval = self.ippSeconds * self.nCohInt * self.nIncohInt | ||
r889 | ||||
|
r587 | return timeInterval | ||
r889 | ||||
r1338 | @property | |||
def ippSeconds(self): | ||||
|
r1191 | ''' | ||
''' | ||||
return self.ipp_sec | ||||
|
r487 | noise = property(getNoise, "I'm the 'nHeights' property.") | ||
r889 | ||||
|
r502 | class Correlation(JROData): | ||
r889 | ||||
|
r502 | def __init__(self): | ||
''' | ||||
Constructor | ||||
''' | ||||
self.radarControllerHeaderObj = RadarControllerHeader() | ||||
self.systemHeaderObj = SystemHeader() | ||||
self.type = "Correlation" | ||||
self.data = None | ||||
self.dtype = None | ||||
self.nProfiles = None | ||||
self.heightList = None | ||||
self.channelList = None | ||||
self.flagNoData = True | ||||
|
r568 | self.flagDiscontinuousBlock = False | ||
|
r502 | self.utctime = None | ||
|
r1326 | self.timeZone = 0 | ||
|
r502 | self.dstFlag = None | ||
self.errorCount = None | ||||
self.blocksize = None | ||||
|
r1092 | self.flagDecodeData = False # asumo q la data no esta decodificada | ||
self.flagDeflipData = False # asumo q la data no esta sin flip | ||||
|
r502 | self.pairsList = None | ||
self.nPoints = None | ||||
|
r850 | |||
|
r502 | def getPairsList(self): | ||
r889 | ||||
|
r502 | return self.pairsList | ||
r889 | ||||
|
r1092 | def getNoise(self, mode=2): | ||
r889 | ||||
|
r502 | indR = numpy.where(self.lagR == 0)[0][0] | ||
indT = numpy.where(self.lagT == 0)[0][0] | ||||
r889 | ||||
|
r1092 | jspectra0 = self.data_corr[:, :, indR, :] | ||
|
r502 | jspectra = copy.copy(jspectra0) | ||
r889 | ||||
|
r502 | num_chan = jspectra.shape[0] | ||
num_hei = jspectra.shape[2] | ||||
r889 | ||||
|
r1092 | freq_dc = jspectra.shape[1] / 2 | ||
ind_vel = numpy.array([-2, -1, 1, 2]) + freq_dc | ||||
r889 | ||||
|
r1092 | if ind_vel[0] < 0: | ||
|
r1187 | ind_vel[list(range(0, 1))] = ind_vel[list( | ||
range(0, 1))] + self.num_prof | ||||
r889 | ||||
if mode == 1: | ||||
|
r1092 | jspectra[:, freq_dc, :] = ( | ||
jspectra[:, ind_vel[1], :] + jspectra[:, ind_vel[2], :]) / 2 # CORRECCION | ||||
r889 | ||||
|
r502 | if mode == 2: | ||
r889 | ||||
|
r1092 | vel = numpy.array([-2, -1, 1, 2]) | ||
xx = numpy.zeros([4, 4]) | ||||
r889 | ||||
|
r502 | for fil in range(4): | ||
|
r1167 | xx[fil, :] = vel[fil]**numpy.asarray(list(range(4))) | ||
r889 | ||||
|
r502 | xx_inv = numpy.linalg.inv(xx) | ||
|
r1092 | xx_aux = xx_inv[0, :] | ||
r889 | ||||
|
r502 | for ich in range(num_chan): | ||
|
r1092 | yy = jspectra[ich, ind_vel, :] | ||
jspectra[ich, freq_dc, :] = numpy.dot(xx_aux, yy) | ||||
|
r502 | |||
|
r1092 | junkid = jspectra[ich, freq_dc, :] <= 0 | ||
|
r502 | cjunkid = sum(junkid) | ||
r889 | ||||
|
r502 | if cjunkid.any(): | ||
|
r1092 | jspectra[ich, freq_dc, junkid.nonzero()] = ( | ||
jspectra[ich, ind_vel[1], junkid] + jspectra[ich, ind_vel[2], junkid]) / 2 | ||||
r889 | ||||
|
r1092 | noise = jspectra0[:, freq_dc, :] - jspectra[:, freq_dc, :] | ||
r889 | ||||
|
r502 | return noise | ||
|
r587 | |||
r1338 | @property | |||
def timeInterval(self): | ||||
r889 | ||||
r1338 | return self.ippSeconds * self.nCohInt * self.nProfiles | |||
r889 | ||||
|
r850 | def splitFunctions(self): | ||
r889 | ||||
|
r850 | pairsList = self.pairsList | ||
ccf_pairs = [] | ||||
acf_pairs = [] | ||||
ccf_ind = [] | ||||
acf_ind = [] | ||||
r889 | for l in range(len(pairsList)): | |||
|
r850 | chan0 = pairsList[l][0] | ||
chan1 = pairsList[l][1] | ||||
r889 | ||||
|
r1092 | # Obteniendo pares de Autocorrelacion | ||
|
r850 | if chan0 == chan1: | ||
acf_pairs.append(chan0) | ||||
acf_ind.append(l) | ||||
else: | ||||
ccf_pairs.append(pairsList[l]) | ||||
ccf_ind.append(l) | ||||
r889 | ||||
|
r850 | data_acf = self.data_cf[acf_ind] | ||
data_ccf = self.data_cf[ccf_ind] | ||||
return acf_ind, ccf_ind, acf_pairs, ccf_pairs, data_acf, data_ccf | ||||
r1338 | @property | |||
def normFactor(self): | ||||
|
r850 | acf_ind, ccf_ind, acf_pairs, ccf_pairs, data_acf, data_ccf = self.splitFunctions() | ||
acf_pairs = numpy.array(acf_pairs) | ||||
|
r1092 | normFactor = numpy.zeros((self.nPairs, self.nHeights)) | ||
r889 | ||||
|
r850 | for p in range(self.nPairs): | ||
pair = self.pairsList[p] | ||||
r889 | ||||
|
r850 | ch0 = pair[0] | ||
ch1 = pair[1] | ||||
r889 | ||||
|
r1092 | ch0_max = numpy.max(data_acf[acf_pairs == ch0, :, :], axis=1) | ||
ch1_max = numpy.max(data_acf[acf_pairs == ch1, :, :], axis=1) | ||||
normFactor[p, :] = numpy.sqrt(ch0_max * ch1_max) | ||||
r889 | ||||
|
r850 | return normFactor | ||
r889 | ||||
|
r1092 | |||
r922 | class Parameters(Spectra): | |||
|
r502 | |||
|
r1092 | groupList = None # List of Pairs, Groups, etc | ||
data_param = None # Parameters obtained | ||||
data_pre = None # Data Pre Parametrization | ||||
data_SNR = None # Signal to Noise Ratio | ||||
abscissaList = None # Abscissa, can be velocities, lags or time | ||||
utctimeInit = None # Initial UTC time | ||||
paramInterval = None # Time interval to calculate Parameters in seconds | ||||
|
r802 | useLocalTime = True | ||
|
r1092 | # Fitting | ||
data_error = None # Error of the estimation | ||||
r889 | constants = None | |||
|
r513 | library = None | ||
|
r1092 | # Output signal | ||
outputInterval = None # Time interval to calculate output signal in seconds | ||||
data_output = None # Out signal | ||||
|
r850 | nAvg = None | ||
r922 | noise_estimation = None | |||
|
r1092 | GauSPC = None # Fit gaussian SPC | ||
r889 | ||||
|
r502 | def __init__(self): | ||
''' | ||||
Constructor | ||||
''' | ||||
self.radarControllerHeaderObj = RadarControllerHeader() | ||||
self.systemHeaderObj = SystemHeader() | ||||
self.type = "Parameters" | ||||
|
r1326 | self.timeZone = 0 | ||
r889 | ||||
|
r804 | def getTimeRange1(self, interval): | ||
r889 | ||||
|
r502 | datatime = [] | ||
r889 | ||||
|
r608 | if self.useLocalTime: | ||
|
r1092 | time1 = self.utctimeInit - self.timeZone * 60 | ||
|
r608 | else: | ||
|
r802 | time1 = self.utctimeInit | ||
r889 | ||||
|
r608 | datatime.append(time1) | ||
|
r804 | datatime.append(time1 + interval) | ||
|
r502 | datatime = numpy.array(datatime) | ||
r889 | ||||
r922 | return datatime | |||
r923 | ||||
r1338 | @property | |||
def timeInterval(self): | ||||
r923 | ||||
|
r937 | if hasattr(self, 'timeInterval1'): | ||
return self.timeInterval1 | ||||
else: | ||||
return self.paramInterval | ||||
|
r928 | |||
|
r1001 | def setValue(self, value): | ||
|
r1167 | print("This property should not be initialized") | ||
|
r1001 | |||
return | ||||
|
r928 | def getNoise(self): | ||
return self.spc_noise | ||||
|
r1187 | noise = property(getNoise, setValue, "I'm the 'Noise' property.") | ||
class PlotterData(object): | ||||
''' | ||||
Object to hold data to be plotted | ||||
''' | ||||
|
r1299 | MAXNUMX = 200 | ||
MAXNUMY = 200 | ||||
|
r1187 | |||
r1343 | def __init__(self, code, exp_code, localtime=True): | |||
r1279 | ||||
|
r1221 | self.key = code | ||
|
r1187 | self.exp_code = exp_code | ||
self.ready = False | ||||
|
r1285 | self.flagNoData = False | ||
|
r1327 | self.localtime = localtime | ||
|
r1187 | self.data = {} | ||
self.meta = {} | ||||
self.__heights = [] | ||||
def __str__(self): | ||||
dum = ['{}{}'.format(key, self.shape(key)) for key in self.data] | ||||
|
r1308 | return 'Data[{}][{}]'.format(';'.join(dum), len(self.times)) | ||
|
r1187 | |||
def __len__(self): | ||||
r1343 | return len(self.data) | |||
|
r1187 | |||
def __getitem__(self, key): | ||||
r1343 | if isinstance(key, int): | |||
return self.data[self.times[key]] | ||||
elif isinstance(key, str): | ||||
ret = numpy.array([self.data[x][key] for x in self.times]) | ||||
|
r1187 | if ret.ndim > 1: | ||
ret = numpy.swapaxes(ret, 0, 1) | ||||
r1343 | return ret | |||
|
r1187 | |||
def __contains__(self, key): | ||||
r1343 | return key in self.data[self.min_time] | |||
|
r1187 | |||
def setup(self): | ||||
''' | ||||
Configure object | ||||
''' | ||||
self.type = '' | ||||
self.ready = False | ||||
|
r1308 | del self.data | ||
|
r1187 | self.data = {} | ||
self.__heights = [] | ||||
self.__all_heights = set() | ||||
r1279 | ||||
|
r1187 | def shape(self, key): | ||
''' | ||||
Get the shape of the one-element data for the given key | ||||
''' | ||||
r1343 | if len(self.data[self.min_time][key]): | |||
return self.data[self.min_time][key].shape | ||||
|
r1187 | return (0,) | ||
r1343 | def update(self, data, tm, meta={}): | |||
|
r1187 | ''' | ||
Update data object with new dataOut | ||||
''' | ||||
r1279 | ||||
r1343 | self.data[tm] = data | |||
|
r1187 | |||
r1343 | for key, value in meta.items(): | |||
setattr(self, key, value) | ||||
|
r1229 | |||
|
r1187 | def normalize_heights(self): | ||
''' | ||||
Ensure same-dimension of the data for different heighList | ||||
''' | ||||
H = numpy.array(list(self.__all_heights)) | ||||
H.sort() | ||||
for key in self.data: | ||||
shape = self.shape(key)[:-1] + H.shape | ||||
for tm, obj in list(self.data[key].items()): | ||||
|
r1322 | h = self.__heights[self.times.tolist().index(tm)] | ||
|
r1187 | if H.size == h.size: | ||
continue | ||||
index = numpy.where(numpy.in1d(H, h))[0] | ||||
dummy = numpy.zeros(shape) + numpy.nan | ||||
if len(shape) == 2: | ||||
dummy[:, index] = obj | ||||
else: | ||||
dummy[index] = obj | ||||
self.data[key][tm] = dummy | ||||
|
r1308 | self.__heights = [H for tm in self.times] | ||
|
r1187 | |||
|
r1308 | def jsonify(self, tm, plot_name, plot_type, decimate=False): | ||
|
r1187 | ''' | ||
Convert data to json | ||||
''' | ||||
r1343 | meta = {} | |||
meta['xrange'] = [] | ||||
dy = int(len(self.yrange)/self.MAXNUMY) + 1 | ||||
tmp = self.data[tm][self.key] | ||||
shape = tmp.shape | ||||
if len(shape) == 2: | ||||
data = self.roundFloats(self.data[tm][self.key][::, ::dy].tolist()) | ||||
elif len(shape) == 3: | ||||
dx = int(self.data[tm][self.key].shape[1]/self.MAXNUMX) + 1 | ||||
|
r1221 | data = self.roundFloats( | ||
r1343 | self.data[tm][self.key][::, ::dx, ::dy].tolist()) | |||
meta['xrange'] = self.roundFloats(self.xrange[2][::dx].tolist()) | ||||
|
r1187 | else: | ||
r1343 | data = self.roundFloats(self.data[tm][self.key].tolist()) | |||
|
r1221 | |||
ret = { | ||||
'plot': plot_name, | ||||
'code': self.exp_code, | ||||
'time': float(tm), | ||||
'data': data, | ||||
} | ||||
meta['type'] = plot_type | ||||
meta['interval'] = float(self.interval) | ||||
meta['localtime'] = self.localtime | ||||
r1343 | meta['yrange'] = self.roundFloats(self.yrange[::dy].tolist()) | |||
r1279 | meta.update(self.meta) | |||
|
r1221 | ret['metadata'] = meta | ||
|
r1187 | return json.dumps(ret) | ||
@property | ||||
def times(self): | ||||
''' | ||||
Return the list of times of the current data | ||||
''' | ||||
r1343 | ret = [t for t in self.data] | |||
|
r1187 | ret.sort() | ||
r1343 | return numpy.array(ret) | |||
|
r1187 | |||
@property | ||||
def min_time(self): | ||||
''' | ||||
Return the minimun time value | ||||
''' | ||||
return self.times[0] | ||||
@property | ||||
def max_time(self): | ||||
''' | ||||
Return the maximun time value | ||||
''' | ||||
return self.times[-1] | ||||
r1343 | # @property | |||
# def heights(self): | ||||
# ''' | ||||
# Return the list of heights of the current data | ||||
# ''' | ||||
|
r1187 | |||
r1343 | # return numpy.array(self.__heights[-1]) | |||
|
r1187 | |||
@staticmethod | ||||
def roundFloats(obj): | ||||
if isinstance(obj, list): | ||||
return list(map(PlotterData.roundFloats, obj)) | ||||
elif isinstance(obj, float): | ||||
return round(obj, 2) | ||||