Source code for ReadSAC
# -*- coding: utf-8 -*-
##################################################
# © 2017 ETH Zurich, Swiss Seismological Service #
# Stefano Marano' - wavedec at gmail dot com #
##################################################
"""
Routines for reading SAC files
"""
import glob
import numpy as np
import logging
import struct
from wdSettings import Components
[docs]def readSac(sacFile):
"""Load a single SAC file.
Parameters
----------
sacFile : string
Path to the SAC file.
Returns
-------
data : float array
Data, amplitudes
t : float array
Time axis
sachead : float array
SAC header
"""
for ESTR in ["<", ">"]:
#ESTR=">" # big endian # eg, SESAME format
#ESTR="<" # little endian # eg, SED format
#ESTR="@" # same as machine
SHEAD="%c70f35l5L8s16s8s8s8s8s8s8s8s8s8s8s8s8s8s8s8s8s8s8s8s8s8s" % ESTR
f=open(sacFile, mode="rb")
sachead_size=struct.calcsize(SHEAD)
tstr=f.read(sachead_size)
sachead=struct.unpack(SHEAD, tstr)
nvhdr = sachead[76]
if nvhdr == 4 | nvhdr == 5:
# Old sac format. Never tested.
logging.warning("NVHDR = {0}, file {1} may be from old SAC version.".format(nvhdr, sacFile))
elif nvhdr != 6:
# We are reading in the wrong byte order.
f.close()
elif nvhdr == 6:
# Good, we are reading in the propoer byte order.
break
else:
logging.error("NVHDR = {0}, file {1} may be corrupted.".format(nvhdr, sacFile))
dt=sachead[0]
npts=sachead[79]
t=np.arange(0, npts*dt, dt)
dsize=struct.calcsize("%c%df" % (ESTR,npts))
dat_str=f.read(dsize)
data=np.array(struct.unpack("%c%df" % (ESTR, npts), dat_str))
f.close()
return(data, t, sachead)
[docs]def readSacDir(sac_dir):
"""Load SAC files from a folder.
Parameters
----------
sac_dir : string
Path to the folder containing the SAC files.
Returns
-------
y : 2d float array
It is an array of size (K, L). Each column contains the signal at the l-th location.
info : 2d array
An array containing information about sensor location and channels.
It has L rows, where L is the number of channels.
Each rows has the following form:
pos_x, pos_y, pos_z, cmp, Ts
where the first three fields are the position of the sensor in [m].
cmp is the component code.
Ts is the sampling time as read from the SAC file.
Ts : float
Sampling time in [s].
"""
fileList = sorted(glob.glob(sac_dir+'/*.sac'))
N_files = len(fileList)
if N_files < 1:
raise Exception('No files found in {0}'.format(sac_dir))
info=np.zeros((N_files,6))
for ff in range(0,N_files):
logging.debug("Loading SAC file " + fileList[ff])
(data, t, sachead) = readSac(fileList[ff])
if ff == 0:
K = len(data)
Ts = sachead[0] # Sampling interval
y=np.zeros((K,N_files))
else:
if Ts != sachead[0]:
#print(fileList[ff])
#print(sachead[0])
raise Exception('Sampling time Ts is not consistent across input files.')
if K != len(data):
#print(fileList[ff])
#print(len(data))
# TODO found in SED database a dataset with this problem
raise Exception('Data length K is not consistent across input files.')
y[:,ff] = data
info[ff,0] = sachead[47] # x coordinate
info[ff,1] = sachead[48] # y coordinate
info[ff,2] = sachead[49] # z coordinate
info[ff,4] = sachead[0] # Sampling interval
cmp = sachead[129].split(b'\x00')[0].strip() # terminate string with null char and remove trailing whitespaces
try:
info[ff,3] = Components[cmp] # component
except KeyError:
raise Exception('Unknown SAC component {0} found while reading {1}'.format(cmp, fileList[ff]))
return(y, info, Ts, fileList)