/**
* PhaseSignalToNoise.java
*
* @author Created by Omnicore CodeGuide
*/
package edu.sc.seis.sod.process.waveform;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import edu.iris.Fissures.FissuresException;
import edu.iris.Fissures.IfEvent.EventAccessOperations;
import edu.iris.Fissures.IfEvent.NoPreferredOrigin;
import edu.iris.Fissures.IfNetwork.Channel;
import edu.iris.Fissures.IfSeismogramDC.RequestFilter;
import edu.iris.Fissures.model.TimeInterval;
import edu.iris.Fissures.network.ChannelImpl;
import edu.iris.Fissures.seismogramDC.LocalSeismogramImpl;
import edu.sc.seis.TauP.TauModelException;
import edu.sc.seis.fissuresUtil.bag.LongShortTrigger;
import edu.sc.seis.fissuresUtil.bag.PhaseNonExistent;
import edu.sc.seis.fissuresUtil.bag.SimplePhaseStoN;
import edu.sc.seis.fissuresUtil.bag.TauPUtil;
import edu.sc.seis.fissuresUtil.cache.CacheEvent;
import edu.sc.seis.sod.ConfigurationException;
import edu.sc.seis.sod.CookieJar;
import edu.sc.seis.sod.SodUtil;
import edu.sc.seis.sod.Threadable;
import edu.sc.seis.sod.status.StringTreeLeaf;
/** Calculates triggers, via LongShortSignalToNoise, and checks to see if a
* trigger exists within +- the time interval for the given phase name. Uses the
* first phase returned, ignoring later phases, such as triplications.
*
* The first trigger within the time window of the phase, if there is one, is
* added to the cookieJar with key "sod_phaseStoN_"+phaseName for use by later
* subsetters or later velocity output. */
public class PhaseSignalToNoise implements WaveformProcess, Threadable {
public PhaseSignalToNoise(Element config) throws ConfigurationException, TauModelException{
NodeList childNodes = config.getChildNodes();
Node node;
for(int counter = 0; counter < childNodes.getLength(); counter++) {
node = childNodes.item(counter);
if(node instanceof Element) {
Element element = (Element)node;
if(element.getTagName().equals("phaseName")) {
phaseName = SodUtil.getNestedText(element);
} else if(element.getTagName().equals("modelName")) {
modelName = SodUtil.getNestedText(element);
}else if(element.getTagName().equals("ratio")) {
ratio = Float.parseFloat(SodUtil.getNestedText(element));
} else if(element.getTagName().equals("shortOffsetBegin")) {
shortOffsetBegin = SodUtil.loadTimeInterval(element);
} else if(element.getTagName().equals("shortOffsetEnd")) {
shortOffsetEnd = SodUtil.loadTimeInterval(element);
} else if(element.getTagName().equals("longOffsetBegin")) {
longOffsetBegin = SodUtil.loadTimeInterval(element);
} else if(element.getTagName().equals("longOffsetEnd")) {
longOffsetEnd = SodUtil.loadTimeInterval(element);
}
}
}
taupUtil = TauPUtil.getTauPUtil(modelName);
phaseStoN = new SimplePhaseStoN(phaseName,
shortOffsetBegin,
shortOffsetEnd,
longOffsetBegin,
longOffsetEnd,
taupUtil);
}
public boolean isThreadSafe(){
return true;
}
public WaveformResult accept(CacheEvent event,
ChannelImpl channel,
RequestFilter[] original,
RequestFilter[] available,
LocalSeismogramImpl[] seismograms,
CookieJar cookieJar) throws Exception {
if (seismograms.length == 0 ) {
return new WaveformResult(seismograms, new StringTreeLeaf(this, false, "no seismograms"));
}
try {
LongShortTrigger trigger = calcTrigger(event, channel, seismograms);
if (trigger != null) {
if (trigger.getValue() > ratio) {
cookieJar.put(getCookieName(), trigger);
return new WaveformResult(seismograms,
new StringTreeLeaf(this, true));
}
return new WaveformResult(seismograms,
new StringTreeLeaf(this, false, "trigger="+trigger.getValue()+" < "+ratio));
} else {
return new WaveformResult(seismograms,
new StringTreeLeaf(this, false, "trigger is null"));
}
} catch (PhaseNonExistent e) {
// no phase at this distance, just fail
return new WaveformResult(seismograms,
new StringTreeLeaf(this, false, "Phase does not exist"));
}
}
/** This method exists to make the trigger available to other subsetters
* or processors so they don't have to call accept, which adds it to the
* cookieJar. */
public LongShortTrigger calcTrigger(EventAccessOperations event,
Channel channel,
LocalSeismogramImpl[] seismograms) throws NoPreferredOrigin, FissuresException, PhaseNonExistent, TauModelException {
// find the first seismogram with a non-null trigger, probably the first
// that overlaps the timewindow, and return it.
for (int i = 0; i < seismograms.length; i++) {
LongShortTrigger trigger =
phaseStoN.process(channel.getSite().getLocation(), event.get_preferred_origin(), seismograms[i]);
if (trigger != null) { return trigger; }
}
return null;
}
public String getCookieName() {
return PHASE_STON_PREFIX+getPhaseName();
}
public String getPhaseName() {
return phaseName;
}
public String toString() {
return "PhaseSignalToNoise("+getPhaseName()+")";
}
public static final String PHASE_STON_PREFIX = "sod_phaseStoN_";
protected SimplePhaseStoN phaseStoN;
protected float ratio = 1.0f;
protected String phaseName;
protected TimeInterval shortOffsetBegin, shortOffsetEnd;
protected TimeInterval longOffsetBegin, longOffsetEnd;
protected String modelName = "prem";
protected TimeInterval triggerWindow;
protected TauPUtil taupUtil;
}