# -*-perl-*- package Astro::FITS::HdrTrans; =head1 NAME Astro::FITS::HdrTrans - Translate FITS headers to standardised form =head1 SYNOPSIS use Astro::FITS::HdrTrans qw/ translate_from_FITS translate_to_FITS /; %generic_headers = translate_from_FITS(\%FITS_headers); %FITS_headers = translate_to_FITS(\%generic_headers); @headers = Astro::FITS::HdrTrans->generic_headers(); @classes = Astro::FITS::HdrTrans->translation_classes(); =head1 DESCRIPTION Converts information contained in instrument-specific FITS headers to and from generic headers. A list of generic headers are given at the end of the module documentation. =cut use 5.006; use strict; use warnings; use warnings::register; use Carp; use vars qw/ $VERSION $DEBUG @ISA /; use Exporter 'import'; our @EXPORT_OK = qw( translate_from_FITS translate_to_FITS ); $VERSION = '0.05'; $DEBUG = 0; # The reference list of classes we can try This list should be # extended whenever new translation tables are added. They should # have a corresponding Astro::FITS::HdrTrans:: module available Note # that there are more perl modules in the distribution than are listed # here. This is because some perl modules provide a base set of # translations shared by multiple instruments. my @REF_CLASS_LIST = qw/ ACSIS IRCAM CGS4 UIST UFTI JCMT_GSD JCMT_GSD_DB MICHELLE SCUBA SCUBA2 UKIRTDB WFCAM IRIS2 /; # This is the actual list that is currently supported. It should always # default to the reference list my @local_class_list = @REF_CLASS_LIST; =head1 CLASS METHODS Some class methods are available =over 4 =item B Returns a list of all the generic headers that can in principal be used for header translation. Note that not all the instruments support all the headers. @hdrs = Astro::FITS::HdrTrans->generic_headers(); =cut my @generic_headers = qw( AIRMASS_START AIRMASS_END ALTITUDE AMBIENT_TEMPERATURE AZIMUTH_START AZIMUTH_END BACKEND BACKEND_SECTIONS BANDWIDTH_MODE BOLOMETERS CAMERA CAMERA_NUMBER CHOP_ANGLE CHOP_COORDINATE_SYSTEM CHOP_FREQUENCY CHOP_THROW CONFIGURATION_INDEX COORDINATE_SYSTEM COORDINATE_UNITS COORDINATE_TYPE CYCLE_LENGTH DEC_BASE DEC_SCALE DEC_SCALE_UNITS DEC_TELESCOPE_OFFSET DETECTOR_BIAS DETECTOR_INDEX DETECTOR_READ_TYPE DR_GROUP DR_RECIPE ELEVATION_START ELEVATION_END EPOCH EQUINOX EXPOSURE_TIME FILENAME FILTER FRONTEND GAIN GALACTIC_LATITUDE GALACTIC_LONGITUDE GRATING_DISPERSION GRATING_NAME GRATING_ORDER GRATING_WAVELENGTH HUMIDITY INSTRUMENT INST_DHS LATITUDE LONGITUDE MSBID NSCAN_POSITIONS NUMBER_OF_COADDS NUMBER_OF_CYCLES NUMBER_OF_DETECTORS NUMBER_OF_EXPOSURES NUMBER_OF_OFFSETS NUMBER_OF_READS NUMBER_OF_SUBFRAMES NUMBER_OF_SUBSCANS OBJECT OBSERVATION_MODE OBSERVATION_NUMBER OBSERVATION_TYPE POLARIMETER POLARIMETRY PROJECT RA_BASE RA_SCALE RA_SCALE_UNITS RA_TELESCOPE_OFFSET RECEIVER_TEMPERATURE REST_FREQUENCY ROTATION SAMPLING SCAN_INCREMENT SEEING SLIT_ANGLE SLIT_NAME SLIT_WIDTH SPEED_GAIN STANDARD SWITCH_MODE SYSTEM_TEMPERATURE SYSTEM_VELOCITY TAU TELESCOPE TILE_NUMBER USER_AZIMUTH_CORRECTION USER_ELEVATION_CORRECTION UTDATE UTEND UTSTART VELOCITY VELOCITY_REFERENCE_FRAME VELOCITY_TYPE WAVEPLATE_ANGLE X_BASE Y_BASE X_OFFSET Y_OFFSET X_REQUESTED Y_REQUESTED X_SCALE Y_SCALE X_DIM Y_DIM X_LOWER_BOUND X_UPPER_BOUND Y_LOWER_BOUND Y_UPPER_BOUND ZENITH_DISTANCE_START ZENITH_DISTANCE_END ); sub generic_headers { my $class = shift; return @generic_headers; } =item B Return the names of all the translation classes that will be tried when translating a FITS header. @classes = Astro::FITS::HdrTrans->translation_classes(); If arguments are supplied, the list of translation classes is set to the supplied values. Astro::FITS::HdrTrans->translation_classes( @new ); =cut sub translation_classes { my $class = shift; if (@_) { @local_class_list = @_; } return @local_class_list; } =item B Revert back to the reference list of translation classes. Astro::FITS::HdrTrans->reset_translation_classes; Useful if the list has been modified for a specific translation. =cut sub reset_classes { my $class = shift; @local_class_list = @REF_CLASS_LIST; } =item B Allows additional classes to be pushed on the list of valid translation classes. Astro::FITS::HdrTrans->push_class( $class ); The class[es] can be specified either as a list or a reference to an array. =cut sub push_class { my $class = shift; my @new = @_; # check for array ref @new = ( ref($new[0]) ? @{ $new[0] } : @new ); push(@local_class_list, @new); return @local_class_list; } =back =head1 FUNCTIONS The following functions are available. They can be exported but are not exported by default. =over 4 =item B Converts a hash containing instrument-specific FITS headers into a hash containing generic headers. %generic_headers = translate_from_FITS(\%FITS_headers, class => \@classes, prefix => 'ORAC_', ); This method takes a reference to a hash containing untranslated headers, and a hash reference containing the following optional keys: =over 8 =item * class - A reference to a list of subclasses to try to use for header translations. This list overrides the default list. If left blank, the default list will be used, as returned by the C method. This is sometimes required to break degeneracy when you know you have a limited set of valid instruments. =item * prefix - A string prefix to add to the front of every translated header name. For example, if this prefix is set to 'ORAC_', then the translated header for the instrument value, whose key is normally 'INSTRUMENT', will have a key named 'ORAC_INSTRUMENT'. The original keys will not be in the returned hash. If left blank, no prefix will be added. =back This method returns a hash of generic headers. This function dies if the header translation fails in any way. =cut sub translate_from_FITS { my $FITS_header = shift; my %options = @_; # translation classes my @classes; if( exists( $options{class} ) && defined( $options{class} ) && ref( $options{class} ) eq 'ARRAY' ) { @classes = @{$options{class}}; } else { @classes = __PACKAGE__->translation_classes; } my $prefix; if( exists( $options{prefix} ) && defined( $options{prefix} ) ) { $prefix = $options{prefix}; } # determine which class can be used for the translation my $class = _determine_class( $FITS_header, \@classes, 1 ); # we know this class is already loaded so do the translation return $class->translate_from_FITS( $FITS_header, $prefix ); } =item B Converts a hash containing generic headers into one containing instrument-specific FITS headers. %FITS_headers = translate_to_FITS(\%generic_headers, class => \@classes, ); This method takes a reference to a hash containing untranslated headers, and a hash reference containing the following optional keys: =over 8 =item * class - A reference to a list of subclasses to try to use for header translations. This list overrides the default list. If left blank, the default list will be used. =item * prefix - A string prefix to remove from the generic header key before doing header translation. Why you would want to do this is if you've used a prefix in the C call, and want to translate back from the generic headers returned from that method. If left blank, no prefix will be removed. =back This method returns a hash of instrument-specific headers. This function dies if the header translation fails in any way. =cut sub translate_to_FITS { my $generic_header = shift; my %options = @_; my @classes; if( exists( $options{class} ) && defined( $options{class} ) && ref( $options{class} ) eq 'ARRAY' ) { @classes = @{$options{class}}; } else { @classes = __PACKAGE__->translation_classes; } my $prefix; if( exists( $options{prefix} ) && defined( $options{prefix} ) ) { $prefix = $options{prefix}; } else { $prefix = ''; } # We need to strip off any prefix before figuring out what # class we need to use. my %stripped_header; while( my ( $key, $value ) = each( %{$generic_header} ) ) { $key =~ s/^$prefix//; $stripped_header{$key} = $value; } # Check the UTSTART, UTEND, and UTDATE headers to make sure they're # Time::Piece objects. for my $h (qw/ UTSTART UTEND UTDATE / ) { if( exists( $stripped_header{$h} ) && defined( $stripped_header{$h} ) && ! UNIVERSAL::isa( $stripped_header{$h}, "Time::Piece" ) ) { warnings::warnif( "Warning: $h generic header is not a Time::Piece object" ); } } # determine which class can be used for the translation my $class = _determine_class( \%stripped_header, \@classes, 0 ); return $class->translate_to_FITS( \%stripped_header ); } =back =begin __PRIVATE_FUNCTIONS__ =head1 PRIVATE FUNCTIONS =over 4 =item B<_determine_class> Determine which class should be used for the translation (either way). It is given a reference to the header hash and a reference to an array of classes which can be queried. $class = _determine_class( \%hdr, \@classes, $fromfits ); The classes are loaded for each test. Failure to load indicates failure to translate. The third argument is a boolean indicating whether the class is being used to translate from FITS (true) or to FITS (false). This is used for error message clarity. =cut sub _determine_class { my $hdr = shift; my $classes = shift; my $fromfits = shift; # Determine the class name so we can use the appropriate subclass # for header translations. We're going to use the "can_translate" method # in each subclass listed in @$classes. my %result = (); my $base = "Astro::FITS::HdrTrans::"; foreach my $subclass ( @$classes ) { my $class = $base.$subclass; print "Trying class $class\n" if $DEBUG; # Try a class and if it fails to load, skip eval "require $class"; next if ( $@ ); if( $class->can("can_translate") ) { if( $class->can_translate( $hdr ) ) { print "Class $class matches\n" if $DEBUG; $result{$subclass}++; } } else { # What to do, what to do? } } if( ( scalar keys %result ) > 1 ) { croak "Ambiguities in determining which header translations to use (". join(",",keys %result).")"; } if( ( scalar keys %result ) == 0 ) { # We couldn't figure out which one to use. croak "Unable to determine header translation subclass. No matches for these headers when trying to convert " . ($fromfits ? 'from' : 'to' ) . " FITS using the following classes: ".join(",",@$classes); } # The class we wanted is the only key in the hash my @matched = keys %result; my $class = $base . $matched[0]; return $class; } =back =end __PRIVATE_FUNTCTIONS__ =head1 AUTHOR Brad Cavanagh Eb.cavanagh@jach.hawaii.eduE, Tim Jenness Et.jenness@jach.hawaii.eduE =head1 COPYRIGHT Copyright (C) 2003-2005 Particle Physics and Astronomy Research Council. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,Suite 330, Boston, MA 02111-1307, USA =cut 1;