package Astro::FITS::HdrTrans::WFCAM; =head1 NAME Astro::FITS::HdrTrans::WFCAM - UKIRT WFCAM translations =head1 SYNOPSIS use Astro::FITS::HdrTrans::WFCAM; %gen = Astro::FITS::HdrTrans::WFCAM->translate_from_FITS( %hdr ); =head1 DESCRIPTION This class provides a generic set of translations that are specific to the WFCAM camera of the United Kingdom Infrared Telescope. =cut use 5.006; use warnings; use strict; use Carp; # Inherit from UKIRTNew. use base qw/ Astro::FITS::HdrTrans::UKIRTNew /; # We want the FITS standard versions of DATE-OBS/DATE-END parsing # Not the UKIRT-specific versions that have Z problems. use Astro::FITS::HdrTrans::FITS qw/ UTSTART UTEND /; use vars qw/ $VERSION /; $VERSION = "1.62"; # For a constant mapping, there is no FITS header, just a generic # header that is constant. my %CONST_MAP = ( POLARIMETRY => 0, ); # NULL mappings used to override base class implementations my @NULL_MAP = qw/ DETECTOR_INDEX WAVEPLATE_ANGLE /; # Unit mapping implies that the value propogates directly # to the output with only a keyword name change. my %UNIT_MAP = ( # WFCAM specific CAMERA_NUMBER => "CAMNUM", DETECTOR_READ_TYPE => "READMODE", NUMBER_OF_COADDS => "NEXP", NUMBER_OF_JITTER_POSITIONS => "NJITTER", NUMBER_OF_MICROSTEP_POSITIONS => "NUSTEP", TILE_NUMBER => "TILENUM", # CGS4 + MICHELLE + WFCAM CONFIGURATION_INDEX => 'CNFINDEX', ); # Create the translation methods. __PACKAGE__->_generate_lookup_methods( \%CONST_MAP, \%UNIT_MAP, \@NULL_MAP ); =head1 METHODS =over 4 =item B The name of the instrument required to match (case insensitively) against the INSTRUME/INSTRUMENT keyword to allow this class to translate the specified headers. Called by the default C method. $inst = $class->this_instrument(); Returns "WFCAM". =cut sub this_instrument { return "WFCAM"; } =back =head1 COMPLEX CONVERSIONS These methods are more complicated than a simple mapping. We have to provide both from- and to-FITS conversions All these routines are methods and the to_ routines all take a reference to a hash and return the translated value (a many-to-one mapping). The from_ methods take a reference to a generic hash and return a translated hash (sometimes these are many-to-many). =over 4 =item B Returns the data units. This uses the C header, with a default of "counts/exp" (unless the observations was with a ND read type between 2006-10-23 and 2006-12-20 when the default was "counts/sec"). =cut sub to_DATA_UNITS { my $self = shift; my $FITS_headers = shift; my $data_units = 'counts/exp'; if ( defined( $FITS_headers->{BUNIT} ) ) { $data_units = $FITS_headers->{BUNIT}; } else { my $date = $self->to_UTDATE( $FITS_headers ); if ( $date > 20061023 && $date < 20061220 ) { my $read_type = $self->to_DETECTOR_READ_TYPE( $FITS_headers ); if ( substr( $read_type, 0, 2 ) eq 'ND' ) { $data_units = 'counts/sec'; } } } return $data_units; } =item B Returns the declination pixel scale in in arcseconds per pixel. For Cameras 1 and 3, it scales the C keyword to the C generic header. For Cameras 2 and 4, it scales the C keyword instead. =cut sub to_DEC_SCALE { my $self = shift; my $FITS_headers = shift; my $scale; my $camnum = $self->to_CAMERA_NUMBER( $FITS_headers ); if ( defined( $camnum ) ) { if ( defined( $FITS_headers->{CD2_1} ) && defined( $FITS_headers->{CD1_2} ) && defined( $FITS_headers->{CD2_2} ) ) { if ( $camnum == 1 ) { $scale = $FITS_headers->{CD2_1} * 3600; } elsif ( $camnum == 3 ) { $scale = $FITS_headers->{CD2_1} * 3600; } elsif ( $camnum == 2 || $camnum == 4 ) { $scale = $FITS_headers->{CD2_2} * 3600; } } } return $scale; } =item B For Cameras 1 and 3, it scales the C generic header to the C header. For Cameras 2 and 4, it scales C to the C header. The returned units are degrees per pixel. =cut sub from_DEC_SCALE { my $self = shift; my $generic_headers = shift; my %return_hash; my $dec_scale = $generic_headers->{'DEC_SCALE'}; my $camnum = $generic_headers->{'CAMERA_NUMBER'}; if ( defined( $dec_scale ) && defined( $camnum ) ) { if ( $camnum == 1 || $camnum == 3 ) { $return_hash{'CD2_1'} = $dec_scale / 3600; } elsif ( $camnum == 2 || $camnum == 4 ) { $return_hash{'CD2_2'} = $dec_scale / 3600; } } return %return_hash; } =item B Determines the gain entirely from camera number. The GAIN FITS header is not used. =cut sub to_GAIN { my $self = shift; my $FITS_headers = shift; my $gain; if ( defined( $FITS_headers->{CAMNUM} ) ) { my $camnum = $FITS_headers->{CAMNUM}; if ( $camnum == 1 || $camnum == 2 || $camnum == 3 ) { $gain = 4.6; } elsif ( $camnum == 4 ) { $gain = 5.6; } else { $gain = 1.0; } } else { $gain = 1.0; } return $gain; } =item B This is a null operation. The C FITS header in WFCAM data is always incorrect. =cut sub from_GAIN { return (); } =item B Return the number of offsets (jitters and micro steps). =cut sub to_NUMBER_OF_OFFSETS { my $self = shift; my $FITS_headers = shift; my $njitter = ( defined( $FITS_headers->{NJITTER} ) ? $FITS_headers->{NJITTER} : 1 ); my $nustep = ( defined( $FITS_headers->{NUSTEP} ) ? $FITS_headers->{NUSTEP} : 1 ); return $njitter * $nustep + 1; } =item B Returns the C header converted to degrees. =cut sub to_RA_BASE { my $self = shift; my $FITS_headers = shift; return ($FITS_headers->{RABASE} * 15.0); } =item B Returns the right-ascension pixel scale in arcseconds per pixel. For Cameras 1 and 3, it scales the C keyword to the C generic header. For Cameras 2 and 4, it scales the C keyword instead. =cut sub to_RA_SCALE { my $self = shift; my $FITS_headers = shift; my $scale; my $camnum = $self->to_CAMERA_NUMBER( $FITS_headers ); if ( defined( $camnum ) ) { if ( defined( $FITS_headers->{CD1_1} ) && defined( $FITS_headers->{CD1_2} ) ) { if ( $camnum == 1 || $camnum == 3 ) { $scale = $FITS_headers->{CD1_2} * 3600; } elsif ( $camnum == 2 || $camnum == 4 ) { $scale = $FITS_headers->{CD1_1} * 3600; } } } return $scale; } =item B For Cameras 1 and 3, it scales the C generic header to the C keyword. For Cameras 2 and 4, scales C to the C keyword. Returned units are degrees per pixel. =cut sub from_RA_SCALE { my $self = shift; my $generic_headers = shift; my %return_hash; my $ra_scale = $generic_headers->{'RA_SCALE'}; my $camnum = $generic_headers->{'CAMERA_NUMBER'}; if ( defined( $ra_scale ) && defined( $camnum ) ) { if ( $camnum == 1 || $camnum == 3 ) { $return_hash{'CD1_2'} = $ra_scale / 3600; } elsif ( $camnum == 2 || $camnum == 4 ) { $return_hash{'CD1_1'} = $ra_scale / 3600; } } return %return_hash; } =item B Determines the rotation of the array in world co-ordinates. =cut sub to_ROTATION { my $self = shift; my $FITS_headers = shift; my $cd11 = $FITS_headers->{CD1_1}; my $cd12 = $FITS_headers->{CD1_2}; my $cd21 = $FITS_headers->{CD2_1}; my $cd22 = $FITS_headers->{CD2_2}; my $rad = 45 / atan2( 1, 1 ); my $rho_a = $rad * atan2( -$cd12 / $rad, $cd22 / $rad ); my $rho_b = $rad * atan2( $cd21 / $rad, $cd11 / $rad ); my $rotation = -0.5 * ( $rho_a + $rho_b ); return $rotation; } =back =head1 SEE ALSO C, C. =head1 AUTHOR Brad Cavanagh Eb.cavanagh@jach.hawaii.eduE, Tim Jenness Et.jenness@jach.hawaii.eduE. Malcolm J. Currie Emjc@jach.hawaii.eduE =head1 COPYRIGHT Copyright (C) 2008 Science and Technology Facilities Council. 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;