[seiscomp, scanloc] Install, add .gitignore
This commit is contained in:
962
bin/sh2proc
Executable file
962
bin/sh2proc
Executable file
@ -0,0 +1,962 @@
|
||||
#!/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"""
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
Read Seismic Handler data from stdin. Provide inventory and configuration in XML
|
||||
cat shm.evt | sh2proc --inventory-db=inventory.xml --config-db=config.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
|
Reference in New Issue
Block a user