gtiwhere.pro 6.13 KB
;+
; NAME:
;   GTIWHERE
;
; AUTHOR:
;   Craig B. Markwardt, NASA/GSFC Code 662, Greenbelt, MD 20770
;   craigm@lheamail.gsfc.nasa.gov
;
; PURPOSE:
;   Locate a list of times within a Good Time Interval (GTI) array
;
; CALLING SEQUENCE:
;   WH = GTIWHERE(TIME, GTI, COUNT=, INTERVALS=, /INVERT, /INCLUDE)
;
; DESCRIPTION: 
;
;   The function GTIWHERE is an efficient method to determine which
;   good time interval (GTI) a particular time sample falls into, if
;   any.  The user passes an array of one or more times in the TIME
;   parameter, and GTIWHERE determines which of these times are in a
;   good interval.  The INTERVALS keyword returns an identification of
;   which interval the time fell into.
;
;   It should be noted that this function is not constrained to
;   operation only on time arrays.  It should work on any
;   one-dimensional quantity with intervals.
;
;   The definition of "inside" a good-time interval is
;      TIME GE GTI(0,i) AND TIME LT GTI(1,i)     (INCLUDE=0)
;      TIME GE GTI(0,i) AND TIME LE GTI(1,i)     (INCLUDE=1)
;   where GTI(*,i) is the i'th interval.
;
;   When using /INVERT, the definition of "outside" a bad-time
;   interval is
;      TIME LT GTI(0,i) OR TIME GE GTI(1,i)    (INCLUDE=0)
;      TIME LT GTI(0,i) OR TIME GT GTI(1,i)    (INCLUDE=0)
;
; INPUTS:
;
;   TIME - an array of times, in no particular order.
;
;   GTI - a 2xNINTERVAL array where NINTERVAL is the number of
;         intervals.  GTI(*,i) represents the start and stop times of
;         interval number i.  The intervals must be non-overlapping
;         and time-ordered (use GTITRIM to achieve this).
;
;         A scalar value of zero indicates that the GTI is empty, ie,
;         there are no good intervals.
;
; KEYWORDS:
;
;   COUNT - upon return, the number of resulting intervals.  A value
;           of zero indicates no good time intervals.
;
;   INTERVALS - upon return, an array of integers specifying the
;               interval number each time falls into.  The number of
;               elements of INTERVALS is COUNT (ie, there is one
;               INTERVAL for each returned index).
;
;   INCLUDE - if set then the endpoints are considered closed at both
;             ends; by convention the endpoints are normally treated
;             as closed at the left and open at the right.
;
;   INVERT - if set, the array GTI is treated as a "bad" time
;            interval.  Only times *outside* of the GTI are selected.
;            In this case there are NGTI+1 possible intervals, where
;            NGTI is the number of time intervals passed in GTI.  The
;            indices returned in the INTERVALS keyword start from the
;            "left."
;
; RETURNS:
;
;   An array of integer indices indicating which elements of TIME are
;   within the "good" intervals.  The number of selected elements is
;   COUNT.  If COUNT is zero, then a scalar value of -1L is returned,
;   indicating no selected values.
;
;
; SEE ALSO:
;
;   GTISEG, GTIMERGE, GTIENLARGE
;
; MODIFICATION HISTORY:
;   Written, CM, 1997-2001
;   Documented, CM, Apr 2001
;   Added usage message, CM, 2006 Aug 18
;   Handle the case if /INVERT and /INCLUDE, which changes the
;     boundary conditions slightly, CM, 2007 Dec 15
;   Handle case of empty input GTI and /INVERT; also handle 
;     INVERT in case where MIN(TIME) touches MIN(GTI), CM, 2008 Jul 08
;   Return -1L for no interval found, instead of 0L, CM, 2011 Jan 05
;
;  $Id: gtiwhere.pro,v 1.7 2011/02/09 03:22:48 cmarkwar Exp $
;
;-
; Copyright (C) 1997-2001, 2006, 2007, 2008, 2011 Craig Markwardt
; This software is provided as is without any warranty whatsoever.
; Permission to use, copy, modify, and distribute modified or
; unmodified copies is granted, provided this copyright and disclaimer
; are included unchanged.
;-
function gtiwhere, time, gti, count=count, INTERVALS=vv, $
                   invert=invert, include=include, query=query

  if keyword_set(query) then return, 1
  
  count = 0L
  vv = -1L

  if n_params() EQ 0 then begin
      message, 'USAGE: GTIWHERE(TIME, GTI, COUNT=, INTERVALS=, /INVERT, /INCLUDE)', /info
      goto, FINISH
  endif

  if n_elements(gti) LT 2 then begin
      if keyword_set(invert) then begin
          ;; All times are accepted when /INVERT is set and GTI is null
          count = n_elements(time)
          vv = lonarr(count)
          wh = lindgen(count)
      endif
      goto, FINISH
  endif
          
  ngti = n_elements(gti)/2

  if keyword_set(invert) then begin
      vgti = [ min([gti(*), time-1]), gti(*), max([gti(*), time+1]) ]
      ngti = ngti + 1
  endif else begin
      vgti = gti
  endelse
  vgti = reform(vgti, 2, ngti, /overwrite)

  if ngti LT 5 then begin
      ;; Optimization for a few GTIs, which is faster by using WHERE
      ;; rather than VALUE_LOCATE
      mint = min(time, max=maxt)
      vv = lonarr(n_elements(time))

      for i = 0L, ngti-1 do begin
          if mint GT vgti(1,i) then goto, NEXT_INTERVAL
          if maxt LT vgti(0,i) then goto, NEXT_INTERVAL
          if keyword_set(include) AND keyword_set(invert) then begin
              wh1 = where(time GT vgti(0,i) AND time LT vgti(1,i), ct1)
          endif else if keyword_set(include) then begin
              wh1 = where(time GE vgti(0,i) AND time LE vgti(1,i), ct1)
          endif else begin
              wh1 = where(time GE vgti(0,i) AND time LT vgti(1,i), ct1)
          endelse
          
          if ct1 GT 0 then begin
              if count EQ 0 then wh = wh1 else wh = [ wh, wh1 ]
              count = count + ct1
              vv(wh1) = i
          endif
          NEXT_INTERVAL:
      endfor

  endif else begin
      ;; Standard uses VALUE_LOCATE which is faster for all but the
      ;; cases NGTI between 1 and 5.
      vv = value_locate(vgti(0,*), time)
      if keyword_set(include) AND keyword_set(invert) then begin
          wh = where( (vv GT 0) AND (time LT vgti(1,vv>0)), count )
      endif else if keyword_set(include) then begin
          wh = where( (vv GE 0) AND (time LE vgti(1,vv>0)), count )
      endif else begin
          wh = where( (vv GE 0) AND (time LT vgti(1,vv>0)), count )
      endelse
  endelse

  FINISH:
  if count LE 0 then begin
      vv = -1L
      return, -1L
  endif
  vv = vv(wh)
  return, wh
end