Time Standard Constraints#

The BasicTimeSeries discussion discusses a novel feature of MsPASS that allows the framework to support both active source and passive array data. This section contains additional implementation details that some users may find important. The most notable example is simulation data created for testing or comparison with data (i.e. traditional synthetic seismogram concepts).

First, we will state what we define as the finite set of time issues the framework needed to support:

  1. The data for passive array recording can be assumed to be based on a global timing standard. Today that usually means GPS clocks but older data used a variety of timing instrumentation that has evolved with time. The only difference in older data is timing precision. We lump all such data by tagging them the TimeReferenceType as UTC. There are other global time standards like GPS time (such data do not include leap seconds) but we assume such data are converted to UTC on import. Data derived from a UTC standard can be in one of two states:

    1. Set with time 0 defined as UTC 0 time. That is, the instant of New Year in 1970 (Jan. 1, 1970:00:00:00.0). Time tag for a given sample (the output of the time method in BasicTimeSeries) is then a unix epoch seconds since epoch time 0. Note for obpsy users that time is identical to the output of the timestamp method of UTCDateTime.

    2. An algorithm can apply the ator method that will produce an internal shift of time 0 by a specified number of seconds. Two choices of that shift are most common. First, shifting by the origin of an event converts time to travel times. Second, MsPASS has an algorithm called ArrivalTimeReference that shifts data so time 0 is the arrival time of a particular phase. Note that time could be a predicted time from an earth model and hypocenter estimate or a time measured by picking or correlation method. The arrival time reference is commonly used to improve plotting (visualization) of data or simply to reduce the volume of data to handle. That is, algorithms aimed to process data around a specific section of data defined relative to a seismic phase nearly always window data using this time standard. SAC users may understand the output of ator method as a generalization of the finite list of time standards supported in SAC.

    Unshifted UTC data normally have the internal boolean attribute t0shift_is_valid set false because no conversion shift is being stored. Calling ator stores the supplied UTC reference time, changes the reference type to Relative, and sets this flag true so rtoa can reverse the conversion. Custom readers for unusual formats must set the time standard deliberately. Python uses the tref property for that purpose; C++ provides set_tref.

  2. Active source data are normally best loaded with the TimeReferenceType set as Relative (TimeReferenceType.Relative in python or TimeReferenceType::Relative in C++). Relative time in this context is nearly always equivalent to having time 0 set to the shot time. In the design of MsPASS, however, we aimed to support the range of instrumentation in active source data acquisition with three different absolute time precisions:

  1. Much old data had very imprecise absolute timing or none at all. It is common, for example, with old SEGY files to find none of the absolute fields defined. That is the case because in traditional active source processing like classic seismic reflection processing all that matters in processing is travel time. In that case absolute time tags are baggage useful only for bookkeeping.

  2. Newer data seismic reflection data loggers always have a system clock that keeps time but not necessarily with high accuracy. These data loggers always write a date stamp to their internal file format, but it cannot be used to converted accurately to absolute time using that time stamp.

  3. Many of newer systems record time synchronized with GPS receivers. IRIS PASSCAL instruments beginning with the old SGR’s through the most recent (at this writing) Nodal instruments depend upon accurate internal clocks. Data from this type of instrumentation is conceptually identical to data that begins as UTC but is time shifted so TimeReferenceType is “Relative” and the time is shifted so 0 is the shot time.

The common denominator for all active source data is that the they should have TimeReferenceType set to “Relative”. The first two cases when the absolute timing is not reliable should have the internal attribute t0shift_is_valid set to False. That assures the users won’t make the mistake of calling the rtoa function and creating a potentially misleading (at best) data set. In Python, assign d.tref = TimeReferenceType.Relative; changing tref alone does not create or validate a stored UTC shift. The complete set of methods available to handle this issue are discussed at the end of this document.

  1. Synthetic seismograms are a special case that have to be handled with care. The reason is putting synthetic seismogram signals into this framework almost always has to be carefully aware of the distinction between UTC and Relative time. I know of now synthetic seismogram generator that internally uses anything other than the equivalent of a Relative time standard with 0 being source origin time. Matching recorded signals, however, may require shifting either the data to relative time or shifting the synthetics to UTC time. The two are completely equivalent but how they impact MsPASS is very different and must be considered. Shifting data with a UTC time stamp to relative time is simplest - it involves only a call to ator. The inverse requires some care with the synthetics as you would need to override the safeties described below.

With that background, here we summarize the API used to manage time. All of these are components of the BasicTimeSeries base class, but there are some deviations between the C++ and python APIs.
  • The time stamp that is to handle time 0 is managed in C++ by a getter with he simple name t0() and a setter called set_t0(). In python these are merged to a single symbol t0 with decorators in a fairly standard way to make t0 appear like a class attribute as in the following:

    d.t0 = arrival_time      # t0 as a setter (calls the C++ set_t0 implementation)
    t = d.t0                 # t0 as a getter
    
  • There are several convenience methods that are useful for managing time as a variable. The time(int i) method can be used get the computed time (Relative or absolute depending on the TimeReferenceType set) of sample number i. endtime() is a special case that returns time(npts-1) where npts is the number of points in the signal. Finally, there is the inverse function sample_number(double t) that returns the integer sample number computed for time t rounded to the nearest sample. Users are cautioned that none of these methods check the validity of a time or sample number with respect to the data. e.g. it would be ill-advised to use the output of sample_number(t) as an index into the data array without checking the result is >=0 and < npts.

  • There are two methods to switch between UTC and Relative time standards. Use ator(time_shift) to translate the time origin to the time defined by the epoch time time_shift. Note a convenient way to get such a time from a date string is the use obspy’s UTCDateTime and read the timestamp property on the UTCDateTime object. The inverse of ator is rtoa(). Note the method has no arguments and uses the value of time_shift applied when ator is called to restore time to UTC. Finally, there is a shift(delta_time) method intended to be used to tweek time 0. That method should only be used if the data are in Relative time created by an earlier call to the ator method. The main use of the shift method is for things like switching between two relative time stamps (e.g. between two phase arrival times or between source origin time and a phase time).

  • The problem of how to define if a relative time standard should be treated as reliable is managed internally by a private (technically protected in C++ but private from a python perspective), boolean attribute called t0shift_is_valid in the C++ code. That attribute can be interrogated with the method shifted(). The shifted method returns that flag’s value: true means a stored UTC conversion shift is available. It is normally false for unshifted UTC data and for relative active-source data without reliable absolute timing. Python changes the time-reference enum with the tref property; the C++ method is set_tref(rtype).

    The dangerous setter force_t0_shift(t) stores t as the conversion shift and makes shifted() true, but it does not change t0 or tref. Its primary purpose is matching a relative synthetic to data stored in UTC. For a synthetic whose time axis is already relative to origin, use it as follows:

    synthetic.tref = TimeReferenceType.Relative
    synthetic.force_t0_shift(origin_time)
    synthetic.rtoa()
    

    After rtoa, the time standard is UTC and origin_time has been added to the relative t0. Do not use force_t0_shift when the absolute reference is unknown; doing so bypasses the safeguard that normally makes rtoa reject that conversion.