Source code for obspy.clients.neic.client
# -*- coding: utf-8 -*-
"""
NEIC CWB Query service client for ObsPy.
:copyright:
The ObsPy Development Team (devs@obspy.org) & David Ketchum
:license:
GNU Lesser General Public License, Version 3
(https://www.gnu.org/copyleft/lesser.html)
"""
import io
import socket
import traceback
from time import sleep
from obspy import Stream, UTCDateTime, read
from obspy.clients.neic.util import ascdate, asctime
from obspy.clients.httpproxy import get_proxy_tuple, http_proxy_connect
[docs]
class Client(object):
"""
NEIC CWB QueryServer request client for waveform data
:type host: str, optional
:param host: The IP address or DNS name of the server
(default is "137.227.224.97" for cwbpub.cr.usgs.gov)
:type port: int, optional
:param port: The port of the QueryServer (default is ``2061``)
:type timeout: int, optional
:param timeout: Wait this much time before timeout is raised
(default is ``30``)
:type debug: bool, optional
:param debug: if ``True``, print debug information (default is ``False``)
.. rubric:: Example
>>> from obspy.clients.neic import Client
>>> client = Client()
>>> t = UTCDateTime() - 5 * 3600 # 5 hours before now
>>> st = client.get_waveforms("IU", "ANMO", "00", "BH?", t, t + 10)
>>> print(st) # doctest: +ELLIPSIS
3 Trace(s) in Stream:
IU.ANMO.00.BH... | 40.0 Hz, 401 samples
IU.ANMO.00.BH... | 40.0 Hz, 401 samples
IU.ANMO.00.BH... | 40.0 Hz, 401 samples
>>> st = client.get_waveforms_nscl("IUANMO BH.00", t, 10)
>>> print(st) # doctest: +ELLIPSIS
3 Trace(s) in Stream:
IU.ANMO.00.BH... | 40.0 Hz, 401 samples
IU.ANMO.00.BH... | 40.0 Hz, 401 samples
IU.ANMO.00.BH... | 40.0 Hz, 401 samples
"""
[docs]
def __init__(self, host="137.227.224.97", port=2061, timeout=30,
debug=False):
"""
Initializes access to a CWB QueryServer
"""
if debug:
print("int __init__" + host + "/" + str(port) + " timeout=" +
str(timeout))
self.host = host
self.port = port
self.timeout = timeout
self.debug = debug
self.proxy = get_proxy_tuple()
[docs]
def get_waveforms(self, network, station, location, channel, starttime,
endtime):
"""
Gets a waveform for a specified net, station, location and channel
from start time to end time. The individual elements can contain
wildcard ``"?"`` representing one character, matches of character
ranges (e.g. ``channel="BH[Z12]"``). All fields are left justified and
padded with spaces to the required field width if they are too short.
Use get_waveforms_nscl for seednames specified with regular
expressions.
.. rubric:: Notes
Using ``".*"`` regular expression might or might not work. If the 12
character seed name regular expression is less than 12 characters it
might get padded with spaces on the server side.
:type network: str
:param network: The 2 character network code or regular expression
(will be padded with spaces to the right to length 2)
:type station: str
:param station: The 5 character station code or regular expression
(will be padded with spaces to the right to length 5)
:type location: str
:param location: The 2 character location code or regular expression
(will be padded with spaces to the right to length 2)
:type channel: str
:param channel: The 3 character channel code or regular expression
:type starttime: :class:`~obspy.core.utcdatetime.UTCDateTime`
:param starttime: Start date and time.
:type endtime: :class:`~obspy.core.utcdatetime.UTCDateTime`
:param endtime: End date and time.
:rtype: :class:`~obspy.core.stream.Stream`
:returns: Stream object with requested data
.. rubric:: Example
>>> from obspy.clients.neic import Client
>>> client = Client()
>>> t = UTCDateTime() - 5 * 3600 # 5 hours before now
>>> st = client.get_waveforms("IU", "ANMO", "0?", "BH?", t, t + 10)
>>> print(st) # doctest: +ELLIPSIS
3 Trace(s) in Stream:
IU.ANMO.00.BH... | 40.0 Hz, 401 samples
IU.ANMO.00.BH... | 40.0 Hz, 401 samples
IU.ANMO.00.BH... | 40.0 Hz, 401 samples
"""
# padding channel with spaces does not make sense
if len(channel) < 3 and channel != ".*":
msg = "channel expression matches less than 3 characters " + \
"(use e.g. 'BHZ', 'BH?', 'BH[Z12]', 'B??')"
raise Exception(msg)
seedname = '%-2s%-5s%s%-2s' % (network, station, channel, location)
# allow UNIX style "?" wildcard
seedname = seedname.replace("?", ".")
return self.get_waveforms_nscl(seedname, starttime,
endtime - starttime)
[docs]
def get_waveforms_nscl(self, seedname, starttime, duration):
"""
Gets a regular expression of channels from a start time for a duration
in seconds. The regular expression must represent all characters of
the 12-character NNSSSSSCCCLL pattern e.g. "US.....[BSHE]HZ.." is
valid, but "US.....[BSHE]H" is not. Complex regular expressions are
permitted "US.....BHZ..|CU.....[BH]HZ.."
.. rubric:: Notes
For detailed information regarding the usage of regular expressions
in the query, see also the documentation for CWBQuery ("CWBQuery.doc")
available at ftp://hazards.cr.usgs.gov/CWBQuery/.
Using ".*" regular expression might or might not work. If the 12
character seed name regular expression is less than 12 characters it
might get padded with spaces on the server side.
:type seedname: str
:param seedname: The 12 character seedname or 12 character regexp
matching channels
:type start: :class:`~obspy.core.utcdatetime.UTCDateTime`
:param start: The starting date/time to get
:type duration: float
:param duration: The duration in seconds to get
:rtype: :class:`~obspy.core.stream.Stream`
:returns: Stream object with requested data
.. rubric:: Example
>>> from obspy.clients.neic import Client
>>> from obspy import UTCDateTime
>>> client = Client()
>>> t = UTCDateTime() - 5 * 3600 # 5 hours before now
>>> st = client.get_waveforms_nscl("IUANMO BH.00", t, 10)
>>> print(st) # doctest: +ELLIPSIS
3 Trace(s) in Stream:
IU.ANMO.00.BH... | 40.0 Hz, 401 samples
IU.ANMO.00.BH... | 40.0 Hz, 401 samples
IU.ANMO.00.BH... | 40.0 Hz, 401 samples
"""
start = str(UTCDateTime(starttime)).replace("T", " ").replace("Z", "")
line = "'-s' '%s' '-b' '%s' '-d' '%s'\t" % \
(seedname, start, duration)
if self.debug:
print(ascdate() + " " + asctime() + " line=" + line)
# prepare for routing through http_proxy_connect
address = (self.host, self.port)
if self.proxy:
proxy = (self.proxy.hostname, self.proxy.port)
auth = ((self.proxy.username, self.proxy.password) if
self.proxy.username else None)
success = False
while not success:
try:
if self.proxy:
s, _, _ = http_proxy_connect(address, proxy, auth,
timeout=self.timeout)
# This socket is already connected to the proxy
else:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if self.timeout is not None:
s.settimeout(self.timeout)
s.connect((self.host, self.port))
with io.BytesIO() as tf:
s.send(line.encode('ascii', 'strict'))
if self.debug:
print(ascdate(), asctime(), "Connected - start reads")
slept = 0
maxslept = self.timeout / 0.05
totlen = 0
while True:
try:
# Recommended bufsize is a small power of 2.
data = s.recv(4096)
if self.debug:
print(ascdate(), asctime(), "read len",
str(len(data)), " total", str(totlen))
_pos = data.find(b"<EOR>")
# <EOR> can be after every 512 bytes which seems to
# be the record length cwb query uses.
if _pos >= 0 and (_pos + totlen) % 512 == 0:
if self.debug:
print(ascdate(), asctime(), b"<EOR> seen")
tf.write(data[0:_pos])
totlen += len(data[0:_pos])
tf.seek(0)
try:
st = read(tf, 'MSEED')
except Exception:
st = Stream()
st.trim(starttime, starttime + duration)
s.close()
success = True
break
else:
totlen += len(data)
tf.write(data)
slept = 0
except socket.error:
if slept > maxslept:
print(ascdate(), asctime(),
"Timeout on connection",
"- try to reconnect")
slept = 0
s.close()
sleep(0.05)
slept += 1
except socket.error:
print(traceback.format_exc())
print("CWB QueryServer at " + self.host + "/" + str(self.port))
raise
except Exception as e:
print(traceback.format_exc())
print("**** exception found=" + str(e))
raise
if self.debug:
print(ascdate() + " " + asctime() + " success? len=" +
str(totlen))
st.merge(-1)
return st
if __name__ == '__main__':
import doctest
doctest.testmod(exclude_empty=True)