package Util::FITSfile;
##############################################################################
#
# DESCRIPTION: This class is used for operating on a single FITS file.
# DESCRIPTION: For the most part it is an interface to the "futils" FTOOLS.
#
# HISTORY
# HISTORY: $Log: FITSfile.pm,v $
# HISTORY: Revision 1.3 2014/02/27 07:01:06 apsop
# HISTORY: VERSION header now shows CVS Revision
# HISTORY:
# HISTORY: Revision 1.2 2006/08/01 20:35:34 apsop
# HISTORY: Add in CVS history indicator.
# HISTORY:
# HISTORY: 1.0 -> 1.1 2000-06-19
# HISTORY: added stats method
# HISTORY:
# HISTORY: 1.1 -> 1.2 2000-07-06
# HISTORY: Added interface to fmodhead. Also now dump column names to a
# HISTORY: temporary file if the list is too long for a parameter value.
# HISTORY:
# HISTORY: 1.2 -> 1.3 2000-07-18
# HISTORY: Modified table method to handle the care where the file has
# HISTORY: only one row and the rows are specified as "-". In this case we
# HISTORY: have to explicitly set the row number to "1", since ftabpar
# HISTORY: doesn't understand "-".
# HISTORY:
# HISTORY: 1.3 -> 1.4 2000-08-15
# HISTORY: added import_header method
# HISTORY:
# HISTORY: 1.4 -> 1.5 2001-03-20
# HISTORY: Modified site_independant_checksum to be more robust.
# HISTORY: Now removed HISTORY and COMMENT keywords and inline comments.
# HISTORY:
# HISTORY: 1.5 -> 1.6 2003-03-17
# HISTORY: Added append_to method
# HISTORY:
# HISTORY: 1.6 -> 1.7 2003-10-10
# HISTORY: Fixed some stderr output when calling the rows method on a
# HISTORY: corrupted FITS file
# HISTORY:
# HISTORY: 1.7 -> 1.8 2004-05-04
# HISTORY: Added "keywords" method which returns all
# HISTORY: keywordas as a hash. This method should be used with caution as it
# HISTORY: may be removed in the future.
# HISTORY:
# HISTORY: 1.8 -> 1.9 2004-05-26
# HISTORY: Added list_hdus method
#
# VERSION: $Revision: 1.3 $
#
##############################################################################
use Util::Ftool;
use Util::HEAdas;
use strict;
my $CHECKSUM_TOOL="";
my $INVOCATION;
###############################################################################
# If the extension, "ext", is omitted, it defaults to "1".
# The primary HDU is "0".
# Note that ext can be either numerical or symbolic - i.e. it may refer
# to the EXTNAME keyword of the desired extension. Don't use
# brackets around the extension name argument.
# Specifications are optional FITSIO file selection specifications.
# These do need to include all punctuation, such as brackets.
# See the FITSIO extended filename syntax documentation for more information.
###############################################################################
sub new { #(filename, ext, specifications)
my $proto = shift;
my $class = ref($proto) || $proto;
my $self={};
$self->{NAME} =shift;
$self->{EXT} =shift;
$self->{SPECS}=shift || "";
############################################
# stuff to keep temporary file names unique
############################################
$INVOCATION++;
my $temp_suffix="_$self->{NAME}_$INVOCATION.tmp";
#################################
# default to the first extension
#################################
if( ! defined($self->{EXT}) ) {$self->{EXT}=1};
$self->{COLS}="-"; # these default to all rows and columns
$self->{ROWS}="-";
$self->{DUMP_SEPARATOR}=" "; # field separator for fdumps
$self->{MANY_KEYWORDS}=0; #flag for using fmodhead or not.
$self->{FMODHEAD_TEMPLATE}="fmodhead_template$temp_suffix";
$self->{COLUMN_FILE}="fits_columns$temp_suffix";
$self->{MAX_COL_CHARACTERS}=80;
bless($self,$class);
return $self;
}
##################################
# ACCESSORS:
##################################
######################################################
# get or set the name of the file without decorations
######################################################
sub name {
my $self = shift;
if (@_) { $self->{NAME} = shift }
return $self->{NAME};
}
######################################################
# get or set the FITS extension
######################################################
sub ext {
my $self = shift;
if (@_) { $self->{EXT} = shift }
return $self->{EXT};
}
######################################################
# get or set the file specifiers.
######################################################
sub specs {
my $self = shift;
if (@_) { $self->{SPECS} = shift }
return $self->{SPECS};
}
##############################################################################
##############################################################################
# Sets the current column specifier which is handed off to most FTOOLS
# verbatim. This is a space separated list of column names, though sometimes
# only a single column name is appropriate. Multiple arguments
# will be concatenated.
# If there are too many columns to fit on a single parameter line, this method
# will dump them to a temporary file
##############################################################################
sub cols {
my $self = shift;
$self->{COLS} = join ' ', @_;
##################################################################
# check if we have too many columns to fit on one parameter line
##################################################################
if( length($self->{COLS}) > $self->{MAX_COL_CHARACTERS} ) {
###################################
# need to dump columns into a file
###################################
open LIST, ">$self->{COLUMN_FILE}";
foreach (split /\s/, $self->{COLS} ) {
print LIST "$_\n";
}
$self->{COLS}="\@$self->{COLUMN_FILE}";
}
return $self;
}
##########################################################################
# set the row specifier which is handed verbatim to most FTOOLS.
# By convention it can consist of a comma-separated list of
# single numbers or ranges specified as two numbers separated by a "-".
# However, note that sometimes only a single number is appropriate.
#########################################################################
sub rows {
my $self = shift;
$self->{ROWS} = shift;
return $self;
}
##########################################################################
# set the character used as a field separator in fdump. You probably don't
# need to worry about this unless you have a table with a string column
# containing a "|" character
##########################################################################
sub dump_separator {
my $self = shift;
$self->{DUMP_SEPARATOR} = shift;
return $self;
}
###########################################################################
# return the current fdump field separator. See above.
###########################################################################
sub get_dump_separator {
my $self = shift;
return $self->{DUMP_SEPARATOR};
}
###########################################################################
# return the file name will all extended filename syntax decorations
# attached.
###########################################################################
sub fullname {
my $self=shift;
return "$self->{NAME}\[$self->{EXT}\]" . $self->{SPECS};
}
###########################################################################
# Get or set the Util::Tool object in the class data which will be
# used to calculate site-independant checksums.
###########################################################################
sub checksum_tool {
my $self = shift;
if (@_) {
########################################
# set the checksum program
########################################
my $path=shift;
my $command;
($path,$command)= $path =~ /(.*)\/(.*)/;
$CHECKSUM_TOOL = Util::Tool->new($path,$command)
->verbose(0);
}
###############################################
# reset the input to nothing before returning
###############################################
$CHECKSUM_TOOL->stdin("")
->command_line("");
return $CHECKSUM_TOOL;
}
#################
# METHODS:
#################
####################################################
# return the number of columns currently specified.
# If no columns are specified,
# it returns the total number of columns in the table.
####################################################
sub ncols {
my $self=shift;
if($self->{COLS} eq "-") {
##########################################
# all the columns are currently specified
# so we need to read the tfields keyword
##########################################
return $self->keyword("TFIELDS");
} else {
###################################
# count the number of columns
###################################
unless( $self->{COLS} =~ /^@/ ) {
#####################################
# the columns are listed explicitly
#####################################
return split /\s/, $self->{COLS};
} else {
###################################
# the columns are listed in a file
###################################
open LIST, "<$self->{COLUMN_FILE}";
my @cols=<LIST>;
close LIST;
return @cols;
}
}
} # end of ncols method
####################################################
# return the number of rows currently specified.
# If no rows are specified,
# it returns the total number of rows in the table.
####################################################
sub nrows {
my $self=shift;
###############################################
# parse the number of rows from the specifier
###############################################
my $nrows=0;
foreach (split /\s*,\s*/, $self->{ROWS} ) {
if( /-/ ) {
#############################################
# specifier contains a dash, meaning a range
#############################################
my ($from,$to) = /([^-]*)-([^-]*)/;
######################################
# if values are left out they default
# to the ends of the file
######################################
if(!defined $from || $from eq "" ) { $from=1 }
if(!defined $to || $to eq "" ) {$to=$self->keyword("NAXIS2") }
##############################################
# if we couldn't read the NAXIS2 keyword,
# $to will still be undefined. In this case
# we return zero rows for this file
##############################################
if(!defined $to || $to eq "" ) {return 0 }
######################################################
# add the number of rows in this segment to the total
######################################################
$nrows += $to - $from +1;
} else {
#################################
# specifier is a single number
#################################
$nrows++;
}
} # end of loop over specifier segments
return $nrows;
} # end of nrows method
#############################################################################
# returns the total number of HDUs in the FITS file
#############################################################################
sub nhdus {
my $self=shift;
my $fstruct = Util::Ftool->new("fstruct")
->verbose(0)
->params({infile => $self->{NAME},
outfile => "STDOUT" });
$fstruct->run();
my $nhdus = $fstruct->parfile()->read("totalhdu");
return $nhdus;
}
#############################################################################
# returns a list of all the HDUs in the FITS file
#############################################################################
sub list_hdus {
my $self = shift;
###############################################
# use "ftlist to get a summary of the file
###############################################
my $ftlist = Util::HEAdas->new("ftlist")
->verbose(0)
->params({infile=>$self->{NAME},
option=>"H",
outfile=>"-",
clobber=>"no",
include=>"",
exclude=>"",
section=>":",
columns=>"",
rows=>"",
vector=>"",
separator=>"|",
rownum=>"no",
colheader=>"no"});
$ftlist->run();
############################################################
# parse the ftlist output to get a list of extension names
############################################################
my @lines = split /^/m, $ftlist->stdout();
my @extensions=();
foreach my $line (@lines) {
chomp($line);
my @fields = split /\s+/, $line;
####################################
# skip the header
####################################
if(! @fields || $fields[0] ne "HDU" ) { next; }
push @extensions, $fields[2];
}
return (@extensions);
} # end of list_hdus method
#############################################################################
# returns the number corresponding to a given column name or 0 if the column
# is not contained in the file
#############################################################################
sub find_column {
my $self = shift;
my $col = shift;
#############################################################
# loop over columns until we find the one we are looking for
#############################################################
my $ncols=$self->keyword("TFIELDS");
unless(defined $ncols) { return 0; }
my $i;
for($i=1; $i<=$ncols; $i++) {
if($self->keyword("TTYPE${i}") eq $col ) { return $i }
}
############################################
# if we get here, we didn't find the column
############################################
return 0;
} # end of find_column method
#############################################################################
#############################################################################
# Dump the contents of a table into a single long string. This is
# really just an interface to the fdump FTOOL. This low level access method
# is mostly for internal use. Most table access should be done with the
# "table" method.
#############################################################################
sub dump_table {
my $self=shift;
#################################
# dump the file
#################################
my $cols = $self->{COLS};
$cols =~ s/ /,/g;
my $fdump=Util::HEAdas->new("ftlist")
->params({infile => $self->fullname(),
outfile => "-",
columns => $cols,
"rows" => $self->{ROWS},
separator => $self->{DUMP_SEPARATOR},
option => 'T',
colheader => 'no',
rownum => 'no',
clobber => 'no'})
->verbose(0)
->run();
########################################
# trim leading and trailing blank lines
########################################
$fdump->{STDOUT} =~ s/^[\s\n]*//;
$fdump->{STDOUT} =~ s/[\s\n]*$//;
return $fdump->{STDOUT};
}
#############################################################################
#############################################################################
# Returns the currently specified rows and columns of the table.
# If only one column and row are specified, it returns that value -
# either as a scalar or as an array with a single value, depending on the
# context.
# If more than one row or column are specified, returns the values in a
# a single array cycling fastest over rows.
# Note that with two columns this can be convenient if you want to put
# the values into a hash.
# For three or more columns you may need to do some awkward reshuffling.
#############################################################################
sub table {
my $self=shift;
################################################
# get the number of columns currently specified
################################################
my $ncols = $self->ncols();
if($ncols == 1 ) {
#################################################
# only one column, The default " " dump_separator
# will work best here - that way whitespace
# is trimmed automaitcally by fdump
#################################################
unless( $self->nrows() == 1 ) {
###################################################
# dump multiple rows of a single column
# the default " " field separator is best here
# since with it, fdump automatically trims
# whitespace from string columns
###################################################
return (split /\s*\n\s*/, $self->dump_table() );
} else {
###########################################################
# we only need a single value, so use ftabpar
# note we have to be careful of the case where the table
# only has one row.
###########################################################
my $row=$self->{ROWS};
if($row eq '-' ) { $row=1 }
my $reader=Util::Ftool->new("ftabpar")
->params({fitsfile => $self->fullname(),
column => $self->{COLS},
row => $row,
element => 1, })
->run();
my $value;
if($reader->parfile()->read("undef") eq "no") {
$value= $reader->parfile()->read("value");
}
if(wantarray() ) { return ($value); }
else { return $value; }
} # end if we only want one row and column
} elsif($ncols >1 ) {
####################################################
# multiple columns, by default use "|" as the field
# separator
####################################################
my $sep=$self->get_dump_separator();
if( $sep =~ /^\s*$/ ) {
###############################
# spaces generally won't cut it
################################
$sep="|";
$self->dump_separator($sep);
}
return ( split /\s*[\n\|]\s*/, $self->dump_table() );
} else {
###########################
# no columns
###########################
return undef;
}
} # end of dump_column method
######################################################################
# Check if the current column is in order.
# - returns 1 if the column is in order
# - returns 0 if the column is not in order or if there is an error from
# cktime
# The default column name is "TIME" if none is explicitly specified.
#
# An empty table is considered to be ordered.
######################################################################
sub isOrdered {
my $self=shift;
my $unique=shift || "";
###################################
# make sure the table is not empty
# cktime gives an error if it is
###################################
unless($self->keyword("NAXIS2")) { return 1; }
#####################################
# get the columns name
#####################################
my $col=$self->{COLS};
if($col eq "" || $col eq "-" ) { $col="TIME" }
#####################################
# checkequal parameter based on
# the "unique" argument
#####################################
my $ckequal;
if($unique) {$ckequal="yes"}
else {$ckequal="no" }
#########################################
# set up and run the cktime FTOOL
#########################################
my $cktime=Util::Ftool->new("cktime")
->params({infile =>$self->fullname(),
colname=>$col,
ckequal=>$ckequal})
->run();
#######################################
# parse the output and return the
# appropriate value
#######################################
my $stdout=$cktime->stdout();
if($stdout =~ /Same time as previous/ ||
$stdout =~ /Out of time order/ ) { return 0 }
else { return 1 }
}
########################################################################
########################################################################
# sort a table by the current column(s).
# The default column is "TIME" if none is explicitly specified.
########################################################################
sub sort {
my $self=shift;
my $col=$self->{COLS};
if($col eq "" || $col eq "-" ) { $col="TIME" }
####################################
# defaults
####################################
my $ascend="yes";
my $method="insert";
my $unique="no";
########################################
# interpret options
########################################
my $arg;
while($arg=shift) {
if($arg eq "reverse") {$ascend="no"} # sort in decending order
if($arg eq "unique") {$unique="yes"} # delete rows with same key
if($arg eq "insert" ||
$arg eq "heap" || # sort algorithms
$arg eq "shell" ) {$method=$arg}
}
####################################
# filename
####################################
my $full=$self->fullname();
my $file=$self->name();
###########################################
# run fmemsort
###########################################
Util::Ftool->new("fmemsort")
->params({infile => $full,
outfile => "!$file",
columns => $col,
method => $method,
ascend => $ascend,
load2mem => "yes",
copyprime=> "yes",
copyall => "yes",
unique => $unique,
history => "yes"})
->run();
#####################################################
# reset filttering specifications to null since
# they will be automatically fulfilled in the new
# version of this file
#####################################################
$self->{SPECS}="";
return $self;
} # end of sort method
############################################################################
# Read or set a keyword value from the header
# - If one argument is given, read the keyword of that name.
# - If two or more arguments are given, write the value in the second
# argument to the keyword and return a reference to this object.
# - If three arguments are given, the third is the inline keyword comment.
############################################################################
sub keyword {
my $self=shift;
my $key=shift;
my $value=shift;
my $comment=shift || " ";
unless( defined $value ) {
###############################
# read the keyword value
###############################
my $fkeypar=Util::HEAdas->new("ftkeypar")
->params({infile=>$self->fullname(),
keyword =>$key});
$fkeypar->seriousness(0);
$fkeypar->run();
my $exists=$fkeypar->parfile()->read("exist");
if($exists eq "yes") {
#######################################
# keyword exists so return its value
#######################################
my $value=$fkeypar->parfile()->read("value");
#############################
# trim quotes and whitespace
#############################
$value =~ s/^'//;
$value =~ s/'$//;
$value =~ s/^\s*//;
$value =~ s/\s*$//;
return $value;
} else {
########################
# keyword does not exist
########################
return undef;
}
} else {
##############################
# we need to write a keyword
##############################
unless($self->{MANY_KEYWORDS}) {
########################################
# just add this keyword using fparkey
#######################################
Util::HEAdas->new("fthedit")
->params({value => $value,
infile => $self->fullname(),
keyword => $key,
comment => $comment,
operation=> 'add'})
->run();
} else {
############################################################
# this is just one of a long string of keywords which will
# be added with fmodhead
############################################################
if( $value =~ /\s/ ) { $value="'$value'" }
open TEMPLATE, ">>$self->{FMODHEAD_TEMPLATE}";
print TEMPLATE "${key}=$value / $comment\n";
close TEMPLATE;
}
return $self;
} # end of we are writing a keyword
} # end of keyword method
############################################################################
############################################################################
# begin a long set of keywords to add using fmodhead instead of fparkey
# The keywords themselves are specified using the keyword method.
############################################################################
sub begin_many_keywords {
my $self=shift;
$self->{MANY_KEYWORDS}=1;
unlink $self->{FMODHEAD_TEMPLATE};
return $self;
}
############################################################################
############################################################################
# End a long list of keyword modifications and apply those modifications to the
# file. See the begin_many_keywords method.
############################################################################
sub end_many_keywords {
my $self=shift;
Util::HEAdas->new("fthedit")
->params({infile => $self->fullname(),
keyword => '@'.$self->{FMODHEAD_TEMPLATE} })
->run();
$self->{MANY_KEYWORDS}=0;
unlink $self->{FMODHEAD_TEMPLATE};
}
############################################################################
############################################################################
# Get a hash of all the keywords in an HDU
############################################################################
sub keywords {
my $self=shift;
#################################
# dump the file
#################################
my $fdump=Util::HEAdas->new("ftlist")
->params({infile => $self->fullname(),
outfile => "STDOUT",
option => 'K',
separator=> ' ',
clobber => 'no'})
->verbose(0)
->run();
##############################
# trim leading blank lines
##############################
$fdump->{STDOUT} =~ s/^[\s\n]*//;
#######################################################
# Clean out history, comment and end keywords, mostly
# because they don't have equal signs
#######################################################
$fdump->{STDOUT} =~ s/^(COMMENT|HISTORY|END)[^\n]*\n//;
my %hash;
foreach ( split "\n", $fdump->{STDOUT} ){
if( /^([\w-]+)\s*=\s*'(.+)'/ ){
$hash{$1} = $2;
$hash{$1} =~ s/ +$//;
}elsif( /^([\w-]+)\s*=\s*([^\s\/]+)/ ){
$hash{$1} = $2;
}
}
return %hash;
}
############################################################################
############################################################################
# copy the header from another file into this one, optionally omitting
# all the listed keywords.
############################################################################
sub import_header { #(source, [except|include], key1, key2...)
my $self=shift;
my $source=shift;
my @keys=@_;
######################################################
# do we want to include or omit the listed keywords?
######################################################
my $except="";
if($keys[0] eq "except" ) {
$except=shift @keys;
}
if($keys[0] eq "include" ) {
shift @keys;
}
#############################
# should we copy comments?
#############################
my $comment="yes";
if($except && @keys != (@keys = grep {$_ ne "COMMENT"} @keys ) ||
@keys != (@keys = grep {$_ ne "!COMMENT"} @keys ) ) {
$comment="no";
}
##################################
# should we copy HISTORY records?
##################################
my $history="yes";
if($except && @keys != (@keys = grep {$_ ne "HISTORY"} @keys ) ||
@keys != (@keys = grep {$_ ne "!HISTORY"} @keys ) ) {
$history="no";
}
##################################
# should we copy scale records?
##################################
my $scale="yes";
if($except && @keys != (@keys = grep {$_ ne "scale"} @keys ) ||
@keys != (@keys = grep {$_ ne "!scale"} @keys ) ) {
$scale="no";
}
###################################
# edit the key list if we need to
###################################
if($except) {
foreach (@keys) {
if(!/^!/) { $_ = "!$_" }
}
}
#########################################
# print the key list to a temporary file
#########################################
my $keyword_list="import_header_keyword_list.tmp";
open LIST, ">$keyword_list";
print LIST join "\n", @keys;
close LIST;
################################
# run the cphead FTOOL
################################
Util::Ftool->new("cphead")
->params({infile=>$source,
outfile=>$self->fullname(),
keyfil=>$keyword_list,
scale=>$scale,
comment=>$comment,
history=>$history})
->run();
unlink $keyword_list;
return $self;
} # end of import_header method
############################################################################
############################################################################
# Modify the in-line comment for the current column of a FITS table.
# Note that the current columns must be set to a single column name.
############################################################################
sub column_comment {
my $self=shift;
my $comment=shift;
################################
# get the column number
################################
my $fcolpar=Util::Ftool->new("fcolpar")
->params({infile => $self->fullname(),
colname => $self->{COLS},
exact => "yes"})
->run();
if($fcolpar->had_error() ) { return }
my $column_number=$fcolpar->parfile()->read("colnum");
my $key="TTYPE$column_number";
###############################################
# set the comment, and keep the value the same
###############################################
$self->keyword($key,$self->{COLS},$comment);
} # end of column_comment method
############################################################################
# This method generates a site-independant checksum for a FITS file.
# It does this by dumping the headers of the file,
# deleting keywords such as DATE, ORIGIN, and CHECKSUM,
# and generating a checksum of that. Note that if FITS data checksums
# have been generated, this takes the data parts of the file into account.
############################################################################
sub site_independant_checksum {
my $self=shift;
#################################
# dump the file
#################################
my $fdump=Util::Ftool->new("fdump")
->params({infile =>$self->name(),
outfile =>"STDOUT",
"rows" =>"-",
columns =>"-",
prhead =>"yes",
prdata =>"no",
showcol =>"no",
showunit =>"no",
showscale=>"yes",
showrow =>"no",
page =>"no",
wrap =>"no",
align =>"no",
xdisp =>"b",
pagewidth=>"256"})
->verbose(0)
->run();
###########################################
# remove certain keywords
###########################################
$fdump->{STDOUT} =~ s/^\s*$//gm;
$fdump->{STDOUT} =~ s/^DATE .*$//gm;
$fdump->{STDOUT} =~ s/^ORIGIN .*$//gm;
$fdump->{STDOUT} =~ s/^CHECKSUM.*$//gm;
$fdump->{STDOUT} =~ s/^HISTORY .*$//gm;
$fdump->{STDOUT} =~ s/^COMMENT .*$//gm;
###############################
# remove inline comments
###############################
$fdump->{STDOUT} =~ s/''//g;
$fdump->{STDOUT} =~ s/(^........= '[^']*').*$/$1/gm; # string values
$fdump->{STDOUT} =~ s/(^........= \s*[^'][^\s]*).*$/$1/gm; # non-string
#########################################
# remove extraneous white space
#########################################
$fdump->{STDOUT} =~ s/\s*$//gm; # clip off white space at end of line
$fdump->{STDOUT} =~ s/\n{2,}/\n/g; # remove empty lines
my $sum=$self->checksum_tool();
$sum->stdin($fdump->{STDOUT})
->run();
my $answer;
($answer)= $sum->stdout() =~ /(\S*)/;
return $answer;
}
#############################################################################
#############################################################################
# Run fcopy to copy this FITS file to another. This is mostly useful
# if you want to use the extended file name syntax to filter a file.
# If no filename is given, fcopy will overwrite the input file, effectively
# screening in place.
#############################################################################
sub copy {
my $self=shift;
my $newfile=shift;
if( ! defined $newfile) { $newfile="!".$self->name() }
Util::HEAdas->new("ftcopy")
->params({infile => $self->fullname(),
outfile => $newfile })
->run();
if($newfile eq "!".$self->name() ) {
##############################################################
# we're overwritting this file, so
# reset the filtering specifications to null
# since those are automatically met by the new file
# and specifying them would force reading the file to memory
# in any subsequent FTOOLS calls
#############################################################
$self->{SPECS}="";
}
return $self;
} # end of copy method
#############################################################################
#############################################################################
# This is similar to the copy method, except that it only copies the current
# HDU of the FITS file. It is basicly a wrapper aourn fextract.
#############################################################################
sub extract {
my $self=shift;
my $newfile=shift;
if( ! defined $newfile) { $newfile="!".$self->name() }
Util::Ftool->new("fextract")
->params({infile => $self->fullname(),
outfile => $newfile,
clobber => "yes" })
->run();
if($newfile eq "!".$self->name() ) {
##############################################################
# we're overwritting this file, so
# reset the filtering specifications to null
# since those are automatically met by the new file
# and specifying them would force reading the file to memory
# in any subsequent FTOOLS calls
#############################################################
$self->{SPECS}="";
}
return $self;
} # end of extract method
#############################################################################
#############################################################################
# append the current extension to another FITS file using "fappend".
#############################################################################
sub append_to {
my $self = shift;
my $file = shift;
Util::HEAdas->new("ftappend")
->params({infile => $self->fullname(),
outfile => $file,
history => "no"})
->run();
} # end of append_to method
#############################################################################
#############################################################################
# run fverify on the file. Don't report anything unless there is an error.
# Returns 1 if there was an error, and 0 otherwise.
#############################################################################
sub verify {
my $self=shift;
my $log=Util::Ftool->log();
##################################################
# set up and run the fverify tool
##################################################
my $fverify=Util::HEAdas->new("ftverify")
->params({infile => $self->fullname(),
outfile => "STDOUT",
prhead => "no",
testdata => "yes"})
->verbose(0)
->run();
if($fverify->had_error() || $fverify->parfile()->read("numerrs")) {
#################################################
# only dump the fverify output if there was an
# error to avoid filling up the log with trash
# note we set fverify to not be verbose above
#################################################
$log->entry("output from fverify");
$log->text($fverify->stdout() );
return 1;
}
############################
# no error if we got here
############################
return 0;
} # end of verify method
###########################################################################
###########################################################################
# Calculate statistics for the current row and column.
# The statistics are calculated using the fstatistic FTOOL and are returned
# as an array: ($sum,$mean,$sigma,$min,$max).
###########################################################################
sub stats {
my $self=shift;
my $tool=Util::Ftool->new("fstatistic")
->params({infile => $self->fullname(),
colname => $self->{COLS},
"rows" => $self->{ROWS},
outfile => "STDOUT",
maxval => "INDEF",
minval => "INDEF"})
->verbose(0)
->run();
my $parfile=$tool->parfile();
my $sum=$parfile->read("sum");
my $mean=$parfile->read("mean");
my $sigma=$parfile->read("sigma");
my $min = $parfile->read("min");
my $max = $parfile->read("max");
$tool->DESTROY;
return ($sum,$mean,$sigma,$min,$max);
} #end of stats method
############################################################################
############################################################################
# clean up temporary files
############################################################################
sub DESTROY {
my $self=shift;
unlink $self->{FMODHEAD_TEMPLATE};
unlink $self->{COLUMN_FILE};
} # end of destructor method
1;