Util::Extractor (version 1.8)


package Util::Extractor;

##############################################################################
#
# DESCRIPTION: This is an interface to the extractor FTOOL which is the standard
# DESCRIPTION: program for screening event files and extracting images, 
# DESCRIPTION: spectra and lightcurves.
# DESCRIPTION: This class does a little more than just run the extractor.
# DESCRIPTION: It can also handle GTI files and knows mission specific
# DESCRIPTION: information by reading the xselect.mdb file. 
# DESCRIPTION: This class should be able to replace calls to xselect and
# DESCRIPTION: do so more efficiently and with better error checking.
#
# HISTORY: 1.0 -> 1.1 2000-06-20
# HISTORY: Now the instrument method returns $self.
# HISTORY: Fixed a bug in the gti method which mishandles a single gti file name
# HISTORY: 
# HISTORY: 1.1 -> 1.2 2000-08-09
# HISTORY: Now set binf to handle ASCA SIS region selection properly.
# HISTORY: Added bin_size method to set lightcurve bin size.
# HISTORY: Made temporary file names unique.
# HISTORY: 
# HISTORY: 1.2 -> 1.3 2002-04-22
# HISTORY: Slight modification of initialization to make it work with
# HISTORY: new Ftools class
# HISTORY:
# HISTORY: 1.3 -> 1.4 2003-05-28
# HISTORY: Add support for 'raw' coordinate type
# HISTORY: 
# HISTORY: 1.4 -> 1.5 2003-07-10
# HISTORY: Now correctly handle the situation where the screening
# HISTORY: criteria produice no GTIs. This required a change to the 
# HISTORY: gti method. Now you specify "NONE" to that method to 
# HISTORY: indicate that all times are good. No arguments indicates
# HISTORY: that no times are good. Added the has_good_time method.
# HISTORY: The extrator will not run if there is no good time.
# HISTORY:
# HISTORY: 1.5 -> 1.6 2004-04-08
# HISTORY: now read the "events" parameter from the MDB
# HISTORY: 
# HISTORY: 1.6 -> 1.7 2004-05-26
# HISTORY: now explicitly clode the MDB file after reading it
# HISTORY:
# HISTORY: 1.7 -> 1.8 2004-06-04
# HISTORY: now set the ccol parameter 

#
# VERSION: 1.8
#
##############################################################################

use Util::Ftool;
@ISA=("Util::Ftool");
use strict;

use Util::FITSfile;

#################################
# used to keep file names unique
#################################
my $INVOCATION;


###############################
# mission database class data
###############################
my $MDB={}; # mission database
my $KEYS=""; # a list of unique keys in the mission database
my %ALIASES=(ae=>"ASTRO-E",
             xrs=>"XRS",
             xis0=>"XIS-0",
             xis1=>"XIS-1",
             xis2=>"XIS-2",
             xis3=>"XIS-3",
             hxd_wel=>"HXD-WELL",
             hxd_trn=>"HXD-TRN",

             mn=>"ASCA",
             ad=>"ASCA",
             s0=>"SIS0",
             s1=>"SIS1",
             g2=>"GIS2",
             g3=>"GIS3",

             sw=>"SWIFT",
	     st=>"SWIFT",
             xrt=>"XRT",
             uvot=>"UVOT",
             bat=>"BAT" );

#there's probably a better way of doing this


################################################
# constructor
################################################
sub new {
    my $self=shift;

    $self=$self->SUPER::new("extractor");

    ##################################################################
    # increment the invocation count and set the temp file extension
    ##################################################################
    $INVOCATION++;
    my $tmp_ext="_extractor_${INVOCATION}.tmp";

    ###################################
    # temporary file names
    ###################################
    $self->{REGION_TMP}="region$tmp_ext";
    $self->region();

    $self->{GTI_TMP}="gti$tmp_ext";
    $self->{NO_GOOD_TIME}=0;
    $self->gti("NONE");

    $self->{ROW_SELECTION}="";
    $self->{ROW_SELECTION_FILE}="";

    $self->{INFILES_TMP}="infiles$tmp_ext";
    unlink $self->{INFILES_TMP};
    
    ##################################################
    # flag to set if any of the criteria are
    # so invalid that we should not run the extractor
    ##################################################



    ######################################
    # initially empty instrument database
    ######################################
    $self->{IDB}={};

    ##############################################
    # maximum length of a criterion string before
    # it must be written to a temporary file
    ##############################################
    $self->{CRITERIA_INLINE_LENGTH}=80;
    $self->{GTIS_INLINE_LENGTH}=80;

    ############################
    # default coordinate types
    ############################
    $self->{COORD_TYPE}      = "det";
    $self->{WMAP_COORD_TYPE} = "det";

    ###########################################
    # name of a sample input file which
    # can be queried if we need to know about
    # e.g. column order
    ###########################################
    $self->{SAMPLE_INPUT}="";

    return($self);
   
}

#####################
# ACCESSORS:
#####################

#####################################################################
# returns false if all the time has been filtered out.
# returns true if at least some good time made it through the filter.
#####################################################################
sub has_good_time {
    my $self = shift;

    return ! $self->{NO_GOOD_TIME};
}

########################################################################
########################################################################
# if the Util::Tool verbosity is greater than one and the log is defined,
# returns the log object. Otherwise returns false
########################################################################
sub very_verbose {
    my $self=shift;

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

    if($log && $self->verbose_level()>1) { return $log }
    else                                 {return undef }
}

########################################################################
########################################################################
# Set the mission parameters in the extractor class data.
# This method reads and parses the xselect.mdb mission database file.
# Note you have to also set the instrument before you can apply
# any of these values.
########################################################################
sub mission {
    my $self=shift;

    #####################################
    # determine mission alias if any
    #####################################
    my $chosen_mission=$self->alias(shift);

    ###########################################################
    # empty the mission database if it has already been filled
    ###########################################################
    if($MDB) { $MDB={}; };

    ################################################
    # find the xselect mission database file
    ################################################
    my $mdb_file=$self->install_dir()."/bin/xselect.mdb";
    unless(-f $mdb_file ) {
        $self->setup_error(3, "No mission databse file $mdb_file" );
    }
    

    ################################################
    # open the file containing the mission database
    # and loop over all its rows
    ################################################
    open MDB, "<$mdb_file" or return;

    my $tag;
    my $key;
    my $value;
    my $mission;
    my %unique_keys;

    while(<MDB>) {
        chomp;
    
        #################################
        # ignore blank and comment lines
        #################################
        unless( /^\s*!/ || /^\s*$/ ) {
    
            ###############################
            # parse the line
            ###############################
            ($tag,$value) = /^(\S*)\s*(.*)$/;
    
            my @inst=split /:/, $tag;
            $mission=shift @inst;
            $key= pop @inst;
            my $inst=join ":", @inst;
    
            if($mission eq $chosen_mission ) {
                ####################################################
                # initialize the MDB entry for this key if it does
                # not already exist
                ####################################################
                unless($MDB->{$key}) { $MDB->{$key}={} }

                ###########################################
                # save this keyword in the mission database
                ###########################################
                $MDB->{$key}->{$inst}=$value;

                ###########################################
                # update the list of unique keys
                ###########################################
                $unique_keys{$key}="";
              
            } # end if this line if for our chosen mission
        } # end of this is not a comment or blank line
    } # end of loop over lines in xselect.mdb file
    
    close MDB;

    ###################################
    # save the list of unique keywords
    ###################################
    $KEYS = [ keys %unique_keys];


} # end of mission method

##########################################################################
# Set the instrument-specific parameters for this extractor object.
# It actually populates the instrument database by extracting values
# from the mission database.
##########################################################################
sub instrument { #(instrument, mode, submode ...)
    my $self=shift;

    #####################################################
    # assemble all mission specifiers into a single array
    # and apply aliases
    #####################################################
    my @instrument=split /:/, join(":", (@_));
    foreach (@instrument) {
        $_ = $self->alias($_);
    }

    #####################################################
    # loop over all the keywords in the mission database
    #####################################################
    my $key;
    foreach $key ( @$KEYS ) {
        #################################################################
        # loop over sucessively more general instrument specifications
        # until we get to one which has an entry. 
        #################################################################
        my $value;
        my @inst=@instrument;
        while(! defined ($value=$MDB->{$key}->{join ":", @inst}) ) {
            
            if( !@inst) {
                #############################################
                # there are no matches for this keyword,
                # so break out of the loop.  
                # Otherwise, we would loop forever.
                ##############################################
                last;
            }

            pop @inst;

        } # end of loop over instrument specifier generalizations

        #####################################################
        # save the keyword values in the instrument database
        #####################################################
        if(defined $value) {
            $self->{IDB}->{$key} = $value;
        }

    } # end of loop over keywords

    return $self;

} # end of instrument method

#############################################################################
# get a value out of the mission database. Actually this method gets a value
# from the instument databse which is a subset of the mission database.
# The mission and instrument must be set before this method may be used
#############################################################################
sub mdb {
    my $self=shift;
    my $key=shift;

    return $self->{IDB}->{$key} || "";

} # end of mdb method

###########################################################################
# get an alias for a mission or instrument name.
# This translates from the terms we will commonly use to the terms 
# used in the xselect.mdb file
###########################################################################
sub alias {
    my $self=shift;
    my $value=shift;

    if(exists $ALIASES{$value}) { return $ALIASES{$value} }
    else                        { return $value           }
} # end of alias method



##########################################################
# Set the region filters to be used in the extractor run.
# Concatenates multiple files.
##########################################################
sub region { #(region1, region2 ...)
    my $self=shift;
    my @list=@_;

    ################################
    # clear out any temporary files
    ################################
    unlink $self->{REGION_TMP};


    if(@list==0 || !$list[0] ) {
        ####################
        # no region filter
        ####################
        $self->params({regionfile=>"NONE"});
        return $self;
    }

    my $regionfile;

    if(@list==1) {
        ################################################
        # single region filter - specify it explicitly
        ###############################################
        $regionfile=$list[0];
        

    } else {
        ################################################
        # multiple regions. We have to concatenate them
        ################################################
        $regionfile=$self->{REGION_TMP};
        open OUT, "$regionfile";
        foreach (@list) {
            if( ! -e $_ ) {
                ##########################################
                # file does not exist, so report an error
                ##########################################
                $self->setup_error(2,"Region filter $_ does not exist");
            } else {
                #####################################
                # concatenate file
                #####################################
                open IN, "<$_";
                print OUT <IN>;
                close IN;
            }
        } # end of loop over files

        close OUT;
    }

    ##################################
    # set the parameter
    # and talk to the log
    ##################################
    $self->params({regionfile=>$regionfile});

    my $log;
    if($log=$self->very_verbose() ) {
        $log->entry("Filtering with region file:");
        $log->file($regionfile);
    }

    return $self;
            
        
}


########################################################################
# set the GTI time screening files.
# Multiple GTIs are combined with the specified logic ("and" or "or").
# specify "NONE" to indicate that no time filtering should be done.
# specify no arguments to indicate that there is no good time
# and that the filtering should produce no output.
#######################################################################
sub gti { #(gti1, gti2, logic ...)
    my $self=shift;
    
    #############################################
    # reset the "all time filtered out" flag
    #############################################
    $self->{NO_GOOD_TIME}=0;

    ##############################################
    # no arguments means no good time
    # we need to set a flag to indicate that we will
    # not be filtering
    ##############################################
    if( @_ == 0 ) {
        $self->{NO_GOOD_TIME}=1;
        $self->params({timefile=>""});
        return $self;
    }

    ##############################################
    # "NONE" means don't time filter
    ##############################################
    if( $_[0] eq "NONE" ) {
        $self->params({timefile=>"NONE"});
        return $self;
    }
    ###############################################
    # If there are multiple GTIs given, the last 
    # argumement should be the logic indicator
    # it may be indicated with several formats and
    # defaults to "OR"
    #############################################
    my $logic=pop;
    if( uc($logic) eq 'AND' || $logic eq '&&' || $logic eq '&') {
        ################
        # AND
        ################
        $logic="AND";

    } elsif(uc($logic) eq 'OR' || $logic eq '||' || $logic eq '|') {
        ###############
        # OR
        ###############
        $logic="OR";

    } else {
        ################################################
        # this looks more like a GTI file name
        # so set to the default and push the argument 
        # back onto the GTI list
        ################################################
        push @_, ($logic);
        $logic="OR";
    }

    ###################################################
    # the arguments left in @_ are the GTI file names
    # clear out any temporary files
    ###################################################
    my @gtis=@_;
    my $timefile;

    if($gtis[0] ne $self->{GTI_TMP}) {unlink $self->{GTI_TMP} };        

    if(@gtis==1) {
        ################################################
        # single GTI file - specify it explicitly
        ################################################
        $timefile=$gtis[0];

    } else {
        ################################################
        # multiple GTI files. We have to merge them
        ################################################
        my $ingtis = join " ", @gtis;

        my $tmp_file;
        if(length($ingtis) > $self->{GTIS_INLINE_LENGTH} ) {
            #########################################################
            # we need to dump the GTIs to a file for input to mgtime
            #########################################################
            $tmp_file="extractor_gti_list.tmp";
            open  GTIS, ">$tmp_file";
            print GTIS join "\n",@gtis;
            close GTIS;
            $ingtis="\@$tmp_file";
        }

        ####################################
        # merge
        ####################################
        $timefile=$self->{GTI_TMP};
        Util::Ftool->new("mgtime")
                   ->params({ingtis   => $ingtis,
                             outgti   => $timefile,
                             merge    => $logic,
                             instarts => "START",
                             instops  => "STOP",
                             indates  =>  "MJDREF",
                             intimes  =>" ",
                             outstart => "START",
                             outstop  => "STOP"})
                   ->run();

        #################################
        # cleanup
        #################################
        if( defined $tmp_file) {unlink $tmp_file}

    }


    #######################################################
    # set the parameter to point to the combined GTI file
    # set the GTI extension name to STDGTI
    # since this is what maketime outputs
    # There's probably a more general and
    # elegant way of handling this
    #######################################################
    $self->params({timefile => $timefile,
                   gtinam   => "STDGTI"  });

    ####################################
    # dump the GTIs to the log
    ####################################
    my $log;
    if($log=$self->very_verbose() ) {
        $log->entry("GTIs from screening criteria:");
        $log->text(Util::FITSfile->new($timefile)
                                   ->dump_table() );
    }

    return $self;
            
        
}

#############################################################
# set the GTIs by way of a set of screening criteria
# and a filter file by running the maketime FTOOL
#############################################################
sub criteria {
    my $self=shift;
    my $criteria=shift;

    ################################################
    # log the screening criteria
    ################################################
    my $log;
    if($log=$self->very_verbose() ) {
        $log->entry("Using screening criteria:");
        $log->text($criteria);
    }


    ###############################
    # set up the maketime FTOOL
    ###############################
    my $maketime=Util::Ftool->new("maketime")
                            ->params({compact=> "no",
                                      copykw => "yes",
                                      histkw => "no",
                                      prefr  => 0.0,
                                      postfr => 1.0});


    ##################################################################
    # We will place short criteria in the parfile directly
    # but long or multiple line ones have to go into a temporary file
    ##################################################################
    my $criteria_file="extractor_criteria.tmp";

    if( $criteria !~ /\n/ && 
       length($criteria) <=$self->{CRITERIA_INLINE_LENGTH} ) {
        ########################
        # give criteria in-line
        ########################
        $maketime->params({expr=>$criteria});

    } else {
        ######################################
        # write criteria to a temporary file
        ######################################
        unlink $criteria_file;
        open CRITERIA, ">$criteria_file";
        print CRITERIA "$criteria";
        close CRITERIA;

        $maketime->params({expr=>"\@$criteria_file"});
    }


    ################################################################
    # loop over all filter files and make a GTI file for each
    ################################################################
    my @gtis=();
    my $filter;
    while( $filter=shift ) {
        ##########################################
        # run maketime 
        ##########################################
        my $gti="${filter}_gti.tmp";
        unlink $gti;
        $maketime->params({infile  => $filter,
                           outfile => $gti    })
                 ->run();

        ###################################################
        # did we produce a GTI file with something in it?
        ###################################################
        if(! $maketime->had_error() && Util::FITSfile->new($gti,1)
                                                     ->keyword("NAXIS2") ){
            ################################################
            # this is a good GTI file, so remember its name
            ################################################
            push @gtis, ($gti);
        } else {
            ###############################
            # no GTIS in this file
            ###############################
            unlink $gti;

            my $log;
            if($log=$self->very_verbose() ) {
                $log->entry("No GTIs from $filter");
            }

        } # end of check for whether we produced a good GTI file

    } # end of loop creating a GTI for each filter
    
    
    ###############################################################
    # check how many GTI files we just made and behave accordingly
    ###############################################################
    if(@gtis == 0 ) {
        ################################################
        # no GTI files
        ################################################
        $log->entry("No GTIs produced");
        $self->gti();

    } elsif( @gtis == 1) {
        #####################################
        # there's only one GTI, so rename it
        # to the official temp file
        ####################################
        rename $gtis[0], $self->{GTI_TMP};
        $gtis[0] = $self->{GTI_TMP};
        $self->gti(@gtis);
    } else {
        ###################################################
        # multiple GTIs - need to clean up temporary files
        ###################################################
        $self->gti(@gtis,"OR");
        foreach (@gtis) {unlink;}
    }


    #######################
    # cleanup
    #######################
    unlink $criteria_file;

    return $self;

} # end of criteria method

#################################################################################
# This method sets a row selection criterion to apply to all the input 
# files. It should be in the form of a CFITSIO extended filename
# row selection statement, except without enclosing brackets.
# The expression may contain newline characters.
# If the argument is blank or omitted, then any previously set criteria
# will be removed.
# This method must be called before setting the input files.
################################################################################
sub row_selection {
    my $self = shift;
    my $criteria=shift || "";
    
    #################################
    # report the criteria to the log
    #################################
    my $log;
    if($criteria && ($log=$self->very_verbose()) ) {
        $log->entry("Using row selection criteria:");
        $log->text($criteria);
    }
    
    ######################################################
    # if there is no argument or the argument is blank,
    # then we have no row filtering criteria
    #####################################################
    if(!$criteria) {
        $self->{ROW_SELECTION} ="";

    } elsif(length($criteria) > $self->{CRITERIA_INLINE_LENGTH} ||
       $criteria =~ /\n/ ) {
        #########################################
        # long or multi-line criteria
        # so dump them to a temporary file
        #########################################
        my $tmp = "extractor_row_selection_${INVOCATION}.tmp";
        unlink $tmp;
        open TMP, ">$tmp";
        print TMP $criteria;
        close TMP;
        
        $self->{ROW_SELECTION} = "[ @".$tmp." ]";
        $self->{ROW_SELECTION_FILE} = $tmp;

    } else {
        ######################################################
        # the row selection is simple enough we can inline it
        ######################################################
        $self->{ROW_SELECTION} = "[ $criteria ]";
    }
    
    return $self;
    
} # end of row_selection method



####################################################
# input event file(s)
####################################################
sub infiles { #(file1, file2 ...)
    my $self=shift;
    my @list=@_;

    if(@list == 0 ) {
        #####################################
        # no input files - this is an error
        ####################################
        $self->setup_error(2,"Specified nothing for extractor input file");

    } elsif(@list==1) {
        ##################################
        # one input file list it directly
        ##################################
        $self->params({filename=>$list[0].$self->{ROW_SELECTION}});
        $self->{SAMPLE_INPUT} = $list[0];

   } else {
        ################################################
        # multiple input files - these must be listed
        # in a temporary file
        ###############################################
        open LIST, ">$self->{INFILES_TMP}";
        foreach (@list) {
            print LIST "${_}$self->{ROW_SELECTION}\n";
        }
        close LIST;

        $self->params({filename=>"\@$self->{INFILES_TMP}" });
        $self->{SAMPLE_INPUT} = $list[0];
    }

    return $self;

}  # end of infiles method

#############################################################################
# set the lightcurve bin size and optionally the fraction of a bin
# which has to be within a GTI for it to be included. This defaults
# to "1".
#############################################################################
sub bin_size { #(bin_size, lcthresh )
    my $self=shift;
    my $bin_size=shift;
    my $lcthresh=shift;

    unless(defined($lcthresh)) {$lcthresh=1}

    $self->params({binlc    => $bin_size,
                   lcthresh => $lcthresh });
    return $self;

} # end of bin_size method

##############################################################################
# set the coordinates "det" or "sky" to be used for filtering and 
# image extraction. The default is "det".
##############################################################################
sub coord_type {
    my $self=shift;
    my $type=shift;

    $self->{COORD_TYPE} = $type;

    return $self;
}

##############################################################################
# set the coordinates "det" or "sky" to be used for WMAPs in spectra
# The default is "det".
##############################################################################
sub wmap_coord_type {
    my $self=shift;
    my $type=shift;

    $self->{WMAP_COORD_TYPE} = $type;

    return $self;
}


#############################################################################
# specify the output file name and type.
# Note that the mission and instrument must be specified before this
# method is called, since it needs to reference the MDB
#############################################################################
sub outfile {
    my $self=shift;
    my $file=shift;
    my $type=shift || "event";

    ####################################
    # clobber the outfile if it exists
    # and remember its name
    ####################################
    unlink $file;
    $self->{OUTFILE}=$file;

    ####################################################
    # set the parameters to set for this file type
    ####################################################
    if( $type =~ /event/i ) { 
        #############################
        # event files
        #############################
        $self->params({eventsout => $file,
                       imgfile   => "NONE",
                       phafile   => "NONE",
                       fitsbinlc => "NONE" });

    } elsif($type =~ /image/i ) { 
        #######################################
        # image
        #######################################
        $self->params({eventsout => "NONE",
                       imgfile   => $file ,
                       phafile   => "NONE",
                       fitsbinlc => "NONE" });

    } elsif($type =~  /spec/i ) { 
        ###########################################
        # spectrum
        ###########################################
        $self->params({eventsout => "NONE",
                       imgfile   => "NONE",
                       phafile   => $file ,
                       fitsbinlc => "NONE" });

    } elsif($type =~ /light/i ) {
        ########################################
        # light curve
        #######################################
        $self->params({eventsout => "NONE",
                       imgfile   => "NONE",
                       phafile   => "NONE",
                       fitsbinlc => $file  });

    } else {
        ##########################
        # unknown
        ##########################
        $self->setup_error(2,"Unknown file type $type for extractor");
    }

    #################################
    # set generic parameters
    #################################
    $self->params({ecol      => $self->mdb("ecol"),
                   ccol      => $self->mdb("ccol"),
                   "gti"     => $self->mdb("gti" ),
                   binf      => $self->mdb("fbin"),
                   binh      => $self->mdb("hbin"),
                   wtmapb    => $self->mdb("wtmapb"),
                   wtmapfix  => $self->mdb("wtmapfix"),
                   events    => $self->mdb("events"),
                   copyall   => "yes"});

    ####################################################
    # get a sample fits input file which we can use to 
    # track down the column numbers for TLMIN/TLMAX
    ###################################################
    unless($self->{SAMPLE_INPUT}) {
        $self->log()->error(2,"Must Specify input files before output");
    }

    my $sample = Util::FITSfile->new($self->{SAMPLE_INPUT},
                                     $self->mdb("events")  );


    ####################################
    # highest PHA channel
    ####################################
    my $phamax = $self->mdb("phamax");
    if($phamax eq "TLMAX") {
          $phamax .= $sample->find_column($self->mdb("ecol"));
    }


    $self->params({phamax=>$phamax});


    ####################################
    # filtering/image coordinates
    ####################################
    if($self->{COORD_TYPE} =~ /det/i ) {
        #######################
        # detector coordinates
        #######################
        my $x_size = $self->mdb("detxsiz");
        if($x_size eq "TLMAX") {
              $x_size .= $sample->find_column($self->mdb("detx"));
        }

        my $y_size = $self->mdb("detysiz");
        if($y_size eq "TLMAX") {
              $y_size .= $sample->find_column($self->mdb("dety"));
        }

        $self->params({xcolf => $self->mdb("detx"),
                       ycolf => $self->mdb("dety"),
                       xfkey => $x_size,
                       yfkey => $y_size });

    } elsif($self->{COORD_TYPE} =~ /raw/i ) {
        #######################
        # detector coordinates
        #######################
        my $x_size = $self->mdb("rawxsiz");
        if($x_size eq "TLMAX") {
              $x_size .= $sample->find_column($self->mdb("rawx"));
        }

        my $y_size = $self->mdb("rawysiz");
        if($y_size eq "TLMAX") {
              $y_size .= $sample->find_column($self->mdb("rawy"));
        }

        $self->params({xcolf => $self->mdb("rawx"),
                       ycolf => $self->mdb("rawy"),
                       xfkey => $x_size,
                       yfkey => $y_size });

    } elsif($self->{COORD_TYPE} =~ /sky/i ) {
        #######################
        # sky coordinates
        #######################
        $self->params({xcolf => $self->mdb("x"),
                       ycolf => $self->mdb("y")});
    }

    ####################################
    # WMAP coordinates
    ####################################
    if($self->{WMAP_COORD_TYPE} =~ /det/i ) {
        #######################
        # detector coordinates
        #######################
        $self->params({xcolh     => $self->mdb("detx"),
                       ycolh     => $self->mdb("dety") });

    } elsif($self->{WMAP_COORD_TYPE} =~ /sky/i ) {
        #######################
        # detector coordinates
        #######################
        $self->params({xcolh     => $self->mdb("x"),
                       ycolh     => $self->mdb("y") });
    }




    return $self;


} # end of outfile method

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

########################################################################
# simple error handler to report setup errors.
# It uses the log if available, but has a backup plan in case it isn't
#######################################################################
sub setup_error {
    my $self=shift;
    my $level=shift;
    my $message=shift;

    my $log=$self->log();
    if($log) {
        ###########################
        # use the log
        ###########################
        $log->error($level,$message);

    } else {
        ############################
        # we got no log
        ############################
        print STDERR "$message\n";
        exit $level;
    }

} # end of setup_error method

#######################################################################
# We override the Ftool run method because sometimes we will not want
# to run the tool at all - for example if time screening has excluded
# all times.
#######################################################################
sub run {
    my $self = shift;

    if($self->{NO_GOOD_TIME}) {
        $self->log()->entry("No good time intervals. Extractor not run.");
        return;
    }

    ##############################################################
    # if we get here then it is OK to actually run the extractor
    #############################################################
    $self->SUPER::run();
} # end of run method

    

######################################
# destructor - delete temporary files
######################################
sub DESTROY {
    my $self=shift;

    ##############################
    # delete the temporary files
    ##############################
    unlink $self->{INFILES_TMP};
    unlink $self->{REGION_TMP};
    unlink $self->{GTI_TMP};
    unlink $self->{ROW_SELECTION_FILE};


    ################################
    # inherit the parent destructor
    ################################
    $self->SUPER::DESTROY();


}