1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

# http://pyrocko.org - GPLv3 

# 

# The Pyrocko Developers, 21st Century 

# ---|P------/S----------~Lg---------- 

from __future__ import division, absolute_import 

 

import os 

import logging 

from pyrocko import util, trace 

 

from . import (mseed, sac, kan, segy, yaff, seisan_waveform, gse1, gcf, 

datacube, suds, css, gse2) 

from .io_common import FileLoadError, FileSaveError 

 

import numpy as num 

 

try: 

from . import tdms_idas 

except ImportError: 

from . import mock_module as tdms_idas 

 

 

__doc__ = util.parse_md(__file__) 

 

logger = logging.getLogger('pyrocko.io') 

 

 

def allowed_formats(operation, use=None, default=None): 

if operation == 'load': 

lst = ['detect', 'from_extension', 'mseed', 'sac', 'segy', 'seisan', 

'seisan.l', 'seisan.b', 'kan', 'yaff', 'gse1', 'gse2', 'gcf', 

'datacube', 'suds', 'css', 'tdms'] 

 

elif operation == 'save': 

lst = ['mseed', 'sac', 'text', 'yaff', 'gse2'] 

 

if use == 'doc': 

return ', '.join("``'%s'``" % fmt for fmt in lst) 

 

elif use == 'cli_help': 

return ', '.join(fmt + ['', ' [default]'][fmt == default] 

for fmt in lst) 

 

else: 

return lst 

 

 

def load(filename, format='mseed', getdata=True, substitutions=None): 

'''Load traces from file. 

 

:param format: format of the file (%s) 

:param getdata: if ``True`` (the default), read data, otherwise only read 

traces metadata 

:param substitutions: dict with substitutions to be applied to the traces 

metadata 

 

:returns: list of loaded traces 

 

When *format* is set to ``'detect'``, the file type is guessed from the 

first 512 bytes of the file. Only Mini-SEED, SAC, GSE1, and YAFF format are 

detected. When *format* is set to ``'from_extension'``, the filename 

extension is used to decide what format should be assumed. The filename 

extensions considered are (matching is case insensitive): ``'.sac'``, 

``'.kan'``, ``'.sgy'``, ``'.segy'``, ``'.yaff'``, everything else is 

assumed to be in Mini-SEED format. 

 

This function calls :py:func:`iload` and aggregates the loaded traces in a 

list. 

''' 

 

return list(iload( 

filename, format=format, getdata=getdata, substitutions=substitutions)) 

 

 

load.__doc__ %= allowed_formats('load', 'doc') 

 

 

def detect_format(filename): 

try: 

f = open(filename, 'rb') 

data = f.read(512) 

except OSError as e: 

raise FileLoadError(e) 

finally: 

f.close() 

 

formats = [ 

(yaff, 'yaff'), 

(mseed, 'mseed'), 

(sac, 'sac'), 

(gse1, 'gse1'), 

(gse2, 'gse2'), 

(datacube, 'datacube'), 

(suds, 'suds'), 

(tdms_idas, 'tdms')] 

 

for mod, fmt in formats: 

if mod.detect(data): 

return fmt 

 

raise FileLoadError(UnknownFormat(filename)) 

 

 

def iload(filename, format='mseed', getdata=True, substitutions=None): 

'''Load traces from file (iterator version). 

 

This function works like :py:func:`load`, but returns an iterator which 

yields the loaded traces. 

''' 

load_data = getdata 

 

toks = format.split('.', 1) 

if len(toks) == 2: 

format, subformat = toks 

else: 

subformat = None 

 

try: 

mtime = os.stat(filename)[8] 

except OSError as e: 

raise FileLoadError(e) 

 

def subs(tr): 

make_substitutions(tr, substitutions) 

tr.set_mtime(mtime) 

return tr 

 

extension_to_format = { 

'.yaff': 'yaff', 

'.sac': 'sac', 

'.kan': 'kan', 

'.segy': 'segy', 

'.sgy': 'segy', 

'.gse': 'gse2', 

'.wfdisc': 'css', 

'.tdms': 'tdms' 

} 

 

if format == 'from_extension': 

format = 'mseed' 

extension = os.path.splitext(filename)[1] 

format = extension_to_format.get(extension.lower(), 'mseed') 

 

if format == 'detect': 

format = detect_format(filename) 

 

format_to_module = { 

'kan': kan, 

'segy': segy, 

'yaff': yaff, 

'sac': sac, 

'mseed': mseed, 

'seisan': seisan_waveform, 

'gse1': gse1, 

'gse2': gse2, 

'gcf': gcf, 

'datacube': datacube, 

'suds': suds, 

'css': css, 

'tdms': tdms_idas 

} 

 

add_args = { 

'seisan': {'subformat': subformat}, 

} 

 

if format not in format_to_module: 

raise UnsupportedFormat(format) 

 

mod = format_to_module[format] 

 

for tr in mod.iload( 

filename, load_data=load_data, **add_args.get(format, {})): 

 

yield subs(tr) 

 

 

def save(traces, filename_template, format='mseed', additional={}, 

stations=None, overwrite=True, **kwargs): 

'''Save traces to file(s). 

 

:param traces: a trace or an iterable of traces to store 

:param filename_template: filename template with placeholders for trace 

metadata. Uses normal python '%%(placeholder)s' string templates. 

The following placeholders are considered: ``network``, 

``station``, ``location``, ``channel``, ``tmin`` 

(time of first sample), ``tmax`` (time of last sample), 

``tmin_ms``, ``tmax_ms``, ``tmin_us``, ``tmax_us``. The versions 

with '_ms' include milliseconds, the versions with '_us' include 

microseconds. 

:param format: %s 

:param additional: dict with custom template placeholder fillins. 

:param overwrite': if ``False``, raise an exception if file exists 

:returns: list of generated filenames 

 

.. note:: 

Network, station, location, and channel codes may be silently truncated 

to file format specific maximum lengthes. 

''' 

 

if isinstance(traces, trace.Trace): 

traces = [traces] 

 

if format == 'from_extension': 

format = os.path.splitext(filename_template)[1][1:] 

 

if format == 'mseed': 

return mseed.save(traces, filename_template, additional, 

overwrite=overwrite, **kwargs) 

 

elif format == 'gse2': 

return gse2.save(traces, filename_template, additional, 

overwrite=overwrite, **kwargs) 

 

elif format == 'sac': 

fns = [] 

for tr in traces: 

fn = tr.fill_template(filename_template, **additional) 

if not overwrite and os.path.exists(fn): 

raise FileSaveError('file exists: %s' % fn) 

 

if fn in fns: 

raise FileSaveError('file just created would be overwritten: ' 

'%s (multiple traces map to same filename)' 

% fn) 

 

util.ensuredirs(fn) 

 

f = sac.SacFile(from_trace=tr) 

if stations: 

s = stations[tr.network, tr.station, tr.location] 

f.stla = s.lat 

f.stlo = s.lon 

f.stel = s.elevation 

f.stdp = s.depth 

f.cmpinc = s.get_channel(tr.channel).dip + 90. 

f.cmpaz = s.get_channel(tr.channel).azimuth 

 

f.write(fn) 

fns.append(fn) 

 

return fns 

 

elif format == 'text': 

fns = [] 

for tr in traces: 

fn = tr.fill_template(filename_template, **additional) 

if not overwrite and os.path.exists(fn): 

raise FileSaveError('file exists: %s' % fn) 

 

if fn in fns: 

raise FileSaveError('file just created would be overwritten: ' 

'%s (multiple traces map to same filename)' 

% fn) 

 

util.ensuredirs(fn) 

x, y = tr.get_xdata(), tr.get_ydata() 

num.savetxt(fn, num.transpose((x, y))) 

fns.append(fn) 

return fns 

 

elif format == 'yaff': 

return yaff.save(traces, filename_template, additional, 

overwrite=overwrite, **kwargs) 

else: 

raise UnsupportedFormat(format) 

 

 

save.__doc__ %= allowed_formats('save', 'doc') 

 

 

class UnknownFormat(Exception): 

def __init__(self, filename): 

Exception.__init__(self, 'Unknown file format: %s' % filename) 

 

 

class UnsupportedFormat(Exception): 

def __init__(self, format): 

Exception.__init__(self, 'Unsupported file format: %s' % format) 

 

 

def make_substitutions(tr, substitutions): 

if substitutions: 

tr.set_codes(**substitutions)