• Antelope Release 5.4 Linux 2.6.32-220.el6.x86_64 2014-05-02

 

NAME

bptracepanel - BRTT tk canvas item extension for displaying trace waveform data

SYNOPSIS


package require Buplot

canvaspathName create bptracepanel viewportName ?options?

DESCRIPTION

This is a special tk canvas item extension available through the Buplot package in the Antelope tcl/tk extensions for the purpose of displaying trace waveform data along with timing annotations, channel labeling and diplays of various glyphs, such as background glyphs for indicating data gaps. This canvas item widgets act as normal tk canvas items, including such functionality as the ability to display in scrolled canvases, and should be thought of as extensions to the various items that are described in canvas(n). Although this is written as a tcl/tk extension, the bptracepanel canvas item will almost always be used from a Python script through the tkinter Python extension.

BPTRACEPANEL ITEM OPTIONS

This canvas item is different from the other buplot canvas items inthat the viewport functionality relating to plot scaling is controlled through the bptracepanel options instead of through the viewport itself. However, viewport size and margins are still honored. The plot scaling in time, the position of the traces vertically and the trace amplitude scaling are all done within the bptracepanel items themselves. Each bptracepanel item must have an associated viewport, but the viewport scaling parameters are automatically slaved to the bptracepanel item plot scaling instead of the other way around as with the other buplot items. This was necessitated by the very dynamic nature of the bptracepanel item time-axis plot scaling which can be configured to update to real-time on a regular basis, automatically with no intefvention on the script level. A fundamental restriction of this canvas item is that every trace is displayed with the same time scaling (subject to time offsets defined by -data_timeoffset below).

Trace data being accumulated with this canvas item are pre-pixelated into buffers that correspond to the horizontal (time) pixel range being displayed. Certain display functions can be quickly executed from these pixelated buffers as long as the time scaling has not changed. These functions include trace amplitude scaling, trace plot mode changes (see adisplay_mode below) and changing of trace vertical positions and heights.

A bptracepanel item is created with the following command:

With the following item options:

The following item options are specific to btracepanel items:

The instance parameter is a copy of the instance counter specified when the callback was defined in the item configuration call. There will always be a single associate array parameter with the name set to the trace string handle assigned by the bptracepanel canvas item object. Each new trace that is created is assigned a unique trace string handle by the bptracepanel canvas item object code and this trace string handle is used by all subsequent calls as a way of specifying particular traces for action. For instance, when a trace is meant to display data from a buvector(3) object, as specified through the -data option described above, the application script code would typically call the -data item configuration option within this callback. The application script will not know the trace string handle until this callback is executed and that handle is necessary in the -data item configuration call. For ORB and Datascope data, the data plumbing happens automatically and typically does not require the connection to be in this callback.

For this particular example the trace string handle is trhndl.000 and the associative array contains parameter data for this trace including the trace label, any defined trace tags, the defined orbtag (orb) or dbtag (db) along with the nscl and sc values, the background color and the position (y) and height (h). The actual computed position and height where the trace will be rendered are specified as y_trplot and h_trplot.

For ORB data sources, the Antelope parameter file string also contains a channel parameter which is in the form orbtag:nscl which specifies the new data source and SEED net_sta_chan[_loc]. Following is an example of the returned parameter file string for a new channel.


instance 0
channel ucsdorb:N4_P46A_HHE

In this example a new ORB channel with seed code N4_P46A_HHE has been read from the ORB with orbtag ucsdorb. New channels are only processed with this callback once. This callback provides a means for determining the traces to display and their positions at run time in reponse to the data streams instead of having to specify them as a static list before any data connections are processed. For instance, in the orbrtd(1) script ORB data channels for display can be specified using regular expression matches against the real-time data streams instead of having to pre-assign trace displays statically at startup. This is accomplished using this callback. If a new channel should be displayed, then a bptracepanel item configuration call should be made in this callback using the -trace option described above.

For Datascope database data sources, the Antelope parameter file string also contains a dbchannels parameter which is a list of strings with each string reresenting a single channel from the database in the form sta chan nscl tstart tend, where sta chan are the CSS station and channels codes, nscl is the corresponding SEED net_sta_chan[_loc], tstart is the earliest epoch time of the channel and tend is the latest epoch time of the channel. Following is an example of the returned parameter file string for a new channel. As well, the parameter file contains a dbtags parameter with the database tag name.


instance 0
dbtag datadb
dbchannels &Tbl{
    AAK BHE ZZ_AAK_BHE 706139710.250000 706139822.650000
    AAK BHN ZZ_AAK_BHN 706139710.250000 706139822.650000
    AAK BHZ ZZ_AAK_BHZ 706139710.250000 706139822.650000
    CHM BHE ZZ_CHM_BHE 706139715.200000 706139841.500000
    CHM BHN ZZ_CHM_BHN 706139715.200000 706139841.500000
    CHM BHZ ZZ_CHM_BHZ 706139715.200000 706139841.500000
    EKS2 BHE ZZ_EKS2_BHE 706139704.700000 706139801.550000
    EKS2 BHN ZZ_EKS2_BHN 706139704.700000 706139801.550000
    EKS2 BHZ ZZ_EKS2_BHZ 706139704.700000 706139801.550000
    KBK BHE ZZ_KBK_BHE 706139714.400000 706139838.450000
    KBK BHN ZZ_KBK_BHN 706139714.400000 706139838.450000
    KBK BHZ ZZ_KBK_BHZ 706139714.400000 706139838.450000
    KMI BHN ZZ_KMI_BHN 706139638.905640 706139638.905640
    TKM BHE ZZ_TKM_BHE 706139719.050000 706139855.950000
    TKM BHN ZZ_TKM_BHN 706139719.050000 706139855.950000
    TKM BHZ ZZ_TKM_BHZ 706139719.050000 706139855.950000
    USP BHE ZZ_USP_BHE 706139715.700000 706139843.350000
    USP BHN ZZ_USP_BHN 706139715.700000 706139843.350000
    USP BHZ ZZ_USP_BHZ 706139715.700000 706139843.350000
}

For database sources this callback is only made once when the database is opened. All of the channels and time ranges are specified in the single call.

The parameter file must contain an associate array named layer_definitions and a table named layers. The layer_definitions array defines the various plotting layers that will be displayed. Each entry in the layer_definitions array is another associative array with a unique and arbitrary layer name. Within each layer are other parameters that define the appearance of that layer. The layers table defines the layers to be displayed and the order in which they are to be rendered. Layer plotting parameters are defined in the following section LAYER RENDERING.

The parameter file must contain a parameter named trace_handle which must be set to the trace string handle of the trace whose parameters are to be modified. There must also be one or more associate arrays with names corresponding to the default names as defined in the default layer_definitions array set by the -plot_params option. Within each of the layers are the parameters that are to be changed. For this example the again parameyer for the data_trace layer is changed to 1.5 for the trace with string handle trhndl.009.

LAYER RENDERING

The bptracepanel item uses a layered rendering methodology to insure that various plot objects overlay each other properly. Each of the display layers are defined and given names using the -plot_params item configuration option. The various layer rendering parameters are defined below.

BASIC SETUP AND DATA PIXELATION

For want of a better section title, this section describes the calls that should be made to configure a bptracepanel item for displaying trace data. Most previous typical trace display widgets operate in a linear fashion in which the application script would effect the ordering of traces and the data plotted in the traces in the same manner as the other buplot canvas items. For instance, in the bppolyline canvas item a buvector(3) object is passed to a particular item instance directly through using an item configuration call. The item caches the entire vector and replots it automatically whenever the display window scaling or size are changed. The script coding for this is straightforward.

However, trace plotting has some distinct differences from the typical bppolyline function plotting. First, trace waveform data can contain many more points than a typical generic X-Y function that would be plotted by bppolyline. Also, it is often desirable to display large numbers of traces in record sections as well as long time durations in which a single time pixel might contain a large number of trace samples. For example, if we wanted to plot 100 traces over a time window of one week in which the trace channels were sampled at 100 samples per second, the input trace sample data would take up at least 24 Gbytes of data. We could not cache that much data without incuring severe performance penalties. If we were to just represent the 100 traces over one week as buvector(3) objects and try to plot them with bppolyline, we could run out of heap memory altogether or at the very least incur severe paging performance degredation which would cause the display to be very slow. The display of large amounts of dense trace data requires a different approach. Instead of reading all the data into a heap cache, we need to only read the data when it is time to plot the data. That is the way that the dbpick(1) program works. The only data read is the data being displayed and the data is only read when the display is rendered.

Second, trace waveform data can come in an incremental real-time manner, such as trace data being acquired using an Antelope ORB. In this situation it is desirable to make animated time scrolling displays, such as the displays made by orbmonrtd(1). Data and displays of this type cannot be assimilated a priori into heap memory caches but must be assimilated in pieces as the data is read. A related issue with real-time trace data is that it is desirable to automatically configure the entire trace display dynamically as new data channels become available.

These important differences between waveform trace data and generic X-Y function data make it difficult to maintain a simple linear interface to the application code. Within the application code we often do not know beforehand how to even configure the display since we may not be sure what data channels will be in the database, which can change with time, or ORB, which definitely changes with time, where we are getting the data. We deal with these issues in bptracepanel through a set of configuration options in which we describe the data "plumbing" between traces and data sources. The actual reading and transfer of the data from the source into the display is then done automatically as the data needs to be displayed without any intervention at the scripting level.

The direct way of connecting data sources to display traces is through the -orb, -db and -trace item configfuration options described previously. The idea is to set up continuous data sources from either ORBs or Datascope databases through the -orb and -db item configuration options. Specifying these options causes separate data reading and prcessing threads to be launched for each data source. Once the data sources have been defined and launched, individual display traces can be specified through the -trace item configuration option. This option basically defines where a single trace will be displayed within the bptracepanel item window and where that trace will get its data. All of the memory management, data processing and display rendering associated with tha data trace are then handled automatically by the bptracepanel item. Each ORB or database data source is identified through tag strings assigned by the application programmer. Further identification of data channels is through specification of SEED net_sta_chan_loc codes, for ORB data sources, or CSS "sta chan" codes, for database data sources.

After setting up data sources and plumbing the data channels to the various trace displays using the appropriate bptracepanel item configuration options, the actual internal processing and display rendering is deferred until data is available and ready for displaying. With this approach you can configure your traces through the -trace option before you actually start up your data sources through the -orb and -db options. Once the data sources are started, all of the internal configuration is data driven.

An important feature of bptracepanel is that it will support dynamic trace configuration based upon the data streams themselves. An example of this can be seen in orbrtd(1) where regular expressions can be used to define the channels to be displayed. This is accomplished through the -new_channel_callback bptracepanel item configuration option. bptracepanel items use application programmer defined python callback procedures to effect data driven dynamic configuration. By registering a python callback with the -new_channel_callback option, this callback will be called whenever a new channel of data is seen by any of the data streams. The callback can then look at the data source tag and channel code to determine if the data should be displayed. The callback procedure effects the display of new channels by making one or more calls to the -trace item configuration option to add in new channels (note that a single channel of input data can be displayed across multiple bptracepanel trace displays).

Another important callback is specified through the -trace_creation_callback bptracepanel item configuration option. This is also a python callback that is called whenever a new trace has been created and is ready for rendering. The callback is passed an argument that contains all of the bptracepanel internal parameters associated with that trace. In particular, an important internal parameter called the trace handle string is passed back to the application script through this callback. Internally, bptracepanel keeps track of the individual traces through a unique string assigned by bptarcepanel to each trace known as the trace handle string. Subsequent calls to bptracepanel item configuration options that modify various parameters associated with a particular trace must contain this trace handle string as a reference to the trace. The application script is made aware of the trace handle string for newly created traces through the trace creation callback procedure. Note that application defined tags for each of the traces can be assigned through the -trace item configuration option with the tag= fields and these fields will show up in the trace creation callback arguments. This provides the application programmer with a way to identify new traces and associate the bptracepanel assigned trace handle strings with application program specific configuration parameters.

As described previously the dense nature of waveform data causes poor performance when using straightforward rendering methods. In bptracepanel items the incoming waveform data is always first rendered into what we call a pixelation buffer. The idea behind a pixelation buffer is that it maps data sample values into time pixel slots based on the currently defined buffer pixel width and time values at each edge of the buffer. Since many data samples may end up landing in the sampe time pixel slot, the internal pixelation buffer contains only the minimum and maximum data samples in each of the time pixel slots. Given a pixelation buffer width of 1000 pixels, the amount of heap memory required for a single trace would be about 8000 bytes regardless of the sample rate of the data being displayed. That turns our example described previously into a heap memory buffer of about 800,000 bytes instead of the 24 Gbytes of raw data. Much more manageable. A second rendering stage then takes place in which the traces are rendered into an actual pixmap from the internal pixelation buffer. In this stage only the minimum and maximum trace values over the 1000 time pixels are rendered instead of the original 60 million sample values from each original trace and the only plot scaling and pixelation is done in the vertical direction since the time scaling and pixelation has already be done. This means that the display rendering can quickly adapt to any changes that do not involve changing the time scaling, such as auto amplitude scaling or even changing display modes from normal line plotting to something more exotic, such as various color plotting methods.

Even in situations where the time values change at each edge of the internal pixelation buffer, as long as the time window or the buffer width do not change it is often possible to simply shift the buffer in situations where the time at both ends changes equally, such as a time shift that would come about in an animated real-time display. After applying a time shift as an integer pixel shift, new data could then be accumulated into the existing pixelation buffer without the need to repixelate the entire buffer. This is the manner in which orbrtd(1) works when it is displaying data from real-time ORB acquisition. The required computer resources are minimized in this fashion.

However, there are situations in which the internal pixelation buffer time scales or width do change. When this happens it is necessary to completely rerender the pixelation buffer from the original data samples. Since the raw input data is not cached in heap memory, repixelation requires some application program intervention and this is accomplished through the -repixelate_callback bptracepanel item configuration option. This is also a python callback that is called whenever a bptracepanel item internal pixelation buffer needs to be rerendered. The strategy for replaying the data sources for rerendering is up to the application programmer. When a repixelation occurs the bptracepanel item will automatically clear out the existing pixelation buffer as well as clear out the display pixmap. A simple strategy for repixelation when the data source is from an ORB is to rewind that data source through a call to the -orb bptracepanel item configuration option using the after= field which will cause the ORB read pointer to be reset and the subsequent display rendering will happen naturally and automatically as the ORB data is reread and processed.

A side effect of this approach to dynamic data processing is that when trace data is assimilated within the application program directly, such as the generation of a test signal as a buvector(3) object, the manner in which the application generated trace data is made available to the bptracepanel item becomes manual instead of automatic for ORB and database sources. This can be seen in the code example that follows. Our code example generates a sawtooth test signal as a single buplot(3) object and then displays this same trace over 10 traces within a bptracepanel item. The display is set up to slave the bptracepanel item window to the viewport window. The initial specification of the 10 traces is accomplished through 10 calls to the -trace bptracepanel item configuration option with only the label= and positioning fields specified. Note there are no orb= or db= fields since there are no such data sources in the example. You can specify traces like this with no data sources. They will display with their trace labels as blank traces. The -trace_create_callback option is used to specify a python callback procedure when the traces are created. In the example trace creation callback procedure we can see that the data gets injected through the -data bptracepanel item configuration option. Calling this option causes the vector data to be immediately rendered into the bptracepanel item internal pixelation buffer. Because the example is set up to slave the bptracepanel item display window to be slaved to the viewport, the internal pixelation buffer must be rerendered whenever the window is resized horizontally. The rerendring of the pixelation buffer is accomplished through the -repixelate_callback option and the repixelation callback procedure in the example. All this procedure does is to reinject the vector data using the same -data configuration option as in the trace creation callback. This is the basic sequence of calls that should be made for any data that is generated and managed within the application programs.

EXAMPLES

Following is an example of using a simple bptracepanel item extension written as a python script.

#!/opt/antelope/python2.7.6/bin/python

import os
import sys
import signal

signal.signal(signal.SIGINT, signal.SIG_DFL)

sys.path.append(os.environ['ANTELOPE'] + "/data/python")

import antelope.elog as elog
import antelope.stock as stock
from antelope.buplot import *
from antelope.buvector import *

class PfSubs(object):
    """
    PfSubs is a set of class methods for converting Antelope
    parameter file strings to/from python dictionaries
    """
    def __init__ (self):
        return

    @classmethod
    def pfstring2py (self, pfstring, pyd=None):
        """
        PfSubs.pfstring2py(pfstring, pyd=None) is a class method
        for converting Antelope parameter file strings to python
        dictionaries

        Parameters
        ----------
        pfstring:
            input Antelope parameter file string
        pyd:
            optional input python dictionary object. If
            specified then the output phython disctionary
            object is formed by appending the Antelope
            parameter file string to the input object

        Returns
        -------
        pyd:
            output python dictionary object
        """
        pf = stock.ParameterFile()
        pf.pfcompile (pfstring)
        pyf = pf.pf2dict()
        if pyd != None:
            pyd.update(pyf)
        else:
            pyd = pyf
        return (pyd)

    @classmethod
    def py2pfstring (self, pyd):
        """
        PfSubs.py2pfstring(pyd) is a class method for converting python
        dictionaries to Antelope parameter file strings

        Parameters
        ----------
        pyd:
            input python dictionary object

        Returns
        -------
        pfstring:
            output Antelope parameter file string
        """
        pf = stock.ParameterFile()
        pf['foo'] = pyd
        pfstr = pf.pf2string()
        pfstr = pfstr[4:-2]
        return pfstr

class TraceDisplay(object):
    """
    TraceDisplay() is the main trace display class.

    In this example all of the parameters are hard-wired
    internally in the class code.
    """

    instance  = -1
    instances = {}

    def __init__ (self):

        """
        set up an object instance counter and an object instance dictionary
        """

        TraceDisplay.instance += 1
        TraceDisplay.instances[TraceDisplay.instance] = self

        """
        grab buplot main window
        """

        self.mw = Buplot().mw

        """
        set root window title and geometry
        """

        self.mw.wm_title("test_bptracepanel")
        self.mw.geometry ("1000x1000")

        self.mw.grid_rowconfigure ( 0 , weight = 1 )
        self.mw.grid_columnconfigure ( 0 , weight = 1 )

        """
        set 80 pixel left margin where the channel labels willl be displayed
        """

        self.mleft = 80

        """
        set times at right and left ends of window
        """

        self.timeright = stock.now()
        self.twin = 600.0
        self.timeleft = self.timeright - self.twin

        """
        create stock tkinter canvas - note that dimensions are not set,
        they will slave to the root window
        """

        self.canvas = BCanvas ( self.mw,
                            borderwidth = 0,
                            highlightthickness = 0,
                            background = "#800000" )

        """
        and this is what slaves the canvas dimensions to the root window
        """

        self.canvas.grid_configure ( column = 0, row = 0, sticky = 'nsew')

        """
        create a buplot viewport as a canvas item and set the left margin,
        note that since the viewport dimensions were not specified, the
        will slave to the canvas dimensions
        """

        self.vp_item = self.canvas.create_bpviewport ("tracevp", 0, 0,
                            mleft = self.mleft,
                            mright = 0,
                            mbottom = 0,
                            mtop = 0,
                            fill = '#e0e0e0',
                            fill_frame = 'lightblue',
                            tag = "tracevp")

        """
        create the buplot tracepanel as a canvas item, set the trace_creation_callback
        and repixelate_callback to TraceDisplay class methods (note the use of the class
        instance variable that will be used by the class method callback in instantiate
        the object making the callback), set the time scales, set the panel and pixmap
        plot scales and dimensions so they are slaved to the viewport (this will cause
        these to automatically adjust to changing window dimensions)
        """

        self.tr_item = self.canvas.create_bptracepanel  ("tracevp",
                            trace_creation_callback = 'TraceDisplay.trace_creation_callback %d' % TraceDisplay.instance,
                            repixelate_callback = 'TraceDisplay.repixelate_callback %d' % TraceDisplay.instance,
                            timeright = self.timeright,
                            twin = self.twin,
                            panel_height = 'viewport',
                            pixmap_height = 'viewport',
                            panel_width = 'viewport',
                            pixmap_width = 'viewport' )

        """
        setup a test data trace as a buvector2 object
        """

        self.vec = self.test_data (self.timeleft, self.timeright)

        """
        initialize an internal object list of traces and a dictionary
        for holding the data input specifications for each trace
        """

        self.traces = []
        self.dataspcs = {}

        """
        create ten test traces, the arguments to the trace creation option
        (trace=) are the trace label (label=), the trace y position (y=) and
        the trace height (h=), note the trace y position is set to 'auto',
        meaning automatically compute the trace y position so it is adjacent
        to the prior trace, and the trace height is 'fit', meaning the trace
        heights are all adjusted to fit within the pixmap height, which is
        slaved to the viewport height, which is slaved to the canvas height,
        which is slaved to the root window height
        """

        for i in range(10):
            args = "label=test%2.2d,y=auto,h=fit" % i
            self.canvas.itemconfigure (self.tr_item,
                    trace = args,
                )

    def order_traces (self):
        """
        self.order_traces() is a method for ordering the traces displayed
        from top to bottom in alphabetical order according to the trace labels

        Returns
        -------
        traces:
            ordered list of trace handles
        """
        self.traces.sort(key = lambda key: key[1]['label'])
        traces = [key[0] for key in self.traces]
        return (traces)

    def test_data (self, tstart, tend):
        """
        self.test_data(tstart, tend) is a method that is called to create
        a test data trace

        Parameters
        ----------
        tstart:
            time at beginning of the trace
        tend:
            time at end of the trace

        Returns
        -------
        vec:
            trace buvector2 handle string
        """
        samprate = 100.0
        dt = 1.0/samprate
        istart = int((tstart - self.timeleft)*samprate)
        iend = int((tend - self.timeleft)*samprate)
        nsamp = iend - istart
        if nsamp < 1:
            return None
        t0 = tstart + istart*dt
        data = [0.0 for i in range(nsamp)]
        for i in range(nsamp):
            t = (i+istart)*dt
            t /= 10.0
            it = int(t)
            t -= it
            data[i] = -0.5 + t
        vec = buvector2_create_tsamp (nsamp, t0, samprate, data)
        return vec

    @classmethod
    def trace_creation_callback (self, pfstring):
        """
        TraceDisplay.trace_creation_callback(pfstring) is a
        class method that is called by the bptracepanel item
        widget whenever a new trace has been created and is
        being displayed. It is called with a single Antelope
        parameter file string parameter.

        Parameters
        ----------
        pfstring:
            Input Antelope parameter file string. This string
            will contain at a minimum a parameter named
            'instance' that corresponds to the instance option,
            specified in the trace_creation_callback= option
            when the bptracepanel item was created, and can be
            used to obtain the specific object instance. This
            parameter file string will also have a single
            associative array with name set to the trace
            handle string and the array values set to the
            various trace parameters, such as trace label,
            trace y value and trace height value.

        Returns
        -------
        ret:
            an output string set to 'ok' should normally be return
        """
        argsd = PfSubs.pfstring2py (pfstring)
        instance = int(argsd['instance'])
        obj = TraceDisplay.instances[instance]
        keys = argsd.keys()
        for key in keys:
            if key == 'instance':
                continue
            else:
                trace_handle = key
                break

        dataspc = 'type=vector,vec=%s,trace=%s' % (obj.vec, trace_handle)
        obj.dataspcs[trace_handle] = dataspc

        obj.canvas.itemconfigure (obj.tr_item, data = dataspc)

        obj.traces.append((trace_handle, argsd[trace_handle]))
        traces = {'traces': obj.order_traces()}
        obj.canvas.itemconfigure (obj.tr_item, traces = PfSubs.py2pfstring(traces))

        return "ok"

    @classmethod
    def repixelate_callback (self, pfstring):
        """
        TraceDisplay.repixelate_callback(pfstring) is a
        class method that is called by the bptracepanel item
        widget whenever the time scales or the pixmap width
        have changed and a repixelation of the display needs
        to be rendered. It is called with a single Antelope
        parameter file string parameter.

        Parameters
        ----------
        pfstring:
            Input Antelope parameter file string. This string
            will contain at a minimum a parameter named
            'instance' that corresponds to the instance option,
            specified in the trace_creation_callback= option
            when the bptracepanel item was created, and can be
            used to obtain the specific object instance.

        Returns
        -------
        ret:
            an output string set to 'ok' should normally be return
        """
        argsd = PfSubs.pfstring2py (pfstring)
        instance = int(argsd['instance'])
        obj = TraceDisplay.instances[instance]

        for (trace, params) in obj.traces:
            obj.canvas.itemconfigure (obj.tr_item, data = obj.dataspcs[trace] )

        return "ok"

if len(sys.argv) != 1:
    print "usage: test_bptracepanel"
    exit (1)

plot = TraceDisplay()

plot.mw.mainloop()

Following is an example of using a bptracepanel item extension to display data from an ORB.


#!/opt/antelope/python2.7.6/bin/python

import os
import sys
import signal

signal.signal(signal.SIGINT, signal.SIG_DFL)

sys.path.append(os.environ['ANTELOPE'] + "/data/python")

import antelope.elog as elog
import antelope.stock as stock
from antelope.buplot import *
from antelope.buvector import *

class PfSubs(object):
    """
    PfSubs is a set of class methods for converting Antelope
    parameter file strings to/from python dictionaries
    """
    def __init__ (self):
        return

    @classmethod
    def pfstring2py (self, pfstring, pyd=None):
        """
        PfSubs.pfstring2py(pfstring, pyd=None) is a class method
        for converting Antelope parameter file strings to python
        dictionaries

        Parameters
        ----------
        pfstring:
            input Antelope parameter file string
        pyd:
            optional input python dictionary object. If
            specified then the output phython disctionary
            object is formed by appending the Antelope
            parameter file string to the input object

        Returns
        -------
        pyd:
            output python dictionary object
        """
        pf = stock.ParameterFile()
        pf.pfcompile (pfstring)
        pyf = pf.pf2dict()
        if pyd != None:
            pyd.update(pyf)
        else:
            pyd = pyf
        return (pyd)

    @classmethod
    def py2pfstring (self, pyd):
        """
        PfSubs.py2pfstring(pyd) is a class method for converting python
        dictionaries to Antelope parameter file strings

        Parameters
        ----------
        pyd:
            input python dictionary object

        Returns
        -------
        pfstring:
            output Antelope parameter file string
        """
        pf = stock.ParameterFile()
        pf['foo'] = pyd
        pfstr = pf.pf2string()
        pfstr = pfstr[4:-2]
        return pfstr

class TraceDisplay(object):
    """
    TraceDisplay(orbname, select) is the main trace display class.

    Parameters
    ----------
    orbname:
        input ORB name
    select:
        input ORB select expression
    """

    instance  = -1
    instances = {}

    def __init__ (self, orbname, select):

        """
        set up an object instance counter and an object instance dictionary
        """

        TraceDisplay.instance += 1
        TraceDisplay.instances[TraceDisplay.instance] = self

        """
        grab buplot main window
        """

        self.mw = Buplot().mw

        """
        set root window title and geometry
        """

        self.mw.wm_title("test_bptracepanel")
        self.mw.geometry ("1000x1000")

        self.mw.grid_rowconfigure ( 0 , weight = 1 )
        self.mw.grid_columnconfigure ( 0 , weight = 1 )

        """
        set 80 pixel left margin where the channel labels willl be displayed
        """

        self.mleft = 80

        """
        set time window
        """

        self.twin = 86400.0

        """
        create stock tkinter canvas - note that dimensions are not set,
        they will slave to the root window
        """

        self.canvas = BCanvas ( self.mw,
                            borderwidth = 0,
                            highlightthickness = 0,
                            background = "#800000" )

        """
        and this is what slaves the canvas dimensions to the root window
        """

        self.canvas.grid_configure ( column = 0, row = 0, sticky = 'nsew')

        """
        create a buplot viewport as a canvas item and set the left margin,
        note that since the viewport dimensions were not specified, the
        will slave to the canvas dimensions
        """

        self.vp_item = self.canvas.create_bpviewport ("tracevp", 0, 0,
                            mleft = self.mleft,
                            mright = 0,
                            mbottom = 0,
                            mtop = 0,
                            fill = '#e0e0e0',
                            fill_frame = 'lightblue',
                            tag = "tracevp")

        """
        create the buplot tracepanel as a canvas item, set the new_channel_callback,
        trace_creation_callback and repixelate_callback to TraceDisplay class methods
        (note the use of the class instance variable that will be used by the class
        method callback in instantiate the object making the callback), set the time
        scales to slave to real-time through the timefuture and timeupdate_interval
        options, set the panel and pixmap plot scales and dimensions so they are slaved
        to the viewport (this will cause these to automatically adjust to changing
        window dimensions)
        """

        self.tr_item = self.canvas.create_bptracepanel  ("tracevp",
                            new_channel_callback = 'TraceDisplay.new_channel_callback %d' % TraceDisplay.instance,
                            trace_creation_callback = 'TraceDisplay.trace_creation_callback %d' % TraceDisplay.instance,
                            repixelate_callback = 'TraceDisplay.repixelate_callback %d' % TraceDisplay.instance,
                            twin = self.twin,
                            timefuture = 300.0,
                            timeupdate_interval = 10.0,
                            foreground_tmark_now = 'red',
                            rerenderupdate_interval = 1.0,
                            label_font = 'courier 5',
                            panel_height = 'viewport',
                            pixmap_height = 'viewport',
                            panel_width = 'viewport',
                            pixmap_width = 'viewport' )

        """
        initialize an internal object list of traces and a dictionary
        for holding the data input specifications for each trace
        """

        self.traces = []
        self.dataspcs = {}

        """
        Launch a single ORB source thread.
        """

        args = 'orb=dataorb,orbname=%s,accept=%s,after=%.1f' % (orbname, select, stock.now()-86400.0-300.0)
        self.canvas.itemconfigure (self.tr_item, orb = args )

    def order_traces (self):
        """
        self.order_traces() is a method for ordering the traces displayed
        from top to bottom in alphabetical order according to the trace labels

        Returns
        -------
        traces:
            ordered list of trace handles
        """
        self.traces.sort(key = lambda key: key[1]['label'])
        traces = [key[0] for key in self.traces]
        return (traces)

    @classmethod
    def new_channel_callback (self, pfstring):
        """
        TraceDisplay.new_channel_callback(pfstring) is a
        class method that is called by the bptracepanel item
        widget whenever a new data SEED channel is read by
        the ORB data source thread. It is called with a single
        Antelope parameter file string parameter.

        This callback services new channels by adding them as
        new traces using the trace option.

        Parameters
        ----------
        pfstring:
            Input Antelope parameter file string. This string
            will contain a parameter named 'instance' that
            corresponds to the instance option, specified in
            the new_channel_callback= option when the bptracepanel
            item was created, and can be used to obtain the
            specific object instance. This parameter file string
            will also have a parameter named 'channel' which
            contains the new data channel specified in the
            form <orbtag>:<net_sta_chan_loc>.

        Returns
        -------
        ret:
            an output string set to 'ok' should normally be return
        """
        argsd = PfSubs.pfstring2py (pfstring)
        instance = int(argsd['instance'])
        obj = TraceDisplay.instances[instance]
        (orbtag, nscl) = argsd['channel'].split(':')

        args = 'orb=%s,nscl=%s,label=%s,y=auto,h=fit' % (orbtag, nscl, nscl)
        obj.canvas.itemconfigure (obj.tr_item, trace = args)

        return "ok"

    @classmethod
    def trace_creation_callback (self, pfstring):
        """
        TraceDisplay.trace_creation_callback(pfstring) is a
        class method that is called by the bptracepanel item
        widget whenever a new trace has been created and is
        being displayed. It is called with a single Antelope
        parameter file string parameter.

        This callback saves the returned trace handle string,
        along with the set of trace parameters (which contains
        the trace labels for sorting) and re-arranges the traces
        vertically according to trace label alphabetic order.

        Parameters
        ----------
        pfstring:
            Input Antelope parameter file string. This string
            will contain at a minimum a parameter named
            'instance' that corresponds to the instance option,
            specified in the trace_creation_callback= option
            when the bptracepanel item was created, and can be
            used to obtain the specific object instance. This
            parameter file string will also have a single
            associative array with name set to the trace
            handle string and the array values set to the
            various trace parameters, such as trace label,
            trace y value and trace height value.

        Returns
        -------
        ret:
            an output string set to 'ok' should normally be return
        """
        argsd = PfSubs.pfstring2py (pfstring)
        instance = int(argsd['instance'])
        obj = TraceDisplay.instances[instance]
        keys = argsd.keys()
        for key in keys:
            if key == 'instance':
                continue
            else:
                trace_handle = key
                break

        obj.traces.append((trace_handle, argsd[trace_handle]))
        traces = {'traces': obj.order_traces()}
        obj.canvas.itemconfigure (obj.tr_item, traces = PfSubs.py2pfstring(traces))

        return "ok"

    @classmethod
    def repixelate_callback (self, pfstring):
        """
        TraceDisplay.repixelate_callback(pfstring) is a
        class method that is called by the bptracepanel item
        widget whenever the time scales or the pixmap width
        have changed and a repixelation of the display needs
        to be rendered. It is called with a single Antelope
        parameter file string parameter.

        This callback services repixelations by rewinding the
        input ORB read pointer to replay the data packets.

        Parameters
        ----------
        pfstring:
            Input Antelope parameter file string. This string
            will contain at a minimum a parameter named
            'instance' that corresponds to the instance option,
            specified in the trace_creation_callback= option
            when the bptracepanel item was created, and can be
            used to obtain the specific object instance.

        Returns
        -------
        ret:
            an output string set to 'ok' should normally be return
        """
        argsd = PfSubs.pfstring2py (pfstring)
        instance = int(argsd['instance'])
        obj = TraceDisplay.instances[instance]

        args = 'orb=dataorb,after=%.1f' % (stock.now()-86400.0-300.0)
        obj.canvas.itemconfigure (obj.tr_item, orb = args )

        return "ok"

if len(sys.argv) != 1 and len(sys.argv) != 3:
    print "usage: test_bptracepanel_orb [orbname select_expr]"
    exit (1)

orbname = 'bbarray.ucsd.edu:gsn'
select = '.*_LHZ_00.*'
if len(sys.argv) == 3:
    orbname = sys.argv[1]
    select = sys.argv[2]

plot = TraceDisplay(orbname, select)

plot.mw.mainloop()

Following is an example of using a bptracepanel item extension to display data from a Datascope database.


#!/opt/antelope/python2.7.6/bin/python

import os
import sys
import signal

signal.signal(signal.SIGINT, signal.SIG_DFL)

sys.path.append(os.environ['ANTELOPE'] + "/data/python")

import antelope.elog as elog
import antelope.stock as stock
from antelope.buplot import *
from antelope.buvector import *
from ttk import *
import math

class PfSubs(object):
    """
    PfSubs is a set of class methods for converting Antelope
    parameter file strings to/from python dictionaries
    """
    def __init__ (self):
        return

    @classmethod
    def pfstring2py (self, pfstring, pyd=None):
        """
        PfSubs.pfstring2py(pfstring, pyd=None) is a class method
        for converting Antelope parameter file strings to python
        dictionaries

        Parameters
        ----------
        pfstring:
            input Antelope parameter file string
        pyd:
            optional input python dictionary object. If
            specified then the output phython disctionary
            object is formed by appending the Antelope
            parameter file string to the input object

        Returns
        -------
        pyd:
            output python dictionary object
        """
        pf = stock.ParameterFile()
        pf.pfcompile (pfstring)
        pyf = pf.pf2dict()
        if pyd != None:
            pyd.update(pyf)
        else:
            pyd = pyf
        return (pyd)

    @classmethod
    def py2pfstring (self, pyd):
        """
        PfSubs.py2pfstring(pyd) is a class method for converting python
        dictionaries to Antelope parameter file strings

        Parameters
        ----------
        pyd:
            input python dictionary object

        Returns
        -------
        pfstring:
            output Antelope parameter file string
        """
        pf = stock.ParameterFile()
        pf['foo'] = pyd
        pfstr = pf.pf2string()
        pfstr = pfstr[4:-2]
        return pfstr

class TraceDisplay(object):
    """
    TraceDisplay(dbname, select) is the main trace display class.

    Parameters
    ----------
    dbname:
        input database name
    select:
        input database select expression
    """

    instance  = -1
    instances = {}

    def __init__ (self, dbname, select):

        """
        set up an object instance counter and an object instance dictionary
        """

        TraceDisplay.instance += 1
        TraceDisplay.instances[TraceDisplay.instance] = self

        """
        grab buplot main window
        """

        self.mw = Buplot().mw

        """
        set root window title and geometry
        """

        self.mw.wm_title("test_bptracepanel")
        self.mw.geometry ("1000x1000")

        self.mw.grid_rowconfigure ( 0 , weight = 1 )
        self.mw.grid_columnconfigure ( 0 , weight = 1 )

        """
        set 120 pixel left margin where the channel labels willl be displayed
        """

        self.mleft = 120

        """
        set time window
        """

        self.twin = 86400.0

        """
        create stock tkinter canvas - note that dimensions are not set,
        they will slave to the root window
        """

        self.canvas = BCanvas ( self.mw,
                            borderwidth = 0,
                            highlightthickness = 0,
                            background = "#800000" )

        """
        and this is what slaves the canvas dimensions to the root window
        """

        self.canvas.grid_configure ( column = 0, row = 0, sticky = 'nsew')

        """
        create a buplot viewport as a canvas item and set the left margin,
        note that since the viewport dimensions were not specified, the
        will slave to the canvas dimensions
        """

        self.vp_item = self.canvas.create_bpviewport ("tracevp", 0, 0,
                            mleft = self.mleft,
                            mright = 0,
                            mbottom = 0,
                            mtop = 0,
                            fill = '#e0e0e0',
                            fill_frame = 'lightblue',
                            tag = "tracevp")

        """
        create the buplot tracepanel as a canvas item, set the new_channel_callback,
        to TraceDisplay class methods
        (note the use of the class instance variable that will be used by the class
        method callback in instantiate the object making the callback), set the time
        scales to slave to real-time through the timefuture and timeupdate_interval
        options, set the panel and pixmap plot scales and dimensions so they are slaved
        to the viewport, this will cause these to automatically adjust to changing
        window dimensions)
        """

        self.tr_item = self.canvas.create_bptracepanel  ("tracevp",
                            new_channel_callback = 'TraceDisplay.new_channel_callback %d' % TraceDisplay.instance,
                            panel_height = 'viewport',
                            pixmap_height = 'viewport',
                            panel_width = 'viewport',
                            pixmap_width = 'viewport' )

        """
        Launch a single database source thread.
        """

        if select != None and select != '':
            args = 'db=datadb,dbname=%s,accept=%s' % (dbname, select)
        else:
            args = 'db=datadb,dbname=%s' % dbname
        self.canvas.itemconfigure (self.tr_item, db = args )

    @classmethod
    def new_channel_callback (self, pfstring):
        """
        TraceDisplay.new_channel_callback(pfstring) is a
        class method that is called by the bptracepanel item
        widget whenever a new database is opened by
        the database data source thread. It is called with a single
        Antelope parameter file string parameter.

        This callback services new channels by adding them as
        new traces using the trace option.

        Parameters
        ----------
        pfstring:
            Input Antelope parameter file string. This string
            will contain a parameter named 'instance' that
            corresponds to the instance option, specified in
            the new_channel_callback= option when the bptracepanel
            item was created, and can be used to obtain the
            specific object instance. This parameter file string
            will also have a parameter named 'dbtag' which
            contains the new database tag name for the new data
            channels. The new CSS sta and chan codes, along with
            their corresponding SEED net_sta_chan_loc codes and
            time ranges for each channel are contained in a
            table named 'dbchannels'.

        Returns
        -------
        ret:
            an output string set to 'ok' should normally be return
        """
        argsd = PfSubs.pfstring2py (pfstring)
        instance = int(argsd['instance'])
        obj = TraceDisplay.instances[instance]
        channels = argsd['dbchannels']
        dbtag = argsd['dbtag']
        tleft = None
        tright = None
        for channel in channels:
            (sta, chan, nscl, tstart, tend) = channel.split()
            tstart = float(tstart)
            tend = float(tend)
            if tleft == None or tstart < tleft:
                tleft = tstart
            if tright == None or tend > tright:
                tright = tend
            args = 'db=%s,sc=%s,label=%s,y=auto,h=fit' % (dbtag, "%s %s" % (sta, chan), "%s %s" % (sta, chan))
            obj.canvas.itemconfigure (obj.tr_item, trace = args)

        obj.twin = (tright - tleft) * 1.1
        obj.timeright = tright + 0.05 * obj.twin
        obj.canvas.itemconfigure (obj.tr_item, timeright = obj.timeright, twin = obj.twin)

        return "ok"

if len(sys.argv) != 1 and len(sys.argv) != 3:
    print "usage: test_bptracepanel_db [dbname select_expr]"
    exit (1)

dbname = '/opt/antelope/data/db/demo/demo'
select = ''
if len(sys.argv) == 3:
    dbname = sys.argv[1]
    select = sys.argv[2]

plot = TraceDisplay(dbname, select)

plot.mw.mainloop()

AUTHOR

Danny Harvey, BRTT
Printer icon