File Coverage

blib/lib/Astro/FITS/HdrTrans/UKIRTDB.pm
Criterion Covered Total %
statement 130 204 63.7
branch 34 90 37.7
condition 10 48 20.8
subroutine 24 28 85.7
pod 19 19 100.0
total 217 389 55.7


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