| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Astro::FITS::HdrTrans::UKIRTDB; | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | =head1 NAME | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  | Astro::FITS::HdrTrans::UKIRTDB - UKIRT Database Table translations | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  | %generic_headers = translate_from_FITS(\%FITS_headers, \@header_array); | 
| 10 |  |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  | %FITS_headers = transate_to_FITS(\%generic_headers, \@header_array); | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 14 |  |  |  |  |  |  |  | 
| 15 |  |  |  |  |  |  | Converts information contained in UKIRTDB FITS headers to and from | 
| 16 |  |  |  |  |  |  | generic headers. See Astro::FITS::HdrTrans for a list of generic | 
| 17 |  |  |  |  |  |  | headers. | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | =cut | 
| 20 |  |  |  |  |  |  |  | 
| 21 | 10 |  |  | 10 |  | 7345568 | use 5.006; | 
|  | 10 |  |  |  |  | 43 |  | 
| 22 | 10 |  |  | 10 |  | 58 | use warnings; | 
|  | 10 |  |  |  |  | 21 |  | 
|  | 10 |  |  |  |  | 459 |  | 
| 23 | 10 |  |  | 10 |  | 70 | use strict; | 
|  | 10 |  |  |  |  | 20 |  | 
|  | 10 |  |  |  |  | 309 |  | 
| 24 | 10 |  |  | 10 |  | 60 | use Carp; | 
|  | 10 |  |  |  |  | 27 |  | 
|  | 10 |  |  |  |  | 819 |  | 
| 25 |  |  |  |  |  |  |  | 
| 26 | 10 |  |  | 10 |  | 705 | use Time::Piece; | 
|  | 10 |  |  |  |  | 11953 |  | 
|  | 10 |  |  |  |  | 95 |  | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | # Inherit from Base | 
| 29 | 10 |  |  | 10 |  | 889 | use base qw/ Astro::FITS::HdrTrans::JAC /; | 
|  | 10 |  |  |  |  | 29 |  | 
|  | 10 |  |  |  |  | 1833 |  | 
| 30 |  |  |  |  |  |  |  | 
| 31 | 10 |  |  | 10 |  | 71 | use vars qw/ $VERSION /; | 
|  | 10 |  |  |  |  | 49 |  | 
|  | 10 |  |  |  |  | 25521 |  | 
| 32 |  |  |  |  |  |  |  | 
| 33 |  |  |  |  |  |  | # Note that we use %02 not %03 because of historical reasons | 
| 34 |  |  |  |  |  |  | $VERSION = "1.64"; | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | # for a constant mapping, there is no FITS header, just a generic | 
| 37 |  |  |  |  |  |  | # header that is constant | 
| 38 |  |  |  |  |  |  | my %CONST_MAP = ( | 
| 39 |  |  |  |  |  |  | COORDINATE_UNITS => 'degrees', | 
| 40 |  |  |  |  |  |  | ); | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | # NULL mappings used to override base class implementations | 
| 43 |  |  |  |  |  |  | my @NULL_MAP = (); | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | # unit mapping implies that the value propogates directly | 
| 46 |  |  |  |  |  |  | # to the output with only a keyword name change | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | my %UNIT_MAP = ( | 
| 49 |  |  |  |  |  |  | AIRMASS_START        => "AMSTART", | 
| 50 |  |  |  |  |  |  | AIRMASS_END          => "AMEND", | 
| 51 |  |  |  |  |  |  | CAMERA               => "CAMLENS", | 
| 52 |  |  |  |  |  |  | CAMERA_NUMBER        => "CAMNUM", | 
| 53 |  |  |  |  |  |  | CONFIGURATION_INDEX  => "CNFINDEX", | 
| 54 |  |  |  |  |  |  | DEC_BASE             => "DECBASE", | 
| 55 |  |  |  |  |  |  | DEC_SCALE            => "PIXELSIZ", | 
| 56 |  |  |  |  |  |  | DEC_TELESCOPE_OFFSET => "DECOFF", | 
| 57 |  |  |  |  |  |  | DETECTOR_READ_TYPE   => "MODE", | 
| 58 |  |  |  |  |  |  | DR_GROUP             => "GRPNUM", | 
| 59 |  |  |  |  |  |  | DR_RECIPE            => "RECIPE", | 
| 60 |  |  |  |  |  |  | EQUINOX              => "EQUINOX", | 
| 61 |  |  |  |  |  |  | FILTER               => "FILTER", | 
| 62 |  |  |  |  |  |  | FILENAME             => "FILENAME", | 
| 63 |  |  |  |  |  |  | GAIN                 => "DEPERDN", | 
| 64 |  |  |  |  |  |  | GRATING_DISPERSION   => "GDISP", | 
| 65 |  |  |  |  |  |  | GRATING_ORDER        => "GORDER", | 
| 66 |  |  |  |  |  |  | INSTRUMENT           => "INSTRUME", | 
| 67 |  |  |  |  |  |  | NUMBER_OF_COADDS => 'NEXP', | 
| 68 |  |  |  |  |  |  | NUMBER_OF_EXPOSURES  => "NEXP", | 
| 69 |  |  |  |  |  |  | OBJECT               => "OBJECT", | 
| 70 |  |  |  |  |  |  | OBSERVATION_MODE     => "INSTMODE", | 
| 71 |  |  |  |  |  |  | OBSERVATION_NUMBER   => "RUN", | 
| 72 |  |  |  |  |  |  | OBSERVATION_TYPE     => "OBSTYPE", | 
| 73 |  |  |  |  |  |  | PROJECT              => "PROJECT", | 
| 74 |  |  |  |  |  |  | RA_SCALE             => "PIXELSIZ", | 
| 75 |  |  |  |  |  |  | RA_TELESCOPE_OFFSET  => "RAOFF", | 
| 76 |  |  |  |  |  |  | TELESCOPE            => "TELESCOP", | 
| 77 |  |  |  |  |  |  | WAVEPLATE_ANGLE      => "WPLANGLE", | 
| 78 |  |  |  |  |  |  | Y_BASE               => "DECBASE", | 
| 79 |  |  |  |  |  |  | X_DIM                => "DCOLUMNS", | 
| 80 |  |  |  |  |  |  | Y_DIM                => "DROWS", | 
| 81 |  |  |  |  |  |  | X_OFFSET             => "RAOFF", | 
| 82 |  |  |  |  |  |  | Y_OFFSET             => "DECOFF", | 
| 83 |  |  |  |  |  |  | X_SCALE              => "PIXELSIZ", | 
| 84 |  |  |  |  |  |  | Y_SCALE              => "PIXELSIZ", | 
| 85 |  |  |  |  |  |  | X_LOWER_BOUND        => "RDOUT_X1", | 
| 86 |  |  |  |  |  |  | X_UPPER_BOUND        => "RDOUT_X2", | 
| 87 |  |  |  |  |  |  | Y_LOWER_BOUND        => "RDOUT_Y1", | 
| 88 |  |  |  |  |  |  | Y_UPPER_BOUND        => "RDOUT_Y2" | 
| 89 |  |  |  |  |  |  | ); | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  |  | 
| 92 |  |  |  |  |  |  | # Create the translation methods | 
| 93 |  |  |  |  |  |  | __PACKAGE__->_generate_lookup_methods( \%CONST_MAP, \%UNIT_MAP, \@NULL_MAP ); | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  |  | 
| 96 |  |  |  |  |  |  | =head1 METHODS | 
| 97 |  |  |  |  |  |  |  | 
| 98 |  |  |  |  |  |  | =over 4 | 
| 99 |  |  |  |  |  |  |  | 
| 100 |  |  |  |  |  |  | =item B<can_translate> | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  | Determine if this class can handle the translation. Returns true | 
| 103 |  |  |  |  |  |  | if the TELESCOP is "UKIRT" and there is a "FILENAME" key and | 
| 104 |  |  |  |  |  |  | a "RAJ2000" key. These keywords allow the DB results to be disambiguated | 
| 105 |  |  |  |  |  |  | from the actual file headers. | 
| 106 |  |  |  |  |  |  |  | 
| 107 |  |  |  |  |  |  | $cando = $class->can_translate( \%hdrs ); | 
| 108 |  |  |  |  |  |  |  | 
| 109 |  |  |  |  |  |  | =cut | 
| 110 |  |  |  |  |  |  |  | 
| 111 |  |  |  |  |  |  | sub can_translate { | 
| 112 | 20 |  |  | 20 | 1 | 65 | my $self = shift; | 
| 113 | 20 |  |  |  |  | 50 | my $FITS_headers = shift; | 
| 114 | 20 | 50 | 100 |  |  | 147 | if (exists $FITS_headers->{TELESCOP} | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 115 |  |  |  |  |  |  | && $FITS_headers->{TELESCOP} =~ /UKIRT/ | 
| 116 |  |  |  |  |  |  | && exists $FITS_headers->{FILENAME} | 
| 117 |  |  |  |  |  |  | && exists $FITS_headers->{RAJ2000}) { | 
| 118 | 1 |  |  |  |  | 5 | return 1; | 
| 119 |  |  |  |  |  |  | } | 
| 120 |  |  |  |  |  |  | } | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | =back | 
| 123 |  |  |  |  |  |  |  | 
| 124 |  |  |  |  |  |  | =head1 COMPLEX CONVERSIONS | 
| 125 |  |  |  |  |  |  |  | 
| 126 |  |  |  |  |  |  | These methods are more complicated than a simple mapping. We have to | 
| 127 |  |  |  |  |  |  | provide both from- and to-FITS conversions All these routines are | 
| 128 |  |  |  |  |  |  | methods and the to_ routines all take a reference to a hash and return | 
| 129 |  |  |  |  |  |  | the translated value (a many-to-one mapping) The from_ methods take a | 
| 130 |  |  |  |  |  |  | reference to a generic hash and return a translated hash (sometimes | 
| 131 |  |  |  |  |  |  | these are many-to-many) | 
| 132 |  |  |  |  |  |  |  | 
| 133 |  |  |  |  |  |  | =over 4 | 
| 134 |  |  |  |  |  |  |  | 
| 135 |  |  |  |  |  |  | =item B<to_INST_DHS> | 
| 136 |  |  |  |  |  |  |  | 
| 137 |  |  |  |  |  |  | Sets the INST_DHS header. | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | =cut | 
| 140 |  |  |  |  |  |  |  | 
| 141 |  |  |  |  |  |  | sub to_INST_DHS { | 
| 142 | 1 |  |  | 1 | 1 | 3 | my $self = shift; | 
| 143 | 1 |  |  |  |  | 3 | my $FITS_headers = shift; | 
| 144 | 1 |  |  |  |  | 2 | my $return; | 
| 145 |  |  |  |  |  |  |  | 
| 146 | 1 | 50 |  |  |  | 4 | if ( exists( $FITS_headers->{DHSVER} ) ) { | 
| 147 | 0 |  |  |  |  | 0 | $FITS_headers->{DHSVER} =~ /^(\w+)/; | 
| 148 | 0 |  |  |  |  | 0 | my $dhs = uc($1); | 
| 149 | 0 |  |  |  |  | 0 | $return = $FITS_headers->{INSTRUME} . "_$dhs"; | 
| 150 |  |  |  |  |  |  | } else { | 
| 151 | 1 |  |  |  |  | 12 | my $dhs = "UKDHS"; | 
| 152 | 1 |  |  |  |  | 5 | $return = $FITS_headers->{INSTRUME} . "_$dhs"; | 
| 153 |  |  |  |  |  |  | } | 
| 154 |  |  |  |  |  |  |  | 
| 155 | 1 |  |  |  |  | 4 | return $return; | 
| 156 |  |  |  |  |  |  |  | 
| 157 |  |  |  |  |  |  | } | 
| 158 |  |  |  |  |  |  |  | 
| 159 |  |  |  |  |  |  | =item B<to_EXPOSURE_TIME> | 
| 160 |  |  |  |  |  |  |  | 
| 161 |  |  |  |  |  |  | Converts either the C<EXPOSED> or C<DEXPTIME> FITS header into | 
| 162 |  |  |  |  |  |  | the C<EXPOSURE_TIME> generic header. | 
| 163 |  |  |  |  |  |  |  | 
| 164 |  |  |  |  |  |  | =cut | 
| 165 |  |  |  |  |  |  |  | 
| 166 |  |  |  |  |  |  | sub to_EXPOSURE_TIME { | 
| 167 | 1 |  |  | 1 | 1 | 3 | my $self = shift; | 
| 168 | 1 |  |  |  |  | 3 | my $FITS_headers = shift; | 
| 169 | 1 |  |  |  |  | 2 | my $return; | 
| 170 |  |  |  |  |  |  |  | 
| 171 | 1 | 50 | 33 |  |  | 19 | if ( exists( $FITS_headers->{'EXPOSED'} ) && defined( $FITS_headers->{'EXPOSED'} ) ) { | 
|  |  | 0 | 0 |  |  |  |  | 
|  |  | 0 | 0 |  |  |  |  | 
| 172 | 1 |  |  |  |  | 3 | $return = $FITS_headers->{'EXPOSED'}; | 
| 173 |  |  |  |  |  |  | } elsif ( exists( $FITS_headers->{'DEXPTIME'} ) && defined( $FITS_headers->{'DEXPTIME'} ) ) { | 
| 174 | 0 |  |  |  |  | 0 | $return = $FITS_headers->{'DEXPTIME'}; | 
| 175 |  |  |  |  |  |  | } elsif ( exists( $FITS_headers->{'EXP_TIME'} ) && defined( $FITS_headers->{'EXP_TIME'} ) ) { | 
| 176 | 0 |  |  |  |  | 0 | $return = $FITS_headers->{'EXP_TIME'}; | 
| 177 |  |  |  |  |  |  | } | 
| 178 | 1 |  |  |  |  | 5 | return $return; | 
| 179 |  |  |  |  |  |  | } | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | =item B<to_COORDINATE_TYPE> | 
| 182 |  |  |  |  |  |  |  | 
| 183 |  |  |  |  |  |  | Converts the C<EQUINOX> FITS header into B1950 or J2000, depending | 
| 184 |  |  |  |  |  |  | on equinox value, and sets the C<COORDINATE_TYPE> generic header. | 
| 185 |  |  |  |  |  |  |  | 
| 186 |  |  |  |  |  |  | =cut | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | sub to_COORDINATE_TYPE { | 
| 189 | 1 |  |  | 1 | 1 | 3 | my $self = shift; | 
| 190 | 1 |  |  |  |  | 2 | my $FITS_headers = shift; | 
| 191 | 1 |  |  |  |  | 3 | my $return; | 
| 192 | 1 | 50 |  |  |  | 12 | if (exists($FITS_headers->{EQUINOX})) { | 
| 193 | 1 | 50 |  |  |  | 15 | if ($FITS_headers->{EQUINOX} =~ /1950/) { | 
|  |  | 50 |  |  |  |  |  | 
| 194 | 0 |  |  |  |  | 0 | $return = "B1950"; | 
| 195 |  |  |  |  |  |  | } elsif ($FITS_headers->{EQUINOX} =~ /2000/) { | 
| 196 | 1 |  |  |  |  | 4 | $return = "J2000"; | 
| 197 |  |  |  |  |  |  | } | 
| 198 |  |  |  |  |  |  | } | 
| 199 | 1 |  |  |  |  | 11 | return $return; | 
| 200 |  |  |  |  |  |  | } | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  | =item B<to_GRATING_NAME> | 
| 203 |  |  |  |  |  |  |  | 
| 204 |  |  |  |  |  |  | =cut | 
| 205 |  |  |  |  |  |  |  | 
| 206 |  |  |  |  |  |  | sub to_GRATING_NAME { | 
| 207 | 1 |  |  | 1 | 1 | 3 | my $self = shift; | 
| 208 | 1 |  |  |  |  | 2 | my $FITS_headers = shift; | 
| 209 | 1 |  |  |  |  | 2 | my $return; | 
| 210 | 1 | 50 |  |  |  | 6 | if (exists($FITS_headers->{GRATING})) { | 
|  |  | 50 |  |  |  |  |  | 
| 211 | 0 |  |  |  |  | 0 | $return = $FITS_headers->{GRATING}; | 
| 212 |  |  |  |  |  |  | } elsif (exists($FITS_headers->{GRISM})) { | 
| 213 | 1 |  |  |  |  | 3 | $return = $FITS_headers->{GRISM}; | 
| 214 |  |  |  |  |  |  | } | 
| 215 | 1 |  |  |  |  | 2 | return $return; | 
| 216 |  |  |  |  |  |  | } | 
| 217 |  |  |  |  |  |  |  | 
| 218 |  |  |  |  |  |  | =item B<to_GRATING_WAVELENGTH> | 
| 219 |  |  |  |  |  |  |  | 
| 220 |  |  |  |  |  |  | =cut | 
| 221 |  |  |  |  |  |  |  | 
| 222 |  |  |  |  |  |  | sub to_GRATING_WAVELENGTH { | 
| 223 | 1 |  |  | 1 | 1 | 3 | my $self = shift; | 
| 224 | 1 |  |  |  |  | 3 | my $FITS_headers = shift; | 
| 225 | 1 |  |  |  |  | 1 | my $return; | 
| 226 | 1 | 50 |  |  |  | 5 | if (exists($FITS_headers->{GLAMBDA})) { | 
|  |  | 50 |  |  |  |  |  | 
| 227 | 0 |  |  |  |  | 0 | $return = $FITS_headers->{GLAMBDA}; | 
| 228 |  |  |  |  |  |  | } elsif (exists($FITS_headers->{CENWAVL})) { | 
| 229 | 0 |  |  |  |  | 0 | $return = $FITS_headers->{CENWAVL}; | 
| 230 |  |  |  |  |  |  | } | 
| 231 | 1 |  |  |  |  | 3 | return $return; | 
| 232 |  |  |  |  |  |  | } | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  | =item B<to_SLIT_ANGLE> | 
| 235 |  |  |  |  |  |  |  | 
| 236 |  |  |  |  |  |  | Converts either the C<SANGLE> or the C<SLIT_PA> header into the C<SLIT_ANGLE> | 
| 237 |  |  |  |  |  |  | generic header. | 
| 238 |  |  |  |  |  |  |  | 
| 239 |  |  |  |  |  |  | =cut | 
| 240 |  |  |  |  |  |  |  | 
| 241 |  |  |  |  |  |  | sub to_SLIT_ANGLE { | 
| 242 | 1 |  |  | 1 | 1 | 3 | my $self = shift; | 
| 243 | 1 |  |  |  |  | 2 | my $FITS_headers = shift; | 
| 244 | 1 |  |  |  |  | 3 | my $return; | 
| 245 | 1 | 50 |  |  |  | 18 | if (exists($FITS_headers->{'SANGLE'})) { | 
|  |  | 50 |  |  |  |  |  | 
| 246 | 0 |  |  |  |  | 0 | $return = $FITS_headers->{'SANGLE'}; | 
| 247 |  |  |  |  |  |  | } elsif (exists($FITS_headers->{'SLIT_PA'} )) { | 
| 248 | 0 |  |  |  |  | 0 | $return = $FITS_headers->{'SLIT_PA'}; | 
| 249 |  |  |  |  |  |  | } | 
| 250 | 1 |  |  |  |  | 5 | return $return; | 
| 251 |  |  |  |  |  |  |  | 
| 252 |  |  |  |  |  |  | } | 
| 253 |  |  |  |  |  |  |  | 
| 254 |  |  |  |  |  |  | =item B<to_SLIT_NAME> | 
| 255 |  |  |  |  |  |  |  | 
| 256 |  |  |  |  |  |  | Converts either the C<SLIT> or the C<SLITNAME> header into the C<SLIT_NAME> | 
| 257 |  |  |  |  |  |  | generic header. | 
| 258 |  |  |  |  |  |  |  | 
| 259 |  |  |  |  |  |  | =cut | 
| 260 |  |  |  |  |  |  |  | 
| 261 |  |  |  |  |  |  | sub to_SLIT_NAME { | 
| 262 | 1 |  |  | 1 | 1 | 3 | my $self = shift; | 
| 263 | 1 |  |  |  |  | 2 | my $FITS_headers = shift; | 
| 264 | 1 |  |  |  |  | 2 | my $return; | 
| 265 | 1 | 50 |  |  |  | 7 | if (exists($FITS_headers->{'SLIT'})) { | 
|  |  | 50 |  |  |  |  |  | 
| 266 | 0 |  |  |  |  | 0 | $return = $FITS_headers->{'SLIT'}; | 
| 267 |  |  |  |  |  |  | } elsif (exists($FITS_headers->{'SLITNAME'} )) { | 
| 268 | 1 |  |  |  |  | 3 | $return = $FITS_headers->{'SLITNAME'}; | 
| 269 |  |  |  |  |  |  | } | 
| 270 | 1 |  |  |  |  | 2 | return $return; | 
| 271 |  |  |  |  |  |  |  | 
| 272 |  |  |  |  |  |  | } | 
| 273 |  |  |  |  |  |  |  | 
| 274 |  |  |  |  |  |  | =item B<to_SPEED_GAIN> | 
| 275 |  |  |  |  |  |  |  | 
| 276 |  |  |  |  |  |  | =cut | 
| 277 |  |  |  |  |  |  |  | 
| 278 |  |  |  |  |  |  | sub to_SPEED_GAIN { | 
| 279 | 1 |  |  | 1 | 1 | 1 | my $self = shift; | 
| 280 | 1 |  |  |  |  | 12 | my $FITS_headers = shift; | 
| 281 | 1 |  |  |  |  | 2 | my $return; | 
| 282 |  |  |  |  |  |  |  | 
| 283 | 1 | 50 |  |  |  | 6 | if ( exists( $FITS_headers->{'SPD_GAIN'} ) ) { | 
|  |  | 50 |  |  |  |  |  | 
| 284 | 0 |  |  |  |  | 0 | $return = $FITS_headers->{'SPD_GAIN'}; | 
| 285 |  |  |  |  |  |  | } elsif ( exists( $FITS_headers->{'WAVEFORM'} ) ) { | 
| 286 | 1 | 50 |  |  |  | 6 | if ( $FITS_headers->{'WAVEFORM'} =~ /thermal/i ) { | 
| 287 | 0 |  |  |  |  | 0 | $return = 'thermal'; | 
| 288 |  |  |  |  |  |  | } else { | 
| 289 | 1 |  |  |  |  | 2 | $return = 'normal'; | 
| 290 |  |  |  |  |  |  | } | 
| 291 |  |  |  |  |  |  | } | 
| 292 | 1 |  |  |  |  | 4 | return $return; | 
| 293 |  |  |  |  |  |  | } | 
| 294 |  |  |  |  |  |  |  | 
| 295 |  |  |  |  |  |  | =item B<to_STANDARD> | 
| 296 |  |  |  |  |  |  |  | 
| 297 |  |  |  |  |  |  | Converts either the C<STANDARD> header (if it exists) or uses the | 
| 298 |  |  |  |  |  |  | C<OBJECT> or C<RECIPE> headers to determine if an observation is of a | 
| 299 |  |  |  |  |  |  | standard.  If the C<OBJECT> header starts with either B<BS> or B<FS>, | 
| 300 |  |  |  |  |  |  | I<or> the DR recipe contains the word STANDARD, it is assumed to be a | 
| 301 |  |  |  |  |  |  | standard. | 
| 302 |  |  |  |  |  |  |  | 
| 303 |  |  |  |  |  |  | =cut | 
| 304 |  |  |  |  |  |  |  | 
| 305 |  |  |  |  |  |  | sub to_STANDARD { | 
| 306 | 1 |  |  | 1 | 1 | 3 | my $self = shift; | 
| 307 | 1 |  |  |  |  | 2 | my $FITS_headers = shift; | 
| 308 |  |  |  |  |  |  |  | 
| 309 |  |  |  |  |  |  | # Set false as default so we do not have to repeat this in the logic | 
| 310 |  |  |  |  |  |  | # below (could just use undef == false) | 
| 311 | 1 |  |  |  |  | 2 | my $return = 0;               # default false | 
| 312 |  |  |  |  |  |  |  | 
| 313 | 1 | 50 | 33 |  |  | 17 | if ( exists( $FITS_headers->{'STANDARD'} ) && | 
|  |  | 0 | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 314 |  |  |  |  |  |  | length( $FITS_headers->{'STANDARD'} . "") > 0 ) { | 
| 315 |  |  |  |  |  |  |  | 
| 316 | 1 | 50 |  |  |  | 17 | if ($FITS_headers->{'STANDARD'} =~ /^[tf]$/i) { | 
|  |  | 50 |  |  |  |  |  | 
| 317 |  |  |  |  |  |  | # Raw header read from FITS header | 
| 318 | 0 |  |  |  |  | 0 | $return = (uc($FITS_headers->{'STANDARD'}) eq 'T'); | 
| 319 |  |  |  |  |  |  | } elsif ($FITS_headers->{'STANDARD'} =~ /^[01]$/) { | 
| 320 |  |  |  |  |  |  | # Translated header either so a true logical | 
| 321 | 1 |  |  |  |  | 4 | $return = $FITS_headers->{'STANDARD'}; | 
| 322 |  |  |  |  |  |  | } | 
| 323 |  |  |  |  |  |  |  | 
| 324 |  |  |  |  |  |  | } elsif ( ( exists $FITS_headers->{OBJECT} && | 
| 325 |  |  |  |  |  |  | $FITS_headers->{'OBJECT'} =~ /^[bf]s/i ) || | 
| 326 |  |  |  |  |  |  | ( exists( $FITS_headers->{'RECIPE'} ) && | 
| 327 |  |  |  |  |  |  | $FITS_headers->{'RECIPE'} =~ /^standard/i | 
| 328 |  |  |  |  |  |  | )) { | 
| 329 |  |  |  |  |  |  | # Either we have an object with name prefix of BS or FS or | 
| 330 |  |  |  |  |  |  | # our recipe looks suspiciously like a standard. | 
| 331 | 0 |  |  |  |  | 0 | $return = 1; | 
| 332 |  |  |  |  |  |  |  | 
| 333 |  |  |  |  |  |  | } | 
| 334 |  |  |  |  |  |  |  | 
| 335 | 1 |  |  |  |  | 4 | return $return; | 
| 336 |  |  |  |  |  |  |  | 
| 337 |  |  |  |  |  |  | } | 
| 338 |  |  |  |  |  |  |  | 
| 339 |  |  |  |  |  |  | =item B<to_UTDATE> | 
| 340 |  |  |  |  |  |  |  | 
| 341 |  |  |  |  |  |  | =cut | 
| 342 |  |  |  |  |  |  |  | 
| 343 |  |  |  |  |  |  | sub to_UTDATE { | 
| 344 | 1 |  |  | 1 | 1 | 2 | my $self = shift; | 
| 345 | 1 |  |  |  |  | 2 | my $FITS_headers = shift; | 
| 346 | 1 |  |  |  |  | 2 | my $return; | 
| 347 |  |  |  |  |  |  |  | 
| 348 | 1 | 50 |  |  |  | 4 | if ( exists( $FITS_headers->{'UT_DATE'} ) ) { | 
| 349 | 1 |  |  |  |  | 2 | my $datestr = $FITS_headers->{'UT_DATE'}; | 
| 350 | 1 |  |  |  |  | 4 | $return = _parse_date($datestr); | 
| 351 | 1 | 50 |  |  |  | 3 | die "Error parsing date \"$datestr\"" unless defined $return; | 
| 352 | 1 |  |  |  |  | 5 | $return = $return->strftime('%Y%m%d'); | 
| 353 |  |  |  |  |  |  | } | 
| 354 |  |  |  |  |  |  |  | 
| 355 | 1 |  |  |  |  | 48 | return $return; | 
| 356 |  |  |  |  |  |  |  | 
| 357 |  |  |  |  |  |  | } | 
| 358 |  |  |  |  |  |  |  | 
| 359 |  |  |  |  |  |  | =item B<to_UTSTART> | 
| 360 |  |  |  |  |  |  |  | 
| 361 |  |  |  |  |  |  | Strips the optional 'Z' from the C<DATE-OBS> header, or if that header does | 
| 362 |  |  |  |  |  |  | not exist, combines the C<UT_DATE> and C<RUTSTART> headers into a unified | 
| 363 |  |  |  |  |  |  | C<UTSTART> header. | 
| 364 |  |  |  |  |  |  |  | 
| 365 |  |  |  |  |  |  | =cut | 
| 366 |  |  |  |  |  |  |  | 
| 367 |  |  |  |  |  |  | sub to_UTSTART { | 
| 368 | 2 |  |  | 2 | 1 | 5 | my $self = shift; | 
| 369 | 2 |  |  |  |  | 3 | my $FITS_headers = shift; | 
| 370 | 2 |  |  |  |  | 5 | my $return; | 
| 371 |  |  |  |  |  |  |  | 
| 372 | 2 | 50 | 0 |  |  | 5 | if ( exists( $FITS_headers->{'DATE_OBS'} ) ) { | 
|  |  | 0 | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 373 | 2 |  |  |  |  | 5 | my $dateobs = $FITS_headers->{'DATE_OBS'}; | 
| 374 | 2 |  |  |  |  | 6 | $return = $self->_parse_iso_date( $dateobs ); | 
| 375 |  |  |  |  |  |  | } elsif (exists($FITS_headers->{'UT_DATE'}) && defined($FITS_headers->{'UT_DATE'}) && | 
| 376 |  |  |  |  |  |  | exists($FITS_headers->{'RUTSTART'}) && defined( $FITS_headers->{'RUTSTART'} ) ) { | 
| 377 |  |  |  |  |  |  | # Use the default UTDATE translation but insert "-" for ISO parsing | 
| 378 | 0 |  |  |  |  | 0 | my $ut = $self->to_UTDATE($FITS_headers); | 
| 379 | 0 |  |  |  |  | 0 | $ut = join("-", substr($ut,0,4), substr($ut,4,2), substr($ut,6,2)); | 
| 380 | 0 |  |  |  |  | 0 | my $hour = int($FITS_headers->{'RUTSTART'}); | 
| 381 | 0 |  |  |  |  | 0 | my $minute = int( ( $FITS_headers->{'RUTSTART'} - $hour ) * 60 ); | 
| 382 | 0 |  |  |  |  | 0 | my $second = int( ( ( ( $FITS_headers->{'RUTSTART'} - $hour ) * 60) - $minute ) * 60 ); | 
| 383 | 0 |  |  |  |  | 0 | $return = $self->_parse_iso_date( $ut . "T$hour:$minute:$second" ); | 
| 384 |  |  |  |  |  |  | } | 
| 385 |  |  |  |  |  |  |  | 
| 386 | 2 |  |  |  |  | 5 | return $return; | 
| 387 |  |  |  |  |  |  | } | 
| 388 |  |  |  |  |  |  |  | 
| 389 |  |  |  |  |  |  | =item B<from_UTSTART> | 
| 390 |  |  |  |  |  |  |  | 
| 391 |  |  |  |  |  |  | Converts the C<UTSTART> generic header into C<UT_DATE>, C<RUTSTART>, | 
| 392 |  |  |  |  |  |  | and C<DATE-OBS> database headers. | 
| 393 |  |  |  |  |  |  |  | 
| 394 |  |  |  |  |  |  | =cut | 
| 395 |  |  |  |  |  |  |  | 
| 396 |  |  |  |  |  |  | sub from_UTSTART { | 
| 397 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 398 | 0 |  |  |  |  | 0 | my $generic_headers = shift; | 
| 399 | 0 |  |  |  |  | 0 | my %return_hash; | 
| 400 | 0 | 0 |  |  |  | 0 | if (exists($generic_headers->{UTSTART})) { | 
| 401 | 0 |  |  |  |  | 0 | my $t = _parse_date( $generic_headers->{'UTSTART'} ); | 
| 402 | 0 |  |  |  |  | 0 | my $month = $t->month; | 
| 403 | 0 |  |  |  |  | 0 | $month =~ /^(.{3})/; | 
| 404 | 0 |  |  |  |  | 0 | $month = $1; | 
| 405 | 0 |  |  |  |  | 0 | $return_hash{'UT_DATE'} = $month . " " . $t->mday . " " . $t->year; | 
| 406 | 0 |  |  |  |  | 0 | $return_hash{'RUTSTART'} = $t->hour + ($t->min / 60) + ($t->sec / 3600); | 
| 407 | 0 |  |  |  |  | 0 | $return_hash{'DATE_OBS'} = $generic_headers->{'UTSTART'}; | 
| 408 |  |  |  |  |  |  | } | 
| 409 | 0 |  |  |  |  | 0 | return %return_hash; | 
| 410 |  |  |  |  |  |  | } | 
| 411 |  |  |  |  |  |  |  | 
| 412 |  |  |  |  |  |  | =item B<to_UTEND> | 
| 413 |  |  |  |  |  |  |  | 
| 414 |  |  |  |  |  |  | Strips the optional 'Z' from the C<DATE-END> header, or if that header does | 
| 415 |  |  |  |  |  |  | not exist, combines the C<UT_DATE> and C<RUTEND> headers into a unified | 
| 416 |  |  |  |  |  |  | C<UTEND> header. | 
| 417 |  |  |  |  |  |  |  | 
| 418 |  |  |  |  |  |  | =cut | 
| 419 |  |  |  |  |  |  |  | 
| 420 |  |  |  |  |  |  | sub to_UTEND { | 
| 421 | 1 |  |  | 1 | 1 | 3 | my $self = shift; | 
| 422 | 1 |  |  |  |  | 3 | my $FITS_headers = shift; | 
| 423 | 1 |  |  |  |  | 2 | my $return; | 
| 424 |  |  |  |  |  |  |  | 
| 425 | 1 | 50 | 0 |  |  | 4 | if ( exists( $FITS_headers->{'DATE_END'} ) ) { | 
|  |  | 0 | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 426 | 1 |  |  |  |  | 3 | my $dateend = $FITS_headers->{'DATE_END'}; | 
| 427 | 1 |  |  |  |  | 4 | $return = $self->_parse_iso_date( $dateend ); | 
| 428 |  |  |  |  |  |  | } elsif (exists($FITS_headers->{'UT_DATE'}) && defined($FITS_headers->{'UT_DATE'}) && | 
| 429 |  |  |  |  |  |  | exists($FITS_headers->{'RUTEND'}) && defined( $FITS_headers->{'RUTEND'} ) ) { | 
| 430 |  |  |  |  |  |  | # Use the default UTDATE translation but insert "-" for ISO parsing | 
| 431 | 0 |  |  |  |  | 0 | my $ut = $self->to_UTDATE($FITS_headers); | 
| 432 | 0 |  |  |  |  | 0 | $ut = join("-", substr($ut,0,4), substr($ut,4,2), substr($ut,6,2)); | 
| 433 | 0 |  |  |  |  | 0 | my $hour = int($FITS_headers->{'RUTEND'}); | 
| 434 | 0 |  |  |  |  | 0 | my $minute = int( ( $FITS_headers->{'RUTEND'} - $hour ) * 60 ); | 
| 435 | 0 |  |  |  |  | 0 | my $second = int( ( ( ( $FITS_headers->{'RUTEND'} - $hour ) * 60) - $minute ) * 60 ); | 
| 436 | 0 |  |  |  |  | 0 | $return = $self->_parse_iso_date( $ut . "T$hour:$minute:$second" ); | 
| 437 |  |  |  |  |  |  | } | 
| 438 |  |  |  |  |  |  |  | 
| 439 | 1 |  |  |  |  | 3 | return $return; | 
| 440 |  |  |  |  |  |  | } | 
| 441 |  |  |  |  |  |  |  | 
| 442 |  |  |  |  |  |  | =item B<from_UTEND> | 
| 443 |  |  |  |  |  |  |  | 
| 444 |  |  |  |  |  |  | Converts the C<UTEND> generic header into C<UT_DATE>, C<RUTEND> | 
| 445 |  |  |  |  |  |  | and C<DATE-END> database headers. | 
| 446 |  |  |  |  |  |  |  | 
| 447 |  |  |  |  |  |  | =cut | 
| 448 |  |  |  |  |  |  |  | 
| 449 |  |  |  |  |  |  | sub from_UTEND { | 
| 450 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 451 | 0 |  |  |  |  | 0 | my $generic_headers = shift; | 
| 452 | 0 |  |  |  |  | 0 | my %return_hash; | 
| 453 | 0 | 0 |  |  |  | 0 | if (exists($generic_headers->{UTEND})) { | 
| 454 | 0 |  |  |  |  | 0 | my $t = _parse_date( $generic_headers->{'UTEND'} ); | 
| 455 | 0 |  |  |  |  | 0 | my $month = $t->month; | 
| 456 | 0 |  |  |  |  | 0 | $month =~ /^(.{3})/; | 
| 457 | 0 |  |  |  |  | 0 | $month = $1; | 
| 458 | 0 |  |  |  |  | 0 | $return_hash{'UT_DATE'} = $month . " " . $t->mday . " " . $t->year; | 
| 459 | 0 |  |  |  |  | 0 | $return_hash{'RUTEND'} = $t->hour + ($t->min / 60) + ($t->sec / 3600); | 
| 460 | 0 |  |  |  |  | 0 | $return_hash{'DATE_END'} = $generic_headers->{'UTEND'}; | 
| 461 |  |  |  |  |  |  | } | 
| 462 | 0 |  |  |  |  | 0 | return %return_hash; | 
| 463 |  |  |  |  |  |  | } | 
| 464 |  |  |  |  |  |  |  | 
| 465 |  |  |  |  |  |  | =item B<to_X_BASE> | 
| 466 |  |  |  |  |  |  |  | 
| 467 |  |  |  |  |  |  | Converts the decimal hours in the FITS header C<RABASE> into | 
| 468 |  |  |  |  |  |  | decimal degrees for the generic header C<X_BASE>. | 
| 469 |  |  |  |  |  |  |  | 
| 470 |  |  |  |  |  |  | =cut | 
| 471 |  |  |  |  |  |  |  | 
| 472 |  |  |  |  |  |  | sub to_X_BASE { | 
| 473 | 1 |  |  | 1 | 1 | 2 | my $self = shift; | 
| 474 | 1 |  |  |  |  | 3 | my $FITS_headers = shift; | 
| 475 | 1 |  |  |  |  | 12 | my $return; | 
| 476 | 1 | 50 |  |  |  | 6 | if (exists($FITS_headers->{RABASE})) { | 
| 477 | 1 |  |  |  |  | 4 | $return = $FITS_headers->{RABASE} * 15; | 
| 478 |  |  |  |  |  |  | } | 
| 479 | 1 |  |  |  |  | 4 | return $return; | 
| 480 |  |  |  |  |  |  | } | 
| 481 |  |  |  |  |  |  |  | 
| 482 |  |  |  |  |  |  | =item B<from_X_BASE> | 
| 483 |  |  |  |  |  |  |  | 
| 484 |  |  |  |  |  |  | Converts the decimal degrees in the generic header C<X_BASE> | 
| 485 |  |  |  |  |  |  | into decimal hours for the FITS header C<RABASE>. | 
| 486 |  |  |  |  |  |  |  | 
| 487 |  |  |  |  |  |  | =cut | 
| 488 |  |  |  |  |  |  |  | 
| 489 |  |  |  |  |  |  | sub from_X_BASE { | 
| 490 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 491 | 0 |  |  |  |  | 0 | my $generic_headers = shift; | 
| 492 | 0 |  |  |  |  | 0 | my %return_hash; | 
| 493 | 0 | 0 |  |  |  | 0 | if (exists($generic_headers->{X_BASE})) { | 
| 494 | 0 |  |  |  |  | 0 | $return_hash{'RABASE'} = $generic_headers->{X_BASE} / 15; | 
| 495 |  |  |  |  |  |  | } | 
| 496 | 0 |  |  |  |  | 0 | return %return_hash; | 
| 497 |  |  |  |  |  |  | } | 
| 498 |  |  |  |  |  |  |  | 
| 499 |  |  |  |  |  |  | =item B<to_RA_BASE> | 
| 500 |  |  |  |  |  |  |  | 
| 501 |  |  |  |  |  |  | Converts the decimal hours in the FITS header C<RABASE> into | 
| 502 |  |  |  |  |  |  | decimal degrees for the generic header C<RA_BASE>. | 
| 503 |  |  |  |  |  |  |  | 
| 504 |  |  |  |  |  |  | =cut | 
| 505 |  |  |  |  |  |  |  | 
| 506 |  |  |  |  |  |  | sub to_RA_BASE { | 
| 507 | 1 |  |  | 1 | 1 | 3 | my $self = shift; | 
| 508 | 1 |  |  |  |  | 2 | my $FITS_headers = shift; | 
| 509 | 1 |  |  |  |  | 2 | my $return; | 
| 510 | 1 | 50 |  |  |  | 4 | if (exists($FITS_headers->{RABASE})) { | 
| 511 | 1 |  |  |  |  | 5 | $return = $FITS_headers->{RABASE} * 15; | 
| 512 |  |  |  |  |  |  | } | 
| 513 | 1 |  |  |  |  | 4 | return $return; | 
| 514 |  |  |  |  |  |  | } | 
| 515 |  |  |  |  |  |  |  | 
| 516 |  |  |  |  |  |  | =item B<from_RA_BASE> | 
| 517 |  |  |  |  |  |  |  | 
| 518 |  |  |  |  |  |  | Converts the decimal degrees in the generic header C<RA_BASE> | 
| 519 |  |  |  |  |  |  | into decimal hours for the FITS header C<RABASE>. | 
| 520 |  |  |  |  |  |  |  | 
| 521 |  |  |  |  |  |  | =cut | 
| 522 |  |  |  |  |  |  |  | 
| 523 |  |  |  |  |  |  | sub from_RA_BASE { | 
| 524 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 525 | 0 |  |  |  |  | 0 | my $generic_headers = shift; | 
| 526 | 0 |  |  |  |  | 0 | my %return_hash; | 
| 527 | 0 | 0 |  |  |  | 0 | if (exists($generic_headers->{RA_BASE})) { | 
| 528 | 0 |  |  |  |  | 0 | $return_hash{'RABASE'} = $generic_headers->{RA_BASE} / 15; | 
| 529 |  |  |  |  |  |  | } | 
| 530 | 0 |  |  |  |  | 0 | return %return_hash; | 
| 531 |  |  |  |  |  |  | } | 
| 532 |  |  |  |  |  |  |  | 
| 533 |  |  |  |  |  |  | =back | 
| 534 |  |  |  |  |  |  |  | 
| 535 |  |  |  |  |  |  | =head1 INTERNAL METHODS | 
| 536 |  |  |  |  |  |  |  | 
| 537 |  |  |  |  |  |  | =over 4 | 
| 538 |  |  |  |  |  |  |  | 
| 539 |  |  |  |  |  |  | =item B<_fix_dates> | 
| 540 |  |  |  |  |  |  |  | 
| 541 |  |  |  |  |  |  | Handle the case where DATE_OBS and/or DATE_END are given, and convert | 
| 542 |  |  |  |  |  |  | them into DATE-OBS and/or DATE-END. | 
| 543 |  |  |  |  |  |  |  | 
| 544 |  |  |  |  |  |  | =cut | 
| 545 |  |  |  |  |  |  |  | 
| 546 |  |  |  |  |  |  | sub _fix_dates { | 
| 547 | 1 |  |  | 1 |  | 4 | my ( $class, $FITS_headers ) = @_; | 
| 548 |  |  |  |  |  |  |  | 
| 549 | 1 | 50 |  |  |  | 4 | if( defined( $FITS_headers->{'DATE_OBS'} ) ) { | 
| 550 | 1 |  |  |  |  | 23 | $FITS_headers->{'DATE-OBS'} = $class->_parse_iso_date( $FITS_headers->{'DATE_OBS'} ); | 
| 551 |  |  |  |  |  |  | } | 
| 552 | 1 | 50 |  |  |  | 6 | if( defined( $FITS_headers->{'DATE_END'} ) ) { | 
| 553 | 1 |  |  |  |  | 4 | $FITS_headers->{'DATE-END'} = $class->_parse_iso_date( $FITS_headers->{'DATE_END'} ); | 
| 554 |  |  |  |  |  |  | } | 
| 555 |  |  |  |  |  |  |  | 
| 556 |  |  |  |  |  |  | } | 
| 557 |  |  |  |  |  |  |  | 
| 558 |  |  |  |  |  |  | =item B<_parse_date> | 
| 559 |  |  |  |  |  |  |  | 
| 560 |  |  |  |  |  |  | Parses a string as a date. Returns a C<Time::Piece> object. | 
| 561 |  |  |  |  |  |  |  | 
| 562 |  |  |  |  |  |  | $time = _parse_date( $date ); | 
| 563 |  |  |  |  |  |  |  | 
| 564 |  |  |  |  |  |  | Returns C<undef> if the time could not be parsed. | 
| 565 |  |  |  |  |  |  | Returns the object unchanged if the argument is already a C<Time::Piece>. | 
| 566 |  |  |  |  |  |  |  | 
| 567 |  |  |  |  |  |  | It will also recognize a MySQL style date: '2002-03-15 07:04:00' | 
| 568 |  |  |  |  |  |  | and a simple YYYYMMDD. | 
| 569 |  |  |  |  |  |  |  | 
| 570 |  |  |  |  |  |  | The date is assumed to be in UT. | 
| 571 |  |  |  |  |  |  |  | 
| 572 |  |  |  |  |  |  | =cut | 
| 573 |  |  |  |  |  |  |  | 
| 574 |  |  |  |  |  |  | sub _parse_date { | 
| 575 | 1 |  |  | 1 |  | 3 | my $date = shift; | 
| 576 |  |  |  |  |  |  |  | 
| 577 |  |  |  |  |  |  | # If we already have a Time::Piece return | 
| 578 | 1 | 50 |  |  |  | 7 | return bless $date, "Time::Piece" | 
| 579 |  |  |  |  |  |  | if UNIVERSAL::isa( $date, "Time::Piece"); | 
| 580 |  |  |  |  |  |  |  | 
| 581 |  |  |  |  |  |  | # We can use Time::Piece->strptime but it requires an exact | 
| 582 |  |  |  |  |  |  | # format rather than working it out from context (and we don't | 
| 583 |  |  |  |  |  |  | # want an additional requirement on Date::Manip or something | 
| 584 |  |  |  |  |  |  | # since Time::Piece is exactly what we want for Astro::Coords) | 
| 585 |  |  |  |  |  |  | # Need to fudge a little | 
| 586 |  |  |  |  |  |  |  | 
| 587 | 1 |  |  |  |  | 1 | my $format; | 
| 588 |  |  |  |  |  |  |  | 
| 589 |  |  |  |  |  |  | # Need to disambiguate ISO date from MySQL date | 
| 590 | 1 | 50 |  |  |  | 23 | if ($date =~ /\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d/) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 591 |  |  |  |  |  |  | # MySQL | 
| 592 | 0 |  |  |  |  | 0 | $format = '%Y-%m-%d %T'; | 
| 593 |  |  |  |  |  |  |  | 
| 594 |  |  |  |  |  |  | } elsif ($date =~ /\d\d\d\d-\d\d-\d\d/) { | 
| 595 |  |  |  |  |  |  | # ISO | 
| 596 |  |  |  |  |  |  |  | 
| 597 |  |  |  |  |  |  | # All arguments should have a day, month and year | 
| 598 | 0 |  |  |  |  | 0 | $format = "%Y-%m-%d"; | 
| 599 |  |  |  |  |  |  |  | 
| 600 |  |  |  |  |  |  | # Now check for time | 
| 601 | 0 | 0 |  |  |  | 0 | if ($date =~ /T/) { | 
| 602 |  |  |  |  |  |  | # Date and time | 
| 603 |  |  |  |  |  |  | # Now format depends on the number of colons | 
| 604 | 0 |  |  |  |  | 0 | my $n = ( $date =~ tr/:/:/ ); | 
| 605 | 0 | 0 |  |  |  | 0 | $format .= "T" . ($n == 2 ? "%T" : "%R"); | 
| 606 |  |  |  |  |  |  | } | 
| 607 |  |  |  |  |  |  | } elsif ($date =~ /^\d\d\d\d\d\d\d\d\b/) { | 
| 608 |  |  |  |  |  |  | # YYYYMMDD format | 
| 609 | 0 |  |  |  |  | 0 | $format = "%Y%m%d"; | 
| 610 |  |  |  |  |  |  | } else { | 
| 611 |  |  |  |  |  |  | # Allow Sybase date for compatability. | 
| 612 |  |  |  |  |  |  | # Mar 15 2002  7:04AM | 
| 613 | 1 |  |  |  |  | 3 | $format = "%b %d %Y %I:%M%p"; | 
| 614 |  |  |  |  |  |  |  | 
| 615 |  |  |  |  |  |  | } | 
| 616 |  |  |  |  |  |  |  | 
| 617 |  |  |  |  |  |  | # Now parse | 
| 618 |  |  |  |  |  |  | # Note that this time is treated as "local" rather than "gm" | 
| 619 | 1 |  |  |  |  | 3 | my $time = eval { Time::Piece->strptime( $date, $format ); }; | 
|  | 1 |  |  |  |  | 5 |  | 
| 620 | 1 | 50 |  |  |  | 62 | if ($@) { | 
| 621 | 0 |  |  |  |  | 0 | return undef; | 
| 622 |  |  |  |  |  |  | } else { | 
| 623 |  |  |  |  |  |  | # Note that the above constructor actually assumes the date | 
| 624 |  |  |  |  |  |  | # to be parsed is a local time not UTC. To switch to UTC | 
| 625 |  |  |  |  |  |  | # simply get the epoch seconds and the timezone offset | 
| 626 |  |  |  |  |  |  | # and run gmtime | 
| 627 |  |  |  |  |  |  | # Sometime around v1.07 of Time::Piece the behaviour changed | 
| 628 |  |  |  |  |  |  | # to return UTC rather than localtime from strptime! | 
| 629 |  |  |  |  |  |  | # The joys of backwards compatibility. | 
| 630 | 1 | 50 |  |  |  | 4 | if ($time->[Time::Piece::c_islocal]) { | 
| 631 | 0 |  |  |  |  | 0 | my $tzoffset = $time->tzoffset; | 
| 632 | 0 |  |  |  |  | 0 | my $epoch = $time->epoch; | 
| 633 | 0 |  |  |  |  | 0 | $time = gmtime( $epoch + $tzoffset->seconds ); | 
| 634 |  |  |  |  |  |  | } | 
| 635 |  |  |  |  |  |  |  | 
| 636 |  |  |  |  |  |  | } | 
| 637 |  |  |  |  |  |  |  | 
| 638 | 1 |  |  |  |  | 2 | return $time; | 
| 639 |  |  |  |  |  |  | } | 
| 640 |  |  |  |  |  |  |  | 
| 641 |  |  |  |  |  |  | =back | 
| 642 |  |  |  |  |  |  |  | 
| 643 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 644 |  |  |  |  |  |  |  | 
| 645 |  |  |  |  |  |  | C<Astro::FITS::HdrTrans>, C<Astro::FITS::HdrTrans::UKIRT>, | 
| 646 |  |  |  |  |  |  | C<Astro::FITS::HdrTrans::Base>. | 
| 647 |  |  |  |  |  |  |  | 
| 648 |  |  |  |  |  |  | =head1 AUTHORS | 
| 649 |  |  |  |  |  |  |  | 
| 650 |  |  |  |  |  |  | Brad Cavanagh E<lt>b.cavanagh@jach.hawaii.eduE<gt>, | 
| 651 |  |  |  |  |  |  | Tim Jenness E<lt>t.jenness@jach.hawaii.eduE<gt> | 
| 652 |  |  |  |  |  |  |  | 
| 653 |  |  |  |  |  |  | =head1 COPYRIGHT | 
| 654 |  |  |  |  |  |  |  | 
| 655 |  |  |  |  |  |  | Copyright (C) 2007-2008 Science and Technology Facilities Council. | 
| 656 |  |  |  |  |  |  | Copyright (C) 2002-2005 Particle Physics and Astronomy Research Council. | 
| 657 |  |  |  |  |  |  | All Rights Reserved. | 
| 658 |  |  |  |  |  |  |  | 
| 659 |  |  |  |  |  |  | This program is free software; you can redistribute it and/or modify it under | 
| 660 |  |  |  |  |  |  | the terms of the GNU General Public License as published by the Free Software | 
| 661 |  |  |  |  |  |  | Foundation; either version 2 of the License, or (at your option) any later | 
| 662 |  |  |  |  |  |  | version. | 
| 663 |  |  |  |  |  |  |  | 
| 664 |  |  |  |  |  |  | This program is distributed in the hope that it will be useful,but WITHOUT ANY | 
| 665 |  |  |  |  |  |  | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A | 
| 666 |  |  |  |  |  |  | PARTICULAR PURPOSE. See the GNU General Public License for more details. | 
| 667 |  |  |  |  |  |  |  | 
| 668 |  |  |  |  |  |  | You should have received a copy of the GNU General Public License along with | 
| 669 |  |  |  |  |  |  | this program; if not, write to the Free Software Foundation, Inc., 59 Temple | 
| 670 |  |  |  |  |  |  | Place,Suite 330, Boston, MA  02111-1307, USA | 
| 671 |  |  |  |  |  |  |  | 
| 672 |  |  |  |  |  |  | =cut | 
| 673 |  |  |  |  |  |  |  | 
| 674 |  |  |  |  |  |  | 1; |