Coverage for /usr/local/lib/python3.11/dist-packages/pyrocko/io/css.py: 67%
76 statements
« prev ^ index » next coverage.py v6.5.0, created at 2024-03-07 11:54 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2024-03-07 11:54 +0000
1# http://pyrocko.org - GPLv3
2#
3# The Pyrocko Developers, 21st Century
4# ---|P------/S----------~Lg----------
6'''
7Reader of CSS wfdisc waveform data.
9See http://nappe.wustl.edu/antelope/css-formats/wfdisc.htm for file format
10reference.
11'''
13import os
14import numpy as num
15import logging
16from struct import unpack
17from pyrocko import trace, util
19logger = logging.getLogger('pyrocko.io.css')
22storage_types = {
23 's4': ('>%ii'),
24 'i4': ('<%ii'),
25}
27template = [
28 ('sta', str, (0, 6), 'station code'),
29 ('chan', str, (7, 15), 'channel code'),
30 ('time', float, (16, 33), 'epoch time of first sample in file'),
31 ('wfid', int, (34, 43), 'waveform identifier'),
32 ('chanid', int, (44, 52), 'channel identifier'),
33 ('jdate', int, (53, 61), 'julian date'),
34 ('endtime', float, (62, 79), 'time +(nsamp -1 )/samles'),
35 ('nsamp', int, (80, 88), 'number of samples'),
36 ('samprate', float, (89, 100), 'sampling rate in samples/sec'),
37 ('calib', float, (101, 117), 'nominal calibration'),
38 ('calper', float, (118, 134), 'nominal calibration period'),
39 ('instype', str, (135, 141), 'instrument code'),
40 ('segtype', str, (142, 143), 'indexing method'),
41 ('datatype', str, (144, 146), 'numeric storage'),
42 ('clip', str, (147, 148), 'clipped flag'),
43 ('dir', str, (149, 213), 'directory'),
44 ('dfile', str, (214, 246), 'data file'),
45 ('foff', int, (247, 257), 'byte offset of data segment within file'),
46 ('commid', int, (258, 267), 'comment identifier'),
47 ('Iddate', util.stt, (268, 287), 'load date')
48]
51class CSSWfError(Exception):
52 def __init__(self, **kwargs):
53 f2str = {
54 str: 'string',
55 int: 'integer',
56 float: 'float',
57 util.stt: 'time'
58 }
59 kwargs['convert'] = f2str[kwargs['convert']]
60 error_str = 'Successfully parsed this:\n'
61 for k, v in kwargs['d'].items():
62 error_str += '%s: %s\n' % (k, v)
64 error_str += '\nFailed to parse the marked section:'
66 istart = kwargs['istart']
67 istop = kwargs['istop']
68 npad = 12
69 error_mark = ' ' * npad
70 error_mark += '^' * (istop - istart)
71 error_str += '\n%s\n%s\n' % (kwargs['data'][istart-npad: istop+npad],
72 error_mark)
73 error_str += 'Expected {desc} (format: {convert})\n'.format(**kwargs)
74 error_str += \
75 'checkout http://nappe.wustl.edu/antelope/css-formats/wfdisc.htm'
76 Exception.__init__(self, error_str)
77 self.error_arguments = kwargs
80class CSSHeaderFile(object):
81 '''
82 CSS Header File
84 :param filename: filename of css header file
86 Note, that all binary data files to which the underlying header file points
87 to will be loaded at once. It is therefore recommended to split header
88 files for large data sets
89 '''
90 def __init__(self, filename):
92 self.fn = filename
93 self.data = []
94 self.read()
96 def read_wf_file(self, fn, nbytes, dtype, foff=0):
97 '''
98 Read binary waveform file
100 :param fn: filename
101 :param nbytes: number of bytes to be read
102 :param dtype: datatype string
103 '''
104 with open(fn, 'rb') as f:
105 fmt = dtype % nbytes
106 f.seek(foff)
107 try:
108 data = num.array(unpack(fmt, f.read(nbytes * 4)),
109 dtype=num.int32)
110 except Exception:
111 logger.exception('Error while unpacking %s' % fn)
112 return
113 return data
115 def read(self):
116 '''
117 Read header file.
118 '''
119 with open(self.fn, 'rb') as f:
120 lines = f.readlines()
121 for iline, line in enumerate(lines):
122 line = str(line.decode('ascii'))
123 d = {}
124 for (ident, convert, (istart, istop), desc) in template:
125 try:
126 d[ident] = convert(line[istart: istop].strip())
127 except Exception:
128 raise CSSWfError(iline=iline+1, data=line,
129 ident=ident, convert=convert,
130 istart=istart+1, istop=istop+1,
131 desc=desc, d=d)
133 fn = os.path.join(self.superdir, d['dir'], d['dfile'])
134 if os.path.isfile(fn):
135 self.data.append(d)
136 else:
137 logger.error(
138 'no such file: %s (see header file: %s, line %s)' % (
139 fn, self.fn, iline+1))
141 @property
142 def superdir(self):
143 return os.path.dirname(self.fn)
145 def iter_pyrocko_traces(self, load_data=True):
146 for idata, d in enumerate(self.data):
147 fn = os.path.join(d['dir'], d['dfile'])
148 logger.debug('converting %s', d['dfile'])
149 try:
150 if load_data:
151 ydata = self.read_wf_file(
152 os.path.join(self.superdir, fn), d['nsamp'],
153 storage_types[d['datatype']],
154 d['foff'])
155 else:
156 ydata = None
158 except IOError as e:
159 if e.errno == 2:
160 logger.debug(e)
161 continue
162 else:
163 raise e
164 dt = 1./d['samprate']
165 yield trace.Trace(station=d['sta'],
166 channel=d['chan'],
167 deltat=dt,
168 tmin=d['time'],
169 tmax=d['time'] + d['nsamp']/d['samprate'],
170 ydata=ydata)
173def iload(file_name, load_data, **kwargs):
174 '''
175 Iteratively load traces from file.
177 :param file_name: css header file name
178 :param load_data: whether or not to load binary data
179 '''
180 wfdisc = CSSHeaderFile(file_name)
181 for pyrocko_trace in wfdisc.iter_pyrocko_traces(load_data=load_data):
182 yield pyrocko_trace