Subs::Attitude (version 0.0)


package Subs::Attitude;
##############################################################################
#
# DESCRIPTION: Extract attitude data. Swift attitude data appears
# DESCRIPTION: in several places in the telemetry. This subroutine
# DESCRIPTION: collects all the attitude data into a single quaternion-
# DESCRIPTION: based attitude file and removed redundant information.
# DESCRIPTION: 
# DESCRIPTION: The main source of attitude data is the "ACS packets"
# DESCRIPTION: (APID 404). The BAT also places ACS records in the
# DESCRIPTION: headers of each LDP and in the body of a special
# DESCRIPTION: LDP generated suring slews.
# DESCRIPTION: The XRT places ACS records in the header for each CCD
# DESCRIPTION: frame, however these are represented by "floats" instead
# DESCRIPTION: of "doubles", so they are not used if higher accuracy
# DESCRIPTION: data are available.
#
# HISTORY: $Log: Attitude.pm,v $
# HISTORY: Revision 1.45  2006/10/01 18:55:57  apsop
# HISTORY: Insert date keywords into the attitude file so that new version of aspect task will work properly.
# HISTORY:
# HISTORY: Revision 1.44  2006/06/28 18:54:42  apsop
# HISTORY: Create new target pointing GTI file.
# HISTORY:
# HISTORY: Revision 1.43  2006/06/15 22:11:52  apsop
# HISTORY: Make pointing and slew GTIs more restrictive, so that times beyond end measurements are excluded.
# HISTORY:
# HISTORY: Revision 1.42  2006/04/27 16:28:12  apsop
# HISTORY: Make a lack of any attitude file a fatal error.
# HISTORY:
# HISTORY: Revision 1.41  2006/03/13 17:39:17  apsop
# HISTORY: Use XRT attitude info in 991 seqs. Fix bug in choosing GTI for mean pointing determination.
# HISTORY:
# HISTORY: Revision 1.40  2006/01/29 19:37:26  apsop
# HISTORY: Add 60 secs of buffer at each end of the range for the att/orbit file.
# HISTORY:
# HISTORY: Revision 1.39  2005/11/20 21:26:10  apsop
# HISTORY: Unexpected OO behaviour for $inter_file requires seperate call to append_to.
# HISTORY:
# HISTORY: Revision 1.38  2005/11/20 20:30:03  apsop
# HISTORY: Check for presence of BUS_V column before making and using ACS_DATA extension in attitude file.
# HISTORY:
# HISTORY: Revision 1.37  2005/11/08 16:58:18  apsop
# HISTORY: Change fdiff to ftdiff and set reltol to 10e-8.  Use caldb to get alignment file in prefilter and abberator.
# HISTORY:
# HISTORY: Revision 1.36  2005/09/26 21:32:35  apsop
# HISTORY: Make not_pointing GTI file.
# HISTORY:
# HISTORY: Revision 1.35  2005/07/29 14:04:03  apsop
# HISTORY: look for ACS packets that are labeled as head2
# HISTORY:
# HISTORY: Revision 1.34  2005/06/01 17:38:51  apsop
# HISTORY: More robust algorithm for selecting GTI file to use for determining the mean pointing.
# HISTORY:
# HISTORY: Revision 1.33  2005/04/19 16:05:48  apsop
# HISTORY: Check for existence of gti files before setting keywords.
# HISTORY:
# HISTORY: Revision 1.32  2005/03/25 21:25:34  apsop
# HISTORY: Fix bug in setting TSTOP for GTIs.  Set gti extname to GTI.
# HISTORY:
# HISTORY: Revision 1.31  2005/03/25 20:18:04  apsop
# HISTORY: Set TSTART and TSTOP for GTI files.
# HISTORY:
# HISTORY: Revision 1.30  2005/03/15 20:04:42  apsop
# HISTORY: Fix bug in call for getting xrt eng hk file name.
# HISTORY:
# HISTORY: Revision 1.30  2005/03/15 19:03:26  apsop
# HISTORY: Check for presence of xrt no position message when making tdrss cat file.
# HISTORY:
# HISTORY: Revision 1.29  2005/02/18 01:53:09  apsop
# HISTORY: Only use secondary attitude data if primary data is not available. Fix up xrt modes.
# HISTORY:
# HISTORY: Revision 1.28  2005/02/14 19:23:39  apsop
# HISTORY: Remove batatt call. Instead pickup bat attitude info from bat2fits output.
# HISTORY:
# HISTORY: Revision 1.27  2005/02/10 02:52:14  apsop
# HISTORY: Get start and stop times from attitude file, as StartEndTimes has not been run yet.
# HISTORY:
# HISTORY: Revision 1.26  2005/02/08 18:24:17  apsop
# HISTORY: Abberation correction using abberator tool with time test.  Move calculation of attorb file to this class as it is needed for abberator.
# HISTORY:
# HISTORY: Revision 1.25  2005/01/12 17:19:13  apsop
# HISTORY: Exclude safeholds from settling and pointing GTIs.  Include 10arcmin flag in pointing GTI.
# HISTORY:
# HISTORY: Revision 1.24  2004/12/10 02:16:09  apsop
# HISTORY: Do a diff of attitude files after normalization, so we have a record of what changed.
# HISTORY:
# HISTORY: Revision 1.23  2004/12/02 18:55:50  apsop
# HISTORY: Normalize quaternions before passing to aspect.
# HISTORY:
# HISTORY: Revision 1.22  2004/11/02 21:12:15  apsop
# HISTORY: Set DATE keyword in attitude file.
# HISTORY:
# HISTORY: Revision 1.21  2004/09/03 00:26:01  apsop
# HISTORY: Temporarily remove bat lc attitude information from attitude file.
# HISTORY:
# HISTORY: Revision 1.20  2004/08/16 15:23:09  apsop
# HISTORY: Turn on writing of HISTORY keywords.
# HISTORY:
# HISTORY: Revision 1.19  2004/07/06 19:59:32  apsop
# HISTORY: Add test for existence of nfi GTI file.
# HISTORY:
# HISTORY: Revision 1.18  2004/06/29 14:34:02  apsop
# HISTORY: New ontarget gti is pointing ANDed with nfi.  Use as input to aspect tool.
# HISTORY:
# HISTORY: Revision 1.17  2004/06/14 14:26:23  apsop
# HISTORY: Write in name of alignment file into attitude file.
# HISTORY:
# HISTORY: Revision 1.16  2004/06/10 19:55:03  apsop
# HISTORY: Add updating of TSTART/TSTOP keywords in second extension.
# HISTORY:
# HISTORY: Revision 1.15  2004/05/28 19:16:07  apsop
# HISTORY: Changes to attitude file format
# HISTORY:
# HISTORY: Revision 1.14  2004/05/06 20:02:33  dah
# HISTORY: Add version number back into the header comments.
# HISTORY:
# HISTORY: Revision 1.13  2004/05/04 16:30:51  dah
# HISTORY: Set ra/dec to 0/0 if not attitude data.  Issue error.
# HISTORY:
# HISTORY: Revision 1.12  2004/04/28 13:45:50  dah
# HISTORY: Change xrt header and engineering file modes to fit new standard.
# HISTORY:
# HISTORY: Revision 1.11  2004/04/16 20:21:18  dah
# HISTORY: Begin using embedded history records
# HISTORY:
#
# VERSION: 0.0
#
#
##############################################################################


use Subs::SwiftSub;
use Subs::NominalPointing;
use Util::AttTool;

@ISA = ('Subs::NominalPointing', 'Subs::SwiftSub');
use strict;

use Util::SwiftTags;

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

    $self->{DESCRIPTION}="Extracting attitude data";

    return $self;
}

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

sub body {
    my $self=shift;

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

    ##############################################
    # extract attitude files from various sources
    ##############################################
    my @acs = $self->extract_acs("acs_att_tmp");
    my @bat = $filename->get('attitude', 'bat', '', '*');
    my @xrt;
    my @batlc;
    unless( @acs || @bat ){
      $log->entry("No primary atttiude data, using secondary sources.");
      push @xrt, $self->extract_xrt("xrt_att_tmp");
      push @bat, $filename->get('attlpd', 'bat', '', '*');
    }else{
      unlink $filename->get('attlpd', 'bat', '', '*');
      my $seq = $jobpar->read('sequence');
      if( $seq%1000==991 ){
        $log->entry("Using xrt attitude data in 991 sequence.");
        push @xrt, $self->extract_xrt("xrt_att_tmp");
      }
    }


    #############################################################
    # merge just the attitude files from the ACS type packets
    ###########################################################
    my $acslist = Util::FITSlist->new(@acs, @bat);
    my @acs_merged;
    unless($acslist->count() == 0) {
      $log->entry("Merging ". join(" ", $acslist->files()) );
      push @acs_merged, 'acs_merged.tmp';
      my $merge_out = $acslist->merge($acs_merged[0]);
      if($merge_out ne $acs_merged[0]) {
        ############################
        # there was only one file
        ###########################
        rename $merge_out, $acs_merged[0];
      }else{
        ##########################
        # delete the files
        ##########################
        foreach ($acslist->files() ) {
	  $log->entry("Deleting $_");
	  unlink $_;
        }
      }
    }else{
      my $index = 0;
      foreach my $lcfile ($filename->get('batlcatt', 'bat', '', '*'),
			  $filename->get('tdrsslcatt', 'bat', '', '*')){

	push @batlc, "bat_lcatt_${index}.tmp";
	Util::FITSfile->new($lcfile, 'ATTITUDE', '[col TIME; QPARAM; POINTING; SOURCE(B)=4]')
	              ->copy($batlc[-1]);
	unlink $lcfile;
	$index++;
      }
    }

    #######################################
    # merge all the attitude data
    #######################################
    my $attlist = Util::FITSlist->new(@acs_merged, @batlc, @xrt);
    if($attlist->count() ==0) {
        $log->error(2, 'No attitude files produced, setting ra,dec to 0,0');
	$jobpar->set({ra => 0.0, dec => 0.0});
        return;
    } else {
        $log->entry("Merging ". join(" ", $attlist->files()) );
    }

    my $attitude = $filename->get('attitude', 's');
    my $merged=$attlist->merge($attitude, '', 'TIME', 'QPARAM', 'POINTING', 'SOURCE');

    ###############################################################################
    # Make a seperate extension with just the extra columns from the ACS packets
    ###############################################################################
    if($merged ne $attitude) {
        ############################
        # there was only one file
        ###########################
        rename $merged, $attitude;
	my $inter_file = Util::FITSfile->new($attitude, 'ATTITUDE');
	if($inter_file->find_column('BUS_V')){
	  $inter_file->specs('[col TIME; POSITION; FLAGS; BUS_V; SOURCE]');
	  $inter_file->append_to($attitude);

	  my $delcol = Util::Ftool->new('fdelcol')
	                        ->params({infile => $attitude.'[1]',
					  confirm => 'no',
					  proceed => 'yes'});

	  $delcol->params({colname => 'POSITION'})->run();
	  $delcol->params({colname => 'FLAGS'})->run();
	  $delcol->params({colname => 'BUS_V'})->run();
	}
    }else{
      if(@acs_merged){
	my $inter_file = Util::FITSfile->new($acs_merged[0], 'ATTITUDE');
	if($inter_file->find_column('BUS_V')){
	  $inter_file->specs('[col TIME; POSITION; FLAGS; BUS_V; SOURCE]');
	  $inter_file->append_to($attitude);
	}
      }
      ##########################
      # delete the files
      ##########################
      foreach ($attlist->files() ) {
	$log->entry("Deleting $_");
	unlink $_;
      }
    }

    ##########################################
    # TIME sort the merged attitude file and 
    # remove overdetermined values
    ##########################################
    $log->entry("Sorting and uniqing $attitude");

    my $now = Util::Date->new();
    my $Tnow = $now->date() .'T'. $now->time();
    my $fits = Util::FITSfile->new($attitude, 0);
    $fits->keyword('DATE', $Tnow);   
    $fits->keyword('MJDREFI', 51910, 'MJD reference day');
    $fits->keyword('MJDREFF', 7.428703700000000E-04, 'MJD reference (fraction of day)');

    $fits->ext(1);
    $fits->keyword('DATE', $Tnow);    
    $fits->keyword('MJDREFI', 51910, 'MJD reference day');
    $fits->keyword('MJDREFF', 7.428703700000000E-04, 'MJD reference (fraction of day)');
    $fits->sort();

    if($fits->nhdus() > 2){
      $fits->ext(2);
      $fits->cols('TIME');
      $fits->sort('unique')
           ->keyword('EXTNAME', 'ACS_DATA');

      my $nrows = $fits->nrows();
      my $tstart = $fits->rows(1     )->table();
      my $tstop  = $fits->rows($nrows)->table();

      $fits->keyword('DATE', $Tnow);    
      $fits->keyword('TSTART', $tstart, 'Time of first attitude record');
      $fits->keyword('TSTOP' , $tstop, 'Time of last attitude record');

      $fits->ext(1);
    }

    Util::AttTool->new("att_thin")
	         ->command_line($attitude)
                 ->run();


    $log->entry("normalizing quaternions");
    my $colfilter = '[col *; QPARAM = QPARAM / sqrt(sum(QPARAM*QPARAM))]';
    Util::HEAdas->new('ftcopy')
	        ->params({
			  infile => $attitude . $colfilter,
			  outfile => 'attitude.tmp',
			 })
		->run;

    ###############################################################
    # Run diff so that we have a record in the log of what changed
    ###############################################################
    my $diff = Util::HEAdas->new('ftdiff')
	                   ->params({infile1 => "$attitude\[1]",
				     infile2 => "attitude.tmp\[1]",
				     reltol => 10e-8});

    $diff->run();

    rename('attitude.tmp', $attitude);

    ##########################################
    # set TSTART and TSTOP in the merged file
    ##########################################
    $fits = Util::FITSfile->new($attitude, 1)
                          ->cols("TIME");

    my $nrows = $fits->nrows();
    my $tstart = $fits->rows(1     )->table();
    my $start_date = Util::Date->new($tstart);
    my $tstop  = $fits->rows($nrows)->table();
    my $stop_date = Util::Date->new($tstop);

    $fits->keyword('TSTART', $tstart, 'Time of first attitude record');
    $fits->keyword('TSTOP' , $tstop, 'Time of last attitude record');
    $fits->keyword('DATE-OBS', $start_date->date() .'T'. $start_date->time(), 
		   'Time of first attitude record');
    $fits->keyword('DATE-END', $stop_date->date() .'T'. $stop_date->time(), 
		   'Time of first attitude record');

    my $align_fits = Util::FITSfile->new($filename->fetch_cal('alignment'), 0);
    $fits->keyword('ALGN_NAM', $align_fits->keyword('FILENAME'), 
		   'Name of alignment teldef file used.');

    #################################################
    # Make settling and pointing gtis
    #################################################
    my $settling = $filename->get('gti', 's', 'st', 0);
    my $pointing = $filename->get('gti', 's', 'po', 0);
    my $not_pointing = $filename->get('gti', 's', 'np', 0);
    my $ontarget = $filename->get('gti', 's', 'ot', 0);
    my $nfis = $filename->get('gti', 's', 'nf', 0);
    my $target_pointing = $filename->get('gti', 's', 'tp', 0);

    if( (grep /ACS_DATA/, $fits->list_hdus()) ){
      my $tempfile = 'acs.tmp';
      Util::HEAdas->new('ftsort')
	          ->params({infile => $attitude.'[ACS_DATA]',
			    outfile => $tempfile,
			    unique => 'yes',
			    columns => 'TIME, FLAGS'})
		  ->run();

      my $maketime = Util::Ftool->new('maketime')
	                        ->params({infile => $tempfile.'[ACS_DATA]',
			   	          compact => 'no'});
				      
      $log->entry("Producing GTIs for settling and pointing.");

      $maketime->params({outfile => $settling,
			 prefr => 0,
			 postfr => 1,
	 	         expr => 'FLAGS == b10x0xxxx'})
               ->run();
    
      $maketime->params({outfile => $pointing,
			 prefr => 0,
			 postfr => 0,
			 expr => 'FLAGS == b11x0xxxx'})
               ->run();

      $maketime->params({outfile => $not_pointing,
			 prefr => 1,
			 postfr => 1,
	 	         expr => 'FLAGS != b11xxxxxx || FLAGS == bxxx1xxxx'})
               ->run();

      if( -s $nfis ){
        Util::Ftool->new('mgtime')
	           ->params({ingtis => $pointing .' '. $nfis,
		   	     outgti => $ontarget,
			     merge => 'AND'})
		   ->run();
      }else{ 
        Util::FITSfile->new($pointing)
	            ->copy($ontarget);
      }

      unlink $tempfile if -f $tempfile;
    }

    foreach my $gtifile ($settling, $pointing, $ontarget, $nfis){
      next unless -s $gtifile;

      my $gtifits = Util::FITSfile->new($gtifile, 1);
      unless( $gtifits->nrows() ){
	$log->error([ 1, ATT_NO_GTI ], "GTI file $gtifile is empty. Deleting.");
	unlink $gtifile;
	next;
      }

      $gtifits->keyword('EXTNAME', 'GTI');
      $gtifits->keyword('TSTART', ($gtifits->cols('START')->table())[0] );
      $gtifits->keyword('TSTOP', ($gtifits->cols('STOP')->table())[-1] );
    }

    ##########################################
    # determine the nominal pointing
    # using method inherited from superclass
    ##########################################
    unless( -f $ontarget ){
      if( -f $pointing ) {
	$ontarget = $pointing;
      }else{
	$ontarget = $self->no_gap_gtis($attitude, 100); 
      }
    }

    $self->determine_pointing($ontarget);

    if($ontarget =~ /\.tmp$/ ) { 
        ####################################
        # delete no-gap temporary GTI file
        ####################################
        unlink $ontarget;
    }

    #################################################################
    # GTI of pointings where we are actually pointing in the nominal
    # direction.  Call this 'tp' for 'target pointing.
    #################################################################
    if( -f $pointing ){
      my $maketime = Util::Ftool->new('maketime')
	                        ->params({infile => $attitude.'[ATTITUDE]',
			   	          compact => 'no',
					  outfile => 'gti.tmp',
					  prefr => 1,
					  postfr => 1,
					  expr => 'near(POINTING[1],' .$jobpar->read('ra') .',1.0) ' .
					      ' && near(POINTING[2],' .$jobpar->read('dec') .',1.0)' })
                                ->run();
				      
      
        Util::Ftool->new('mgtime')
	           ->params({ingtis => $pointing .' gti.tmp',
		   	     outgti => $target_pointing,
			     merge => 'AND'})
		   ->run();

      unlink 'gti.tmp';
    }

    #################################################################
    # Only do abberation correction in velocity adding was turned on
    #################################################################
    my $velocity_add = Util::Date->new('2005-01-31T21:57:00');
    $self->correct_aberration()
      if $tstart < $velocity_add->seconds();

    ############################################################
    # calculate attitude/orbit calculated filtering quantities
    ############################################################
    $self->calculate_att_orb();

} # end of body method

#############################################################################
# Extract an attitude file from the ACS packets
#############################################################################
sub extract_acs {
    my $self=shift;
    my $base=shift;

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

    my @list=();


    ##############################
    # get the ACS packets file
    ##############################
    my @acs = ( $filename->get("telemetry", "*", "acs", "*"),
	        $filename->get("telemetry", "*", "head2", 485) );
    unless(@acs && -f $acs[0]) {
        $log->entry("No ACS packets available");
        return @list;
    }


    ##########################
    # run acs2fits
    ##########################
    my $index=0;
    foreach (@acs) {
        #######################
        # get the file name
        #######################
        my $file = "$base.$index";
        $log->entry("Creating $file from $_");
        unlink $file;

        #############################
        # set up the extraction tool
        #############################
	my $teldef = $filename->fetch_cal('alignment');
        my $acs2fits=Util::AttTool->new('acs2fits');

        $acs2fits->command_line("-infile $_", 
				"-alignfile $teldef",
                                "-outfile $file");

        #####################################
        # run the tool and check for errors 
        #####################################
        $acs2fits->run();
        if($acs2fits->had_error() ) {
            #############
            # error
            #############
            if(-f $file ) {
                $log->entry("Removing $file");
                unlink $file;
            }
        } else {
            #############
            # success
            #############
            push @list, ($file);
        }


    } # end of loop over input files

    return @list;

} # end of extract_acs method


#############################################################################
# Extract an attitude file from the XRT FITS files
#############################################################################
sub extract_xrt {
    my $self=shift;
    my $base=shift;

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

    my @list=();

    ###########################################################
    # types of XRT HK files with attitude data in them
    ###########################################################
    my @modes = ('48a', '4e0', '4e1', '4e2', '4f0', '500', '534', '536', '53a', '540');
###    "48Ah", "4E0h", "4E1h", "4E2h",
###    "4F0h", "500h", "534h", "536h", "53Ah", "540h");

    my @files=();
    my @head_files = ( $filename->get('hk', 'xrt', 'hd', '*') );
    push @files, map $_ .= "[1]", @head_files;

    my $eng_hk = $filename->get('enhk', 'xrt', '', 0);
    if( -f $eng_hk ){
      my $fitsEng = Util::FITSfile->new($eng_hk);
      my $nhdus = $fitsEng->nhdus();
      for(my $iext=1; $iext < $nhdus; $iext++){
	$fitsEng->ext($iext);
	my $name = $fitsEng->keyword('EXTNAME');
	if( grep(/^hk${name}x$/, @modes) ){
	  push @files, $eng_hk . "[$name]";
        }
      }
    }
    

    unless(@files) {
        $log->entry("No attitude data in the XRT FITS files");
        return @list;
    }


    ###################################
    # extract the attitude files
    ###################################
    my $index=0;
    foreach (@files) {
        #######################
        # get the file name
        #######################
        my $file = "$base.".$index++;
        $log->entry("Creating $file from $_");
        unlink $file;

        #############################
        # set up the extraction tool
        #############################
	my $teldef = $filename->fetch_cal('alignment');
        my $tool=Util::AttTool->new('xrtatt');

        $tool->command_line("-infile '$_'", 
			    "-alignfile $teldef",
                            "-outfile $file");

        #####################################
        # run the tool and check for errors 
        #####################################
        $tool->run();
        if($tool->had_error() ) {
            #############
            # error
            #############
            if(-f $file ) {
                $log->entry("Removing $file");
                unlink $file;
            }
        } else {
            #############
            # success
            #############
            push @list, ($file);
        }


    } # end of loop over input files

    return @list;


} # end of extract_xrt method

    

#############################################################################
# create a GTI file from an attitude file which excludes gaps in the 
# attitude records
#############################################################################
sub no_gap_gtis {
    my $self = shift;
    my $att = shift;
    my $gap = shift;

    my $log     =$self->log();

    $log->entry("Creating GTI file which excludes attitude ".
                "record gaps > $gap s");

    #############################################
    # read the time records in the attitude file
    #############################################
    my $fits = Util::FITSfile->new($att, 1);
    my @time = $fits->cols("TIME")->table();

    unless(@time) {
        $log->entry("No attitude records");
        return "";
    }

    #######################################
    # open an ASCII file to hold the data
    #######################################
    my $data = "att_gtis.tmp";
    unlink $data;
    open DATA, ">$data";

    
    my $start=$time[0];
    my $i;
    my $nrows = $fits->nrows();
    for($i=0; $i<$nrows; $i++) {
        
        my $start = $time[$i];

        ###############################################################
        # skip over consecutive rows to find the start of the next gap
        ###############################################################
        while ($i < $nrows && $time[$i] - $time[$i-1] < $gap ) { $i++ }

        ####################
        # record the GTI
        ####################
        print DATA "$start $time[$i-1]\n";

        ###############################################################
        # skip over the gap
        ###############################################################
        while ($i < $nrows && $time[$i] - $time[$i-1] >= $gap ) { $i++ }


    } # end of loop over attitude records

    close DATA;


    my $header = "att_gtis_header.tmp";
    open HEADER, ">$header";
    print HEADER "START 1D\n";
    print HEADER "STOP  1D\n";
    close HEADER;

    my $gti = "att_gtis_fits.tmp";
    
    my $fcreate = Util::Ftool->new("fcreate");

    $fcreate->params({cdfile   => $header,
                      datafile => $data,
                      outfile  => $gti,
                      headfile => " ",
                      tbltype  => "binary",
                      nskip    => 0,
                      nrows    => 0,
                      history  => "yes",
                      morehdr  => 0,
                      extname  => "GTI",
                      anull    => " ",
                      inull    => 0,
                      clobber  => "yes"})
            ->run();

    unlink $data;
    unlink $header;

    unless($fcreate->had_error() ) { return $gti }
    else                           { return "" }

} # end of no_gap_gtis;
        
#############################################################################
# Calculate attitude/orbit derived filter quantities
#############################################################################
sub calculate_att_orb {
    my $self=shift;
    my @columns=@_;

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

    $log->entry("Calculating attitude/orbit filtering quantities");

    #######################################
    # make sure we have an attitude file
    # othewise there's no sense bothering
    #######################################
    my $attitude = $filename->get('attitude', 's');
    unless( -f $attitude ) {
        $log->entry("No attitude data available");
        return;
    }

    ##################################################################
    # assemble the mission zero time into a string to feed prefilter
    ##################################################################
    my $epoch = $procpar->read("refdate")."T".$procpar->read("reftime");

    my $fits = Util::FITSfile->new($attitude, 1);
    ##############################################
    # Add a buffer of 60 seconds to start and stop
    ##############################################
    my $tstart = $fits->keyword("TSTART") - 60.0;
    my $tstop  = $fits->keyword("TSTOP") + 60.0;
    
    my $attorb = $filename->get('attorb', 's');
    unlink $attorb;

    my $cols = 'ALL';
    $cols = join(' ', @columns) if @columns;

    #####################################
    # set up and run prefilter
    #####################################
    my $prefilter = Util::HEAdas->new("prefilter");
    $prefilter->params({outname   => $attorb,
                        columns   => $cols,
                        orbmode   => "TLE_TEXT2",
                        orbname   => $filename->fetch_orbit(),
                        attname   => $attitude,
                        alignfile => 'CALDB',
                        leapname  => $filename->fetch_cal("leapsec"),
                        rigname   => $filename->fetch_cal("rigidity"),
                        start     => $tstart,
                        end       => $tstop,
                        interval  => 1.0, # was 30.0
                        attextrap => '32',
                        origin    => 'GSFC',
                        ranom     => $jobpar->read("ra"),
                        decnom    => $jobpar->read("dec"),
                        missepoch => $epoch,
                        compcols  => "TIME",
                        compapplyquat => "no",
                        chatter   => 4,
                        clobber   => "yes" });

    $log->entry("Running ". $prefilter->name() );
    $prefilter->run();



} # end of calculate_att_orb

#############################################################################
# Calculate attitude/orbit derived filter quantities
#############################################################################
sub correct_aberration {
    my $self=shift;

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

    $log->entry('Doing aberration correction of attitude file');

    #######################################
    # make sure we have an attitude file
    # othewise there's no sense bothering
    #######################################
    my $attitude = $filename->get('attitude', 's');
    unless( -f $attitude ) {
        $log->entry('No attitude data available');
        return;
    }

    $log->entry('Preliminary prefilter run');
    $self->calculate_att_orb('TIME', 'POSITION', 'VELOCITY');

    my $aberration = Util::HEAdas->new('aberrator')
                                 ->params({infile => $attitude,
					   orbfile => $filename->get('attorb', 's'),
					   alignfile => 'CALDB'
					  });

    $log->entry('Run aberrator');
    $aberration->run();

} # end of correct_aberration