Subs::StartEndTimes (version 0.0)


package Subs::StartEndTimes;
##############################################################################
#
# DESCRIPTION: This subroutine does a number of things invoving the time  
# DESCRIPTION: values in every FITS HDU for a list of file types.
# DESCRIPTION: 
# DESCRIPTION: If an extension had TSTART and TSTOP keywords, it checks to
# DESCRIPTION: be sure that neither is zero and that TSTART <= TSTOP.
# DESCRIPTION: It also sets the values for the DATE-OBS, TIME-OBS, DATE-END,
# DESCRIPTION: TIME-END keywords. Finally it keeps a running tally of the 
# DESCRIPTION: earliest TSTART and the latest TSTOP. These values are used
# DESCRIPTION: to mark start and finish of the entire observation.
# DESCRIPTION: 
# DESCRIPTION: Every TIME column is checked for whether it is in order.
# DESCRIPTION: The BAT bcdh files are not checked since the BAT_TELEM column
# DESCRIPTION: is not supposed to be in order.
# DESCRIPTION: 
# DESCRIPTION: Finally, for every GTI extension, the subroutine checks that 
# DESCRIPTION: no two GTIs overlap, that they are in order and that they
# DESCRIPTION: all have STOP > START.
#
# HISTORY: 
# HISTORY: $Log: StartEndTimes.pm,v $
# HISTORY: Revision 1.20  2004/10/12 16:28:09  apsop
# HISTORY: Turn off sort checking of TIME columns in order to decrease run time.
# HISTORY:
# HISTORY: Revision 1.19  2004/08/30 13:19:30  apsop
# HISTORY: Add in new trend types.  Need better way of doing this.
# HISTORY:
# HISTORY: Revision 1.18  2004/08/22 18:42:25  apsop
# HISTORY: Initial changes for new file classes and repository
# HISTORY:
# HISTORY: Revision 1.17  2004/07/12 13:45:40  apsop
# HISTORY: Add timedata type to check list
# HISTORY:
# HISTORY: Revision 1.16  2004/06/08 00:09:36  apsop
# HISTORY: Fix for handling case with no attitude info.
# HISTORY:
# HISTORY: Revision 1.15  2004/06/02 18:53:20  apsop
# HISTORY: Add uvot compression trend file to list of files to proccess
# HISTORY:
# HISTORY: Revision 1.14  2004/05/28 19:45:20  apsop
# HISTORY: Write CVSINIT keyword into all the fits files
# HISTORY:
# HISTORY: Revision 1.13  2004/05/06 20:02:34  dah
# HISTORY: Add version number back into the header comments.
# HISTORY:
# HISTORY: Revision 1.12  2004/04/16 20:21:18  dah
# HISTORY: Begin using embedded history records
# HISTORY:
#
# VERSION: 0.0
#
#
##############################################################################


use Subs::Sub;

@ISA = ("Subs::Sub");
use strict;

sub new {
    my $proto=shift;
    my $self=$proto->SUPER::new();

    $self->{DESCRIPTION}="Determining start and end times of the observation";

    return $self;
}

##################
# METHODS:
##################

sub body {
    my $self=shift;

    my $log     =$self->log();
    my $filename=$self->filename();
    my $procpar =$self->procpar();
    my $jobpar  =$self->jobpar();

    my @types = ('attitude', 'unfiltered', 'hk', 'scenhk', 'enhk', 'rawimage', 'bamdph', 'maskwt', 
		 'bgaoff', 'bamgaoff', 'rawdph', 'dph', 'lightcurve', 'rawlc', 'batlcatt', 'gti', 
		 'ucmp', 'timedata', 'bscalemap', 'bsegment', 'bcatalog', 'btbveto', 'btblongtr', 
		 'btbshorttr', 'btbratetr', 'btbimgtr', 'btbratedg', 'btbrun', 'bgain', 'boffset',
		 'bdetflag', 'bcomman', 'bdaphk', 'bshelllg', 'btrigflx');

    my $tstart;
    my $tstop;

    ##########################################
    # get the maximum reasonable TSTART-TSTOP
    ##########################################
    my $max_duration = $procpar->read("max_duration");

    #######################
    # Setup for UTCF value
    #######################
    my $time_file = $filename->get('timedata', 'swift', '', 0);
    my (@utcf_times, @utcf);
    if( -f $time_file ){
      my $time_fits = Util::FITSfile->new($time_file, 'UTCF');
      my @utcf_times = $time_fits->cols('TIME')->table();
      my @utcf = $time_fits->cols('UTCF')->table();
    }

    ########################
    # loop over file types 
    ########################
    my $type;
    foreach $type (@types) {

        $log->entry("Examining TSTART and TSTOP in all $type files");

        ###################################
        # loop over the files of this type 
        ###################################
        my $file;
        foreach $file ($filename->any($type) ) {

            my $fits = Util::FITSfile->new($file);

            ################################
            # loop over all HDUs
            ################################
            my $nhdus = $fits->nhdus();
            my $ext;
            for($ext=0; $ext<$nhdus; $ext++) {

                $log->entry("Examining $file\[$ext\]");
                $fits->ext($ext);

                ###########################################
                # check if there are TSTART/TSTOP keywords
                ###########################################
                my $start = $fits->keyword("TSTART");
                my $stop  = $fits->keyword("TSTOP");


                if(defined $start && defined $stop ) {
                    ########################################
                    # make some sanity checks
                    ########################################
                    if($start == 0.0 || $stop == 0.0 ||
                       $stop < $start || $stop - $start > $max_duration ) {
                        $log->error(1, "Skipping $file\[$ext\] since it has ".
                                       "invalid TSTART=$start TSTOP=$stop");
                        next;
                    }

                    $log->entry("TSTART=$start TSTOP=$stop");

                    #######################################################
                    # keep a running tally of the absolute start/end_times
                    #######################################################
                    if(!defined($tstart) || $start<$tstart) { $tstart = $start }
                    if(!defined($tstop ) || $stop >$tstop ) { $tstop  = $stop  }

                    $log->entry("Observation tstart=$tstart tstop=$tstop");

		    #######################################
                    # write the DATE/TIME OBS/END keywords
                    #######################################
                    my $first = Util::Date->new($start);
                    my $last  = Util::Date->new($stop);

                    $fits->keyword('DATE-OBS', $first->date().'T'.$first->time() );
                    $fits->keyword('DATE-END', $last->date().'T'.$last->time() );

		    ####################################
		    # Determine UTCFINI and write it in
		    ####################################
		    if(@utcf){
		      my $itime = 0;
		      while( $tstart > $utcf_times[$itime] && 
			     $itime < $#utcf_times){ $itime++ }
		      $fits->keyword('UTCFINIT', $utcf[$itime]);
		    }
		}
                $log->entry("Checking $file\[$ext\]");

                ######################################
                # now check if there is a time column
                ######################################
                #if($ext > 0 && $fits->find_column('TIME') ) {
		#    $fits->cols('TIME');
                #    #######################################
                #    # make sure the column is time ordered
                #    # we don't check BAT BCDH files since their
                #    # BAT_TELEM extension is purposely out of
                #    # time order.
                #    #######################################
                #    if( $fits->keyword('EXTNAME') !~ /^BAT_/ && !$fits->isOrdered() ) {
                #        $log->error(2, "$file\[$ext\] is out of time order");
                #    }
                #}

                ###########################################
                # finally check if this is a GTI extension
                ###########################################
                if($ext > 0) {
                  #####################################################
                  # primary extension doesn't have an EXTNAME keyword
                  #####################################################
		  my $extname = $fits->keyword('EXTNAME');
		  unless(defined $extname){
                    ################################################
                    # sanity check - this can be taken out when the 
                    # FITS files are more stable
                    ################################################
		    $log->error(1, "$file has no EXTNAME in extension 1");

		  } else {
		    if($extname =~  /GTI/) {

		      ###########################################
		      # are the START columns in order?
		      ###########################################
		      unless($fits->cols('START')->isOrdered() ) {
			$log->entry("$file\[$ext\] START column is out of order. Will sort.");
			$fits->sort('START');
		      }

		      ###############################################
		      # are the individual GTIs of positive duration 
		      ###############################################
		      my %intervals = $fits->cols("START", "STOP")->table();
		      foreach (keys %intervals ) {
                        if($intervals{$_} <= $_ ) {
			  $log->error(2, "Invalid GTI".
				      "START=$_ STOP=$intervals{$_} ".
				      "in $file\[$ext\]" );
                        }
		      }

		      ###########################################
		      # are there overlaps
		      ############################################
		      unless($fits->cols('START')->isOrdered('unique') && 
			     $fits->cols('STOP')->isOrdered('unique')) {
			$log->entry("$file\[$ext\] Has overlapping GTIs. Will merge.");

			my $temp = 'merged_gti.tmp';
			Util::Ftool->new('mgtime')
			           ->params({ingtis => "$file\[$ext\] $file\[$ext\]",
					     outgti => $temp,
					     merge  => 'OR'})
				   ->run();

			Util::FITSfile->new($temp)
			              ->import_header("$file\[$ext\]", 'except', '')
			              ->append_to($file);
			
			
			Util::Ftool->new('fdelhdu')
			           ->params({infile => "$file\[$ext\]",
					     confirm => 'no',
					     proceed => 'yes'})
				   ->run();
			unlink $temp;
			$ext--;
		      }

		    }
		  }
                }


            } # end of loop over HDUs
        } # end of loop over files
    } # end of loop over file types
    
    #############################################################
    # check if we got start and stop times from the FITS files
    #############################################################
    unless(defined $tstart && defined $tstop) {
        ########################################################
        # nothing from the FITS files, so try getting
        # the start and stop times directly from the telemetry
        ########################################################
        $log->entry("Extracting observation start and end times from ".
                    "the telemetry secondary headers.");
                    
        my $squirt = Util::Tool->new($procpar->read("squirt"), "squirt");
        $squirt->stdin(1); #so that we don't redirect from /dev/null
        foreach my $telem ($filename->any("telemetry")) {
        
            $squirt->command_line("-e '\"%time2\\n\"; filter: 0;' < $telem 2>&1");
            $squirt->run();

            my @times=split /\s+/, $squirt->stdout();


            @times = sort { $a <=> $b } @times;

            my $max = $times[@times-1];
            if(! defined $tstop || $max > $tstop) { $tstop = $max}
            @times = grep {$_ > $tstop - $max_duration } @times;
            my $min = $times[0];

            $log->entry("In $telem min=$min max=$max");
            
            if(!defined $tstart || $min < $tstart) {$tstart = $min}

        }
    }


    ############################################
    # check if we got absolute start/stop times
    ############################################
    unless(defined $tstart) {
        $log->error(1, "Can't determine observation start time, ".
                       "setting to zero");
        $tstart=0.0;
    }

    unless(defined $tstop) {
        $log->error(1, "Can't determine observation end time, setting to zero");
        $tstop=0.0;
    }


    ####################################
    # convert mission time to date/time
    ####################################
    my $start_date = Util::Date->new($tstart);
    my  $stop_date = Util::Date->new($tstop );

    
    ######################
    # log the results
    ######################
    $log->entry("overall TSTART=$tstart = ".
                $start_date->date()."T". $start_date->time() );

    $log->entry("overall TSTOP=$tstop = ".
                $stop_date->date()."T". $stop_date->time() );

    #####################################
    # ... and record them in the job.par
    #####################################
    $jobpar->set({tstart  => $tstart,
                  tstop   => $tstop,
                  obsdate => $start_date->date(),
                  obstime => $start_date->time(),
                  enddate =>  $stop_date->date(),
                  endtime =>  $stop_date->time() });


} # end of body method