963 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			Plaintext
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			963 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			Plaintext
		
	
	
		
			Executable File
		
	
	
	
	
#!/usr/bin/env seiscomp-python
 | 
						|
# -*- coding: utf-8 -*-
 | 
						|
############################################################################
 | 
						|
# Copyright (C) GFZ Potsdam                                                #
 | 
						|
# All rights reserved.                                                     #
 | 
						|
#                                                                          #
 | 
						|
# GNU Affero General Public License Usage                                  #
 | 
						|
# This file may be used under the terms of the GNU Affero                  #
 | 
						|
# Public License version 3.0 as published by the Free Software Foundation  #
 | 
						|
# and appearing in the file LICENSE included in the packaging of this      #
 | 
						|
# file. Please review the following information to ensure the GNU Affero   #
 | 
						|
# Public License version 3.0 requirements will be met:                     #
 | 
						|
# https://www.gnu.org/licenses/agpl-3.0.html.                              #
 | 
						|
#                                                                          #
 | 
						|
# Author: Alexander Jaeger, Stephan Herrnkind,                             #
 | 
						|
#         Lukas Lehmann, Dirk Roessler#                                    #
 | 
						|
# Email: herrnkind@gempa.de                                                #
 | 
						|
############################################################################
 | 
						|
 | 
						|
 | 
						|
# from time import strptime
 | 
						|
import sys
 | 
						|
import traceback
 | 
						|
import seiscomp.client
 | 
						|
import seiscomp.core
 | 
						|
import seiscomp.datamodel
 | 
						|
import seiscomp.io
 | 
						|
import seiscomp.logging
 | 
						|
import seiscomp.math
 | 
						|
 | 
						|
 | 
						|
TimeFormats = ["%d-%b-%Y_%H:%M:%S.%f", "%d-%b-%Y_%H:%M:%S"]
 | 
						|
 | 
						|
 | 
						|
# SC3 has more event types available in the datamodel
 | 
						|
EventTypes = {
 | 
						|
    "teleseismic quake": seiscomp.datamodel.EARTHQUAKE,
 | 
						|
    "local quake": seiscomp.datamodel.EARTHQUAKE,
 | 
						|
    "regional quake": seiscomp.datamodel.EARTHQUAKE,
 | 
						|
    "quarry blast": seiscomp.datamodel.QUARRY_BLAST,
 | 
						|
    "nuclear explosion": seiscomp.datamodel.NUCLEAR_EXPLOSION,
 | 
						|
    "mining event": seiscomp.datamodel.MINING_EXPLOSION,
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
def wfs2Str(wfsID):
 | 
						|
    return f"{wfsID.networkCode()}.{wfsID.stationCode()}.{wfsID.locationCode()}.{wfsID.channelCode()}"
 | 
						|
 | 
						|
 | 
						|
###############################################################################
 | 
						|
class SH2Proc(seiscomp.client.Application):
 | 
						|
    ###########################################################################
 | 
						|
    def __init__(self):
 | 
						|
        seiscomp.client.Application.__init__(self, len(sys.argv), sys.argv)
 | 
						|
        self.setMessagingEnabled(True)
 | 
						|
        self.setDatabaseEnabled(True, True)
 | 
						|
        self.setLoadInventoryEnabled(True)
 | 
						|
        self.setLoadConfigModuleEnabled(True)
 | 
						|
        self.setDaemonEnabled(False)
 | 
						|
 | 
						|
        self.inputFile = "-"
 | 
						|
        self.streams = None
 | 
						|
 | 
						|
    ###########################################################################
 | 
						|
    def initConfiguration(self):
 | 
						|
        if not seiscomp.client.Application.initConfiguration(self):
 | 
						|
            return False
 | 
						|
 | 
						|
        # If the database connection is passed via command line or configuration
 | 
						|
        # file then messaging is disabled. Messaging is only used to get
 | 
						|
        # the configured database connection URI.
 | 
						|
        if self.databaseURI() != "":
 | 
						|
            self.setMessagingEnabled(False)
 | 
						|
        else:
 | 
						|
            # A database connection is not required if the inventory is loaded
 | 
						|
            # from file
 | 
						|
            if not self.isInventoryDatabaseEnabled():
 | 
						|
                self.setMessagingEnabled(False)
 | 
						|
                self.setDatabaseEnabled(False, False)
 | 
						|
 | 
						|
        return True
 | 
						|
 | 
						|
    ##########################################################################
 | 
						|
    def printUsage(self):
 | 
						|
        print(
 | 
						|
            """Usage:
 | 
						|
  sh2proc [options]
 | 
						|
 | 
						|
Convert Seismic Handler event data to SeisComP XML format which is sent to stdout."""
 | 
						|
        )
 | 
						|
 | 
						|
        seiscomp.client.Application.printUsage(self)
 | 
						|
 | 
						|
        print(
 | 
						|
            """Examples:
 | 
						|
Convert the Seismic Handler file shm.evt to SCML. Receive the database
 | 
						|
connection to read inventory and configuration information from messaging
 | 
						|
  sh2proc shm.evt > event.xml
 | 
						|
 | 
						|
Read Seismic Handler data from stdin. Provide inventory and configuration in XML
 | 
						|
  cat shm.evt | sh2proc --inventory-db=inventory.xml --config-db=config.xml > event.xml
 | 
						|
"""
 | 
						|
        )
 | 
						|
 | 
						|
    ##########################################################################
 | 
						|
    def validateParameters(self):
 | 
						|
        if not seiscomp.client.Application.validateParameters(self):
 | 
						|
            return False
 | 
						|
 | 
						|
        for opt in self.commandline().unrecognizedOptions():
 | 
						|
            if len(opt) > 1 and opt.startswith("-"):
 | 
						|
                continue
 | 
						|
 | 
						|
            self.inputFile = opt
 | 
						|
            break
 | 
						|
 | 
						|
        return True
 | 
						|
 | 
						|
    ###########################################################################
 | 
						|
    def loadStreams(self):
 | 
						|
        now = seiscomp.core.Time.GMT()
 | 
						|
        inv = seiscomp.client.Inventory.Instance()
 | 
						|
 | 
						|
        self.streams = {}
 | 
						|
 | 
						|
        # try to load streams by detecLocid and detecStream
 | 
						|
        mod = self.configModule()
 | 
						|
        if mod is not None and mod.configStationCount() > 0:
 | 
						|
            seiscomp.logging.info("loading streams using detecLocid and detecStream")
 | 
						|
            for i in range(mod.configStationCount()):
 | 
						|
                cfg = mod.configStation(i)
 | 
						|
                net = cfg.networkCode()
 | 
						|
                sta = cfg.stationCode()
 | 
						|
                if sta in self.streams:
 | 
						|
                    seiscomp.logging.warning(
 | 
						|
                        f"ambiguous stream id found for station {net}.{sta}"
 | 
						|
                    )
 | 
						|
                    continue
 | 
						|
 | 
						|
                setup = seiscomp.datamodel.findSetup(cfg, self.name(), True)
 | 
						|
                if not setup:
 | 
						|
                    seiscomp.logging.warning(
 | 
						|
                        f"could not find station setup for {net}.{sta}"
 | 
						|
                    )
 | 
						|
                    continue
 | 
						|
 | 
						|
                params = seiscomp.datamodel.ParameterSet.Find(setup.parameterSetID())
 | 
						|
                if not params:
 | 
						|
                    seiscomp.logging.warning(
 | 
						|
                        f"could not find station parameters for {net}.{sta}"
 | 
						|
                    )
 | 
						|
                    continue
 | 
						|
 | 
						|
                detecLocid = ""
 | 
						|
                detecStream = None
 | 
						|
 | 
						|
                for j in range(params.parameterCount()):
 | 
						|
                    param = params.parameter(j)
 | 
						|
                    if param.name() == "detecStream":
 | 
						|
                        detecStream = param.value()
 | 
						|
                    elif param.name() == "detecLocid":
 | 
						|
                        detecLocid = param.value()
 | 
						|
 | 
						|
                if detecStream is None:
 | 
						|
                    seiscomp.logging.warning(
 | 
						|
                        f"could not find detecStream for {net}.{sta}"
 | 
						|
                    )
 | 
						|
                    continue
 | 
						|
 | 
						|
                loc = inv.getSensorLocation(net, sta, detecLocid, now)
 | 
						|
                if loc is None:
 | 
						|
                    seiscomp.logging.warning(
 | 
						|
                        f"could not find preferred location for {net}.{sta}"
 | 
						|
                    )
 | 
						|
                    continue
 | 
						|
 | 
						|
                components = {}
 | 
						|
                tc = seiscomp.datamodel.ThreeComponents()
 | 
						|
                seiscomp.datamodel.getThreeComponents(tc, loc, detecStream[:2], now)
 | 
						|
                if tc.vertical():
 | 
						|
                    cha = tc.vertical()
 | 
						|
                    wfsID = seiscomp.datamodel.WaveformStreamID(
 | 
						|
                        net, sta, loc.code(), cha.code(), ""
 | 
						|
                    )
 | 
						|
                    components[cha.code()[-1]] = wfsID
 | 
						|
                    seiscomp.logging.debug(f"add stream {wfs2Str(wfsID)} (vertical)")
 | 
						|
                if tc.firstHorizontal():
 | 
						|
                    cha = tc.firstHorizontal()
 | 
						|
                    wfsID = seiscomp.datamodel.WaveformStreamID(
 | 
						|
                        net, sta, loc.code(), cha.code(), ""
 | 
						|
                    )
 | 
						|
                    components[cha.code()[-1]] = wfsID
 | 
						|
                    seiscomp.logging.debug(
 | 
						|
                        f"add stream {wfs2Str(wfsID)} (first horizontal)"
 | 
						|
                    )
 | 
						|
                if tc.secondHorizontal():
 | 
						|
                    cha = tc.secondHorizontal()
 | 
						|
                    wfsID = seiscomp.datamodel.WaveformStreamID(
 | 
						|
                        net, sta, loc.code(), cha.code(), ""
 | 
						|
                    )
 | 
						|
                    components[cha.code()[-1]] = wfsID
 | 
						|
                    seiscomp.logging.debug(
 | 
						|
                        f"add stream {wfs2Str(wfsID)} (second horizontal)"
 | 
						|
                    )
 | 
						|
                if len(components) > 0:
 | 
						|
                    self.streams[sta] = components
 | 
						|
 | 
						|
            return
 | 
						|
 | 
						|
        # fallback loading streams from inventory
 | 
						|
        seiscomp.logging.warning(
 | 
						|
            "no configuration module available, loading streams "
 | 
						|
            "from inventory and selecting first available stream "
 | 
						|
            "matching epoch"
 | 
						|
        )
 | 
						|
        for iNet in range(inv.inventory().networkCount()):
 | 
						|
            net = inv.inventory().network(iNet)
 | 
						|
            seiscomp.logging.debug(
 | 
						|
                f"network {net.code()}: loaded {net.stationCount()} stations"
 | 
						|
            )
 | 
						|
            for iSta in range(net.stationCount()):
 | 
						|
                sta = net.station(iSta)
 | 
						|
                try:
 | 
						|
                    start = sta.start()
 | 
						|
                    if not start <= now:
 | 
						|
                        continue
 | 
						|
                except:
 | 
						|
                    continue
 | 
						|
 | 
						|
                try:
 | 
						|
                    end = sta.end()
 | 
						|
                    if not now <= end:
 | 
						|
                        continue
 | 
						|
                except:
 | 
						|
                    pass
 | 
						|
 | 
						|
                for iLoc in range(sta.sensorLocationCount()):
 | 
						|
                    loc = sta.sensorLocation(iLoc)
 | 
						|
                    for iCha in range(loc.streamCount()):
 | 
						|
                        cha = loc.stream(iCha)
 | 
						|
 | 
						|
                        wfsID = seiscomp.datamodel.WaveformStreamID(
 | 
						|
                            net.code(), sta.code(), loc.code(), cha.code(), ""
 | 
						|
                        )
 | 
						|
                        comp = cha.code()[2]
 | 
						|
                        if sta.code() not in self.streams:
 | 
						|
                            components = {}
 | 
						|
                            components[comp] = wfsID
 | 
						|
                            self.streams[sta.code()] = components
 | 
						|
                        else:
 | 
						|
                            # Seismic Handler does not support network,
 | 
						|
                            # location and channel code: make sure network and
 | 
						|
                            # location codes match first item in station
 | 
						|
                            # specific steam list
 | 
						|
                            oldWfsID = list(self.streams[sta.code()].values())[0]
 | 
						|
                            if (
 | 
						|
                                net.code() != oldWfsID.networkCode()
 | 
						|
                                or loc.code() != oldWfsID.locationCode()
 | 
						|
                                or cha.code()[:2] != oldWfsID.channelCode()[:2]
 | 
						|
                            ):
 | 
						|
                                seiscomp.logging.warning(
 | 
						|
                                    f"ambiguous stream id found for station\
 | 
						|
                                        {sta.code()}, ignoring {wfs2Str(wfsID)}"
 | 
						|
                                )
 | 
						|
                                continue
 | 
						|
 | 
						|
                            self.streams[sta.code()][comp] = wfsID
 | 
						|
 | 
						|
                        seiscomp.logging.debug(f"add stream {wfs2Str(wfsID)}")
 | 
						|
 | 
						|
    ###########################################################################
 | 
						|
    def parseTime(self, timeStr):
 | 
						|
        time = seiscomp.core.Time()
 | 
						|
        for fmt in TimeFormats:
 | 
						|
            if time.fromString(timeStr, fmt):
 | 
						|
                break
 | 
						|
        return time
 | 
						|
 | 
						|
    ###########################################################################
 | 
						|
    def parseMagType(self, value):
 | 
						|
        if value == "m":
 | 
						|
            return "M"
 | 
						|
        if value == "ml":
 | 
						|
            return "ML"
 | 
						|
        if value == "mb":
 | 
						|
            return "mb"
 | 
						|
        if value == "ms":
 | 
						|
            return "Ms(BB)"
 | 
						|
        if value == "mw":
 | 
						|
            return "Mw"
 | 
						|
        if value == "bb":
 | 
						|
            return "mB"
 | 
						|
 | 
						|
        return ""
 | 
						|
 | 
						|
    ###########################################################################
 | 
						|
    def sh2proc(self, file):
 | 
						|
        ep = seiscomp.datamodel.EventParameters()
 | 
						|
        origin = seiscomp.datamodel.Origin.Create()
 | 
						|
        event = seiscomp.datamodel.Event.Create()
 | 
						|
 | 
						|
        origin.setCreationInfo(seiscomp.datamodel.CreationInfo())
 | 
						|
        origin.creationInfo().setCreationTime(seiscomp.core.Time.GMT())
 | 
						|
 | 
						|
        originQuality = None
 | 
						|
        originCE = None
 | 
						|
        latFound = False
 | 
						|
        lonFound = False
 | 
						|
        depthError = None
 | 
						|
        originComments = {}
 | 
						|
 | 
						|
        # variables, reset after 'end of phase'
 | 
						|
        pick = None
 | 
						|
        stationMag = None
 | 
						|
        staCode = None
 | 
						|
        compCode = None
 | 
						|
        stationMagBB = None
 | 
						|
 | 
						|
        ampPeriod = None
 | 
						|
        ampBBPeriod = None
 | 
						|
        amplitudeDisp = None
 | 
						|
        amplitudeVel = None
 | 
						|
        amplitudeSNR = None
 | 
						|
        amplitudeBB = None
 | 
						|
 | 
						|
        magnitudeMB = None
 | 
						|
        magnitudeML = None
 | 
						|
        magnitudeMS = None
 | 
						|
        magnitudeBB = None
 | 
						|
 | 
						|
        # To avoid undefined warning
 | 
						|
        arrival = None
 | 
						|
        phase = None
 | 
						|
 | 
						|
        km2degFac = 1.0 / seiscomp.math.deg2km(1.0)
 | 
						|
 | 
						|
        # read file line by line, split key and value at colon
 | 
						|
        iLine = 0
 | 
						|
        for line in file:
 | 
						|
            iLine += 1
 | 
						|
            a = line.split(":", 1)
 | 
						|
            key = a[0].strip()
 | 
						|
            keyLower = key.lower()
 | 
						|
            value = None
 | 
						|
 | 
						|
            # empty line
 | 
						|
            if len(keyLower) == 0:
 | 
						|
                continue
 | 
						|
 | 
						|
            # end of phase
 | 
						|
            if keyLower == "--- end of phase ---":
 | 
						|
                if pick is None:
 | 
						|
                    seiscomp.logging.warning(f"Line {iLine}: found empty phase block")
 | 
						|
                    continue
 | 
						|
 | 
						|
                if staCode is None or compCode is None:
 | 
						|
                    seiscomp.logging.warning(
 | 
						|
                        f"Line {iLine}: end of phase, stream code incomplete"
 | 
						|
                    )
 | 
						|
                    continue
 | 
						|
 | 
						|
                if not staCode in self.streams:
 | 
						|
                    seiscomp.logging.warning(
 | 
						|
                        f"Line {iLine}: end of phase, station code {staCode} not found in inventory"
 | 
						|
                    )
 | 
						|
                    continue
 | 
						|
 | 
						|
                if not compCode in self.streams[staCode]:
 | 
						|
                    seiscomp.logging.warning(
 | 
						|
                        f"Line {iLine}: end of phase, component\
 | 
						|
                            {compCode} of station {staCode} not found in inventory"
 | 
						|
                    )
 | 
						|
                    continue
 | 
						|
 | 
						|
                streamID = self.streams[staCode][compCode]
 | 
						|
 | 
						|
                pick.setWaveformID(streamID)
 | 
						|
                ep.add(pick)
 | 
						|
 | 
						|
                arrival.setPickID(pick.publicID())
 | 
						|
                arrival.setPhase(phase)
 | 
						|
                origin.add(arrival)
 | 
						|
 | 
						|
                if amplitudeSNR is not None:
 | 
						|
                    amplitudeSNR.setPickID(pick.publicID())
 | 
						|
                    amplitudeSNR.setWaveformID(streamID)
 | 
						|
                    ep.add(amplitudeSNR)
 | 
						|
 | 
						|
                if amplitudeBB is not None:
 | 
						|
                    amplitudeBB.setPickID(pick.publicID())
 | 
						|
                    amplitudeBB.setWaveformID(streamID)
 | 
						|
                    ep.add(amplitudeBB)
 | 
						|
 | 
						|
                if stationMagBB is not None:
 | 
						|
                    stationMagBB.setWaveformID(streamID)
 | 
						|
                    origin.add(stationMagBB)
 | 
						|
                    stationMagContrib = (
 | 
						|
                        seiscomp.datamodel.StationMagnitudeContribution()
 | 
						|
                    )
 | 
						|
                    stationMagContrib.setStationMagnitudeID(stationMagBB.publicID())
 | 
						|
                    if magnitudeBB is None:
 | 
						|
                        magnitudeBB = seiscomp.datamodel.Magnitude.Create()
 | 
						|
                    magnitudeBB.add(stationMagContrib)
 | 
						|
 | 
						|
                if stationMag is not None:
 | 
						|
                    if stationMag.type() in ["mb", "ML"] and amplitudeDisp is not None:
 | 
						|
                        amplitudeDisp.setPickID(pick.publicID())
 | 
						|
                        amplitudeDisp.setWaveformID(streamID)
 | 
						|
                        amplitudeDisp.setPeriod(
 | 
						|
                            seiscomp.datamodel.RealQuantity(ampPeriod)
 | 
						|
                        )
 | 
						|
                        amplitudeDisp.setType(stationMag.type())
 | 
						|
                        ep.add(amplitudeDisp)
 | 
						|
 | 
						|
                    if stationMag.type() in ["Ms(BB)"] and amplitudeVel is not None:
 | 
						|
                        amplitudeVel.setPickID(pick.publicID())
 | 
						|
                        amplitudeVel.setWaveformID(streamID)
 | 
						|
                        amplitudeVel.setPeriod(
 | 
						|
                            seiscomp.datamodel.RealQuantity(ampPeriod)
 | 
						|
                        )
 | 
						|
                        amplitudeVel.setType(stationMag.type())
 | 
						|
                        ep.add(amplitudeVel)
 | 
						|
 | 
						|
                    stationMag.setWaveformID(streamID)
 | 
						|
                    origin.add(stationMag)
 | 
						|
 | 
						|
                    stationMagContrib = (
 | 
						|
                        seiscomp.datamodel.StationMagnitudeContribution()
 | 
						|
                    )
 | 
						|
                    stationMagContrib.setStationMagnitudeID(stationMag.publicID())
 | 
						|
 | 
						|
                    magType = stationMag.type()
 | 
						|
                    if magType == "ML":
 | 
						|
                        if magnitudeML is None:
 | 
						|
                            magnitudeML = seiscomp.datamodel.Magnitude.Create()
 | 
						|
                        magnitudeML.add(stationMagContrib)
 | 
						|
 | 
						|
                    elif magType == "Ms(BB)":
 | 
						|
                        if magnitudeMS is None:
 | 
						|
                            magnitudeMS = seiscomp.datamodel.Magnitude.Create()
 | 
						|
                        magnitudeMS.add(stationMagContrib)
 | 
						|
 | 
						|
                    elif magType == "mb":
 | 
						|
                        if magnitudeMB is None:
 | 
						|
                            magnitudeMB = seiscomp.datamodel.Magnitude.Create()
 | 
						|
                        magnitudeMB.add(stationMagContrib)
 | 
						|
 | 
						|
                pick = None
 | 
						|
                staCode = None
 | 
						|
                compCode = None
 | 
						|
                stationMag = None
 | 
						|
                stationMagBB = None
 | 
						|
                ampPeriod = None
 | 
						|
                ampBBPeriod = None
 | 
						|
                amplitudeDisp = None
 | 
						|
                amplitudeVel = None
 | 
						|
                amplitudeSNR = None
 | 
						|
                amplitudeBB = None
 | 
						|
 | 
						|
                continue
 | 
						|
 | 
						|
            # empty key
 | 
						|
            if len(a) == 1:
 | 
						|
                seiscomp.logging.warning(f"Line {iLine}: key without value")
 | 
						|
                continue
 | 
						|
 | 
						|
            value = a[1].strip()
 | 
						|
            if pick is None:
 | 
						|
                pick = seiscomp.datamodel.Pick.Create()
 | 
						|
                arrival = seiscomp.datamodel.Arrival()
 | 
						|
 | 
						|
            try:
 | 
						|
                ##############################################################
 | 
						|
                # station parameters
 | 
						|
 | 
						|
                # station code
 | 
						|
                if keyLower == "station code":
 | 
						|
                    staCode = value
 | 
						|
 | 
						|
                # pick time
 | 
						|
                elif keyLower == "onset time":
 | 
						|
                    pick.setTime(seiscomp.datamodel.TimeQuantity(self.parseTime(value)))
 | 
						|
 | 
						|
                # pick onset type
 | 
						|
                elif keyLower == "onset type":
 | 
						|
                    found = False
 | 
						|
                    for onset in [
 | 
						|
                        seiscomp.datamodel.EMERGENT,
 | 
						|
                        seiscomp.datamodel.IMPULSIVE,
 | 
						|
                        seiscomp.datamodel.QUESTIONABLE,
 | 
						|
                    ]:
 | 
						|
                        if value == seiscomp.datamodel.EPickOnsetNames.name(onset):
 | 
						|
                            pick.setOnset(onset)
 | 
						|
                            found = True
 | 
						|
                            break
 | 
						|
                    if not found:
 | 
						|
                        raise Exception("Unsupported onset value")
 | 
						|
 | 
						|
                # phase code
 | 
						|
                elif keyLower == "phase name":
 | 
						|
                    phase = seiscomp.datamodel.Phase()
 | 
						|
                    phase.setCode(value)
 | 
						|
                    pick.setPhaseHint(phase)
 | 
						|
 | 
						|
                # event type
 | 
						|
                elif keyLower == "event type":
 | 
						|
                    evttype = EventTypes[value]
 | 
						|
                    event.setType(evttype)
 | 
						|
                    originComments[key] = value
 | 
						|
 | 
						|
                # filter ID
 | 
						|
                elif keyLower == "applied filter":
 | 
						|
                    pick.setFilterID(value)
 | 
						|
 | 
						|
                # channel code, prepended by configured Channel prefix if only
 | 
						|
                # one character is found
 | 
						|
                elif keyLower == "component":
 | 
						|
                    compCode = value
 | 
						|
 | 
						|
                # pick evaluation mode
 | 
						|
                elif keyLower == "pick type":
 | 
						|
                    found = False
 | 
						|
                    for mode in [
 | 
						|
                        seiscomp.datamodel.AUTOMATIC,
 | 
						|
                        seiscomp.datamodel.MANUAL,
 | 
						|
                    ]:
 | 
						|
                        if value == seiscomp.datamodel.EEvaluationModeNames.name(mode):
 | 
						|
                            pick.setEvaluationMode(mode)
 | 
						|
                            found = True
 | 
						|
                            break
 | 
						|
                    if not found:
 | 
						|
                        raise Exception("Unsupported evaluation mode value")
 | 
						|
 | 
						|
                # pick author
 | 
						|
                elif keyLower == "analyst":
 | 
						|
                    creationInfo = seiscomp.datamodel.CreationInfo()
 | 
						|
                    creationInfo.setAuthor(value)
 | 
						|
                    pick.setCreationInfo(creationInfo)
 | 
						|
 | 
						|
                # pick polarity
 | 
						|
                # isn't tested
 | 
						|
                elif keyLower == "sign":
 | 
						|
                    if value == "positive":
 | 
						|
                        sign = "0"  # positive
 | 
						|
                    elif value == "negative":
 | 
						|
                        sign = "1"  # negative
 | 
						|
                    else:
 | 
						|
                        sign = "2"  # unknown
 | 
						|
                    pick.setPolarity(float(sign))
 | 
						|
 | 
						|
                # arrival weight
 | 
						|
                elif keyLower == "weight":
 | 
						|
                    arrival.setWeight(float(value))
 | 
						|
 | 
						|
                # arrival azimuth
 | 
						|
                elif keyLower == "theo. azimuth (deg)":
 | 
						|
                    arrival.setAzimuth(float(value))
 | 
						|
 | 
						|
                # pick theo backazimuth
 | 
						|
                elif keyLower == "theo. backazimuth (deg)":
 | 
						|
                    if pick.slownessMethodID() == "corrected":
 | 
						|
                        seiscomp.logging.debug(
 | 
						|
                            f"Line {iLine}: ignoring parameter: {key}"
 | 
						|
                        )
 | 
						|
                    else:
 | 
						|
                        pick.setBackazimuth(
 | 
						|
                            seiscomp.datamodel.RealQuantity(float(value))
 | 
						|
                        )
 | 
						|
                        pick.setSlownessMethodID("theoretical")
 | 
						|
 | 
						|
                # pick beam slowness
 | 
						|
                elif keyLower == "beam-slowness (sec/deg)":
 | 
						|
                    if pick.slownessMethodID() == "corrected":
 | 
						|
                        seiscomp.logging.debug(
 | 
						|
                            f"Line {iLine}: ignoring parameter: {key}"
 | 
						|
                        )
 | 
						|
                    else:
 | 
						|
                        pick.setHorizontalSlowness(
 | 
						|
                            seiscomp.datamodel.RealQuantity(float(value))
 | 
						|
                        )
 | 
						|
                        pick.setSlownessMethodID("Array Beam")
 | 
						|
 | 
						|
                # pick beam backazimuth
 | 
						|
                elif keyLower == "beam-azimuth (deg)":
 | 
						|
                    if pick.slownessMethodID() == "corrected":
 | 
						|
                        seiscomp.logging.debug(
 | 
						|
                            f"Line {iLine}: ignoring parameter: {key}"
 | 
						|
                        )
 | 
						|
                    else:
 | 
						|
                        pick.setBackazimuth(
 | 
						|
                            seiscomp.datamodel.RealQuantity(float(value))
 | 
						|
                        )
 | 
						|
 | 
						|
                # pick epi slowness
 | 
						|
                elif keyLower == "epi-slowness (sec/deg)":
 | 
						|
                    pick.setHorizontalSlowness(
 | 
						|
                        seiscomp.datamodel.RealQuantity(float(value))
 | 
						|
                    )
 | 
						|
                    pick.setSlownessMethodID("corrected")
 | 
						|
 | 
						|
                # pick epi backazimuth
 | 
						|
                elif keyLower == "epi-azimuth (deg)":
 | 
						|
                    pick.setBackazimuth(seiscomp.datamodel.RealQuantity(float(value)))
 | 
						|
 | 
						|
                # arrival distance degree
 | 
						|
                elif keyLower == "distance (deg)":
 | 
						|
                    arrival.setDistance(float(value))
 | 
						|
 | 
						|
                # arrival distance km, recalculates for degree
 | 
						|
                elif keyLower == "distance (km)":
 | 
						|
                    if isinstance(arrival.distance(), float):
 | 
						|
                        seiscomp.logging.debug(
 | 
						|
                            f"Line {iLine - 1}: ignoring parameter: distance (deg)"
 | 
						|
                        )
 | 
						|
                    arrival.setDistance(float(value) * km2degFac)
 | 
						|
 | 
						|
                # arrival time residual
 | 
						|
                elif keyLower == "residual time":
 | 
						|
                    arrival.setTimeResidual(float(value))
 | 
						|
 | 
						|
                # amplitude snr
 | 
						|
                elif keyLower == "signal/noise":
 | 
						|
                    amplitudeSNR = seiscomp.datamodel.Amplitude.Create()
 | 
						|
                    amplitudeSNR.setType("SNR")
 | 
						|
                    amplitudeSNR.setAmplitude(
 | 
						|
                        seiscomp.datamodel.RealQuantity(float(value))
 | 
						|
                    )
 | 
						|
 | 
						|
                # amplitude period
 | 
						|
                elif keyLower.startswith("period"):
 | 
						|
                    ampPeriod = float(value)
 | 
						|
 | 
						|
                # amplitude value for displacement
 | 
						|
                elif keyLower == "amplitude (nm)":
 | 
						|
                    amplitudeDisp = seiscomp.datamodel.Amplitude.Create()
 | 
						|
                    amplitudeDisp.setAmplitude(
 | 
						|
                        seiscomp.datamodel.RealQuantity(float(value))
 | 
						|
                    )
 | 
						|
                    amplitudeDisp.setUnit("nm")
 | 
						|
 | 
						|
                # amplitude value for velocity
 | 
						|
                elif keyLower.startswith("vel. amplitude"):
 | 
						|
                    amplitudeVel = seiscomp.datamodel.Amplitude.Create()
 | 
						|
                    amplitudeVel.setAmplitude(
 | 
						|
                        seiscomp.datamodel.RealQuantity(float(value))
 | 
						|
                    )
 | 
						|
                    amplitudeVel.setUnit("nm/s")
 | 
						|
 | 
						|
                elif keyLower == "bb amplitude (nm/sec)":
 | 
						|
                    amplitudeBB = seiscomp.datamodel.Amplitude.Create()
 | 
						|
                    amplitudeBB.setAmplitude(
 | 
						|
                        seiscomp.datamodel.RealQuantity(float(value))
 | 
						|
                    )
 | 
						|
                    amplitudeBB.setType("mB")
 | 
						|
                    amplitudeBB.setUnit("nm/s")
 | 
						|
                    amplitudeBB.setPeriod(seiscomp.datamodel.RealQuantity(ampBBPeriod))
 | 
						|
 | 
						|
                elif keyLower == "bb period (sec)":
 | 
						|
                    ampBBPeriod = float(value)
 | 
						|
 | 
						|
                elif keyLower == "broadband magnitude":
 | 
						|
                    magType = self.parseMagType("bb")
 | 
						|
                    stationMagBB = seiscomp.datamodel.StationMagnitude.Create()
 | 
						|
                    stationMagBB.setMagnitude(
 | 
						|
                        seiscomp.datamodel.RealQuantity(float(value))
 | 
						|
                    )
 | 
						|
                    stationMagBB.setType(magType)
 | 
						|
                    stationMagBB.setAmplitudeID(amplitudeBB.publicID())
 | 
						|
 | 
						|
                # ignored
 | 
						|
                elif keyLower == "quality number":
 | 
						|
                    seiscomp.logging.debug(f"Line {iLine}: ignoring parameter: {key}")
 | 
						|
 | 
						|
                # station magnitude value and type
 | 
						|
                elif keyLower.startswith("magnitude "):
 | 
						|
                    magType = self.parseMagType(key[10:])
 | 
						|
                    stationMag = seiscomp.datamodel.StationMagnitude.Create()
 | 
						|
                    stationMag.setMagnitude(
 | 
						|
                        seiscomp.datamodel.RealQuantity(float(value))
 | 
						|
                    )
 | 
						|
 | 
						|
                    if len(magType) > 0:
 | 
						|
                        stationMag.setType(magType)
 | 
						|
                    if magType == "mb":
 | 
						|
                        stationMag.setAmplitudeID(amplitudeDisp.publicID())
 | 
						|
 | 
						|
                    elif magType == "MS(BB)":
 | 
						|
                        stationMag.setAmplitudeID(amplitudeVel.publicID())
 | 
						|
                    else:
 | 
						|
                        seiscomp.logging.debug(
 | 
						|
                            f"Line {iLine}: Magnitude Type not known {magType}."
 | 
						|
                        )
 | 
						|
 | 
						|
                ###############################################################
 | 
						|
                # origin parameters
 | 
						|
 | 
						|
                # event ID, added as origin comment later on
 | 
						|
                elif keyLower == "event id":
 | 
						|
                    originComments[key] = value
 | 
						|
 | 
						|
                # magnitude value and type
 | 
						|
                elif keyLower == "mean bb magnitude":
 | 
						|
                    magType = self.parseMagType("bb")
 | 
						|
                    if magnitudeBB is None:
 | 
						|
                        magnitudeBB = seiscomp.datamodel.Magnitude.Create()
 | 
						|
                    magnitudeBB.setMagnitude(
 | 
						|
                        seiscomp.datamodel.RealQuantity(float(value))
 | 
						|
                    )
 | 
						|
                    magnitudeBB.setType(magType)
 | 
						|
 | 
						|
                elif keyLower.startswith("mean magnitude "):
 | 
						|
                    magType = self.parseMagType(key[15:])
 | 
						|
 | 
						|
                    if magType == "ML":
 | 
						|
                        if magnitudeML is None:
 | 
						|
                            magnitudeML = seiscomp.datamodel.Magnitude.Create()
 | 
						|
                        magnitudeML.setMagnitude(
 | 
						|
                            seiscomp.datamodel.RealQuantity(float(value))
 | 
						|
                        )
 | 
						|
                        magnitudeML.setType(magType)
 | 
						|
 | 
						|
                    elif magType == "Ms(BB)":
 | 
						|
                        if magnitudeMS is None:
 | 
						|
                            magnitudeMS = seiscomp.datamodel.Magnitude.Create()
 | 
						|
                        magnitudeMS.setMagnitude(
 | 
						|
                            seiscomp.datamodel.RealQuantity(float(value))
 | 
						|
                        )
 | 
						|
                        magnitudeMS.setType(magType)
 | 
						|
 | 
						|
                    elif magType == "mb":
 | 
						|
                        if magnitudeMB is None:
 | 
						|
                            magnitudeMB = seiscomp.datamodel.Magnitude.Create()
 | 
						|
                        magnitudeMB.setMagnitude(
 | 
						|
                            seiscomp.datamodel.RealQuantity(float(value))
 | 
						|
                        )
 | 
						|
                        magnitudeMB.setType(magType)
 | 
						|
 | 
						|
                    else:
 | 
						|
                        seiscomp.logging.warning(
 | 
						|
                            f"Line {iLine}: Magnitude type {magType} not defined yet."
 | 
						|
                        )
 | 
						|
 | 
						|
                # latitude
 | 
						|
                elif keyLower == "latitude":
 | 
						|
                    origin.latitude().setValue(float(value))
 | 
						|
                    latFound = True
 | 
						|
                elif keyLower == "error in latitude (km)":
 | 
						|
                    origin.latitude().setUncertainty(float(value))
 | 
						|
 | 
						|
                # longitude
 | 
						|
                elif keyLower == "longitude":
 | 
						|
                    origin.longitude().setValue(float(value))
 | 
						|
                    lonFound = True
 | 
						|
                elif keyLower == "error in longitude (km)":
 | 
						|
                    origin.longitude().setUncertainty(float(value))
 | 
						|
 | 
						|
                # depth
 | 
						|
                elif keyLower == "depth (km)":
 | 
						|
                    origin.setDepth(seiscomp.datamodel.RealQuantity(float(value)))
 | 
						|
                    if depthError is not None:
 | 
						|
                        origin.depth().setUncertainty(depthError)
 | 
						|
                elif keyLower == "depth type":
 | 
						|
                    seiscomp.logging.debug(f"Line {iLine}: ignoring parameter: {key}")
 | 
						|
                elif keyLower == "error in depth (km)":
 | 
						|
                    depthError = float(value)
 | 
						|
                    try:
 | 
						|
                        origin.depth().setUncertainty(depthError)
 | 
						|
                    except seiscomp.core.ValueException:
 | 
						|
                        pass
 | 
						|
 | 
						|
                # time
 | 
						|
                elif keyLower == "origin time":
 | 
						|
                    origin.time().setValue(self.parseTime(value))
 | 
						|
                elif keyLower == "error in origin time":
 | 
						|
                    origin.time().setUncertainty(float(value))
 | 
						|
 | 
						|
                # location method
 | 
						|
                elif keyLower == "location method":
 | 
						|
                    origin.setMethodID(str(value))
 | 
						|
 | 
						|
                # region table, added as origin comment later on
 | 
						|
                elif keyLower == "region table":
 | 
						|
                    originComments[key] = value
 | 
						|
 | 
						|
                # region table, added as origin comment later on
 | 
						|
                elif keyLower == "region id":
 | 
						|
                    originComments[key] = value
 | 
						|
 | 
						|
                # source region, added as origin comment later on
 | 
						|
                elif keyLower == "source region":
 | 
						|
                    originComments[key] = value
 | 
						|
 | 
						|
                # used station count
 | 
						|
                elif keyLower == "no. of stations used":
 | 
						|
                    if originQuality is None:
 | 
						|
                        originQuality = seiscomp.datamodel.OriginQuality()
 | 
						|
                    originQuality.setUsedStationCount(int(value))
 | 
						|
 | 
						|
                # ignored
 | 
						|
                elif keyLower == "reference location name":
 | 
						|
                    seiscomp.logging.debug(f"Line {iLine}: ignoring parameter: {key}")
 | 
						|
 | 
						|
                # confidence ellipsoid major axis
 | 
						|
                elif keyLower == "error ellipse major":
 | 
						|
                    if originCE is None:
 | 
						|
                        originCE = seiscomp.datamodel.ConfidenceEllipsoid()
 | 
						|
                    originCE.setSemiMajorAxisLength(float(value))
 | 
						|
 | 
						|
                # confidence ellipsoid minor axis
 | 
						|
                elif keyLower == "error ellipse minor":
 | 
						|
                    if originCE is None:
 | 
						|
                        originCE = seiscomp.datamodel.ConfidenceEllipsoid()
 | 
						|
                    originCE.setSemiMinorAxisLength(float(value))
 | 
						|
 | 
						|
                # confidence ellipsoid rotation
 | 
						|
                elif keyLower == "error ellipse strike":
 | 
						|
                    if originCE is None:
 | 
						|
                        originCE = seiscomp.datamodel.ConfidenceEllipsoid()
 | 
						|
                    originCE.setMajorAxisRotation(float(value))
 | 
						|
 | 
						|
                # azimuthal gap
 | 
						|
                elif keyLower == "max azimuthal gap (deg)":
 | 
						|
                    if originQuality is None:
 | 
						|
                        originQuality = seiscomp.datamodel.OriginQuality()
 | 
						|
                    originQuality.setAzimuthalGap(float(value))
 | 
						|
 | 
						|
                # creation info author
 | 
						|
                elif keyLower == "author":
 | 
						|
                    origin.creationInfo().setAuthor(value)
 | 
						|
 | 
						|
                # creation info agency
 | 
						|
                elif keyLower == "source of information":
 | 
						|
                    origin.creationInfo().setAgencyID(value)
 | 
						|
 | 
						|
                # earth model id
 | 
						|
                elif keyLower == "velocity model":
 | 
						|
                    origin.setEarthModelID(value)
 | 
						|
 | 
						|
                # standard error
 | 
						|
                elif keyLower == "rms of residuals (sec)":
 | 
						|
                    if originQuality is None:
 | 
						|
                        originQuality = seiscomp.datamodel.OriginQuality()
 | 
						|
                    originQuality.setStandardError(float(value))
 | 
						|
 | 
						|
                # ignored
 | 
						|
                elif keyLower == "phase flags":
 | 
						|
                    seiscomp.logging.debug(f"Line {iLine}: ignoring parameter: {key}")
 | 
						|
 | 
						|
                # ignored
 | 
						|
                elif keyLower == "location input params":
 | 
						|
                    seiscomp.logging.debug(f"Line {iLine}: ignoring parameter: {key}")
 | 
						|
 | 
						|
                # missing keys
 | 
						|
                elif keyLower == "ampl&period source":
 | 
						|
                    seiscomp.logging.debug(f"Line {iLine}: ignoring parameter: {key}")
 | 
						|
 | 
						|
                elif keyLower == "location quality":
 | 
						|
                    seiscomp.logging.debug(f"Line {iLine}: ignoring parameter: {key}")
 | 
						|
 | 
						|
                elif keyLower == "reference latitude":
 | 
						|
                    seiscomp.logging.debug(f"Line {iLine}: ignoring parameter: {key}")
 | 
						|
 | 
						|
                elif keyLower == "reference longitude":
 | 
						|
                    seiscomp.logging.debug(f"Line {iLine}: ignoring parameter: {key}")
 | 
						|
 | 
						|
                elif keyLower.startswith("amplitude time"):
 | 
						|
                    seiscomp.logging.debug(f"Line {iLine}: ignoring parameter: {key}")
 | 
						|
 | 
						|
                # unknown key
 | 
						|
                else:
 | 
						|
                    seiscomp.logging.warning(
 | 
						|
                        "Line {iLine}: ignoring unknown parameter: {key}"
 | 
						|
                    )
 | 
						|
 | 
						|
            except ValueError:
 | 
						|
                seiscomp.logging.warning(f"Line {iLine}: can not parse {key} value")
 | 
						|
            except Exception:
 | 
						|
                seiscomp.logging.error("Line {iLine}: {str(traceback.format_exc())}")
 | 
						|
                return None
 | 
						|
 | 
						|
        # check
 | 
						|
        if not latFound:
 | 
						|
            seiscomp.logging.warning("could not add origin, missing latitude parameter")
 | 
						|
        elif not lonFound:
 | 
						|
            seiscomp.logging.warning(
 | 
						|
                "could not add origin, missing longitude parameter"
 | 
						|
            )
 | 
						|
        elif not origin.time().value().valid():
 | 
						|
            seiscomp.logging.warning(
 | 
						|
                "could not add origin, missing origin time parameter"
 | 
						|
            )
 | 
						|
        else:
 | 
						|
            if magnitudeMB is not None:
 | 
						|
                origin.add(magnitudeMB)
 | 
						|
            if magnitudeML is not None:
 | 
						|
                origin.add(magnitudeML)
 | 
						|
            if magnitudeMS is not None:
 | 
						|
                origin.add(magnitudeMS)
 | 
						|
            if magnitudeBB is not None:
 | 
						|
                origin.add(magnitudeBB)
 | 
						|
 | 
						|
            ep.add(event)
 | 
						|
            ep.add(origin)
 | 
						|
 | 
						|
            if originQuality is not None:
 | 
						|
                origin.setQuality(originQuality)
 | 
						|
 | 
						|
            if originCE is not None:
 | 
						|
                uncertainty = seiscomp.datamodel.OriginUncertainty()
 | 
						|
                uncertainty.setConfidenceEllipsoid(originCE)
 | 
						|
                origin.setUncertainty(uncertainty)
 | 
						|
 | 
						|
            for k, v in originComments.items():
 | 
						|
                comment = seiscomp.datamodel.Comment()
 | 
						|
                comment.setId(k)
 | 
						|
                comment.setText(v)
 | 
						|
                origin.add(comment)
 | 
						|
 | 
						|
        return ep
 | 
						|
 | 
						|
    ###########################################################################
 | 
						|
    def run(self):
 | 
						|
        self.loadStreams()
 | 
						|
 | 
						|
        try:
 | 
						|
            if self.inputFile == "-":
 | 
						|
                f = sys.stdin
 | 
						|
            else:
 | 
						|
                f = open(self.inputFile)
 | 
						|
        except IOError as e:
 | 
						|
            seiscomp.logging.error(str(e))
 | 
						|
            return False
 | 
						|
 | 
						|
        ep = self.sh2proc(f)
 | 
						|
        if ep is None:
 | 
						|
            return False
 | 
						|
 | 
						|
        ar = seiscomp.io.XMLArchive()
 | 
						|
        ar.create("-")
 | 
						|
        ar.setFormattedOutput(True)
 | 
						|
        ar.writeObject(ep)
 | 
						|
        ar.close()
 | 
						|
 | 
						|
        return True
 | 
						|
 | 
						|
 | 
						|
###############################################################################
 | 
						|
def main():
 | 
						|
    try:
 | 
						|
        app = SH2Proc()
 | 
						|
        return app()
 | 
						|
    except:
 | 
						|
        sys.stderr.write(str(traceback.format_exc()))
 | 
						|
 | 
						|
    return 1
 | 
						|
 | 
						|
 | 
						|
if __name__ == "__main__":
 | 
						|
    sys.exit(main())
 | 
						|
 | 
						|
 | 
						|
# vim: ts=4 et
 |