Working with Waveforms

How to read and write waveforms.


Retrieving waves from a query

Waveforms are described in the Wfdisc table, and there are two ways to get waveforms from a query.

Read directly from wfdisc instances

If you have instances from the Wfdisc class, you can easily convert them to ObsPy Trace instances for analysis or plotting. Pisces Wfdisc class instances have a to_trace method, which produces Trace instance from the Wfdisc instance. Alternately, you can use the wfdisc2trace function on a vanilla SQLAlchemy Wfdisc instance (no to_trace method). Finally, you can use the low-level function read_waveform, which underlies all the methods above. This function returns a raw NumPy array instead of an Obspy Trace, however.

from mytables import Wfdisc
from pisces import wfdisc2trace, read_waveform

# loop over 10 BHZ wfdisc instances from the database
for wf in session.query(Wfdisc).filter(Wfdisc.chan == 'BHZ').limit(10):
    # the following two traces should be the same
    tr = wf.to_trace()
    tr = wfdisc2trace(wf) 

    # get the raw data
    data = read_waveform(wf.dir + '/' + wf.dfile, wf.datatype, wf.foff, wf.nsamp)

    #do analysis, writing, and/or plotting here...

Using the pisces.Client class

Adding waveforms to the database

Database-building scripts are in development, but adding waveforms to a database is still relatively easy. Using ObsPy, any number of waveform formats can be read and the basic header "scraped" into a Wfdisc row.

Here's an example for a SAC file.

import os
from glob import glob

from obspy import read
from pisces import db_connect
from pisces.tables.css3 import Wfdisc

session = db_connect(conn='sqlite:///mydatabase.sqlite')



for ifile in glob("*.SAC"):
   tr = read(ifile, format='SAC')[0]
   idir, idfile = os.path.split(ifile)
   wf = Wfdisc(sta=tr.stats.station,, 
               samprate=tr.stats.sampling_rate, nsamp=tr.stats.npts, 
               time=tr.stats.starttime.timestamp, foff=634, dir=idir,
               dfile=idfile, endtime=tr.stats.endtime.timestamp)


Copying (localizing) waveform files

If you want to move waveform files from one database to another one, you may need copy the files and tweak the Wfdisc table. Wfdisc tables contain file name and directory information about the waveforms stored on disk, so those need to be corrected when you move the files. In the example below, we copy wfdisc instances from session1, pointing to the originating database, to session2, which points at the destination database.

import os
import shutil

def copy_waves(wfdiscs, old_base, new_base):
    """Replace old_base with new_base in the .dir attribute of wfdisc list, and copies the 
    waveform files to the new location.

    wfdiscs : list
        Wfdisc instances from source database.
    old_base, new_base : str
        The top of the old (new) data directory trees.  This assumes all wfdiscs originate and 
        end up under single directories. The directory structure under the new_base will mirror
        that under old_base.

    wfdiscs_out : list
        Same as input list, but .dir attribute now points to the new_base location.  

    for wf in wfdiscs:
        # replace the directory
        old_file =  os.path.sep.join([wf.dir, wf.dfile])
        wf.dir.replace(old_base, new_base)
        new_file =  os.path.sep.join([wf.dir, wf.dfile])
            shutil.copyfile(old_file, new_file)
        except IOError:
            # new directory doesn't exist yet.  this is like "mkdir -p" 
            shutil.copyfile(old_file, new_file)

# get the wfdiscs
wfs = session1.query(Wfdisc1).all()

# release the link between the wfdiscs and the originating database
# this makes them "floating" instances, so they can be added to the destination database

# do the copy and tweaking
wfs = copy_waves(wfs, '/old/path/to/top/of/data', '/my/new/path/to/top/of/data')

# add and commit them to the destination database