source: SHX/trunk/SeismicHandler/modules/traces.py @ 1127

Revision 1127, 12.1 KB checked in by klaus, 4 years ago (diff)

thread-safe access to stations DB using singleton class copies

  • Property svn:eol-style set to native
Line 
1# -*- coding: utf-8 -*-
2
3#    This file is part of Seismic Handler eXtended (SHX). For terms of use and
4#    license information please see license.txt and visit
5#    http://www.seismic-handler.org/portal/wiki/Shx/LicenseTerms
6
7import numpy as np
8from obspy.core.util import AttribDict
9from SeismicHandler.basics import Singleton, timeit, timestore
10from SeismicHandler.basics.messages import log_message, subscribe_event
11# disabled: _set_runtime_var
12from obspy.core.stream import Stream, Trace
13from SeismicHandler.config import Settings, set_dsptrcs, set_tottrcs
14from SeismicHandler.modules.stations import Stations
15
16
17META_STATUS_EMPTY    = 0
18META_STATUS_LOCATION = 1
19META_STATUS_COMPLETE = 2
20
21
22class Traces(object):
23    """
24    Supply access for two streams of traces. One is used for plotting, the other
25    holds hidden traces. This will be enhanced later, but for compatibility
26    reasons to existing command procedures it's done that way first.
27    """
28    __metaclass__ = Singleton
29
30    traces = AttribDict()
31
32    def __init__(self, *args, **kwargs):
33        self.traces.visible = Stream()
34        self.traces.hidden = Stream()
35        self.traces.created = Stream()
36        self.traces.overlays = OverlayTraces()
37        self.netdict = {}
38
39    def __getattr__(self, name):
40        try:
41            return self.__class__.__dict__["traces"][name]
42        except:
43            raise AttributeError(name)
44
45    #@timeit
46    def addreplace(self, stream, applyGain=False):
47        """
48        Adds or replaces visible streams depending on runtime setting.
49        """
50        # this is always a global switch!
51        if not Settings.swKeeptraces:
52            log_message("debug.traces", "replacing visible traces")
53            self.traces.visible.clear()
54
55        # check if traces contains relevant information
56        # single trace
57        if isinstance(stream, Trace):
58            stream = Stream(traces=[stream])
59        if isinstance(stream, list):
60            stream = Stream(traces=stream)
61
62        for t in stream:
63            t.establish()
64            t.invalidate_cache()
65            self.cacheNetcode(t)
66
67            if applyGain:
68                # get gain from inventory and apply it to data
69                gain = None
70                try:
71                    gain = float(Stations()[t.id, t.stats.starttime].gain)
72                except KeyError:
73                    msg = "no meta data stored for station '%s' at %s"
74                    log_message("info.traces", msg % (t.id, t.get_info("start")))
75                # data from trace itself, this will override stored information
76                try:
77                    gain = 1e9/t.stats.paz.sensitivity
78                    msg = "gain information attached to trace: %f"
79                    log_message("info.traces", msg % gain)
80                except:
81                    pass
82
83                if gain is None:
84                    msg = "No gain configured for %s at %s"
85                    log_message("warning.traces", msg % (t.id, t.get_info("start")))
86                    gain = 1.
87
88                t.data *= gain
89                msg = "Applied gain factor of %f to %s"
90                log_message("debug.traces", msg % (gain, t.id))
91           
92            t.set_info( 'METASTATUS', self.getMetaStatus(t) )
93       
94        self.traces.visible += stream
95        self.traces.created.clear()
96        self.traces.created += stream
97        self.updateCounter()
98
99    #@timeit
100    def updateCounter(self):
101        dsptrcs = len(self.traces.visible)
102        hidtrcs = len(self.traces.hidden)
103        log_message("info.traces", "Now %u visible and %u hidden traces" % (
104                                                             dsptrcs, hidtrcs))
105
106        #_set_runtime_var("dsptrcs", dsptrcs)
107        #_set_runtime_var("tottrcs", dsptrcs+hidtrcs)
108        set_dsptrcs( dsptrcs )
109        set_tottrcs( dsptrcs+hidtrcs )
110
111    #@timeit
112    def visible_index(self, trace):
113        """
114        Returns position of trace inside visible traces list.
115        """
116        return self.traces.visible.traces.index(trace)
117   
118    def cacheNetcode( self, trace ):
119        """Store netcodes for known stations. Overwrites possibly existing
120        old entries.
121        """
122        upstat = trace.stats.station.upper()
123        self.netdict[upstat] = trace.stats.network.upper()
124   
125    def getNetcode( self, station ):
126        return self.netdict.get( station.upper(), None )
127   
128    def getMetaStatus( self, trace ):
129        "Returns 0, 1 or 2 depending on metadata status."
130        stations = Stations()
131        sname = "%s.%s.%s.%s" % (trace.stats.network, trace.stats.station,
132            trace.stats.location, trace.stats.channel)
133        metatime = trace.stats.starttime \
134            + (trace.stats.endtime-trace.stats.starttime)/2
135        try:
136            r = stations[(sname, metatime)]
137        except:
138            return META_STATUS_EMPTY
139        try:
140            slat = float( r.latitude )
141            slon = float( r.longitude )
142        except:
143            return META_STATUS_EMPTY
144        try:
145            gain = float( r.gain )
146        except:
147            return META_STATUS_LOCATION
148        if gain == 1.0:
149            return META_STATUS_LOCATION
150        if r.poles and r.poles != '[]':
151            return META_STATUS_COMPLETE
152        else:
153            return META_STATUS_LOCATION
154       
155
156
157class OverlayTraces:
158    """
159    Store info about traces drawn above others.
160    Example:
161        overlaydict = {'A':[1,2],'B':[3,4]}
162        allidx = [1,2,3,4]
163    """
164   
165    def __init__( self ):
166        self.clearOverlays()
167       
168    def numberOfOverlays( self ):
169        return (len(self.allidx) - len(self.overlaydict))
170   
171    def addOverlay( self, idxlist ):
172        if len(idxlist) < 2:
173            print "should not happen: short overlay list"
174            return
175        # Check for already specified indices, no index may occur twice.
176        for i in idxlist:
177            if i in self.allidx:
178                print "trace '%s' already in overlay" % i
179                return
180        self.overlaydict[self.nextname] = idxlist
181        self.nextname = chr(ord(self.nextname)+1)
182        self.allidx += idxlist
183        #self.dump()
184   
185    def clearOverlays( self ):
186        self.overlaydict = {}
187        self.allidx = []
188        self.nextname = 'A'
189        self.initPlot(0,0)
190   
191    def initPlot( self, numtraces, traceorder ):
192        "Reset plot parameters before each redraw."
193        self.posdict = {}
194        self.posstore = []
195        self.posidx = -1
196        self.numtraces = numtraces
197        self.traceorder = traceorder
198   
199    def plotPos( self, trcidx, ypos ):
200        "Change plot positions of traces according to overlays."
201        if len(self.overlaydict.keys()) == 0:
202            return ypos
203        if self.traceorder == 0:
204            # traces always plotted top-down
205            trcidx = self.numtraces + 1 - trcidx
206        self.posstore.append( ypos )
207        for ovk in self.overlaydict.keys():
208            if trcidx in self.overlaydict[ovk]:
209                if ovk in self.posdict.keys():
210                    return self.posdict[ovk]
211                else:
212                    self.posidx += 1
213                    self.posdict[ovk] = self.posstore[self.posidx]
214                    return self.posstore[self.posidx]
215        self.posidx += 1
216        return self.posstore[self.posidx]
217   
218    def dump( self ):
219        print 'overlay dict', self.overlaydict
220        print 'overlay allidx', self.allidx
221        print 'overlay pos', self.posdict
222
223#@timeit
224def traces_from_list(selection):
225    """
226    Expand comma separated list of traces into Stream of internal traces.
227
228    Final Stream may contain visible and hidden traces!
229    """
230
231    if isinstance(selection, int):
232        selection = str(selection)
233
234    selection = selection.lower()
235    traces = Traces()
236
237    if selection.startswith("h:"):
238        parents = [traces.traces.hidden]
239        selection = selection[2:]
240    else:
241        parents = [traces.traces.visible]
242
243    selection = selection.split(",")
244
245    if "all" in selection:
246        return parents[0][:]
247
248    if selection[0].startswith("_"):
249        identifier = selection[0][1:].lower()
250
251        if identifier == "created":
252            return [t for t in traces.traces.created]
253
254        # pay attention to visible and invisible traces!
255        # this makes no sense in all cases but sometimes it does...
256        parents = [traces.traces.visible, traces.traces.hidden]
257
258        # get limitations
259        identifier = identifier.split("(")
260        if len(identifier) != 2:
261            return []
262
263        limits = identifier[1][:-1].split(":")
264        if len(limits) == 1:
265            limits.append(limits[0])
266
267        if identifier[0].endswith("~"):
268            ident = identifier[0][:-1]
269            negate = True
270        else:
271            ident = identifier[0]
272            negate = False
273
274        straces = []
275        for tl in parents:
276            for t in tl:
277                try:
278                    check = t.get_info(ident)
279                except:
280                    continue
281                if hasattr(check,'lower'):
282                    check = check.lower()
283               
284                found = False
285                try:
286                    if check >= type(check)(limits[0]) and \
287                                               check <= type(check)(limits[1]):
288                        found = True
289                except:
290                    continue
291
292                if found == negate:
293                    continue
294
295                straces.append(t)
296
297        return straces
298
299    numbers = expandTracelist(selection)
300
301    straces = []
302    for i in numbers:
303        try:
304            # get trace
305            straces.append(parents[0][i])
306        except IndexError:
307            log_message("warning", "trace number %s not found" % (i+1))
308
309    return Stream(straces)
310
311
312#@timeit
313def expandTracelist(selection):
314    """
315    Returns number of trace selection, e.g. 1,3-5 results in [0,2,3,4]. The
316    special value "all" must be treated outside this method.
317
318    >>> expandTracelist("1,3")
319    [0, 2]
320    >>> expandTracelist("3-5,3")
321    [2, 3, 4]
322    >>> expandTracelist(["1", "8-11"])
323    [0, 7, 8, 9, 10]
324    """
325    if isinstance(selection, basestring):
326        selection = selection.split(",")
327
328    numbers = []
329    for s in selection:
330        # range
331        if "-" in s:
332            try:
333                start, stop = map(int, s.split("-"))
334            except:
335                print "  skipping '%s'" % s
336                continue
337
338            if stop < start:
339                start, stop = stop, start
340
341            numbers.extend(range(start - 1, stop))
342        else:
343            if s.isdigit():
344                numbers.append(int(s) - 1)
345            else:
346                print "  skipping '%s'" % s
347
348    # unique
349    numbers = list(set(numbers))
350    numbers.sort()
351
352    return numbers
353
354
355#@timeit
356def new_starttime(src, dst, offset):
357    """
358    Set new starttime with respect to offset, trace's T-ORIGIN and source
359    trace.
360    """
361    dst.stats.starttime = src.stats.starttime
362
363    t_origin = dst.get_info("t-origin")
364    corr = offset - t_origin
365    if corr > 0:
366        dst.stats.starttime += corr
367
368
369#@timeit
370def new_trace_data(length, zero=False):
371    """
372    Return numpy array of length "length". If "zero" is True, all values
373    will be set to 0.
374    """
375
376    if zero:
377        return np.zeros(length, dtype="float32")
378    else:
379        return np.empty(length, dtype="float32")
380
381
382def add_traces(stream, gain=False):
383    """
384    Helper method for "Traces().addreplace(...)
385    """
386    Traces().addreplace(stream, applyGain=gain)
387
388def get_meta_status( trace ):
389    return Traces().getMetaStatus( trace )
390
391
392#@timeit
393def invalidate_all_traces(payload=None):
394    """
395    Voids all trace caches.
396    """
397    for t in Traces().visible:
398        t.invalidate_cache()
399subscribe_event(invalidate_all_traces, "recache")
400
401def addNetcode( station ):
402    "Adds netcode to station if not yet specified and found in cache."
403    traces = Traces()
404    upstat = station.upper()
405    if '.' in station:
406        return upstat
407    netcode = traces.getNetcode( upstat )
408    if netcode == None:
409        return upstat
410    return "%s.%s" % (netcode,upstat)
411
412
413if __name__ == "__main__":
414    import doctest
415    doctest.testmod(exclude_empty=True)
Note: See TracBrowser for help on using the repository browser.