File Coverage

blib/lib/Astro/FITS/HdrTrans/INGRID.pm
Criterion Covered Total %
statement 15 213 7.0
branch 0 54 0.0
condition 0 27 0.0
subroutine 6 36 16.6
pod 30 31 96.7
total 51 361 14.1


line stmt bran cond sub pod time code
1             package Astro::FITS::HdrTrans::INGRID;
2              
3             =head1 NAME
4              
5             Astro::FITS::HdrTrans::INGRID - WHT INGRID translations
6              
7             =head1 SYNOPSIS
8              
9             use Astro::FITS::HdrTrans::INGRID;
10              
11             %gen = Astro::FITS::HdrTrans::INGRID->translate_from_FITS( %hdr );
12              
13             =head1 DESCRIPTION
14              
15             This class provides a generic set of translations that are specific to
16             the INGRID camera of the William Herschel Telescope.
17              
18             =cut
19              
20 10     10   14099622 use 5.006;
  10         51  
21 10     10   82 use warnings;
  10         32  
  10         768  
22 10     10   75 use strict;
  10         62  
  10         285  
23 10     10   65 use Carp;
  10         33  
  10         1342  
24              
25             # Inherit from FITS.
26 10     10   88 use base qw/ Astro::FITS::HdrTrans::FITS /;
  10         23  
  10         32085  
27              
28             our $VERSION = "1.66";
29              
30             # For a constant mapping, there is no FITS header, just a generic
31             # header that is constant.
32             my %CONST_MAP = (
33             POLARIMETRY => 0,
34             OBSERVATION_MODE => 'imaging',
35             WAVEPLATE_ANGLE => 0,
36             );
37              
38             # NULL mappings used to override base-class implementations.
39             my @NULL_MAP = qw/ /;
40              
41             # Unit mapping implies that the value propogates directly
42             # to the output with only a keyword name change.
43              
44             my %UNIT_MAP = (
45             AIRMASS_END => "AIRMASS",
46             AIRMASS_START => "AIRMASS",
47             EXPOSURE_TIME => "EXPTIME",
48             FILTER => "INGF1NAM",
49             INSTRUMENT => "DETECTOR",
50             NUMBER_OF_EXPOSURES => "COAVERAG",
51             NUMBER_OF_READS => "NUMREADS",
52             OBSERVATION_NUMBER => "RUN"
53             );
54              
55              
56             # Create the translation methods.
57             __PACKAGE__->_generate_lookup_methods( \%CONST_MAP, \%UNIT_MAP, \@NULL_MAP );
58              
59             =head1 METHODS
60              
61             =over 4
62              
63             =item B<this_instrument>
64              
65             The name of the instrument required to match (case insensitively)
66             against the INSTRUME/INSTRUMENT keyword to allow this class to
67             translate the specified headers. Called by the default
68             C<can_translate> method.
69              
70             $inst = $class->this_instrument();
71              
72             Returns "INGRID".
73              
74             =cut
75              
76             sub this_instrument {
77 20     20 1 106 return qr/^INGRID/;
78             }
79              
80             =back
81              
82             =head1 COMPLEX CONVERSIONS
83              
84             =over 4
85              
86             =item B<to_DEC_BASE>
87              
88             Converts the base declination from sexagesimal d:m:s to decimal
89             degrees using the C<CAT-DEC> keyword, defaulting to 0.0.
90              
91             =cut
92              
93             sub to_DEC_BASE {
94 0     0 1   my $self = shift;
95 0           my $FITS_headers = shift;
96 0           my $dec = 0.0;
97 0           my $sexa = $FITS_headers->{"CAT-DEC"};
98 0 0         if ( defined( $sexa ) ) {
99 0           $dec = $self->dms_to_degrees( $sexa );
100             }
101 0           return $dec;
102             }
103              
104             =item B<to_DEC_SCALE>
105              
106             Sets the declination scale in arcseconds per pixel. The C<CCDYPIXE>
107             and C<INGPSCAL> headers are used when both are defined. Otherwise it
108             returns a default value of 0.2387 arcsec/pixel, assuming north is up.
109              
110             =cut
111              
112             sub to_DEC_SCALE {
113 0     0 1   my $self = shift;
114 0           my $FITS_headers = shift;
115 0           my $decscale = 0.2387;
116              
117             # Assumes either x-y scales the same or the y corresponds to
118             # declination.
119 0           my $ccdypixe = $self->via_subheader( $FITS_headers, "CCDYPIXE" );
120 0           my $ingpscal = $self->via_subheader( $FITS_headers, "INGPSCAL" );
121 0 0 0       if ( defined $ccdypixe && defined $ingpscal ) {
122 0           $decscale = $ccdypixe * 1000.0 * $ingpscal;
123             }
124 0           return $decscale;
125             }
126              
127             =item B<to_DEC_TELESCOPE_OFFSET>
128              
129             Sets the declination telescope offset in arcseconds. It uses the
130             C<CAT-DEC> and C<DEC> keywords to derive the offset, and if either
131             does not exist, it returns a default of 0.0.
132              
133             =cut
134              
135             sub to_DEC_TELESCOPE_OFFSET {
136 0     0 1   my $self = shift;
137 0           my $FITS_headers = shift;
138 0           my $decoffset = 0.0;
139 0 0 0       if ( exists $FITS_headers->{"CAT-DEC"} && exists $FITS_headers->{DEC} ) {
140              
141             # Obtain the reference and telescope declinations positions measured in degrees.
142 0           my $refdec = $self->dms_to_degrees( $FITS_headers->{"CAT-DEC"} );
143 0           my $dec = $self->dms_to_degrees( $FITS_headers->{DEC} );
144              
145             # Find the offsets between the positions in arcseconds on the sky.
146 0           $decoffset = 3600.0 * ( $dec - $refdec );
147             }
148              
149             # The sense is reversed compared with UKIRT, as these measure the
150             # place son the sky, not the motion of the telescope.
151 0           return -1.0 * $decoffset
152             }
153              
154             =item B<to_DETECTOR_READ_TYPE>
155              
156             Returns the UKIRT-like detector type "STARE" or "NDSTARE" from the
157             FITS C<REDMODE> and C<NUMREADS> keywords.
158              
159             This is guesswork at present.
160              
161             =cut
162              
163             sub to_DETECTOR_READ_TYPE {
164 0     0 1   my $self = shift;
165 0           my $FITS_headers = shift;
166 0           my $read_type;
167 0           my $readout_mode = $FITS_headers->{READMODE};
168 0           my $nreads = $FITS_headers->{NUMREADS};
169 0 0 0       if ( $readout_mode =~ /^mndr/i ||
    0 0        
170             ( $readout_mode =~ /^cds/i && $nreads == 1 ) ) {
171 0           $read_type = "STARE";
172             } elsif ( $readout_mode =~ /^cds/i ) {
173 0           $read_type = "NDSTARE";
174             }
175 0           return $read_type;
176             }
177              
178             =item B<to_DR_RECIPE>
179              
180             Returns the data-reduction recipe name. The selection depends on the
181             values of the C<OBJECT> and C<OBSTYPE> keywords. The default is
182             "QUICK_LOOK". A dark returns "REDUCE_DARK", and an object's recipe is
183             "JITTER_SELF_FLAT".
184              
185             =cut
186              
187             # No clue what the recipe is apart for a dark and assume a dither
188             # pattern means JITTER_SELF_FLAT.
189             sub to_DR_RECIPE {
190 0     0 1   my $self = shift;
191 0           my $FITS_headers = shift;
192 0           my $recipe = "QUICK_LOOK";
193              
194             # Look for a dither pattern. These begin D-<n>/<m>: where
195             # <m> represents the number of jitter positions in the group
196             # and <n> is the number within the group.
197 0           my $object = $FITS_headers->{OBJECT};
198 0 0         if ( $object =~ /D-\d+\/\d+/ ) {
    0          
199 0           $recipe = "JITTER_SELF_FLAT";
200             } elsif ( $FITS_headers->{OBSTYPE} =~ /DARK/i ) {
201 0           $recipe = "REDUCE_DARK";
202             }
203              
204 0           return $recipe;
205             }
206              
207             =item B<to_EQUINOX>
208              
209             Returns the equinox in decimal years. It's taken from the C<CAT-EQUI>
210             keyword, if it exists, defaulting to 2000.0 otherwise.
211              
212             =cut
213              
214             sub to_EQUINOX {
215 0     0 1   my $self = shift;
216 0           my $FITS_headers = shift;
217 0           my $equinox = 2000.0;
218 0 0         if ( exists $FITS_headers->{"CAT-EQUI"} ) {
219 0           $equinox = $FITS_headers->{"CAT-EQUI"};
220 0           $equinox =~ s/[BJ]//;
221             }
222 0           return $equinox;
223             }
224              
225             =item B<to_GAIN>
226              
227             Returns the gain in electrons per data number. This is taken from
228             the C<GAIN> keyword, with a default of 4.1.
229              
230             =cut
231              
232             sub to_GAIN {
233 0     0 1   my $self = shift;
234 0           my $FITS_headers = shift;
235 0           my $gain = 4.1;
236 0           my $subval = $self->via_subheader( $FITS_headers, "GAIN" );
237 0 0         $gain = $subval if defined $subval;
238 0           return $gain;
239             }
240              
241             =item B<to_NUMBER_OF_OFFSETS>
242              
243             Returns the number of offsets. It uses the UKIRT convention so
244             it is equivalent to the number of dither positions plus one.
245             The value is derived from the C<OBJECT> keyword, with a default of 6.
246              
247             =cut
248              
249             sub to_NUMBER_OF_OFFSETS {
250 0     0 1   my $self = shift;
251 0           my $FITS_headers = shift;
252 0           my $noffsets = 5;
253              
254             # Look for a dither pattern. These begin D-<n>/<m>: where
255             # <m> represents the number of jitter positions in the group
256             # and <n> is the number within the group.
257 0           my $object = $FITS_headers->{OBJECT};
258 0 0         if ( $object =~ /D-\d+\/\d+/ ) {
259              
260             # Extract the string between the solidus and the colon. Add one
261             # to match the UKIRT convention.
262 0           $noffsets = substr( $object, index( $object, "/" ) + 1 );
263 0           $noffsets = substr( $noffsets, 0, index( $noffsets, ":" ) );
264             }
265 0           return $noffsets + 1;
266             }
267              
268             =item B<to_OBJECT>
269              
270             Reeturns the object name. It is extracted from the C<OBJECT> keyword.
271              
272             =cut
273              
274             sub to_OBJECT {
275 0     0 1   my $self = shift;
276 0           my $FITS_headers = shift;
277 0           my $object = $FITS_headers->{OBJECT};
278              
279             # Look for a dither pattern. These begin D-<n>/<m>: where
280             # <m> represents the number of jitter positions in the group
281             # and <n> is the number within the group. We want to extract
282             # the actual object name.
283 0 0         if ( $object =~ /D-\d+\/\d+/ ) {
284 0           $object = substr( $object, index( $object, ":" ) + 2 );
285             }
286 0           return $object;
287             }
288              
289             =item B<to_OBSERVATION_TYPE>
290              
291             Determines the observation type from the C<OBSTYPE> keyword provided it is
292             "TARGET" for an object dark frame.
293              
294             =cut
295              
296             sub to_OBSERVATION_TYPE {
297 0     0 1   my $self = shift;
298 0           my $FITS_headers = shift;
299 0           my $obstype = uc( $FITS_headers->{OBSTYPE} );
300 0 0         if ( $obstype eq "TARGET" ) {
301 0           $obstype = "OBJECT";
302             }
303 0           return $obstype;
304             }
305              
306             =item B<to_RA_BASE>
307              
308             Converts the base right ascension from sexagesimal h:m:s to decimal degrees
309             using the C<CAT-RA> keyword, defaulting to 0.0.
310              
311             =cut
312              
313             sub to_RA_BASE {
314 0     0 1   my $self = shift;
315 0           my $FITS_headers = shift;
316 0           my $ra = 0.0;
317 0           my $sexa = $FITS_headers->{"CAT-RA"};
318 0 0         if ( defined( $sexa ) ) {
319 0           $ra = $self->hms_to_degrees( $sexa );
320             }
321 0           return $ra;
322             }
323              
324             =item B<to_RA_SCALE>
325              
326             Sets the right-ascension scale in arcseconds per pixel. The C<CCDXPIXE>
327             and C<INGPSCAL> headers are used when both are defined. Otherwise it
328             returns a default value of 0.2387 arcsec/pixel, assuming east is to
329             the left.
330              
331             =cut
332              
333             sub to_RA_SCALE {
334 0     0 1   my $self = shift;
335 0           my $FITS_headers = shift;
336 0           my $rascale = -0.2387;
337              
338             # Assumes either x-y scales the same or the x corresponds to right
339             # ascension, and right ascension decrements with increasing x.
340 0           my $ccdxpixe = $self->via_subheader( $FITS_headers, "CCDXPIXE" );
341 0           my $ingpscal = $self->via_subheader( $FITS_headers, "INGPSCAL" );
342 0 0 0       if ( defined $ccdxpixe && defined $ingpscal ) {
343 0           $rascale = $ccdxpixe * -1000.0 * $ingpscal;
344             }
345 0           return $rascale;
346             }
347              
348             =item B<to_RA_TELESCOPE_OFFSET>
349              
350             Sets the right-ascension telescope offset in arcseconds. It uses the
351             C<CAT-RA>, C<RA>, C<CAT-DEC> keywords to derive the offset, and if any
352             of these keywords does not exist, it returns a default of 0.0.
353              
354             =cut
355              
356             sub to_RA_TELESCOPE_OFFSET {
357 0     0 1   my $self = shift;
358 0           my $FITS_headers = shift;
359 0           my $raoffset = 0.0;
360              
361 0 0 0       if ( exists $FITS_headers->{"CAT-DEC"} &&
      0        
362             exists $FITS_headers->{"CAT-RA"} && exists $FITS_headers->{RA} ) {
363              
364             # Obtain the reference and telescope sky positions measured in degrees.
365 0           my $refra = $self->hms_to_degrees( $FITS_headers->{"CAT-RA"} );
366 0           my $ra = $self->hms_to_degrees( $FITS_headers->{RA} );
367 0           my $refdec = $self->dms_to_degrees( $FITS_headers->{"CAT-DEC"} );
368              
369             # Find the offset between the positions in arcseconds on the sky.
370 0           $raoffset = 3600.0 * ( $ra - $refra ) * $self->cosdeg( $refdec );
371             }
372              
373             # The sense is reversed compared with UKIRT, as these measure the
374             # place son the sky, not the motion of the telescope.
375 0           return -1.0 * $raoffset;
376             }
377              
378             =item B<to_ROTATION>
379              
380             Returns the orientation of the detector in degrees anticlockwise
381             from north via east.
382              
383             =cut
384              
385             sub to_ROTATION {
386 0     0 1   my $self = shift;
387 0           my $FITS_headers = shift;
388 0           return $self->rotation( $FITS_headers );
389             }
390              
391             =item B<to_SPEED_GAIN>
392              
393             Returns the speed gain. This is either "Normal" or "HiGain", the
394             selection depending on the value of the C<CCDSPEED> keyword.
395              
396             =cut
397              
398             # Fixed values for the gain depend on the camera (SW or LW), and for LW
399             # the readout mode.
400             sub to_SPEED_GAIN {
401 0     0 1   my $self = shift;
402 0           my $FITS_headers = shift;
403 0           my $spd_gain;
404 0           my $speed = $FITS_headers->{CCDSPEED};
405 0 0         if ( $speed =~ /SLOW/ ) {
406 0           $spd_gain = "Normal";
407             } else {
408 0           $spd_gain = "HiGain";
409             }
410 0           return $spd_gain;
411             }
412              
413             =item B<to_STANDARD>
414              
415             Returns whether or not the observation is of a standard source. It is
416             deemed to be a standard when the C<OBSTYPE> keyword is "STANDARD".
417              
418             =cut
419              
420             sub to_STANDARD {
421 0     0 1   my $self = shift;
422 0           my $FITS_headers = shift;
423 0           my $standard = 0;
424 0           my $type = $FITS_headers->{OBSTYPE};
425 0 0         if ( uc( $type ) eq "STANDARD" ) {
426 0           $standard = 1;
427             }
428 0           return $standard;
429             }
430              
431             =item B<to_UTDATE>
432              
433             Returns the UT date as C<Time::Piece> object. It copes with non-standard
434             format in C<DATE-OBS>.
435              
436             =cut
437              
438             sub to_UTDATE {
439 0     0 1   my $self = shift;
440 0           my $FITS_headers = shift;
441 0           return $self->get_UT_date( $FITS_headers );
442             }
443              
444             =item B<to_UTEND>
445              
446             Returns the UT time of the end of the observation as a C<Time::Piece> object.
447              
448             =cut
449              
450             sub to_UTEND {
451 0     0 1   my $self = shift;
452 0           my $FITS_headers = shift;
453              
454             # This is the approximate end UT.
455 0           my $start = $self->to_UTSTART( $FITS_headers );
456 0           return $self->_add_seconds( $start, $FITS_headers->{EXPTIME} );
457             }
458              
459             =item B<to_UTSTART>
460              
461             Returns an estimated UT time of the start of the observation as a
462             C<Time::Piece> object. The start time is derived from the C<DATE-OBS>
463             keyword and if C<DATE-OBS> only supplies a date, the time from the
464             C<UTSTART> keyword is appended before conversaion to a C<Time::Piece>
465             object.
466              
467             =cut
468              
469             sub to_UTSTART {
470 0     0 1   my $self = shift;
471 0           my $FITS_headers = shift;
472 0           my $return;
473 0 0         if ( exists $FITS_headers->{'DATE-OBS'} ) {
474 0           my $iso;
475 0 0         if ( $FITS_headers->{'DATE-OBS'} =~ /T/ ) {
    0          
476             # standard format
477 0           $iso = $FITS_headers->{'DATE-OBS'};
478             } elsif ( exists $FITS_headers->{UTSTART} ) {
479 0           $iso = $FITS_headers->{'DATE-OBS'}. "T" . $FITS_headers->{UTSTART};
480             }
481 0 0         $return = $self->_parse_iso_date( $iso ) if $iso;
482             }
483 0           return $return;
484             }
485              
486             =item B<to_X_LOWER_BOUND>
487              
488             Returns the lower bound along the X-axis of the area of the detector
489             as a pixel index.
490              
491             =cut
492              
493             sub to_X_LOWER_BOUND {
494 0     0 1   my $self = shift;
495 0           my $FITS_headers = shift;
496 0           my @bounds = $self->getbounds( $FITS_headers );
497 0           return $bounds[ 0 ];
498             }
499              
500             =item B<to_X_REFERENCE_PIXEL>
501              
502             Specifies the X-axis reference pixel near the frame centre. It uses
503             the nominal reference pixel if that is correctly supplied, failing
504             that it takes the average of the bounds, and if these headers are also
505             absent, it uses a default which assumes the full array.
506              
507             =cut
508              
509             sub to_X_REFERENCE_PIXEL{
510 0     0 1   my $self = shift;
511 0           my $FITS_headers = shift;
512 0           my $xref;
513 0           my @bounds = $self->getbounds( $FITS_headers );
514 0 0 0       if ( $bounds[ 0 ] > 1 || $bounds[ 1 ] < 1024 ) {
515 0           $xref = nint( ( $bounds[ 0 ] + $bounds[ 1 ] ) / 2 );
516             } else {
517 0           $xref = 512;
518             }
519 0           return $xref;
520             }
521              
522             =item B<to_X_UPPER_BOUND>
523              
524             Returns the upper bound along the X-axis of the area of the detector
525             as a pixel index.
526              
527             =cut
528              
529             sub to_X_UPPER_BOUND {
530 0     0 1   my $self = shift;
531 0           my $FITS_headers = shift;
532 0           my @bounds = $self->getbounds( $FITS_headers );
533 0           return $bounds[ 1 ];
534             }
535              
536             =item B<to_Y_LOWER_BOUND>
537              
538             Returns the lower bound along the Y-axis of the area of the detector
539             as a pixel index.
540              
541             =cut
542              
543             sub to_Y_LOWER_BOUND {
544 0     0 1   my $self = shift;
545 0           my $FITS_headers = shift;
546 0           my @bounds = $self->getbounds( $FITS_headers );
547 0           return $bounds[ 2 ];
548             }
549              
550             =item B<to_Y_REFERENCE_PIXEL>
551              
552             Specifies the Y-axis reference pixel near the frame centre. It uses
553             the nominal reference pixel if that is correctly supplied, failing
554             that it takes the average of the bounds, and if these headers are also
555             absent, it uses a default which assumes the full array.
556              
557             =cut
558              
559             sub to_Y_REFERENCE_PIXEL{
560 0     0 1   my $self = shift;
561 0           my $FITS_headers = shift;
562 0           my $yref;
563 0           my @bounds = $self->getbounds( $FITS_headers );
564 0 0 0       if ( $bounds[ 2 ] > 1 || $bounds[ 3 ] < 1024 ) {
565 0           $yref = nint( ( $bounds[ 2 ] + $bounds[ 3 ] ) / 2 );
566             } else {
567 0           $yref = 512;
568             }
569 0           return $yref;
570             }
571              
572             =item B<to_Y_UPPER_BOUND>
573              
574             Returns the upper bound along the Y-axis of the area of the detector
575             as a pixel index.
576              
577             =cut
578              
579             sub to_Y_UPPER_BOUND {
580 0     0 1   my $self = shift;
581 0           my $FITS_headers = shift;
582 0           my @bounds = $self->getbounds( $FITS_headers );
583 0           return $bounds[ 3 ];
584             }
585              
586             =back
587              
588             # Supplementary methods for the translations
589             # ------------------------------------------
590              
591             =head1 HELPER ROUTINES
592              
593             These are INGRID-specific helper routines.
594              
595             =over 4
596              
597             =item B<dms_to_degrees>
598              
599             Converts a sky angle specified in d:m:s format into decimal degrees.
600             The argument is the sexagesimal-format angle.
601              
602             =cut
603              
604             sub dms_to_degrees {
605 0     0 1   my $self = shift;
606 0           my $sexa = shift;
607 0           my $dms;
608 0 0         if ( defined( $sexa ) ) {
609 0           my @pos = split( /:/, $sexa );
610 0           $dms = $pos[ 0 ] + $pos[ 1 ] / 60.0 + $pos [ 2 ] / 3600.;
611             }
612 0           return $dms;
613             }
614              
615             # Obtain the detector bounds from a section in [xl:xu,yl:yu] syntax.
616             # If the RTDATSEC header is absent, use a default which corresponds
617             # to the full array.
618             sub getbounds{
619 0     0 0   my $self = shift;
620 0           my $FITS_headers = shift;
621 0           my @bounds = ( 1, 1024, 1, 1024 );
622 0 0         if ( exists $FITS_headers->{RTDATSEC} ) {
623 0           my $section = $FITS_headers->{RTDATSEC};
624 0           $section =~ s/\[//;
625 0           $section =~ s/\]//;
626 0           $section =~ s/,/:/g;
627 0           @bounds = split( /:/, $section );
628             }
629 0           return @bounds;
630             }
631              
632             =item B<get_UT_date>
633              
634             Returns the UT date in YYYYMMDD format. It parses the non-standard
635             ddMmmyy C<DATE-OBS> keyword.
636              
637             =cut
638              
639             sub get_UT_date {
640 0     0 1   my $self = shift;
641 0           my $FITS_headers = shift;
642              
643             # This is UT start and time.
644 0           my $dateobs = $FITS_headers->{"DATE-OBS"};
645              
646             # Extract out the data in yyyymmdd format.
647 0           return substr( $dateobs, 0, 4 ) . substr( $dateobs, 5, 2 ) . substr( $dateobs, 8, 2 )
648             }
649              
650             =item B<hms_to_degrees>
651              
652             Converts a sky angle specified in h:m:s format into decimal degrees.
653             It takes no account of latitude. The argument is the sexagesimal
654             format angle.
655              
656             =cut
657              
658             sub hms_to_degrees {
659 0     0 1   my $self = shift;
660 0           my $sexa = shift;
661 0           my $hms;
662 0 0         if ( defined( $sexa ) ) {
663 0           my @pos = split( /:/, $sexa );
664 0           $hms = 15.0 * ( $pos[ 0 ] + $pos[ 1 ] / 60.0 + $pos [ 2 ] / 3600. );
665             }
666 0           return $hms;
667             }
668              
669             =item B<rotation>
670              
671             Derives the rotation angle in degrees from the C<ROTSKYPA> keyword, with a
672             default of 0.0.
673              
674             =cut
675              
676             sub rotation{
677 0     0 1   my $self = shift;
678 0           my $FITS_headers = shift;
679 0           my $rotangle = 0.0;
680              
681 0 0         if ( exists $FITS_headers->{ROTSKYPA} ) {
682 0           $rotangle = $FITS_headers->{ROTSKYPA};
683             }
684 0           return $rotangle;
685             }
686              
687             =back
688              
689             =head1 SEE ALSO
690              
691             C<Astro::FITS::HdrTrans>, C<Astro::FITS::HdrTrans::UKIRT>.
692              
693             =head1 AUTHOR
694              
695             Malcolm J. Currie E<lt>mjc@star.rl.ac.ukE<gt>
696             Brad Cavanagh E<lt>b.cavanagh@jach.hawaii.eduE<gt>,
697             Tim Jenness E<lt>t.jenness@jach.hawaii.eduE<gt>.
698              
699             =head1 COPYRIGHT
700              
701             Copyright (C) 2008 Science and Technology Facilities Council.
702             Copyright (C) 2003-2005 Particle Physics and Astronomy Research Council.
703             All Rights Reserved.
704              
705             This program is free software; you can redistribute it and/or modify it under
706             the terms of the GNU General Public License as published by the Free Software
707             Foundation; either Version 2 of the License, or (at your option) any later
708             version.
709              
710             This program is distributed in the hope that it will be useful,but WITHOUT ANY
711             WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
712             PARTICULAR PURPOSE. See the GNU General Public License for more details.
713              
714             You should have received a copy of the GNU General Public License along with
715             this program; if not, write to the Free Software Foundation, Inc., 59 Temple
716             Place, Suite 330, Boston, MA 02111-1307, USA.
717              
718             =cut
719              
720             1;