source: SHX/trunk/src/SeismicHandler/core/modules/Trace.py @ 153

Revision 153, 10.0 KB checked in by marcus, 14 years ago (diff)
  • final renaming Events to Messages
  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Rev Id Date
Line 
1# -*- coding: utf-8 -*-
2#
3# Copyright (C) 2008-2010 Marcus Walther (walther@szgrf.bgr.de)
4#
5# This file is part of Seismic Handler eXtended (SHX)
6# Full details can be found at project website http://www.seismic-handler.org/
7#
8# SHX is free software; you can redistribute it and/or modify
9# it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as published by
10# the Free Software Foundation; either version 3 of the License, or
11# (at your option) any later version.
12#
13# SHX is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16# GNU Lesser General Public License for more details.
17#
18# You should have received a copy of the GNU Lesser General Public License
19# along with SHX (see license.txt); if not, write to the Free Software
20# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
21
22"""Trace class
23
24    - access to trace (meta)data
25"""
26
27from SeismicHandler.core.shlib import shlib
28from SeismicHandler.core.log import logging
29from SeismicHandler.core.modules.Messages import MessageService, Message
30from SeismicHandler.core.shheaders import MEMBLC, EMPTYVALUES
31from SeismicHandler.core.error import InfoEntryNotFoundError, InfoEntryReadOnlyError
32
33from uuid import uuid4 as uuid
34import ctypes as C
35import numpy as np
36
37# trace info entry flags
38NOCOPY = 0x0800
39RDONLY = 0x0400
40
41class Trace(object):
42    """trace object
43
44        blubb XXX
45    """
46
47    def __init__(self, **kwargs):
48        # holds info entries
49        self._info = {}
50
51        self.logger = logging.getLogger("modules.trace")
52
53        # unique id
54        self.id = uuid().hex
55
56        # update info from memblc / address info supplied
57        if kwargs.has_key("memblc"):
58            self.address = kwargs.get("address")
59            self.update(memblc=kwargs.get("memblc"))
60
61    def __len__(self):
62        """return number of samples"""
63
64        return self.length
65
66    def __getattr__(self, name):
67        """allow access to trace metadata by querying as attribute
68
69        """
70
71        # check for really existing class attribute
72        if name in self.__dict__:
73            return self.__dict__[name]
74
75        # try to return info entry
76        try:
77            return self.__dict__["_info"][name.upper()]
78        except KeyError:
79            raise InfoEntryNotFoundError(name)
80
81        return None
82
83    def __setattr__(self, name, value):
84        idxmap = shlib().idx
85
86        try:
87            # if not found in info entry list set it as normal class attribute
88            if not name.upper() in idxmap:
89                self.__dict__[name] = value
90            else:
91                # check for readonly attributes
92                if idxmap[name.upper()][1] & RDONLY:
93                    raise InfoEntryReadOnlyError(name.upper())
94
95                self.__dict__["_info"][name] = value
96                shlib().setInfoEntry(self.address, name, value)
97
98        except KeyError:
99            self.__dict__[name] = value
100
101    def plot(self):
102        self.logger.info("plot")
103
104        from SeismicHandler.core.modules.Graphics import pylab
105
106        # get data into array
107        pylab.title(self.station)
108        pylab.plot(np.arange(0, self.delta * self.length, self.delta), self.fetchData(), "k")
109        pylab.grid()
110        pylab.show()
111        pylab.close(1)
112
113        del pylab
114
115    def fetchData(self):
116        return (C.c_float * self.length).from_address(self.data)
117
118    def delete(self):
119        self.logger.info("delete")
120
121    def listInfo(self):
122        print "info entries for trace id %s, address %u" % (self.id, self.address)
123        for i in sorted(self._info.keys()):
124            print i, self._info[i]
125
126    def update(self, **kwargs):
127        if not kwargs.has_key("memblc"):
128            raise NotImplementedError("Only updating from memory block supported.")
129
130        self.logger.info("updating from memory block, trace id %s, address %u" % (self.id, self.address))
131
132        mb = kwargs.get("memblc")
133
134        idxmap = shlib().idx
135
136        info = {}
137        # read all info entries
138        for e in idxmap:
139            dattype = idxmap[e][0]
140            abbr = dattype[0]
141            idx =  idxmap[e][2]
142
143            if dattype == "flag":
144                value = mb.fm & (1 << idx)
145            elif dattype == "xtra":
146                # XXX todo read special!
147                # in fact this is only some formatted output of standard entries
148                continue
149            elif dattype == "char":
150                value = chr(mb.cm[idx])
151            else:
152                value = getattr(mb, "%cm" % abbr)[idx]
153
154            try:
155                value = value.value
156            except AttributeError:
157                # leave value as it is
158                pass
159
160            # check for empty values
161            if dattype == "real":
162                if value - EMPTYVALUES[dattype] <= EMPTYVALUES["real_epsilon"]:
163                    value = None
164            elif dattype == "flag":
165                # flags should remain 0 aka "False"
166                pass
167            else:
168                if value == EMPTYVALUES[dattype]:
169                    value = None
170
171            info[e] = value
172
173        self._info = info
174#        self.listInfo()
175
176class Traces(object):
177    """ traces manipulation wrapper to shlib
178
179        - map sh memory traces to python structure
180    """
181
182    # helds addresses and corresponding trace instances
183    traces = []
184    tracedata = {}
185    progress = 0
186
187    def __init__(self):
188        self.sh = shlib().sh
189        MessageService.subscribe(Message(MessageService.REDRAW), self.update)
190        MessageService.subscribe(Message(MessageService.TRACEUPDATE), self.update)
191        self.logger = logging.getLogger("modules.traces")
192        self.update()
193
194    def __getitem__(self, number):
195        if number >= len(self.traces):
196            raise IndexError("trace list end reached")
197
198        return self.tracedata[self.traces[number]]
199
200    def update(self, event=None):
201        if self.progress:
202            self.logger.debug("update already running, skip!")
203            return
204        else:
205            self.progress = 1
206
207        # refresh root pointer
208        slib = self.sh
209        slib.db_root.restype = MEMBLC
210        root = slib.db_root()
211        self.logger.debug("%u traces available", root.im[0])
212
213        # no traces left
214        if not root.im[0]:
215            # but still referenced here?
216            if len(self.traces):
217                self.traces = []
218                # delete all
219                for t in self.tracedata:
220                    self.tracedata[t].delete()
221
222            self.progress = 0
223            return
224
225        addresses = []
226        # save copy(!) of already known addresses
227        old_addresses = self.traces[:]
228
229        p = root.pm[0]
230
231        while p:
232            addresses.append(p)
233
234            mb = MEMBLC.from_address(p)
235
236            if p in self.traces:
237                self.tracedata[p].update(memblc=mb, event=event)
238            else:
239                self.tracedata[p] = Trace(memblc=mb, event=event, address=p)
240                self.traces.append(p)
241
242            p = mb.pm[0]
243
244        new = list(set(addresses) - set(old_addresses))
245        gone = list(set(old_addresses) - set(addresses))
246
247        for addr in gone:
248            # may we get a trigger for a already deleted traces
249            try:
250                # clean up
251                self.tracedata[addr].delete()
252                # delete reference
253                del self.tracedata[addr]
254            except KeyError:
255                pass
256
257            del self.traces[self.traces.index(addr)]
258
259        self.progress = 0
260
261        self.logger.debug("new traces: %s" % new)
262        self.logger.debug("gone traces: %s" % gone)
263
264    def __len__(self):
265        return len(self.traces)
266
267    def get(self, number):
268        """return trace by index using natural count (start from one)
269        """
270        return self.tracedata[self.traces[number-1]]
271
272    def plot(self, share=False, save=None):
273        """debug plot"""
274
275        if not len(self):
276            raise Exception("no traces available")
277
278        from SeismicHandler.core.modules.Graphics import pylab
279
280        fig = pylab.figure()
281        fig.canvas.set_window_title("SHX - plot of %u traces" % len(self))
282        pylab.subplots_adjust(hspace=0.1, wspace=20, left=0.1, right=0.95, bottom=0.05, top=0.95)
283
284        fig.set_facecolor('w')
285
286        for i, trc in enumerate(self):
287            # link x axis if more than one trace
288            if len(fig.axes) and share:
289                ax = pylab.subplot(len(self), 1, i+1, sharex=fig.axes[0])
290            else:
291                ax = pylab.subplot(len(self), 1, i+1)
292
293            # no frame
294            ax.set_frame_on(False)
295
296            # no y ticks
297            ax.yaxis.set_major_locator(pylab.NullLocator())
298
299            # build label from station ...
300            if trc.station:
301                label = trc.station
302            # ... and component name
303            if trc.comp:
304                label += " " + trc.comp
305
306            try:
307                ax.set_ylabel(label, rotation='horizontal')
308            except:
309                pass
310
311            # no ticks for upper traces
312            if i < len(self)-1:
313                ax.xaxis.set_major_locator(pylab.NullLocator())
314            else:
315                ax.xaxis.tick_bottom()
316
317            pylab.plot(np.arange(0, trc.delta * trc.length, trc.delta), trc.fetchData(), "k", linewidth=.5)
318
319            # add some extra space around traces
320            limit = abs(max(pylab.ylim(), key=abs)) * 1.1
321            pylab.ylim(-limit, limit)
322
323        if save:
324            pass
325        else:
326            pylab.show()
327
328if __name__ == "__main__":
329    t = Traces()
330    from SeismicHandler.core.commands import Run
331
332    MessageService.block(Message(MessageService.REDRAW))
333    # create 2 hours of synthetic data (100 Hz)
334    # position set by counter
335    for i in range(5):
336        Run("CREATE SHARP 0.05 100 1. %u .1 .5" % (10+i*6))
337#        Run("create sharp 0.1 7200 1 %u 0.005 .05" % 5**(i+1))
338
339    Run("sum all")
340    MessageService.unblock(Message(MessageService.REDRAW))
341
342    t.plot(True)
Note: See TracBrowser for help on using the repository browser.