You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

524 lines
14 KiB
Python

from __future__ import print_function
from .lineType import Dl, Se, Ff, Pz, Cl
from .basesc3 import sc3
import sys
class prefixable(object):
def adjust(self, prefix):
if prefix:
self.id = "%s:%s" % (prefix, self.id)
class Instruments(object):
def __init__(self, prefix=""):
self.keys = []
self.ses = {}
self.dls = {}
self.fls = {}
self.cls = {}
self._sensors = {}
self._datalogger = {}
self._filters = {}
self._Cal = {}
self._prefix = prefix
def sc3Objs(self):
objs = []
for s in list(self._sensors.values()):
objs.append(s.sc3Obj(self))
for s in list(self._datalogger.values()):
objs.append(s.sc3Obj(self))
for s in list(self._filters.values()):
objs.append(s.sc3Obj(self))
return objs
def add(self, obj):
where = None
if isinstance(obj, Se):
where = self.ses
elif isinstance(obj, Dl):
where = self.dls
elif isinstance(obj, Cl):
where = self.cls
elif isinstance(obj, Ff) or isinstance(obj, Pz):
where = self.fls
else:
raise Exception("Object type %s doesn't fir this class" % type(obj))
if obj.id in self.keys:
raise Exception("Object id %s already exist." % (obj))
self.keys.append(obj.id)
where[obj.id] = obj
return
def instrumentId(self, iid, gain):
if gain is None:
if iid in self.dls:
gain = self.dls[iid].gain
elif iid in self.ses:
gain = self.ses[iid].gain
else:
raise Exception("Instrument iid not found")
siid = "%s/g=%s" % (iid, int(float(gain)))
return siid
def loadDataloggerCalibrations(self, dsm, dsn, dch, dsg, start, end, dd):
cls = []
for cl in self.cls.values():
if cl.type != "L": continue
if cl.match(dsm, dsn):
cls.append(Calibration(cl, dch, start, end))
if len(cls) == 0:
if dsn in self.cls:
print("[%s] No calibrations found for serial number %s and model %s " % (dsm, dsn, dsm), file=sys.stderr)
return
diid = self.instrumentId(dsm, dsg)
try:
datalogger = self._datalogger[diid].sc3Obj(self)
if dd != datalogger.publicID():
raise Exception("Public Id doesn't match")
except:
raise Exception("[%s] Could not retrieve datalogger %s" % (dsm, diid))
for cl in cls:
if (dsm, dsn, dch, start, end) in self._Cal:
## print >> sys.stderr,"[%s] Skiping calibration channel %s" % (dsm, cl.channel)
continue
## print >> sys.stderr,"[%s] Adding calibration %s (%s)" % (dsm, cl.channel, dd)
datalogger.add(cl.sc3Obj(self))
self._Cal[(dsm, dsn, dch, start, end)] = cl
def loadSensorCalibrations(self, ssm, ssn, sch, ssg, start, end, ss):
cls = []
for cl in self.cls.values():
if cl.type != "S": continue
if cl.match(ssm, ssn):
cls.append(Calibration(cl, sch, start, end))
if len(cls) == 0:
if ssn in self.cls:
print("[%s] No calibrations found for serial number %s and model %s " % (ssm,ssn, ssm), file=sys.stderr)
return
siid = self.instrumentId(ssm, ssg)
try:
sensor = self._sensors[siid].sc3Obj(self)
if ss != sensor.publicID():
raise Exception("Public Id doesn't match")
except:
raise Exception("[%s] Could not retrieve sensor %s" % (ssm, siid))
for cl in cls:
if (ssm, ssn, sch, start, end) in self._Cal:
## print >> sys.stderr,"[%s] Skiping calibration channel %s" % (ssm, cl.channel)
continue
## print >> sys.stderr,"[%s] Adding calibration %s channel %s start %s" % (ssm, ssn, cl.channel, start)
sensor.add(cl.sc3Obj(self))
self._Cal[(ssm, ssn, sch, start, end)] = cl
def check(self, networks):
error = []
# Dataloggers check
error.append("* Dataloggers:")
for dl in self.dls.values():
error.extend(dl.check(self))
error.append("")
# Check fir filters
error.append("* Filters:")
for f in self.fls.values():
c = False
for dl in self.dls.values():
c = c or dl.use(f)
if c: break
if not c: error.append(" [%s] filter is not used" % f.id)
error.append("")
# Check the calibrations
error.append("* Calibrations:")
for cl in self.cls.values():
error.extend(cl.check(self))
error.append("")
error.append("* Sensors:")
for f in self.ses.values():
c = False
for network in networks.values():
for station in network.stations:
for location in station.locations:
for channel in location.channels:
c = c or channel.use(f)
if c: break
if c: break
if c: break
if c: break
if not c: error.append(" [%s] sensor is not used" % f.id)
error.append("")
error.append("* Dataloggers:")
for f in self.dls.values():
c = False
for network in networks.values():
c = c or network.use(f)
if c: break
if not c: error.append(" [%s] datalogger is not used" % f.id)
error.append("")
return error
def filterType(self, iid):
if iid not in self.keys:
raise Exception("[%s] Filter id not found" % iid)
if iid not in self.fls:
raise Exception("[%s] Object is not a filter" % iid)
obj = self.fls[iid]
if isinstance(obj, Ff):
fType = 'D'
elif isinstance(obj, Pz):
fType = obj.type
return fType
def filterID(self, iid):
if iid not in self.keys:
raise Exception("[%s] Filter id not found" % iid)
if iid not in self.fls:
raise Exception("[%s] Object is not a filter" % iid)
if iid not in self._filters:
obj = self.fls[iid]
if isinstance(obj, Pz):
## print >> sys.stderr," Generating new Filter (PZ): %s %s" % (iid,obj.type)
newFilter = Paz(obj)
elif isinstance(obj, Ff):
## print >> sys.stderr," Generating new Filter (Fir): %s" % (iid)
newFilter = Fir(obj)
newFilter.adjust(self._prefix)
if newFilter.id != self.prefix(iid):
raise Exception("Invalid filter created %s" % (iid))
self._filters[iid] = newFilter
return self._filters[iid].sc3ID(self)
def prefix(self, iid):
if self._prefix:
iid = "%s:%s" % (self._prefix, iid)
return iid
def dataloggerID(self, iid, gain = None):
if iid not in self.keys:
raise Exception("Object not found.")
if iid not in self.dls:
raise Exception("[%s] Object is not a datalogger" % iid)
diid = self.instrumentId(iid, gain)
if diid not in self._datalogger:
## print >> sys.stderr,"Generating datalogger %s -> %s" % (iid, diid)
newDatalogger = Dataloger(self.dls[iid], gain)
newDatalogger.adjust(self._prefix)
if newDatalogger.id != self.prefix(diid):
raise Exception("Invalid datalogger created %s %s" % (iid, diid))
self._datalogger[diid] = newDatalogger
return self._datalogger[diid].sc3ID(self)
def sensorID(self, iid, gain = None):
if iid not in self.keys:
raise Exception("Object not found.")
if iid not in self.ses:
raise Exception("[%s] Object is not a sensor" % iid)
diid = self.instrumentId(iid, gain)
if diid not in self._sensors:
## print >> sys.stderr,"Generating Sensor %s -> %s" % (iid, diid)
newSensor = Sensor(self.ses[iid], gain)
newSensor.adjust(self._prefix)
if newSensor.id != self.prefix(diid):
raise Exception("Invalid sensor created %s %s" % (iid, diid))
self._sensors[diid] = newSensor
return self._sensors[diid].sc3ID(self)
def _findObject(self, objID, where):
obj = None
for ob in where.values():
obj = ob.sc3Obj(self)
if obj.publicID() == objID:
break;
if not obj:
raise Exception("Object not found: %s " % objID)
return obj
def _findCallibration(self, obj, count, serialNumber, channel, start):
if serialNumber is None:
return None
if channel is None:
return None
for cal in [obj(i) for i in range(0, count)]:
if cal.serialNumber() == serialNumber and cal.channel() == channel:
return cal.gain()
return None
def _sensorGain(self, seID, serialNumber, channel, start):
sensor = self._findObject(seID, self._sensors)
if not sensor:
raise Exception("Not found %s" % seID)
sensorFilter = self._findObject(sensor.response(), self._filters)
if not sensorFilter:
raise Exception("Not found %s" % seID)
gainFrequency = sensorFilter.gainFrequency()
try:
gainUnit = sensor.unit()
except:
print("[%s] No gain unit supplied" % seID, file=sys.stderr)
gainUnit = None
gain = self._findCallibration(sensor.sensorCalibration, sensor.sensorCalibrationCount(), serialNumber, channel, start)
if gain is not None:
## print >> sys.stderr,'[%s] Using sensor gain from calibration %s' % (serialNumber, gain)
pass
else:
gain = sensorFilter.gain()
return (gain, gainFrequency, gainUnit)
def _dataloggerGain(self, dtID, serialNumber, channel, Numerator, Denominator, start):
datalogger = self._findObject(dtID, self._datalogger)
gain = self._findCallibration(datalogger.dataloggerCalibration, datalogger.dataloggerCalibrationCount(), serialNumber, channel, start)
if gain is not None:
##print >> sys.stderr,'[%s] Using datalogger gain from calibration %s' % (serialNumber, gain)
pass
else:
gain = datalogger.gain()
decimation = None
for i in range(0,datalogger.decimationCount()):
decimation = datalogger.decimation(i)
if decimation.sampleRateNumerator() == Numerator and decimation.sampleRateDenominator() == Denominator:
break
decimation = None
if not decimation:
raise Exception("Decimation not found %s/%s" % (Numerator, Denominator))
af = decimation.analogueFilterChain().content().split()
df = decimation.digitalFilterChain().content().split()
for fiID in af:
g = self._findObject(fiID, self._filters).gain()
#print >> sys.stderr,"Multiplying by %s %s" % (fiID, g)
gain = gain * g
for fiID in df:
g = self._findObject(fiID, self._filters).gain()
#print >> sys.stderr,"Multiplying by %s %s" % (fiID, g)
gain = gain * g
return gain
def getChannelGainAttribute(self, dtID, seID, dtSerialNumber, seSerialNumber, dtChannel, seChannel, Numerator, Denominator, channelStart):
if not dtID or not seID:
raise Exception("Empty instruments ID supplied.")
(sensorGain, sensorFrequency,sensorUnit) = self._sensorGain(seID, seSerialNumber, seChannel, channelStart)
dataloggerGain = self._dataloggerGain(dtID, dtSerialNumber, dtChannel, Numerator, Denominator, channelStart)
att = {}
att['Gain'] = sensorGain * dataloggerGain
if sensorFrequency is not None:
att['GainFrequency'] = sensorFrequency
if sensorUnit is not None:
att['GainUnit'] = sensorUnit
return att
class Paz(sc3, prefixable):
def __init__(self, pz):
sc3.__init__(self, 'paz')
self.id = pz.id
self.att = pz.getAttributes()
def sc3Att(self):
att = {}
att['Name'] = self.id
for (key,value) in self.att.items():
if not self.sc3ValidKey(key) or key in att:
print(" [%s] [%s] Ignoring Attribute %s = %s " % (self.sc3Mode, self.id, key,value), file=sys.stderr)
continue
att[key] = value
return att
class Sensor(sc3, prefixable):
def __init__(self, se, gain = None):
sc3.__init__(self, 'sensor')
self.baseid = se.id
self.att = se.getAttributes()
self.pz = se.generatePz(gain)
self.id = "%s/g=%s" % (self.baseid, int(float(self.pz.gain)))
def sc3Resolv(self, inventory):
try:
self.att['Response'] = inventory.filterID(self.pz.id)
## print >> sys.stderr,"Re-used a sensor pole-zero"
except:
inventory.add(self.pz)
self.att['Response'] = inventory.filterID(self.pz.id)
def sc3Att(self):
att = {}
att['Name'] = self.id
for (key, value) in self.att.items():
if not self.sc3ValidKey(key) or key in att:
print(" [%s] [%s] ignoring Attribute %s = %s " % (self.sc3Mode, self.id, key, value), file=sys.stderr)
continue
att[key] = value
## Forcing needed description on the sensor
if 'Description' not in att:
att['Description'] = self.id
return att
class Fir(sc3, prefixable):
def __init__(self, ff):
sc3.__init__(self, 'fir')
self.id = ff.id
self.gain = ff.gain
self.att = ff.getAttributes()
def sc3Att(self):
att = {}
att['Name'] = self.id
for (key,value) in self.att.items():
if not self.sc3ValidKey(key) or key in att :
print(" [%s] [%s] Ignoring Attribute %s = %s " % (self.sc3Mode, self.id, key,value), file=sys.stderr)
continue
att[key] = value
return att
class Decimation(sc3):
def __init__(self, numerator, decimator, dl):
sc3.__init__(self, 'decimation')
self._numerator = numerator
self._denominator = decimator
self.chains = dl.chains[(numerator, decimator)]
self.att = {}
def sc3Resolv(self, inventory):
sequence = {}
sequence['A'] = []
sequence['D'] = []
for stage in self.chains:
sid = inventory.filterID(stage)
ADtype = inventory.filterType(stage)
sequence[ADtype].append(sid)
self.att['AnalogueFilterChain'] = " ".join(sequence['A'])
self.att['DigitalFilterChain'] = " ".join(sequence['D'])
def sc3Att(self):
att = {}
att['SampleRateNumerator'] = self._numerator
att['SampleRateDenominator'] = self._denominator
att.update(self.att)
return att
class Dataloger(sc3, prefixable):
def __init__(self, dl, gain = None):
dcs = []
sc3.__init__(self, 'datalogger', dcs)
if gain:
self.gain = gain
else:
self.gain = dl.gain
self.att = dl.getAttributes()
self.id = "%s/g=%s" % (dl.id, int(float(self.gain)))
self.maxClockDrift = dl.mcld
if dl.chains:
for (num, dec) in dl.chains:
dcs.append(Decimation(num, dec, dl))
self.dcs = dcs
else:
print("[%s] Datalogger %s has no stages." % (self.id, dl), file=sys.stderr)
def sc3Att(self):
att = {}
att['Name'] = self.id
att['Gain'] = self.gain
att['MaxClockDrift'] = self.maxClockDrift
for (key,value) in self.att.items():
if not self.sc3ValidKey(key) or key in att:
print(" [%s] [%s] ignoring Attribute %s = %s " % (self.sc3Mode, self.id, key, value), file=sys.stderr)
continue
att[key] = value
## Forcing needed description on the sensor
if 'Description' not in att:
att['Description'] = self.id
return att
class Calibration(sc3):
def __init__(self, cl, channel, start, end):
if cl.type == "S":
sc3.__init__(self, "sensorCalibration")
else:
sc3.__init__(self, "dataloggerCalibration")
if channel < 0 or channel >= cl.channelCount:
raise Exception("Invalid channel for calibration [%s]" % channel)
self.start = start
self.end = end
self.channel = channel
self.id = cl.id
self.att = cl.getAttributes(channel)
def sc3Att(self):
att = {}
att['SerialNumber'] = self.id
att['Start'] = self.start
if self.end:
att['End'] = self.end
for (key, value) in self.att.items():
if not self.sc3ValidKey(key) or key in att:
print(" [%s] [%s] Ignoring Attribute %s = %s " % (self.sc3Mode, self.id, key,value), file=sys.stderr)
continue
att[key] = value
return att