Source code for obspy.io.xseed.utils

# -*- coding: utf-8 -*-
"""
Various additional utilities for ObsPy xseed.

:copyright:
    The ObsPy Development Team (devs@obspy.org)
:license:
    GNU Lesser General Public License, Version 3
    (https://www.gnu.org/copyleft/lesser.html)
"""
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)
from future.builtins import *  # NOQA @UnusedWildImport
from future.utils import native_str

import re
import sys

from obspy import UTCDateTime
from obspy.core.util.deprecation_helpers import \
    DynamicAttributeImportRerouteModule


# Ignore Attributes of Blockettes
IGNORE_ATTR = ['blockette_id', 'blockette_name', 'compact', 'debug',
               'seed_version', 'strict', 'xseed_version',
               'length_of_blockette', 'blockette_type']


[docs]class SEEDParserException(Exception): pass
[docs]def to_tag(name): """ Creates a XML tag from a given string. """ temp = name.lower().replace(' ', '_') temp = temp.replace('fir_', 'FIR_') temp = temp.replace('a0_', 'A0_') return temp
[docs]def to_string(tag): """ Creates a pretty string from any given XML tag. """ temp = tag.replace('_', ' ').title() temp = temp.replace('Fir', 'FIR') return temp
[docs]def datetime_2_string(dt, compact=False): """ Generates a valid SEED time string from a UTCDateTime object. """ if isinstance(dt, UTCDateTime): return dt.format_seed(compact) elif isinstance(dt, (str, native_str)): dt = dt.strip() if not dt: return "" try: dt = UTCDateTime(dt) return dt.format_seed(compact) except: raise Exception("Invalid datetime %s: %s" % (type(dt), str(dt)))
[docs]def compare_seed(seed1, seed2): """ Compares two SEED files. Only works with a record length of 4096 bytes. """ # Each SEED string should be a multiple of the record length. if (len(seed1) % 4096) != 0: msg = "Length of first SEED string should be a multiple of 4096 bytes" raise Exception(msg) if (len(seed2) % 4096) != 0: msg = "Length of second SEED string should be a multiple of 4096 bytes" raise Exception(msg) # Loop over each record and remove empty ones. obspy.io.xseed doesn't write # empty records. Redundant code to ease coding... recnums = len(seed1) // 4096 new_seed1 = b'' for _i in range(recnums): cur_record = seed1[_i * 4096 + 8:(_i + 1) * 4096].strip() if cur_record == b'': continue new_seed1 += seed1[_i * 4096:(_i + 1) * 4096] seed1 = new_seed1 recnums = len(seed2) // 4096 new_seed2 = b'' for _i in range(recnums): cur_record = seed2[_i * 4096 + 8:(_i + 1) * 4096].strip() if cur_record == b'': continue new_seed2 += seed2[_i * 4096:(_i + 1) * 4096] seed2 = new_seed2 # length should be the same if len(seed1) != len(seed2): msg = "Length of SEED strings differ! (%d != %d)" % (len(seed1), len(seed2)) raise Exception(msg) # version string is always ' 2.4' for output if seed1[15:19] == b' 2.3': seed1 = seed1.replace(b' 2.3', b' 2.4', 1) if seed1[15:19] == b'02.3': seed1 = seed1.replace(b'02.3', b' 2.4', 1) # check for missing '~' in blockette 10 (faulty dataless from BW network) l = int(seed1[11:15]) temp = seed1[0:(l + 8)] if temp.count(b'~') == 4: # added a '~' and remove a space before the next record # record length for now 4096 b_l = ('%04i' % (l + 1)).encode('ascii', 'strict') seed1 = seed1[0:11] + b_l + seed1[15:(l + 8)] + b'~' + \ seed1[(l + 8):4095] + seed1[4096:] # check each byte for i in range(0, len(seed1)): if seed1[i] == seed2[i]: continue temp = seed1[i] + seed2[i] if temp == b'0+': continue if temp == b'0 ': continue if temp == b' +': continue if temp == b'- ': # -056.996398+0031.0 # -56.996398 +31.0 continue
[docs]def lookup_code(blockettes, blkt_number, field_name, lookup_code, lookup_code_number): """ Loops over a list of blockettes until it finds the blockette with the right number and lookup code. """ # List of all possible names for lookup for blockette in blockettes: if blockette.id != blkt_number: continue if getattr(blockette, lookup_code) != lookup_code_number: continue return getattr(blockette, field_name) return None
[docs]def format_resp(number, digits=4): """ Formats a number according to the RESP format. """ format_string = "%%-10.%dE" % digits return format_string % (number)
[docs]def blockette_34_lookup(abbr, lookup): """ Gets certain values from blockette 34. Needed for RESP output. """ try: l1 = lookup_code(abbr, 34, 'unit_name', 'unit_lookup_code', lookup) l2 = lookup_code(abbr, 34, 'unit_description', 'unit_lookup_code', lookup) return l1 + ' - ' + l2 except: msg = '\nWarning: Abbreviation reference not found.' sys.stdout.write(msg) return 'No Abbreviation Referenced'
[docs]def set_xpath(blockette, identifier): """ Returns an X-Path String to a blockette with the correct identifier. """ try: identifier = int(identifier) except: msg = 'X-Path identifier needs to be an integer.' raise TypeError(msg) abbr_path = '/xseed/abbreviation_dictionary_control_header/' end_of_path = '[text()="%s"]/parent::*' if blockette == 30: return abbr_path + \ 'data_format_dictionary/data_format_identifier_code' + \ end_of_path % identifier elif blockette == 31: return abbr_path + \ 'comment_description/comment_code_key' + \ end_of_path % identifier elif blockette == 33: return abbr_path + \ 'generic_abbreviation/abbreviation_lookup_code' + \ end_of_path % identifier elif blockette == 34: return abbr_path + \ 'units_abbreviations/unit_lookup_code' + \ end_of_path % identifier # All dictionary blockettes. elif blockette == 'dictionary': return abbr_path + \ '*/response_lookup_key' + \ end_of_path % identifier msg = 'XPath for blockette %d not implemented yet.' % blockette raise NotImplementedError(msg)
[docs]def get_xpath(xpath): """ Returns lookup key of XPath expression on abbreviation dictionary. """ return int(xpath.split('"')[-2])
[docs]def unique_list(seq): # Not order preserving keys = {} for e in seq: keys[e] = 1 return list(keys.keys())
[docs]def is_resp(filename): """ Check if a file at the specified location appears to be a RESP file. :type filename: str :param filename: Path/filename of a local file to be checked. :rtype: bool :returns: `True` if file seems to be a RESP file, `False` otherwise. """ try: with open(filename, "rb") as fh: try: # lookup the first line that does not start with a hash sign while True: # use splitlines to correctly detect e.g. mac formatted # files on Linux lines = fh.readline().splitlines() # end of file without finding an appropriate line if not lines: return False # check each line after splitting them for line in lines: if line.decode().startswith("#"): continue # do the regex check on the first non-comment line if re.match(r'[bB]0[1-6][0-9]F[0-9]{2} ', line.decode()): return True return False except UnicodeDecodeError: return False except IOError: return False # Remove once 0.11 has been released.
sys.modules[__name__] = DynamicAttributeImportRerouteModule( name=__name__, doc=__doc__, locs=locals(), original_module=sys.modules[__name__], import_map={}, function_map={ 'Blockette34Lookup': 'obspy.io.xseed.utils.blockette_34_lookup', 'DateTime2String': 'obspy.io.xseed.utils.datetime_2_string', 'LookupCode': 'obspy.io.xseed.utils.lookup_code', 'compareSEED': 'obspy.io.xseed.utils.compare_seed', 'formatRESP': 'obspy.io.xseed.utils.format_resp', 'getXPath': 'obspy.io.xseed.utils.get_xpath', 'setXPath': 'obspy.io.xseed.utils.set_xpath', 'toString': 'obspy.io.xseed.utils.to_string', 'toTag': 'obspy.io.xseed.utils.to_tag', 'uniqueList': 'obspy.io.xseed.utils.unique_list'})