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.

3007 lines
102 KiB
Python

#*****************************************************************************
# fseed.py
#
# SEED builder for SeisComP
#
# (c) 2005 Andres Heinloo, GFZ Potsdam
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version. For more information, see http://www.gnu.org/
#*****************************************************************************
from __future__ import (absolute_import, division, print_function,
unicode_literals)
try: str = unicode
except NameError: pass
import sys
import re
import json
import datetime
import seiscomp.mseedlite as mseed
from tempfile import TemporaryFile
from shutil import copyfileobj
from seiscomp import logs
_RECLEN_EXP = 12
def _min_data_gap(fsamp):
return datetime.timedelta(microseconds=1000000/(fsamp * 10))
#_min_ts_gap = datetime.timedelta(minutes=1)
_min_ts_gap = datetime.timedelta(days=3650)
class SEEDError(Exception):
pass
_rx_coeff = re.compile(r'\s*(\S+)\s*')
def _mkseedcoeff_fir(nblk, nfld, ncoeff, s):
pos = 0
n = 0
c = ""
while pos < len(s):
m = _rx_coeff.match(s, pos)
if m is None:
raise SEEDError("blockette %d, field %d: error parsing FIR coefficients at '%s'" % (nblk, nfld, s[pos:]))
try:
v = float(m.group(1))
except ValueError:
raise SEEDError("blockette %d, field %d: error parsing FIR coefficients at '%s'" % (nblk, nfld, s[pos:]))
c += "%14.7E" % (v,)
n += 1
pos = m.end()
if n != ncoeff:
raise SEEDError("blockette %d, field %d: expected %d coefficients, found %d" % (nblk, nfld, ncoeff, n))
return c
def _mkseedcoeff_iir(nblk, nfld, ncoeff, s):
pos = 0
n = 0
c = ""
while pos < len(s):
m = _rx_coeff.match(s, pos)
if m is None:
raise SEEDError("blockette %d, field %d: error parsing IIR coefficients at '%s'" % (nblk, nfld, s[pos:]))
try:
v = float(m.group(1))
except ValueError:
raise SEEDError("blockette %d, field %d: error parsing IIR coefficients at '%s'" % (nblk, nfld, s[pos:]))
c += "%12.5E%12.5E" % (v,0)
n += 1
pos = m.end()
if n != ncoeff:
raise SEEDError("blockette %d, field %d: expected %d coefficients, found %d" % (nblk, nfld, ncoeff, n))
return c
def _mkseedcoeff_polynomial(nblk, nfld, ncoeff, s, gain=1.0):
pos = 0
n = 0
c = ""
while pos < len(s):
m = _rx_coeff.match(s, pos)
if m is None:
raise SEEDError("blockette %d, field %d: error parsing polynomial coefficients at '%s'" % (nblk, nfld, s[pos:]))
try:
v = float(m.group(1))
except ValueError:
raise SEEDError("blockette %d, field %d: error parsing polynomial coefficients at '%s'" % (nblk, nfld, s[pos:]))
c += "%12.5E%12.5E" % (v/(gain**n),0)
n += 1
pos = m.end()
if n != ncoeff:
raise SEEDError("blockette %d, field %d: expected %d coefficients, found %d" % (nblk, nfld, ncoeff, n))
return c
_rx_paz = re.compile(r'\s*([0-9]*)\(\s*([^,]+),\s*([^)]+)\)\s*')
def _mkseedpaz(nblk, nfld, npaz, s):
pos = 0
n = 0
c = ""
l = 0
if not s is None: l = len(s)
while pos < l:
m = _rx_paz.match(s, pos)
if m is None:
raise SEEDError("blockette %d, field %d: error parsing PAZ at '%s'" % (nblk, nfld, s[pos:]))
try:
if len(m.group(1)) > 0:
x = int(m.group(1))
else:
x = 1
rv = float(m.group(2))
iv = float(m.group(3))
except ValueError:
raise SEEDError("blockette %d, field %d: error parsing PAZ at '%s'" % (nblk, nfld, s[pos:]))
for i in range(0, x):
c += "%12.5E%12.5E 0.00000E-00 0.00000E-00" % (rv, iv)
n += x
pos = m.end()
if not npaz is None and (n != npaz):
raise SEEDError("blockette %d, field %d: expected %d PAZ, found %d" % (nblk, nfld, npaz, n))
return (c,n)
def _mkseedfap(nblk, nfld, nfap, s):
pos = 0
n = 0
c = ""
values = s.split()
l = len(values)
while pos < l:
try:
f = float(values[pos])
except:
raise SEEDError("blockette %d, field %d: error parsing FAP at '%s'" % (nblk, nfld, values[pos]))
pos += 1
try:
a = float(values[pos])
except:
raise SEEDError("blockette %d, field %d: error parsing FAP at '%s'" % (nblk, nfld, values[pos]))
pos += 1
try:
p = float(values[pos])
except:
raise SEEDError("blockette %d, field %d: error parsing FAP at '%s'" % (nblk, nfld, values[pos]))
pos += 1
c += "%12.5E%12.5E 0.00000E-00%12.5E 0.00000E-00" % (f, a, p)
n += 1
if not nfap is None and (n != nfap):
raise SEEDError("blockette %d, field %d: expected %d FAP, found %d" % (nblk, nfld, nfap, n))
return (c,n)
def _mkseedstring(nblk, nfld, s, min_length, max_length, flags):
U = L = N = P = S = X = False
rx_list = []
if flags.find("U") != -1:
U = True
rx_list.append("[A-Z]")
if flags.find("L") != -1:
L = True
rx_list.append("[a-z]")
if flags.find("N") != -1:
N = True
rx_list.append("[0-9]")
if flags.find("P") != -1:
P = True
rx_list.append("[^A-Za-z0-9 ]")
if flags.find("S") != -1:
S = True
rx_list.append(" ")
if flags.find("_") != -1:
X = True
rx_list.append("_")
sn = s.strip()[:max_length]
if U and not L:
sn = sn.upper()
elif L and not U:
sn = sn.lower()
if S and not X:
sn = sn.replace("_", " ")
elif X and not S:
sn = sn.replace(" ", "_")
rx = "|".join(rx_list)
sn = "".join(re.findall(rx, sn))
if re.match("(" + rx + ")*$", sn) is None:
raise SEEDError("blockette %d, field %d: cannot convert string \"%s\" with flags %s" % \
(nblk, nfld, s, flags))
if len(sn) < min_length:
if min_length != max_length:
raise SEEDError("blockette %d, field %d: cannot extend string \"%s\" to minimum length %d with flags %s" % \
(nblk, nfld, s, min_length, flags))
else:
sn = (sn + min_length * " ")[:min_length]
if min_length != max_length:
sn += "~"
return sn
def _mkseedtime(nblk, nfld, t):
if t is None:
return "~"
if isinstance(t, datetime.datetime):
tt = t.utctimetuple()
return "%04d,%03d,%02d:%02d:%02d.%04d~" % (t.year, tt[7],
t.hour, t.minute, t.second, t.microsecond // 100)
elif isinstance(t, datetime.date):
tt = datetime.datetime.combine(t, datetime.time(0, 0, 0)).utctimetuple()
return "%04d,%03d~" % (t.year, tt[7])
raise SEEDError("blockette %d, field %d: invalid time object: %s" % (nblk, nfld, str(t)))
def _cmptime(t1, t2):
if t1 is None and t2 is None:
return 0
elif t2 is None or (t1 is not None and t1 < t2):
return -1
elif t1 is None or (t2 is not None and t1 > t2):
return 1
return 0
def _is_fir_response(obj):
return hasattr(obj, "symmetry")
def _is_iir_response(obj):
return hasattr(obj, "denominators")
def _is_paz_response(obj):
return hasattr(obj, "poles")
def _is_poly_response(obj):
return hasattr(obj, "approximationType")
def _is_fap_response(obj):
return hasattr(obj, "tuples")
class _Blockette10(object):
def __init__(self, record_length, start_time, end_time, vol_time,
organization, label):
self.__record_length = record_length
self.__start_time = _mkseedtime(10, 5, start_time)
self.__end_time = _mkseedtime(10, 6, end_time)
self.__vol_time = _mkseedtime(10, 7, vol_time)
self.__organization = _mkseedstring(10, 8, organization, 1, 80, "UNLPS_")
self.__label = _mkseedstring(10, 9, label, 0, 80, "UNLPS_")
self.__len = 13 + len(self.__start_time) + len(self.__end_time) + \
len(self.__vol_time) + len(self.__organization) + \
len(self.__label)
def output(self, f):
blk = "010%4d 2.3%2d%s%s%s%s%s" % (self.__len, self.__record_length,
self.__start_time, self.__end_time, self.__vol_time,
self.__organization, self.__label)
if len(blk) != self.__len:
raise SEEDError("blockette 10 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
class _Blockette11(object):
def __init__(self):
self.__nstations = 0
self.__stat_rec = ()
self.__len = 10
def add_station(self, code, recno):
self.__stat_rec += (_mkseedstring(11, 4, code, 5, 5, "UN"), recno)
self.__nstations += 1
self.__len += 11
def __output_huge(self, f):
n = 0
while n < self.__nstations:
ns = min(self.__nstations - n, 908)
blen = 10 + 11 * ns
blk = ("011%4d%3d" + ns * "%s%6d") % \
((blen, ns) + self.__stat_rec[2*n:2*(n+ns)])
if len(blk) != blen:
raise SEEDError("blockette 11 has invalid length: %d instead of %d" % (len(blk), blen))
f.write_blk(blk)
n += ns
def output(self, f):
if self.__len > 9999:
self.__output_huge(f)
return
blk = ("011%4d%3d" + self.__nstations * "%s%6d") % \
((self.__len, self.__nstations) + self.__stat_rec)
if len(blk) != self.__len:
raise SEEDError("blockette 11 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
class _Blockette12(object):
def __init__(self):
self.__nspans = 0
self.__span_rec = ()
self.__len = 11
def add_span(self, begin, end, recno):
self.__span_rec += (_mkseedtime(12, 4, begin), _mkseedtime(12, 5, end), recno)
self.__nspans += 1
self.__len += 52
def __output_huge(self, f):
n = 0
while n < self.__nspans:
ns = min(self.__nspans - n, 192)
blen = 11 + 52 * ns
blk = ("012%4d%4d" + ns * "%s%s%6d") % \
((blen, ns) + self.__span_rec[3*n:3*(n+ns)])
if len(blk) != blen:
raise SEEDError("blockette 12 has invalid length: %d instead of %d" % (len(blk), blen))
f.write_blk(blk)
n += ns
def output(self, f):
if self.__len > 9999:
self.__output_huge(f)
return
blk = ("012%4d%4d" + self.__nspans * "%s%s%6d") % \
((self.__len, self.__nspans) + self.__span_rec)
if len(blk) != self.__len:
raise SEEDError("blockette 12 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
class _Blockette30(object):
def __init__(self, name, key, family, ddl):
self.__name = _mkseedstring(30, 3, name, 1, 50, "UNLPS")
self.__key = key
self.__family = family
self.__ddl = "~".join(ddl) + "~"
self.__nddl = len(ddl)
self.__len = 16 + len(self.__name) + len(self.__ddl)
def output(self, f):
blk = "030%4d%s%4d%3d%2d%s" % (self.__len, self.__name,
self.__key, self.__family, self.__nddl, self.__ddl)
if len(blk) != self.__len:
raise SEEDError("blockette 30 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
class _Blockette31(object):
def __init__(self, key, cclass, comment):
self.__key = key
self.__cclass = cclass
self.__comment = _mkseedstring(31, 5, comment, 1, 70, "UNLPS")
self.__len = 15 + len(self.__comment)
def output(self, f):
blk = "031%4d%4d%s%s 0" % (self.__len, self.__key, self.__cclass, self.__comment)
if len(blk) != self.__len:
raise SEEDError("blockette 31 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
class _Blockette33(object):
def __init__(self, key, desc):
self.__key = key
self.__desc = _mkseedstring(33, 4, desc, 1, 50, "UNLPS")
self.__len = 10 + len(self.__desc)
def output(self, f):
blk = "033%4d%3d%s" % (self.__len, self.__key, self.__desc)
if len(blk) != self.__len:
raise SEEDError("blockette 33 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
class _Blockette34(object):
def __init__(self, key, name, desc):
self.__key = key
self.__name = _mkseedstring(34, 4, name, 1, 20, "UNP")
self.__desc = _mkseedstring(34, 5, desc, 1, 50, "UNLPS")
self.__len = 10 + len(self.__name) + len(self.__desc)
def output(self, f):
blk = "034%4d%3d%s%s" % (self.__len, self.__key, self.__name,
self.__desc)
if len(blk) != self.__len:
raise SEEDError("blockette 34 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
class _Blockette41(object):
def __init__(self, key, name, symmetry, input_units, output_units, ncoeff,
coeff):
self.__key = key
self.__name = _mkseedstring(41, 4, name, 1, 25, "UN")
self.__symmetry = _mkseedstring(41, 5, symmetry, 1, 1, "U")
self.__input_units = input_units
self.__output_units = output_units
self.__ncoeff = ncoeff
self.__coeff = _mkseedcoeff_fir(41, 9, ncoeff, coeff)
self.__len = 22 + 14 * ncoeff + len(self.__name)
def __output_huge(self, f):
n = 0
while n < self.__ncoeff:
nc = min(self.__ncoeff - n, (9977 - len(self.__name)) // 14)
blen = 22 + 14 * nc + len(self.__name)
blk = "041%4d%4d%s%s%3d%3d%4d%s" % (blen, self.__key,
self.__name, self.__symmetry, self.__input_units,
self.__output_units, self.__ncoeff, self.__coeff[14*n:14*(n+nc)])
if len(blk) != blen:
raise SEEDError("blockette 41 has invalid length: %d instead of %d" % (len(blk), blen))
f.write_blk(blk)
n += nc
def output(self, f):
if self.__len > 9999:
self.__output_huge(f)
return
blk = "041%4d%4d%s%s%3d%3d%4d%s" % (self.__len, self.__key,
self.__name, self.__symmetry, self.__input_units,
self.__output_units, self.__ncoeff, self.__coeff)
if len(blk) != self.__len:
raise SEEDError("blockette 41 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
class _Blockette42(object):
def __init__(self, key, name, input_units, output_units, freq_unit, low_freq,
high_freq, approx_type, approx_lower_bound, approx_upper_bound,
approx_error, ncoeff, coeff):
self.__key = key
self.__name = _mkseedstring(42, 4, name, 1, 25, "UN")
self.__input_units = input_units
self.__output_units = output_units
self.__freq_unit = freq_unit
self.__low_freq = low_freq
self.__high_freq = high_freq
self.__approx_type = approx_type
self.__approx_lower_bound = approx_lower_bound
self.__approx_upper_bound = approx_upper_bound
self.__approx_error = approx_error
self.__ncoeff = ncoeff
self.__coeff = _mkseedcoeff_polynomial(42, 16, ncoeff, coeff)
self.__len = 83 + 24 * ncoeff + len(self.__name)
def output(self, f):
blk = "042%4d%4d%sP%3d%3d%1s%1s%12.5E%12.5E%12.5E%12.5E%12.5E%3d%s" % (self.__len,
self.__key, self.__name, self.__input_units, self.__output_units,
self.__approx_type, self.__freq_unit, self.__low_freq, self.__high_freq,
self.__approx_lower_bound, self.__approx_upper_bound, self.__approx_error,
self.__ncoeff, self.__coeff)
if len(blk) != self.__len:
raise SEEDError("blockette 42 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
class _Blockette43(object):
def __init__(self, key, name, type, input_units, output_units, norm_fac,
norm_freq, nzeros, zeros, npoles, poles):
self.__key = key
self.__name = _mkseedstring(43, 4, name, 1, 25, "UN")
self.__type = _mkseedstring(43, 5, type, 1, 1, "U")
self.__input_units = input_units
self.__output_units = output_units
self.__norm_fac = norm_fac
self.__norm_freq = norm_freq
self.__zeros, nzeros = _mkseedpaz(43, 11, nzeros, zeros)
self.__nzeros = nzeros
self.__poles, npoles = _mkseedpaz(43, 16, npoles, poles)
self.__npoles = npoles
self.__len = 48 + 48 * (nzeros + npoles) + len(self.__name)
def output(self, f):
blk = "043%4d%4d%s%s%3d%3d%12.5E%12.5E%3d%s%3d%s" % \
(self.__len, self.__key, self.__name, self.__type,
self.__input_units, self.__output_units, self.__norm_fac,
self.__norm_freq, self.__nzeros, self.__zeros, self.__npoles,
self.__poles)
if len(blk) != self.__len:
raise SEEDError("blockette 43 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
class _Blockette44(object):
def __init__(self, key, name, type, input_units, output_units,
n_numerators=0, numerators="", n_denominators=0, denominators=""):
self.__key = key
self.__name = _mkseedstring(44, 4, name, 1, 25, "UN")
self.__type = _mkseedstring(44, 5, type, 1, 1, "U")
self.__input_units = input_units
self.__output_units = output_units
self.__n_numerators = n_numerators
self.__numerators = _mkseedcoeff_iir(54, 8, n_numerators, numerators)
self.__n_denominators = n_denominators
self.__denominators = _mkseedcoeff_iir(54, 11, n_denominators, denominators)
self.__len = 26 + len(self.__name) + 24 * n_numerators + 24 * n_denominators
def output(self, f):
blk = "044%4d%4d%s%s%3d%3d%4d%s%4d%s" % (self.__len, self.__key,
self.__name, self.__type, self.__input_units, self.__output_units,
self.__n_numerators, self.__numerators, self.__n_denominators, self.__denominators)
if len(blk) != self.__len:
raise SEEDError("blockette 44 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
class _Blockette45(object):
def __init__(self, key, name, type, input_units, output_units, ntuples, tuples):
self.__key = key
self.__name = _mkseedstring(45, 4, name, 1, 25, "UN")
self.__input_units = input_units
self.__output_units = output_units
self.__tuples, ntuples = _mkseedfap(55, 7, ntuples, tuples)
self.__ntuples = ntuples
self.__len = 21 + 60 * ntuples + len(self.__name)
def output(self, f):
if self.__len < 10000:
blk = "055%4d%4d%s%3d%3d%4d%s" % \
(self.__len, self.__key, self.__name,
self.__input_units, self.__output_units,
self.__ntuples, self.__tuples)
if len(blk) != self.__len:
raise SEEDError("blockette 45 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
else:
btuples = int((9999-21-len(self.__name))/60)
l = 21 + 60 * btuples + len(self.__name)
blk = "055%4d%4d%s%3d%3d%4d%s" % \
(l, self.__key, self.__name,
self.__input_units, self.__output_units,
btuples, self.__tuples[:btuples*60])
if len(blk) != l:
raise SEEDError("blockette 45 has invalid length: %d instead of %d" % (len(blk), l))
f.write_blk(blk)
left_tuples = self.__ntuples-btuples
ofs = btuples*60
while left_tuples > 0:
if left_tuples > btuples:
ntuples = btuples
else:
ntuples = left_tuples
l = 21 + ntuples * 60 + len(self.__name)
blk = "055%4d%4d%s%3d%3d%4d" % \
(l, self.__key, self.__name,
self.__input_units, self.__output_units,
ntuples, self.__tuples[ofs:ofs+ntuples*60])
if len(blk) != l:
raise SEEDError("blockette 45 has invalid length: %d instead of %d" % (len(blk), l))
f.write_blk(blk)
ofs += ntuples*60
left_tuples -= ntuples
class _Blockette47(object):
def __init__(self, key, name, input_rate, deci_fac, deci_offset,
delay, correction):
self.__key = key
self.__name = _mkseedstring(47, 4, name, 1, 25, "UN")
self.__input_rate = input_rate
self.__deci_fac = deci_fac
self.__deci_offset = deci_offset
self.__delay = delay
self.__correction = correction
self.__len = 53 + len(self.__name)
def output(self, f):
blk = "047%4d%4d%s%10.4E%5d%5d%11.4E%11.4E" % (self.__len,
self.__key, self.__name, self.__input_rate, self.__deci_fac,
self.__deci_offset, self.__delay, self.__correction)
if len(blk) != self.__len:
raise SEEDError("blockette 47 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
class _Blockette48(object):
def __init__(self, key, name, gain, gain_freq):
self.__key = key
self.__name = _mkseedstring(48, 4, name, 1, 25, "UN")
self.__gain = gain
self.__gain_freq = gain_freq
self.__len = 37 + len(self.__name)
def output(self, f):
blk = "048%4d%4d%s%12.5E%12.5E 0" % (self.__len, self.__key,
self.__name, self.__gain, self.__gain_freq)
if len(blk) != self.__len:
raise SEEDError("blockette 48 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
class _Blockette50(object):
def __init__(self, stat_code, latitude, longitude, elevation,
site_name, net_id, net_code, start_date, end_date):
self.__stat_code = _mkseedstring(50, 3, stat_code, 5, 5, "UN")
self.__latitude = latitude
self.__longitude = longitude
self.__elevation = elevation
self.__site_name = _mkseedstring(50, 9, site_name, 1, 60, "UNLPS")
self.__net_id = net_id
self.__start_date = _mkseedtime(50, 13, start_date)
self.__end_date = _mkseedtime(50, 14, end_date)
self.__net_code = _mkseedstring(50, 16, net_code, 2, 2, "UN")
self.__len = 59 + len(self.__site_name) + len(self.__start_date) + \
len(self.__end_date)
error = False
if not isinstance(self.__stat_code, str):
print("blockette 50 - station code not set or not of type string", file=sys.stderr)
error = True
if not isinstance(self.__latitude, float):
print("blockette 50 - latitude not set or not of type float", file=sys.stderr)
error = True
if not isinstance(self.__longitude, float):
print("blockette 50 - longitude not set or not of type float", file=sys.stderr)
error = True
if not isinstance(self.__elevation, float):
print("blockette 50 - elevation not set or not of type float", file=sys.stderr)
error = True
if error:
raise SEEDError("blockette 50 - provide correct values. Adjust your inventory XML!")
def output(self, f):
blk = "050%4d%s%10.6f%11.6f%7.1f %s%3d321010%s%sN%s" % \
(self.__len, self.__stat_code, self.__latitude, self.__longitude,
self.__elevation, self.__site_name, self.__net_id,
self.__start_date, self.__end_date, self.__net_code)
if len(blk) != self.__len:
raise SEEDError("blockette 50 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
class _Blockette51(object):
def __init__(self, start_time, end_time, comment_key):
self.__start_time = _mkseedtime(51, 3, start_time)
self.__end_time = _mkseedtime(51, 4, end_time)
self.__comment_key = comment_key
self.__len = 17 + len(self.__start_time) + len(self.__end_time)
def output(self, f):
blk = "051%4d%s%s%4d 0" % (self.__len, self.__start_time,
self.__end_time, self.__comment_key)
if len(blk) != self.__len:
raise SEEDError("blockette 51 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
class _Blockette52(object):
def __init__(self, loc_id, chan_id, instr_id, comment, signal_units,
calibration_units, latitude, longitude, elevation, local_depth,
azimuth, dip, data_format, record_length, sample_rate, clock_drift,
flags, start_date, end_date):
self.__loc_id = _mkseedstring(52, 3, loc_id, 2, 2, "UN")
self.__chan_id = _mkseedstring(52, 4, chan_id, 3, 3, "UN")
self.__instr_id = instr_id
self.__comment = _mkseedstring(52, 7, comment, 0, 30, "UNLPS")
self.__signal_units = signal_units
self.__calibration_units = calibration_units
self.__latitude = latitude
self.__longitude = longitude
self.__elevation = elevation
self.__local_depth = local_depth
self.__azimuth = azimuth
self.__dip = dip
self.__data_format = data_format
self.__record_length = record_length
self.__sample_rate = sample_rate
self.__clock_drift = clock_drift
self.__flags = _mkseedstring(52, 21, flags, 0, 26, "U")
self.__raw_start_date = start_date
self.__raw_end_date = end_date
self.__len = 0
error = False
if not isinstance(self.__loc_id, str):
print("blockette 52 - location ID not set or not of type string", file=sys.stderr)
error = True
if not isinstance(self.__chan_id, str):
print("blockette 52 - channel ID not set or not of type string", file=sys.stderr)
error = True
if not isinstance(self.__len, int):
print("blockette 52 - len not set or not of type int", file=sys.stderr)
error = True
if not isinstance(self.__instr_id, int):
print("blockette 52 - instrument ID not set or not of type integer", file=sys.stderr)
error = True
if not isinstance(self.__comment, str):
print("blockette 52 - comment not set or not of type sting", file=sys.stderr)
error = True
if not isinstance(self.__signal_units, int):
print("blockette 52 - signal_units not set or not of type integer", file=sys.stderr)
error = True
if not isinstance(self.__calibration_units, int):
print("blockette 52 - calibration units not set or not of type integer", file=sys.stderr)
error = True
if not isinstance(self.__latitude, float):
print("blockette 52 - latitude not set or not of type float", file=sys.stderr)
error = True
if not isinstance(self.__longitude, float):
print("blockette 52 - longitude not set or not of type float", file=sys.stderr)
error = True
if not isinstance(self.__elevation, float):
print("blockette 52 - elevation not set or not of type float", file=sys.stderr)
error = True
if not isinstance(self.__local_depth, float):
print("Warning: blockette 52 - local depth not set or not of type float", file=sys.stderr)
error = True
if not isinstance(self.__azimuth, float):
print("blockette 52 - azimuth not set or not of type float", file=sys.stderr)
error = True
if not isinstance(self.__dip, float):
print("blockette 52 - dip not set or not of type float", file=sys.stderr)
error = True
if not isinstance(self.__data_format, int):
print("blockette 52 - data format not set or not of type integer", file=sys.stderr)
error = True
if not isinstance(self.__record_length, int):
print("blockette 52 - record length not set or not of type integer", file=sys.stderr)
error = True
if not isinstance(self.__sample_rate, float):
print("blockette 52 - sample rate not set or not of type float", file=sys.stderr)
error = True
if not isinstance(self.__clock_drift, float):
print("Warning: blockette 52 - clock drift not set or not of type float", file=sys.stderr)
error = True
if error:
raise SEEDError("blockette 52 - provide correct values. Adjust your inventory XML!")
def set_vol_span(self, vol_start, vol_end):
# make verseed happy
if _cmptime(self.__raw_end_date, vol_end) > 0:
self.__raw_end_date = vol_end
if _cmptime(self.__raw_start_date, vol_start) < 0:
self.__raw_start_date = vol_start
def output(self, f):
self.__start_date = _mkseedtime(52, 22, self.__raw_start_date)
self.__end_date = _mkseedtime(52, 23, self.__raw_end_date)
self.__len = 99 + len(self.__comment) + len(self.__flags) + \
len(self.__start_date) + len(self.__end_date)
blk = "052%4d%s%s 0%3d%s%3d%3d%10.6f%11.6f%7.1f%5.1f%5.1f%5.1f%4d%2d%10.4E%10.4E %s%s%sN" % \
(self.__len, self.__loc_id, self.__chan_id, self.__instr_id,
self.__comment, self.__signal_units, self.__calibration_units,
self.__latitude, self.__longitude, self.__elevation,
self.__local_depth, self.__azimuth, self.__dip, self.__data_format,
self.__record_length, self.__sample_rate, self.__clock_drift,
self.__flags, self.__start_date, self.__end_date)
if len(blk) != self.__len:
raise SEEDError("blockette 52 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
class _Blockette53(object):
def __init__(self, type, input_units, output_units, norm_fac,
norm_freq, nzeros, zeros, npoles, poles):
self.__type = _mkseedstring(53, 3, type, 1, 1, "U")
self.__stage = 0
self.__input_units = input_units
self.__output_units = output_units
self.__norm_fac = norm_fac
self.__norm_freq = norm_freq
self.__zeros, nzeros = _mkseedpaz(53, 10, nzeros, zeros)
self.__nzeros = nzeros
self.__poles, npoles = _mkseedpaz(53, 15, npoles, poles)
self.__npoles = npoles
self.__len = 46 + 48 * (nzeros + npoles)
def set_stage(self, stage):
self.__stage = stage
def output(self, f):
blk = "053%4d%s%2d%3d%3d%12.5E%12.5E%3d%s%3d%s" % \
(self.__len, self.__type, self.__stage,
self.__input_units, self.__output_units, self.__norm_fac,
self.__norm_freq, self.__nzeros, self.__zeros, self.__npoles,
self.__poles)
if len(blk) != self.__len:
raise SEEDError("blockette 53 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
class _Blockette54(object):
def __init__(self, type, input_units, output_units,
n_numerators=0, numerators="", n_denominators=0, denominators=""):
self.__type = _mkseedstring(54, 3, type, 1, 1, "U")
self.__stage = 0
self.__input_units = input_units
self.__output_units = output_units
self.__n_numerators = n_numerators
self.__numerators = _mkseedcoeff_iir(54, 8, n_numerators, numerators)
self.__n_denominators = n_denominators
self.__denominators = _mkseedcoeff_iir(54, 11, n_denominators, denominators)
self.__len = 24 + 24 * n_numerators + 24 * n_denominators
def set_stage(self, stage):
self.__stage = stage
def output(self, f):
blk = "054%4d%s%2d%3d%3d%4d%s%4d%s" % (self.__len,
self.__type, self.__stage, self.__input_units, self.__output_units,
self.__n_numerators, self.__numerators, self.__n_denominators, self.__denominators)
if len(blk) != self.__len:
raise SEEDError("blockette 54 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
class _Blockette55(object):
def __init__(self, input_units, output_units, ntuples, tuples):
self.__stage = 0
self.__input_units = input_units
self.__output_units = output_units
self.__tuples, ntuples = _mkseedfap(55, 7, ntuples, tuples)
self.__ntuples = ntuples
self.__len = 19 + 60 * ntuples
def set_stage(self, stage):
self.__stage = stage
def output(self, f):
if self.__len < 10000:
blk = "055%4d%2d%3d%3d%4d%s" % \
(self.__len, self.__stage,
self.__input_units, self.__output_units,
self.__ntuples, self.__tuples)
if len(blk) != self.__len:
raise SEEDError("blockette 55 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
else:
l = 19 + 166*60
blk = "055%4d%2d%3d%3d%4d%s" % \
(l, self.__stage,
self.__input_units, self.__output_units,
166, self.__tuples[:166*60])
if len(blk) != l:
raise SEEDError("blockette 55 has invalid length: %d instead of %d" % (len(blk), l))
f.write_blk(blk)
left_tuples = self.__ntuples-166
ofs = 166*60
while left_tuples > 0:
if left_tuples > 166:
ntuples = 166
else:
ntuples = left_tuples
l = 19 + ntuples * 60
blk = "055%4d%2d%3d%3d%4d%s" % \
(l, self.__stage,
self.__input_units, self.__output_units,
ntuples, self.__tuples[ofs:ofs+ntuples*60])
if len(blk) != l:
raise SEEDError("blockette 55 has invalid length: %d instead of %d" % (len(blk), l))
f.write_blk(blk)
ofs += ntuples*60
left_tuples -= ntuples
class _Blockette57(object):
def __init__(self, input_rate, deci_fac, deci_offset, delay, correction):
self.__stage = 0
self.__input_rate = input_rate
self.__deci_fac = deci_fac
self.__deci_offset = deci_offset
self.__delay = delay
self.__correction = correction
self.__len = 51
def set_stage(self, stage):
self.__stage = stage
def output(self, f):
blk = "057%4d%2d%10.4E%5d%5d%11.4E%11.4E" % (self.__len,
self.__stage, self.__input_rate, self.__deci_fac,
self.__deci_offset, self.__delay, self.__correction)
if len(blk) != self.__len:
raise SEEDError("blockette 57 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
class _Blockette58(object):
def __init__(self, gain, gain_freq):
self.__stage = 0
self.__gain = gain
self.__gain_freq = gain_freq
self.__len = 35
def set_stage(self, stage):
self.__stage = stage
def output(self, f):
blk = "058%4d%2d%12.5E%12.5E 0" % (self.__len, self.__stage,
self.__gain, self.__gain_freq)
if len(blk) != self.__len:
raise SEEDError("blockette 58 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
class _Blockette59(object):
def __init__(self, start_time, end_time, comment_key):
self.__start_time = _mkseedtime(51, 3, start_time)
self.__end_time = _mkseedtime(51, 4, end_time)
self.__comment_key = comment_key
self.__len = 17 + len(self.__start_time) + len(self.__end_time)
def output(self, f):
blk = "059%4d%s%s%4d 0" % (self.__len, self.__start_time,
self.__end_time, self.__comment_key)
if len(blk) != self.__len:
raise SEEDError("blockette 59 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
class _Blockette60(object):
def __init__(self, start_stage):
self.__start_stage = start_stage
self.__ref_list = []
self.__len = 9
def add_stage(self, *keyref):
self.__ref_list.append(keyref)
self.__len += 4 + 4 * len(keyref)
def output(self, f):
blk = "060%4d%2d" % (self.__len, len(self.__ref_list))
for (n, r) in enumerate(self.__ref_list):
blk += ("%2d%2d" + len(r) * "%4d") % \
((self.__start_stage + n, len(r)) + r)
if len(blk) != self.__len:
raise SEEDError("blockette 60 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
class _Blockette61(object):
def __init__(self, name, symmetry, input_units, output_units, ncoeff,
coeff):
self.__stage = 0
self.__name = _mkseedstring(61, 4, name, 1, 25, "UN")
self.__symmetry = _mkseedstring(61, 5, symmetry, 1, 1, "U")
self.__input_units = input_units
self.__output_units = output_units
self.__ncoeff = ncoeff
self.__coeff = _mkseedcoeff_fir(61, 9, ncoeff, coeff)
self.__len = 20 + 14 * ncoeff + len(self.__name)
def set_stage(self, stage):
self.__stage = stage
def __output_huge(self, f):
n = 0
while n < self.__ncoeff:
nc = min(self.__ncoeff - n, (9977 - len(self.__name)) // 14)
blen = 20 + 14 * nc + len(self.__name)
blk = "061%4d%2d%s%s%3d%3d%4d%s" % (blen, self.__stage,
self.__name, self.__symmetry, self.__input_units,
self.__output_units, nc, self.__coeff[14*n:14*(n+nc)])
if len(blk) != blen:
raise SEEDError("blockette 61 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
n += nc
def output(self, f):
if self.__len > 9999:
self.__output_huge(f)
return
blk = "061%4d%2d%s%s%3d%3d%4d%s" % (self.__len, self.__stage,
self.__name, self.__symmetry, self.__input_units,
self.__output_units, self.__ncoeff, self.__coeff)
if len(blk) != self.__len:
raise SEEDError("blockette 61 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
class _Blockette62(object):
def __init__(self, input_units, output_units, freq_unit, low_freq,
high_freq, approx_type, approx_lower_bound, approx_upper_bound,
approx_error, ncoeff, coeff, gain=1.0):
self.__stage = 0
self.__input_units = input_units
self.__output_units = output_units
self.__freq_unit = freq_unit
self.__low_freq = low_freq
self.__high_freq = high_freq
self.__approx_type = approx_type
self.__approx_lower_bound = approx_lower_bound
self.__approx_upper_bound = approx_upper_bound
self.__approx_error = approx_error
self.__ncoeff = ncoeff
self.__coeff = _mkseedcoeff_polynomial(62, 15, ncoeff, coeff, gain)
self.__len = 81 + 24 * ncoeff
def set_stage(self, stage):
self.__stage = stage
def output(self, f):
blk = "062%4dP%2d%3d%3d%1s%1s%12.5E%12.5E%12.5E%12.5E%12.5E%3d%s" % (self.__len,
self.__stage, self.__input_units, self.__output_units,
self.__approx_type[:1], self.__freq_unit[:1], self.__low_freq, self.__high_freq,
self.__approx_lower_bound, self.__approx_upper_bound, self.__approx_error,
self.__ncoeff, self.__coeff)
if len(blk) != self.__len:
raise SEEDError("blockette 62 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
class _Blockette70(object):
def __init__(self, flag, begin, end):
self.__flag = flag
self.__begin = _mkseedtime(70, 4, begin)
self.__end = _mkseedtime(70, 5, end)
self.__len = 54
def output(self, f):
blk = "070%4d%c%s%s" % (self.__len, self.__flag, self.__begin,
self.__end)
if len(blk) != self.__len:
raise SEEDError("blockette 70 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
class _Blockette74(object):
def __init__(self, net_code, stat_code, loc_id, chan_id,
start_time, start_recno, end_time, end_recno):
self.__net_code = _mkseedstring(74, 16, net_code, 2, 2, "UN")
self.__stat_code = _mkseedstring(74, 3, stat_code, 5, 5, "UN")
self.__loc_id = _mkseedstring(74, 4, loc_id, 2, 2, "UN")
self.__chan_id = _mkseedstring(74, 5, chan_id, 3, 3, "UN")
self.__start_time = _mkseedtime(74, 6, start_time)
self.__start_recno = start_recno
self.__end_time = _mkseedtime(74, 9, end_time)
self.__end_recno = end_recno
self.__len = 84
def add_accelerator(self, recno):
pass
def output(self, f):
blk = "074%4d%s%s%s%s%6d 1%s%6d 1 0%s" % (self.__len,
self.__stat_code, self.__loc_id, self.__chan_id,
self.__start_time, self.__start_recno, self.__end_time,
self.__end_recno, self.__net_code)
if len(blk) != self.__len:
raise SEEDError("blockette 74 has invalid length: %d instead of %d" % (len(blk), self.__len))
f.write_blk(blk)
class _FormatDict(object):
__formats = {
"Steim1": ("Steim1 Integer Compression Format", 50,
"F1 P4 W4 D C2 R1 P8 W4 D C2",
"P0 W4 N15 S2,0,1",
"T0 X W4",
"T1 Y4 W7 D C2",
"T2 Y2 W2 D C2",
"T3 N0 W4 D C2"),
"steim1": ("Steim1 Integer Compression Format", 50,
"F1 P4 W4 D C2 R1 P8 W4 D C2",
"P0 W4 N15 S2,0,1",
"T0 X W4",
"T1 Y4 W7 D C2",
"T2 Y2 W2 D C2",
"T3 N0 W4 D C2"),
"Steim2": ("Steim2 Integer Compression Format", 50,
"F1 P4 W4 D C2 R1 P8 W4 D C2",
"P0 W4 N15 S2,0,1",
"T0 X W4",
"T1 Y4 W1 D C2",
"T2 W4 I D2",
"K0 X D30",
"K1 N0 D30 C2",
"K2 Y2 D15 C2",
"K3 Y3 D10 C2",
"T3 W4 I D2",
"K0 Y5 D6 C2",
"K1 Y6 D5 C2",
"K2 X D2 Y7 D4 C2",
"K3 X D30"),
"steim2": ("Steim2 Integer Compression Format", 50,
"F1 P4 W4 D C2 R1 P8 W4 D C2",
"P0 W4 N15 S2,0,1",
"T0 X W4",
"T1 Y4 W1 D C2",
"T2 W4 I D2",
"K0 X D30",
"K1 N0 D30 C2",
"K2 Y2 D15 C2",
"K3 Y3 D10 C2",
"T3 W4 I D2",
"K0 Y5 D6 C2",
"K1 Y6 D5 C2",
"K2 X D2 Y7 D4 C2",
"K3 X D30"),
"mseed10": ("Steim1 Integer Compression Format", 50,
"F1 P4 W4 D C2 R1 P8 W4 D C2",
"P0 W4 N15 S2,0,1",
"T0 X W4",
"T1 Y4 W7 D C2",
"T2 Y2 W2 D C2",
"T3 N0 W4 D C2"),
"mseed11": ("Steim2 Integer Compression Format", 50,
"F1 P4 W4 D C2 R1 P8 W4 D C2",
"P0 W4 N15 S2,0,1",
"T0 X W4",
"T1 Y4 W1 D C2",
"T2 W4 I D2",
"K0 X D30",
"K1 N0 D30 C2",
"K2 Y2 D15 C2",
"K3 Y3 D10 C2",
"T3 W4 I D2",
"K0 Y5 D6 C2",
"K1 Y6 D5 C2",
"K2 X D2 Y7 D4 C2",
"K3 X D30"),
"mseed13": ("GEOSCOPE Multiplexed Format 16 bit gain ranged, 3 bit exponent", 1,
"M0",
"W2 D0-11 A-2048",
"D12-14",
"E2:0:-1"),
"mseed14": ("GEOSCOPE Multiplexed Format 16 bit gain ranged, 4 bit exponent", 1,
"M0",
"W2 D0-11 A-2048",
"D12-15",
"E2:0:-1"),
"mseed0": ("ASCII console log", 80, ""),
"ASCII": ("ASCII console log", 80, ""),
"unknown": ("unknown", 99, "") }
def __init__(self):
self.__num = 0
self.__used = {}
self.__blk = []
def lookup(self, name):
if not name:
name = "unknown"
try:
return self.__used[name]
except KeyError:
pass
self.__num += 1
k = self.__num
self.__used[name] = k
try:
f = self.__formats[name]
except KeyError:
f = (name, 99, "")
b = _Blockette30(name = f[0], key = k, family = f[1], ddl = f[2:])
self.__blk.append(b)
return k
def output(self, f):
for b in self.__blk:
b.output(f)
class _UnitDict(object):
__units = {
"COUNTS": "Digital Counts",
"COUNTS/V": "Counts per Volt",
"M": "Displacement in Meters",
"M/S": "Velocity in Meters per Second",
"M/S**2": "Acceleration in Meters per Second per Second",
"RAD/S": "Angular Velocity in Radians per Second",
"V": "Volts",
"A": "Amperes",
"PA": "Pascal",
"C": "Degree Celsius",
"DEG": "Degree" }
def __init__(self):
self.__num = 0
self.__used = {}
self.__blk = []
def lookup(self, name, remark=None):
if not name:
name = "unknown"
try:
return self.__used[(name, remark)]
except KeyError:
pass
self.__num += 1
k = self.__num
self.__used[(name, remark)] = k
try:
desc = json.loads(remark)['unit']
except Exception:
try:
desc = self.__units[name]
except KeyError:
desc = name
b = _Blockette34(key = k, name = name, desc = desc)
self.__blk.append(b)
return k
def output(self, f):
for b in self.__blk:
b.output(f)
class _CommentDict(object):
def __init__(self):
self.__num = 0
self.__used = {}
self.__blk = []
def lookup(self, cclass, comment):
k = self.__used.get((cclass, comment))
if k is not None:
return k
self.__num += 1
k = self.__num
self.__used[(cclass, comment)] = k
b = _Blockette31(key = k, cclass = cclass, comment = comment)
self.__blk.append(b)
return k
def output(self, f):
for b in self.__blk:
b.output(f)
class _GenericAbbreviationDict(object):
def __init__(self, inventory):
self.__inventory = inventory
self.__num = 0
self.__used_sensor = {}
self.__used_network = {}
self.__blk = [] # blk33
def lookup_sensor(self, name): # instrument id for blk52
sensor = self.__inventory.object.get(name)
if sensor is None:
raise SEEDError("unknown sensor: " + name)
desc = sensor.description
if not desc:
desc = "unknown"
k = self.__used_sensor.get(desc)
if k is not None:
return k
self.__num += 1
k = self.__num
self.__used_sensor[desc] = k
self.__blk.append(_Blockette33(k, desc))
return k
def lookup_network(self, code, start): # network id for blk50
k = self.__used_network.get((code, start))
if k is not None:
return k
self.__num += 1
k = self.__num
self.__used_network[(code, start)] = k
net_tp = self.__inventory.network.get(code)
if net_tp is None:
raise SEEDError("unknown network: %s" % (code,))
netcfg = net_tp.get(start)
if net_tp is None:
raise SEEDError("unknown network: %s.%s" % \
(code, start.isoformat()))
self.__blk.append(_Blockette33(k, netcfg.description))
return k
def output(self, f):
for b in self.__blk:
b.output(f)
class _ResponseContainer(object):
def __init__(self, fac):
self.__fac = fac
def add_sensor(self, name, dev_id, compn):
(x1, x2, sens, sens_freq) = self.__fac._lookup_sensor(name,
dev_id, compn)
if x2 is not None:
self._add_stage(x1, x2)
else:
self._add_stage(x1)
return (sens, sens_freq)
def add_analogue_paz(self, name):
(x1, x2, gain) = self.__fac._lookup_analogue_paz(name)
self._add_stage(x1, x2)
return gain
def add_analogue_iir(self, name):
(x1, x2, gain) = self.__fac._lookup_analogue_iir(name)
self._add_stage(x1, x2)
return gain
def add_analogue_fap(self, name):
(x1, x2, gain) = self.__fac._lookup_analogue_fap(name)
self._add_stage(x1, x2)
return gain
def add_digitizer(self, name, dev_id, compn, sample_rate, sample_rate_div):
(x1, x2, x3, rate, gain) = self.__fac._lookup_digitizer(name,
dev_id, compn, sample_rate, sample_rate_div)
self._add_stage(x1, x2, x3)
return (rate, gain)
def add_digital_paz(self, name, input_rate):
(x1, x2, x3, rate, gain) = self.__fac._lookup_digital_paz(name, input_rate)
self._add_stage(x1, x2, x3)
return (rate, gain)
def add_digital_iir(self, name, input_rate):
(x1, x2, x3, rate, gain) = self.__fac._lookup_digital_iir(name, input_rate)
self._add_stage(x1, x2, x3)
return (rate, gain)
def add_fir(self, name, input_rate):
(x1, x2, x3, rate, gain) = self.__fac._lookup_fir(name, input_rate)
self._add_stage(x1, x2, x3)
return (rate, gain)
class _Response4xContainer(_ResponseContainer):
def __init__(self, fac):
_ResponseContainer.__init__(self, fac)
self.__blk = _Blockette60(1)
def _add_stage(self, *blkref):
self.__blk.add_stage(*blkref)
def output(self, f):
self.__blk.output(f)
class _Response5xContainer(_ResponseContainer):
def __init__(self, fac):
_ResponseContainer.__init__(self, fac)
self.__stage = 1
self.__blk = []
def _add_stage(self, *blk):
for b in blk:
b.set_stage(self.__stage)
self.__blk.append(b)
self.__stage += 1
def output(self, f):
for b in self.__blk:
b.output(f)
class _Response4xFactory(object):
def __init__(self, inventory, unit_dict):
self.__inventory = inventory
self.__unit_dict = unit_dict
self.__num = 0
self.__used_sensor = {}
self.__used_sensor_calib = {}
self.__used_digitizer = {}
self.__used_digitizer_calib = {}
self.__used_analogue_paz = {}
self.__used_analogue_iir = {}
self.__used_analogue_fap = {}
self.__used_digital_paz = {}
self.__used_digital_iir = {}
self.__used_fir = {}
self.__used_fir_deci = {}
self.__blk41 = []
self.__blk42 = []
self.__blk43 = []
self.__blk44 = []
self.__blk45 = []
self.__blk47 = []
self.__blk48 = []
def new_response(self):
return _Response4xContainer(self)
def _lookup_sensor(self, name, dev_id, compn):
sensor = self.__inventory.object.get(name)
if sensor is None:
raise SEEDError("unknown sensor: " + name)
resp = self.__inventory.object.get(sensor.response)
if resp is None:
raise SEEDError("cannot find response for sensor " + sensor.name)
k1 = self.__used_sensor.get(name)
if k1 is None:
unit = None
try:
unit = sensor.unit
except AttributeError:
pass
if unit:
input_units = self.__unit_dict.lookup(unit, sensor.remark)
elif _is_paz_response(resp) and resp.numberOfZeros == 0:
input_units = self.__unit_dict.lookup("M/S**2")
else:
input_units = self.__unit_dict.lookup("M/S")
k1 = self.__num + 1
if _is_paz_response(resp):
if resp.type != "A" and resp.type != "B":
raise SEEDError("invalid PAZ response type of " + resp.name)
b1 = _Blockette43(key = k1,
name = "RS" + name,
type = resp.type,
input_units = input_units,
output_units = self.__unit_dict.lookup("V"),
norm_fac = resp.normalizationFactor,
norm_freq = resp.normalizationFrequency,
nzeros = resp.numberOfZeros,
zeros = resp.zeros,
npoles = resp.numberOfPoles,
poles = resp.poles)
self.__blk43.append(b1)
elif _is_iir_response(resp):
if resp.type != "A" and resp.type != "B":
raise SEEDError("invalid IIR response type of " + resp.name)
b1 = _Blockette44(key = k1,
name = "RS" + name,
type = resp.type,
input_units = input_units,
output_units = self.__unit_dict.lookup("V"),
n_numerators = resp.numberOfNumerators,
numerators = resp.numerators,
n_denominators = resp.numberOfDenominators,
denominators = resp.denominators)
self.__blk44.append(b1)
elif _is_poly_response(resp):
b1 = _Blockette42(key = k1,
name = "RS" + name,
input_units = input_units,
output_units = self.__unit_dict.lookup("V"),
freq_unit = resp.frequencyUnit,
low_freq = sensor.lowFrequency,
high_freq = sensor.highFrequency,
approx_type = resp.approximationType,
approx_lower_bound = resp.approximationLowerBound,
approx_upper_bound = resp.approximationUpperBound,
approx_error = resp.approximationError,
ncoeff = resp.numberOfCoefficients,
coeff = resp.coefficients)
self.__blk42.append(b1)
elif _is_fap_response(resp):
b1 = _Blockette45(key = k1,
name = "RS" + name,
input_units = input_units,
output_units = self.__unit_dict.lookup("V"),
ntuples = resp.numberOfTuples,
tuples = resp.tuples)
self.__blk45.append(b1)
else:
raise SEEDError("unknown response type of sensor " + sensor.name)
self.__num += 1
self.__used_sensor[name] = k1
try:
calib = sensor.calibration[dev_id][compn]
except KeyError:
calib = None
if calib is not None and len(calib) > 0:
calib_list = list(calib.items())
calib_list.sort()
resp_name = "GS" + sensor.name + "_" + dev_id
gain = calib_list[-1][1].gain
else:
calib_list = []
resp_name = "GS" + sensor.name
gain = resp.gain
dev_id = None
compn = None
if gain == 0.0 or gain is None or resp.gainFrequency is None:
return (k1, None, 1.0, 0.0)
k2 = self.__used_sensor_calib.get((name, dev_id, compn))
if k2 is not None:
return (k1, k2, gain, resp.gainFrequency)
k2 = self.__num + 1
b2 = _Blockette48(key = k2,
name = resp_name,
gain = gain,
gain_freq = resp.gainFrequency) #,
#calib_list = calib_list)
self.__blk48.append(b2)
self.__num += 1
self.__used_sensor_calib[(name, dev_id, compn)] = k2
return (k1, k2, gain, resp.gainFrequency)
def _lookup_analogue_paz(self, name):
resp_paz = self.__inventory.object.get(name)
if resp_paz is None:
raise SEEDError("unknown PAZ response: " + name)
k = self.__used_analogue_paz.get(name)
if k is not None:
(k1, k2) = k
return (k1, k2, resp_paz.gain)
#if resp_paz.deci_fac is not None:
# raise SEEDError("expected analogue response, found digital")
if resp_paz.type != "A" and resp_paz.type != "B":
raise SEEDError("invalid PAZ response type of " + resp_paz.name)
k1 = self.__num + 1
k2 = self.__num + 2
b1 = _Blockette43(key = k1,
name = "RA" + name,
type = resp_paz.type,
input_units = self.__unit_dict.lookup("V"),
output_units = self.__unit_dict.lookup("V"),
norm_fac = resp_paz.normalizationFactor,
norm_freq = resp_paz.normalizationFrequency,
nzeros = resp_paz.numberOfZeros,
zeros = resp_paz.zeros,
npoles = resp_paz.numberOfPoles,
poles = resp_paz.poles)
b2 = _Blockette48(key = k2,
name = "GA" + name,
gain = resp_paz.gain,
gain_freq = resp_paz.gainFrequency)
self.__blk43.append(b1)
self.__blk48.append(b2)
self.__num += 2
self.__used_analogue_paz[name] = (k1, k2)
return (k1, k2, resp_paz.gain)
def _lookup_analogue_iir(self, name):
resp_iir = self.__inventory.object.get(name)
if resp_iir is None:
raise SEEDError("unknown IIR response: " + name)
k = self.__used_analogue_iir.get(name)
if k is not None:
(k1, k2) = k
return (k1, k2, resp_iir.gain)
#if resp_iir.deci_fac is not None:
# raise SEEDError("expected analogue response, found digital")
if resp_iir.type != "A" and resp_iir.type != "B":
raise SEEDError("invalid IIR response type of " + resp_iir.name)
k1 = self.__num + 1
k2 = self.__num + 2
b1 = _Blockette44(key = k1,
name = "RA" + name,
type = resp_iir.type,
input_units = self.__unit_dict.lookup("V"),
output_units = self.__unit_dict.lookup("V"),
n_numerators = resp_iir.numberOfNumerators,
numerators = resp_iir.numerators,
n_denominators = resp_iir.numberOfDenominators,
denominators = resp_iir.denominators)
b2 = _Blockette48(key = k2,
name = "GA" + name,
gain = resp_iir.gain,
gain_freq = resp_iir.gainFrequency)
self.__blk44.append(b1)
self.__blk48.append(b2)
self.__num += 2
self.__used_analogue_iir[name] = (k1, k2)
return (k1, k2, resp_iir.gain)
def _lookup_analogue_fap(self, name):
resp_fap = self.__inventory.object.get(name)
if resp_fap is None:
raise SEEDError("unknown FAP response: " + name)
k = self.__used_analogue_fap.get(name)
if k is not None:
(k1, k2) = k
return (k1, k2, resp_fap.gain)
k1 = self.__num + 1
k2 = self.__num + 2
b1 = _Blockette45(key = k1,
name = "RA" + name,
input_units = self.__unit_dict.lookup("V"),
output_units = self.__unit_dict.lookup("V"),
ntuples = resp_fap.numberOfTuples,
tuples = resp_fap.tuples)
b2 = _Blockette48(key = k2,
name = "GA" + name,
gain = resp_fap.gain,
gain_freq = resp_fap.gainFrequency)
self.__blk45.append(b1)
self.__blk48.append(b2)
self.__num += 2
self.__used_analogue_fap[name] = (k1, k2)
return (k1, k2, resp_fap.gain)
def _lookup_digitizer(self, name, dev_id, compn, sample_rate, sample_rate_div):
digi = self.__inventory.object.get(name)
if digi is None:
raise SEEDError("unknown datalogger: " + name)
input_rate = float(sample_rate) / float(sample_rate_div)
try:
stream_deci = digi.decimation[sample_rate][sample_rate_div]
if stream_deci.digitalFilterChain and \
len(stream_deci.digitalFilterChain) > 0:
for f in stream_deci.digitalFilterChain.split():
obj = self.__inventory.object[f]
input_rate *= (obj.decimationFactor or 1)
except KeyError:
pass
k = self.__used_digitizer.get((name, input_rate))
if k is None:
k1 = self.__num + 1
k2 = self.__num + 2
b1 = _Blockette44(key = k1,
name = "RL" + name,
type = "D",
input_units = self.__unit_dict.lookup("V"),
output_units = self.__unit_dict.lookup("COUNTS"))
b2 = _Blockette47(key = k2,
name = "DL" + name,
input_rate = input_rate,
deci_fac = 1,
deci_offset = 0,
delay = 0,
correction = 0)
self.__blk44.append(b1)
self.__blk47.append(b2)
self.__num += 2
self.__used_digitizer[(name, input_rate)] = (k1, k2)
else:
(k1, k2) = k
try:
calib = digi.calibration[dev_id][compn]
except KeyError:
calib = None
if calib is not None and len(calib) > 0:
calib_list = list(calib.items())
calib_list.sort()
resp_name = "GL" + digi.name + "_" + dev_id
gain = calib_list[-1][1].gain
else:
calib_list = []
resp_name = "GL" + digi.name
gain = digi.gain
dev_id = None
compn = None
k3 = self.__used_digitizer_calib.get((name, dev_id, compn))
if k3 is not None:
return (k1, k2, k3, input_rate, gain)
k3 = self.__num + 1
b3 = _Blockette48(key = k3,
name = resp_name,
gain = gain,
gain_freq = 0) #,
#calib_list = calib_list)
self.__blk48.append(b3)
self.__num += 1
self.__used_digitizer_calib[(name, dev_id, compn)] = k3
return (k1, k2, k3, input_rate, gain)
def _lookup_digital_paz(self, name, input_rate):
resp_paz = self.__inventory.object.get(name)
if resp_paz is None:
raise SEEDError("unknown PAZ response: " + name)
deci_fac = resp_paz.decimationFactor or 1
delay = (resp_paz.delay or 0.0) / input_rate
correction = (resp_paz.correction or 0.0) / input_rate
gain = resp_paz.gain or 1.0
gain_freq = resp_paz.gainFrequency or 0.0
k = self.__used_digital_paz.get((name, input_rate))
if k is not None:
(k1, k2, k3) = k
return (k1, k2, k3, input_rate / deci_fac, resp_paz.gain)
#if resp_paz.deci_fac is None:
# raise SEEDError("expected digital response, found analogue")
if resp_paz.type != "D":
raise SEEDError("invalid PAZ response type of " + resp_paz.name)
k1 = self.__num + 1
k2 = self.__num + 2
k3 = self.__num + 3
b1 = _Blockette43(key = k1,
name = "RD" + name,
type = "D",
input_units = self.__unit_dict.lookup("COUNTS"),
output_units = self.__unit_dict.lookup("COUNTS"),
norm_fac = resp_paz.normalizationFactor,
norm_freq = resp_paz.normalizationFrequency,
nzeros = resp_paz.numberOfZeros,
zeros = resp_paz.zeros,
npoles = resp_paz.numberOfPoles,
poles = resp_paz.poles)
b2 = _Blockette47(key = k2,
name = "DD" + name,
input_rate = input_rate,
deci_fac = deci_fac,
deci_offset = 0,
delay = delay,
correction = correction)
b3 = _Blockette48(key = k3,
name = "GD" + name,
gain = gain,
gain_freq = gain_freq)
self.__blk43.append(b1)
self.__blk47.append(b2)
self.__blk48.append(b3)
self.__num += 3
self.__used_digital_paz[(name, input_rate)] = (k1, k2, k3)
return (k1, k2, k3, input_rate / deci_fac, resp_paz.gain)
def _lookup_digital_iir(self, name, input_rate):
resp_iir = self.__inventory.object.get(name)
if resp_iir is None:
raise SEEDError("unknown IIR response: " + name)
deci_fac = resp_iir.decimationFactor or 1
delay = (resp_iir.delay or 0.0) / input_rate
correction = (resp_iir.correction or 0.0) / input_rate
gain = resp_iir.gain or 1.0
gain_freq = resp_iir.gainFrequency or 0.0
k = self.__used_digital_iir.get((name, input_rate))
if k is not None:
(k1, k2, k3) = k
return (k1, k2, k3, input_rate / deci_fac, resp_iir.gain)
#if resp_iir.deci_fac is None:
# raise SEEDError("expected digital response, found analogue")
if resp_iir.type != "D":
raise SEEDError("invalid IIR response type of " + resp_iir.name)
k1 = self.__num + 1
k2 = self.__num + 2
k3 = self.__num + 3
b1 = _Blockette44(key = k1,
name = "RD" + name,
type = "D",
input_units = self.__unit_dict.lookup("COUNTS"),
output_units = self.__unit_dict.lookup("COUNTS"),
n_numerators = resp_iir.numberOfNumerators,
numerators = resp_iir.numerators,
n_denominators = resp_iir.numberOfDenominators,
denominators = resp_iir.denominators)
b2 = _Blockette47(key = k2,
name = "DD" + name,
input_rate = input_rate,
deci_fac = deci_fac,
deci_offset = 0,
delay = delay,
correction = correction)
b3 = _Blockette48(key = k3,
name = "GD" + name,
gain = gain,
gain_freq = gain_freq)
self.__blk44.append(b1)
self.__blk47.append(b2)
self.__blk48.append(b3)
self.__num += 3
self.__used_digital_iir[(name, input_rate)] = (k1, k2, k3)
return (k1, k2, k3, input_rate / deci_fac, resp_iir.gain)
def _lookup_fir(self, name, input_rate):
resp_fir = self.__inventory.object.get(name)
if resp_fir is None:
raise SEEDError("unknown FIR response: " + name)
deci_fac = resp_fir.decimationFactor or 1
delay = (resp_fir.delay or 0.0) / input_rate
correction = (resp_fir.correction or 0.0) / input_rate
gain = resp_fir.gain or 1.0
gain_freq = resp_fir.gainFrequency or 0.0
k = self.__used_fir.get(name)
if k is None:
k1 = self.__num + 1
k3 = self.__num + 2
b1 = _Blockette41(key = k1,
name = "RF" + name,
symmetry = resp_fir.symmetry,
input_units = self.__unit_dict.lookup("COUNTS"),
output_units = self.__unit_dict.lookup("COUNTS"),
ncoeff = resp_fir.numberOfCoefficients,
coeff = resp_fir.coefficients)
b3 = _Blockette48(key = k3,
name = "GF" + name,
gain = gain,
gain_freq = gain_freq)
self.__blk41.append(b1)
self.__blk48.append(b3)
self.__num += 2
self.__used_fir[name] = (k1, k3)
else:
(k1, k3) = k
k2 = self.__used_fir_deci.get((name, input_rate))
if k2 is None:
k2 = self.__num + 1
b2 = _Blockette47(key = k2,
name = "DF" + name + "_" + str(input_rate).replace(".", "_"),
input_rate = input_rate,
deci_fac = deci_fac,
deci_offset = 0,
delay = delay,
correction = correction)
self.__blk47.append(b2)
self.__num += 1
self.__used_fir_deci[(name, input_rate)] = k2
return (k1, k2, k3, input_rate / deci_fac, resp_fir.gain)
def output(self, f):
for b in self.__blk41:
b.output(f)
for b in self.__blk42:
b.output(f)
for b in self.__blk43:
b.output(f)
for b in self.__blk44:
b.output(f)
for b in self.__blk45:
b.output(f)
for b in self.__blk47:
b.output(f)
for b in self.__blk48:
b.output(f)
class _Response5xFactory(object):
def __init__(self, inventory, unit_dict):
self.__inventory = inventory
self.__unit_dict = unit_dict
def new_response(self):
return _Response5xContainer(self)
def _lookup_sensor(self, name, dev_id, compn):
sensor = self.__inventory.object.get(name)
if sensor is None:
raise SEEDError("unknown sensor: " + name)
resp = self.__inventory.object.get(sensor.response)
if resp is None:
raise SEEDError("cannot find response for sensor " + sensor.name)
unit = None
try:
unit = sensor.unit
except AttributeError:
pass
if unit:
input_units = self.__unit_dict.lookup(unit, sensor.remark)
elif _is_paz_response(resp) and resp.numberOfZeros == 0:
input_units = self.__unit_dict.lookup("M/S**2")
else:
input_units = self.__unit_dict.lookup("M/S")
if _is_paz_response(resp):
if resp.type != "A" and resp.type != "B":
raise SEEDError("invalid PAZ response type of " + resp.name)
b1 = _Blockette53(type = resp.type,
input_units = input_units,
output_units = self.__unit_dict.lookup("V"),
norm_fac = resp.normalizationFactor,
norm_freq = resp.normalizationFrequency,
nzeros = resp.numberOfZeros,
zeros = resp.zeros,
npoles = resp.numberOfPoles,
poles = resp.poles)
elif _is_iir_response(resp):
if resp.type != "A" and resp.type != "B":
raise SEEDError("invalid IIR response type of " + resp.name)
b1 = _Blockette54(type = resp.type,
input_units = input_units,
output_units = self.__unit_dict.lookup("V"),
n_numerators = resp.numberOfNumerators,
numerators = resp.numerators,
n_denominators = resp.numberOfDenominators,
denominators = resp.denominators)
self.__blk54.append(b1)
elif _is_poly_response(resp):
b1 = _Blockette62(input_units = input_units,
output_units = self.__unit_dict.lookup("V"),
freq_unit = resp.frequencyUnit,
low_freq = sensor.lowFrequency,
high_freq = sensor.highFrequency,
approx_type = resp.approximationType,
approx_lower_bound = resp.approximationLowerBound,
approx_upper_bound = resp.approximationUpperBound,
approx_error = resp.approximationError,
ncoeff = resp.numberOfCoefficients,
coeff = resp.coefficients)
elif _is_fap_response(resp):
b1 = _Blockette55(input_units = input_units,
output_units = self.__unit_dict.lookup("V"),
ntuples = resp.numberOfTuples,
tuples = resp.tuples)
else:
raise SEEDError("unknown response type of sensor " + sensor.name)
try:
calib = sensor.calibration[dev_id][compn]
except KeyError:
calib = None
if calib is not None and len(calib) > 0:
calib_list = list(calib.items())
calib_list.sort()
gain = calib_list[-1][1].gain
else:
calib_list = []
gain = resp.gain
dev_id = None
compn = None
if gain == 0.0 or gain is None or resp.gainFrequency is None:
return (b1, None, 1.0, 0.0)
b2 = _Blockette58(gain = gain,
gain_freq = resp.gainFrequency) #,
#calib_list = calib_list)
return (b1, b2, gain, resp.gainFrequency)
def _lookup_analogue_paz(self, name):
resp_paz = self.__inventory.object.get(name)
if resp_paz is None:
raise SEEDError("unknown PAZ response: " + name)
if resp_paz.type != "A" and resp_paz.type != "B":
raise SEEDError("invalid PAZ response type of " + resp_paz.name)
b1 = _Blockette53(type = resp_paz.type,
input_units = self.__unit_dict.lookup("V"),
output_units = self.__unit_dict.lookup("V"),
norm_fac = resp_paz.normalizationFactor,
norm_freq = resp_paz.normalizationFrequency,
nzeros = resp_paz.numberOfZeros,
zeros = resp_paz.zeros,
npoles = resp_paz.numberOfPoles,
poles = resp_paz.poles)
b2 = _Blockette58(gain = resp_paz.gain,
gain_freq = resp_paz.gainFrequency)
return (b1, b2, resp_paz.gain)
def _lookup_analogue_iir(self, name):
resp_iir = self.__inventory.object.get(name)
if resp_iir is None:
raise SEEDError("unknown IIR response: " + name)
if resp_iir.type != "A" and resp_iir.type != "B":
raise SEEDError("invalid IIR response type of " + resp_iir.name)
b1 = _Blockette54(type = resp_iir.type,
input_units = self.__unit_dict.lookup("V"),
output_units = self.__unit_dict.lookup("V"),
n_numerators = resp_iir.numberOfNumerators,
numerators = resp_iir.numerators,
n_denominators = resp_iir.numberOfDenominators,
denominators = resp_iir.denominators)
b2 = _Blockette58(gain = resp_iir.gain,
gain_freq = resp_iir.gainFrequency)
return (b1, b2, resp_iir.gain)
def _lookup_analogue_fap(self, name):
resp_fap = self.__inventory.object.get(name)
if resp_fap is None:
raise SEEDError("unknown FAP response: " + name)
gain = resp_fap.gain
if gain is None: gain = 1.0
b1 = _Blockette55(input_units = self.__unit_dict.lookup("V"),
output_units = self.__unit_dict.lookup("V"),
ntuples = resp_fap.numberOfTuples, tuples = resp_fap.tuples)
b2 = _Blockette58(gain = gain,
gain_freq = resp_fap.gainFrequency)
return (b1, b2, gain)
def _lookup_digitizer(self, name, dev_id, compn, sample_rate, sample_rate_div):
digi = self.__inventory.object.get(name)
if digi is None:
raise SEEDError("unknown datalogger: " + name)
input_rate = float(sample_rate) / float(sample_rate_div)
try:
stream_deci = digi.decimation[sample_rate][sample_rate_div]
if stream_deci.digitalFilterChain and \
len(stream_deci.digitalFilterChain) > 0:
for f in stream_deci.digitalFilterChain.split():
obj = self.__inventory.object[f]
input_rate *= (obj.decimationFactor or 1)
except KeyError:
pass
b1 = _Blockette54(type = "D",
input_units = self.__unit_dict.lookup("V"),
output_units = self.__unit_dict.lookup("COUNTS"))
b2 = _Blockette57(input_rate = input_rate,
deci_fac = 1,
deci_offset = 0,
delay = 0,
correction = 0)
try:
calib = digi.calibration[dev_id][compn]
except KeyError:
calib = None
if calib is not None and len(calib) > 0:
calib_list = list(calib.items())
calib_list.sort()
gain = calib_list[-1][1].gain
else:
calib_list = []
gain = digi.gain
dev_id = None
compn = None
b3 = _Blockette58(gain = gain,
gain_freq = 0) #,
#calib_list = calib_list)
return (b1, b2, b3, input_rate, gain)
def _lookup_digital_paz(self, name, input_rate):
resp_paz = self.__inventory.object.get(name)
if resp_paz is None:
raise SEEDError("unknown PAZ response: " + name)
if resp_paz.type != "D":
raise SEEDError("invalid PAZ response type of " + resp_paz.name)
deci_fac = resp_paz.decimationFactor or 1
delay = (resp_paz.delay or 0.0) / input_rate
correction = (resp_paz.correction or 0.0) / input_rate
gain = resp_paz.gain or 1.0
gain_freq = resp_paz.gainFrequency or 0.0
b1 = _Blockette53(type = "D",
input_units = self.__unit_dict.lookup("COUNTS"),
output_units = self.__unit_dict.lookup("COUNTS"),
norm_fac = resp_paz.normalizationFactor,
norm_freq = resp_paz.normalizationFrequency,
nzeros = resp_paz.numberOfZeros,
zeros = resp_paz.zeros,
npoles = resp_paz.numberOfPoles,
poles = resp_paz.poles)
b2 = _Blockette57(input_rate = input_rate,
deci_fac = deci_fac,
deci_offset = 0,
delay = delay,
correction = correction)
b3 = _Blockette58(gain = gain,
gain_freq = gain_freq)
return (b1, b2, b3, input_rate / deci_fac, resp_paz.gain)
def _lookup_digital_iir(self, name, input_rate):
resp_iir = self.__inventory.object.get(name)
if resp_iir is None:
raise SEEDError("unknown IIR response: " + name)
if resp_iir.type != "D":
raise SEEDError("invalid IIR response type of " + resp_iir.name)
deci_fac = resp_iir.decimationFactor or 1
delay = (resp_iir.delay or 0.0) / input_rate
correction = (resp_iir.correction or 0.0) / input_rate
gain = resp_iir.gain or 1.0
gain_freq = resp_iir.gainFrequency or 0.0
b1 = _Blockette54(type = "D",
input_units = self.__unit_dict.lookup("COUNTS"),
output_units = self.__unit_dict.lookup("COUNTS"),
n_numerators = resp_iir.numberOfNumerators,
numerators = resp_iir.numerators,
n_denominators = resp_iir.numberOfDenominators,
denominators = resp_iir.denominators)
b2 = _Blockette57(input_rate = input_rate,
deci_fac = deci_fac,
deci_offset = 0,
delay = delay,
correction = correction)
b3 = _Blockette58(gain = gain,
gain_freq = gain_freq)
return (b1, b2, b3, input_rate / deci_fac, resp_iir.gain)
def _lookup_fir(self, name, input_rate):
resp_fir = self.__inventory.object.get(name)
if resp_fir is None:
raise SEEDError("unknown FIR response: " + name)
deci_fac = resp_fir.decimationFactor or 1
delay = (resp_fir.delay or 0.0) / input_rate
correction = (resp_fir.correction or 0.0) / input_rate
gain = resp_fir.gain or 1.0
gain_freq = resp_fir.gainFrequency or 0.0
b1 = _Blockette61(name = "RF" + name,
symmetry = resp_fir.symmetry,
input_units = self.__unit_dict.lookup("COUNTS"),
output_units = self.__unit_dict.lookup("COUNTS"),
ncoeff = resp_fir.numberOfCoefficients,
coeff = resp_fir.coefficients)
b2 = _Blockette57(input_rate = input_rate,
deci_fac = deci_fac,
deci_offset = 0,
delay = delay,
correction = correction)
b3 = _Blockette58(gain = gain,
gain_freq = gain_freq)
return (b1, b2, b3, input_rate / deci_fac, resp_fir.gain)
def output(self, f):
pass
class _Channel(object):
def __init__(self, inventory, strmcfg, format_dict, unit_dict,
comment_dict, gen_dict, resp_container):
loccfg = strmcfg.mySensorLocation
statcfg = loccfg.myStation
netcfg = statcfg.myNetwork
self.__id = (loccfg.code, strmcfg.code, strmcfg.start)
self.__resp_container = resp_container
self.__comment_dict = comment_dict
self.__comment_blk = []
sensor = inventory.object.get(strmcfg.sensor)
if sensor is None:
raise SEEDError("unknown sensor: " + strmcfg.sensor)
resp = inventory.object.get(sensor.response)
if resp is None:
raise SEEDError("cannot find response for sensor " + sensor.name)
digi = inventory.object.get(strmcfg.datalogger)
#if digi is None:
# raise SEEDError("unknown datalogger referenced in channel %s: %s" % (strmcfg.code, strmcfg.datalogger))
stream_deci = None
if digi:
try:
stream_deci = digi.decimation[strmcfg.sampleRateNumerator][strmcfg.sampleRateDenominator]
except KeyError:
raise SEEDError("cannot find filter chain for stream " + \
str(strmcfg.sampleRateNumerator) + "/" + \
str(strmcfg.sampleRateDenominator) + " of datalogger " + \
digi.name)
unit = None
try:
unit = sensor.unit
except AttributeError:
pass
if unit:
signal_units = unit_dict.lookup(unit, sensor.remark)
elif _is_paz_response(resp) and resp.numberOfZeros == 0:
signal_units = unit_dict.lookup("M/S**2")
else:
signal_units = unit_dict.lookup("M/S")
if strmcfg.sampleRateNumerator == 0 or \
strmcfg.sampleRateDenominator == 0:
raise SEEDError("invalid sample rate %d/%d" % \
(strmcfg.sampleRateNumerator, strmcfg.sampleRateDenominator))
sample_rate = float(strmcfg.sampleRateNumerator) / \
float(strmcfg.sampleRateDenominator)
clock_drift = float(0)
if digi:
if digi.maxClockDrift is not None:
clock_drift = digi.maxClockDrift / sample_rate
self.__chan_blk = _Blockette52(loc_id = loccfg.code,
chan_id = strmcfg.code,
instr_id = gen_dict.lookup_sensor(strmcfg.sensor),
comment = "",
signal_units = signal_units,
calibration_units = 0,
latitude = loccfg.latitude,
longitude = loccfg.longitude,
elevation = loccfg.elevation,
local_depth = strmcfg.depth,
azimuth = strmcfg.azimuth,
dip = strmcfg.dip,
data_format = format_dict.lookup(strmcfg.format),
record_length = 12,
sample_rate = sample_rate,
clock_drift = clock_drift,
flags = strmcfg.flags,
start_date = strmcfg.start,
end_date = strmcfg.end)
(sens, sens_freq) = resp_container.add_sensor(strmcfg.sensor,
strmcfg.sensorSerialNumber, strmcfg.sensorChannel)
if stream_deci:
if stream_deci.analogueFilterChain:
if len(stream_deci.analogueFilterChain) > 0:
for f in stream_deci.analogueFilterChain.split():
obj = inventory.object[f]
if _is_paz_response(obj):
gain = resp_container.add_analogue_paz(f)
sens *= gain
elif _is_iir_response(obj):
gain = resp_container.add_analogue_iir(f)
sens *= gain
elif _is_fap_response(obj):
gain = resp_container.add_analogue_fap(f)
sens *= gain
else:
raise SEEDError("invalid filter type: %s (%s)" % (f, obj.name))
(rate, gain) = resp_container.add_digitizer(strmcfg.datalogger,
strmcfg.dataloggerSerialNumber, strmcfg.dataloggerChannel,
strmcfg.sampleRateNumerator, strmcfg.sampleRateDenominator)
if gain is not None:
sens *= gain
if stream_deci.digitalFilterChain:
if len(stream_deci.digitalFilterChain) > 0:
for f in stream_deci.digitalFilterChain.split():
obj = inventory.object[f]
if _is_paz_response(obj):
(rate, gain) = resp_container.add_digital_paz(f, rate)
elif _is_iir_response(obj):
(rate, gain) = resp_container.add_digital_iir(f, rate)
elif _is_fir_response(obj):
(rate, gain) = resp_container.add_fir(f, rate)
else:
raise SEEDError("invalid filter type: %s (%s)" % (f, obj.name))
sens *= gain
if sens_freq > rate / 5:
sens_freq = rate / 5
#if sample_rate != rate:
# print digi.name, netcfg.code, statcfg.code, strmcfg.code, "expected sample rate", sample_rate, "actual", rate
if strmcfg.gain is None:
strmcfg.gain = sens
if strmcfg.gainFrequency is None:
strmcfg.gainFrequency = sens_freq
if _is_poly_response(resp):
self.__stage0_blk = _Blockette62(input_units = signal_units,
output_units = unit_dict.lookup("COUNTS"),
freq_unit = resp.frequencyUnit,
low_freq = sensor.lowFrequency,
high_freq = sensor.highFrequency,
approx_type = resp.approximationType,
approx_lower_bound = resp.approximationLowerBound,
approx_upper_bound = resp.approximationUpperBound,
approx_error = resp.approximationError,
ncoeff = resp.numberOfCoefficients,
coeff = resp.coefficients,
gain = sens)
else:
self.__stage0_blk = _Blockette58(gain = strmcfg.gain,
gain_freq = strmcfg.gainFrequency)
def add_comment(self, com):
self.__comment_blk.append(_Blockette59(start_time = com.start,
end_time = com.end,
comment_key = self.__comment_dict.lookup('C', com.text)))
def output(self, f, vol_start, vol_end):
self.__chan_blk.set_vol_span(vol_start, vol_end)
self.__chan_blk.output(f)
self.__resp_container.output(f)
self.__stage0_blk.output(f)
for b in self.__comment_blk:
b.output(f)
class _Station(object):
def __init__(self, inventory, statcfg, format_dict, unit_dict,
comment_dict, gen_dict, resp_fac):
self.__inventory = inventory
self.__statcfg = statcfg
self.__format_dict = format_dict
self.__unit_dict = unit_dict
self.__comment_dict = comment_dict
self.__gen_dict = gen_dict
self.__resp_fac = resp_fac
self.__recno = 0
self.__id = (statcfg.myNetwork.code, statcfg.myNetwork.start,
statcfg.code, statcfg.start)
self.__channel = {}
self.__comment_blk = []
site_name = statcfg.description
if not site_name:
if statcfg.place and statcfg.country:
site_name = statcfg.place + ", " + statcfg.country
else:
site_name = statcfg.code
self.__stat_blk = _Blockette50(stat_code = statcfg.code,
latitude = statcfg.latitude,
longitude = statcfg.longitude,
elevation = statcfg.elevation,
site_name = site_name,
net_id = gen_dict.lookup_network(statcfg.myNetwork.code,
statcfg.myNetwork.start),
net_code = statcfg.myNetwork.code,
start_date = statcfg.start,
end_date = statcfg.end)
for com in statcfg.comment.values():
self.__comment_blk.append(_Blockette51(start_time = com.start,
end_time = com.end,
comment_key = self.__comment_dict.lookup('S', com.text)))
def add_chan(self, strmcfg):
loccfg = strmcfg.mySensorLocation
if (loccfg.code, strmcfg.code, strmcfg.start) in self.__channel:
return
cha = _Channel(self.__inventory, strmcfg, self.__format_dict,
self.__unit_dict, self.__comment_dict, self.__gen_dict,
self.__resp_fac.new_response())
self.__channel[(loccfg.code, strmcfg.code, strmcfg.start)] = cha
for com in strmcfg.comment.values():
cha.add_comment(com)
def get_id(self):
return self.__id
def get_recno(self):
return self.__recno
def output(self, f, vol_start, vol_end):
self.__recno = f.get_recno()
self.__stat_blk.output(f)
for b in self.__comment_blk:
b.output(f)
chan_list = [self.__channel[k] for k in sorted(self.__channel.keys())]
for c in chan_list:
c.output(f, vol_start, vol_end)
f.flush()
class _TimeSeries(object):
def __init__(self, span, net_code, stat_code, loc_id, chan_id,
start_time, end_time, recno):
self.__span = span
self.__net_code = net_code
self.__stat_code = stat_code
self.__loc_id = loc_id
self.__chan_id = chan_id
self.__start_time = start_time
self.__start_recno = recno
self.__end_time = end_time
self.__end_recno = recno
def extend(self, start_time, end_time, recno):
if start_time < self.__start_time:
self.__start_time = start_time
if end_time > self.__end_time:
self.__end_time = end_time
self.__end_recno = recno
self.__span.extend(start_time, end_time)
def get_series_data(self):
return (self.__net_code, self.__stat_code, self.__loc_id,
self.__chan_id, self.__start_time, self.__end_time)
def output(self, f, data_start):
b = _Blockette74(self.__net_code, self.__stat_code, self.__loc_id,
self.__chan_id, self.__start_time, self.__start_recno + data_start,
self.__end_time, self.__end_recno + data_start)
b.output(f)
class _Timespan(object):
def __init__(self):
self.__start_time = None
self.__end_time = None
self.__recno = 0
self.__series = []
def new_time_series(self, net, sta, loc, cha, start_time, end_time, recno):
if len(self.__series) == 0:
self.__start_time = start_time
self.__end_time = end_time
else:
self.extend(start_time, end_time)
ts = _TimeSeries(self, net, sta, loc, cha, start_time, end_time, recno)
self.__series.append(ts)
return ts
def overlap(self, start_time, end_time):
return self.__start_time - _min_ts_gap <= start_time <= self.__end_time + _min_ts_gap or \
self.__start_time - _min_ts_gap <= end_time <= self.__end_time + _min_ts_gap
def extend(self, start_time, end_time):
if start_time < self.__start_time:
self.__start_time = start_time
if end_time > self.__end_time:
self.__end_time = end_time
def get_span_data(self):
return (self.__start_time, self.__end_time, self.__recno)
def get_series_data(self):
return [ s.get_series_data() for s in self.__series ]
def output_index(self, f, data_start):
self.__recno = f.get_recno()
b = _Blockette70("P", self.__start_time, self.__end_time)
b.output(f)
for s in self.__series:
s.output(f, data_start)
f.flush()
class _WaveformData(object):
def __init__(self):
self.__fd = TemporaryFile()
self.__recno = 0
self.__cur_rec = None
self.__cur_series = None
self.__span = []
def __get_time_series(self, rec):
for s in self.__span:
if s.overlap(rec.begin_time, rec.end_time):
break
else:
s = _Timespan()
self.__span.append(s)
return s.new_time_series(rec.net, rec.sta, rec.loc, rec.cha,
rec.begin_time, rec.end_time, self.__recno)
def add_data(self, rec):
if self.__cur_rec is None:
self.__cur_rec = rec
self.__cur_series = self.__get_time_series(rec)
#if rec.encoding != 10 and rec.encoding != 11:
# logs.warning("%s %s %s %s cannot merge records with encoding %d" % \
# (rec.net, rec.sta, rec.loc, rec.cha, rec.encoding))
return
if self.__cur_rec.net == rec.net and self.__cur_rec.sta == rec.sta and \
self.__cur_rec.loc == rec.loc and self.__cur_rec.cha == rec.cha:
contiguous = True
if rec.encoding == 10 or rec.encoding == 11:
if abs(rec.begin_time - self.__cur_rec.end_time) > _min_data_gap(rec.fsamp):
contiguous = False
if rec.X_minus1 is None:
logs.debug("%s %s %s %s X[-1] not defined" %
(rec.net, rec.sta, rec.loc, rec.cha))
contiguous = False
else:
contiguous = False
if self.__cur_rec.fsamp != rec.fsamp:
logs.debug("%s %s %s %s sample rate changed from %f to %f" %
(rec.net, rec.sta, rec.loc, rec.cha, self.__cur_rec.fsamp,
rec.fsamp))
contiguous = False
if self.__cur_rec.encoding != rec.encoding:
logs.debug("%s %s %s %s encoding changed from %d to %d" %
(rec.net, rec.sta, rec.loc, rec.cha, self.__cur_rec.encoding,
rec.encoding))
contiguous = False
if contiguous and self.__cur_rec.Xn != rec.X_minus1:
logs.debug("%s %s %s %s non-contiguous data: %d != %d" %
(rec.net, rec.sta, rec.loc, rec.cha, self.__cur_rec.Xn,
rec.X_minus1))
contiguous = False
if contiguous and self.__cur_rec.size + rec.nframes * 64 <= (1 << _RECLEN_EXP):
self.__cur_rec.merge(rec)
else:
self.__recno += 1
if abs(rec.begin_time - self.__cur_rec.end_time) <= _min_ts_gap:
self.__cur_series.extend(rec.begin_time, rec.end_time,
self.__recno)
else:
self.__cur_series = self.__get_time_series(rec)
self.__cur_rec.write(self.__fd, _RECLEN_EXP)
self.__cur_rec = rec
else:
self.__recno += 1
self.__cur_series = self.__get_time_series(rec)
self.__cur_rec.write(self.__fd, _RECLEN_EXP)
self.__cur_rec = rec
def get_series_data(self):
return sum([ s.get_series_data() for s in self.__span ], [])
def output_vol(self, f):
b = _Blockette12()
for s in self.__span:
b.add_span(*s.get_span_data())
b.output(f)
def output_index(self, f, data_start):
for s in self.__span:
s.output_index(f, data_start)
def output_data(self, fd, data_start):
if self.__cur_rec is not None:
self.__cur_rec.write(self.__fd, _RECLEN_EXP)
self.__cur_rec = None
self.__cur_series = None
self.__fd.seek(0)
#copyfileobj(self.__fd, fd)
i = 0
for rec in mseed.Input(self.__fd):
rec.recno = data_start + i
rec.write(fd, _RECLEN_EXP)
i += 1
self.__fd.close()
class _RecordBuilder(object):
def __init__(self, type, fd):
self.__recno = 1
self.__type = type
self.__fd = fd
self.__buf = None
def flush(self):
if self.__buf is not None:
self.__buf += ((1 << _RECLEN_EXP) - len(self.__buf)) * " "
self.__fd.write(self.__buf.encode("ascii", "replace"))
self.__buf = None
def reset(self, type, fd, recno = None):
self.flush()
self.__type = type
self.__fd = fd
if recno is not None:
self.__recno = recno
def get_recno(self):
return self.__recno
def write_blk(self, s):
if self.__buf is None:
self.__buf = "%06d%c " % (self.__recno, self.__type)
self.__recno += 1
b = 0
while len(s) - b > (1 << _RECLEN_EXP) - len(self.__buf):
e = b + (1 << _RECLEN_EXP) - len(self.__buf)
self.__buf += s[b:e]
self.__fd.write(self.__buf.encode("ascii", "replace"))
self.__buf = "%06d%c*" % (self.__recno, self.__type)
self.__recno += 1
b = e
self.__buf += s[b:]
if len(self.__buf) > (1 << _RECLEN_EXP) - 8:
self.flush()
class SEEDVolume(object):
def __init__(self, inventory, organization, label, resp_dict=True):
self.__inventory = inventory
self.__organization = organization
self.__label = label or ""
self.__vol_start_time = datetime.datetime(2100,1,1,0,0,0)
self.__vol_end_time = datetime.datetime(1971,1,1,0,0,0)
self.__format_dict = _FormatDict()
self.__unit_dict = _UnitDict()
self.__comment_dict = _CommentDict()
self.__gen_dict = _GenericAbbreviationDict(inventory)
self.__station = {}
self.__waveform_data = None
if resp_dict:
self.__resp_fac = _Response4xFactory(inventory, self.__unit_dict)
else:
self.__resp_fac = _Response5xFactory(inventory, self.__unit_dict)
def add_chan(self, net_code, stat_code, loc_id, chan_id, start_time, end_time, strict=False):
found = False
net_tp = self.__inventory.network.get(net_code)
if net_tp is not None:
for netcfg in net_tp.values():
# if _cmptime(start_time, netcfg.end) <= 0 and \
# _cmptime(end_time, netcfg.start) >= 0:
sta_tp = netcfg.station.get(stat_code)
if sta_tp is not None:
for statcfg in sta_tp.values():
# if _cmptime(start_time, statcfg.end) <= 0 and \
# _cmptime(end_time, statcfg.start) >= 0:
sta = self.__station.get((net_code, netcfg.start, stat_code, statcfg.start))
if sta is None:
sta = _Station(self.__inventory, statcfg, self.__format_dict,
self.__unit_dict, self.__comment_dict, self.__gen_dict,
self.__resp_fac)
self.__station[(net_code, netcfg.start, stat_code, statcfg.start)] = sta
loc_tp = statcfg.sensorLocation.get(loc_id)
if loc_tp is not None:
for loccfg in loc_tp.values():
# if _cmptime(start_time, strmcfg.end) <= 0 and \
# _cmptime(end_time, strmcfg.start) >= 0:
strm_tp = loccfg.stream.get(chan_id)
if strm_tp is not None:
for strmcfg in strm_tp.values():
if _cmptime(start_time, strmcfg.end) <= 0 and \
_cmptime(end_time, strmcfg.start) >= 0:
if _cmptime(start_time, self.__vol_start_time) < 0:
self.__vol_start_time = start_time
if _cmptime(end_time, self.__vol_end_time) > 0:
self.__vol_end_time = end_time
sta.add_chan(strmcfg)
found = True
if not found:
if strict:
raise SEEDError("cannot find %s %s %s %s %s %s" % \
(net_code, stat_code, loc_id, chan_id, start_time, end_time))
else:
logs.warning("cannot find %s %s %s %s %s %s" %
(net_code, stat_code, loc_id, chan_id, start_time, end_time))
def add_data(self, rec):
if self.__waveform_data is None:
self.__waveform_data = _WaveformData()
self.__waveform_data.add_data(rec)
def __output_vol(self, vol_creat_time, sta_list, rb):
b1 = _Blockette10(record_length = _RECLEN_EXP,
start_time = self.__vol_start_time,
end_time = self.__vol_end_time,
vol_time = vol_creat_time,
organization = self.__organization,
label = self.__label)
b2 = _Blockette11()
for sta in sta_list:
(net_code, net_start, stat_code, stat_start) = sta.get_id()
b2.add_station(stat_code, sta.get_recno())
b1.output(rb)
b2.output(rb)
if self.__waveform_data is not None:
self.__waveform_data.output_vol(rb)
rb.flush()
def output(self, dest, strict=False):
vol_creat_time = datetime.datetime.utcnow()
if self.__waveform_data is not None:
for (net_code, stat_code, loc_id, chan_id, start_time, end_time) in \
self.__waveform_data.get_series_data():
try:
self.add_chan(net_code, stat_code, loc_id, chan_id, start_time, end_time, strict)
except SEEDError as e:
if strict:
raise SEEDError("%s.%s.%s.%s.%s: %s" % \
(net_code, stat_code, loc_id, chan_id, start_time.isoformat(), e))
logs.warning("%s.%s.%s.%s.%s: %s" %
(net_code, stat_code, loc_id, chan_id, start_time.isoformat(), e))
sta_list = [self.__station[k] for k in sorted(self.__station.keys())]
if isinstance(dest, str):
fd = file(dest, "wb")
elif hasattr(dest, "write"):
fd = dest
else:
raise TypeError("invalid file object")
try:
filename = fd.name
except AttributeError:
filename = '<???>'
rb = _RecordBuilder("V", fd)
self.__output_vol(vol_creat_time, sta_list, rb)
rb.reset("A", fd)
self.__format_dict.output(rb)
self.__comment_dict.output(rb)
self.__gen_dict.output(rb)
self.__unit_dict.output(rb)
self.__resp_fac.output(rb)
rb.flush()
rb.reset("S", fd)
for sta in sta_list:
sta.output(rb, self.__vol_start_time, self.__vol_end_time)
if self.__waveform_data is not None:
index_start = rb.get_recno()
rb.reset("T", fd)
self.__waveform_data.output_index(rb, 0)
data_start = rb.get_recno()
rb.reset("T", fd, index_start)
fd.seek((1 << _RECLEN_EXP) * (index_start - 1), 0)
self.__waveform_data.output_index(rb, data_start)
self.__waveform_data.output_data(fd, data_start)
fd.seek(0, 0)
rb.reset("V", fd, 1)
self.__output_vol(vol_creat_time, sta_list, rb)
if isinstance(dest, str):
fd.close()