File Coverage

blib/lib/Astro/FITS/HdrTrans/Base.pm
Criterion Covered Total %
statement 255 288 88.5
branch 37 54 68.5
condition 14 30 46.6
subroutine 128 130 98.4
pod 40 117 34.1
total 474 619 76.5


line stmt bran cond sub pod time code
1             package Astro::FITS::HdrTrans::Base;
2              
3             =head1 NAME
4              
5             Astro::FITS::HdrTrans::Base - Base class for header translation
6              
7             =head1 SYNOPSIS
8              
9             use base qw/ Astro::FITS::HdrTrans::Base /;
10              
11             %generic = Astro::FITS::HdrTrans::Base->translate_from_FITS( \%fits );
12             %fits = Astro::FITS::HdrTrans::Base->translate_to_FITS( \%gen );
13              
14             =head1 DESCRIPTION
15              
16             This is the header translation base class. Not to be confused with
17             C<Astro::FITS::HdrTrans> itself, which is a high level abstraction
18             class. In general users should use C<Astro::FITS::HdrTrans>
19             for initiating header translations unless they know what they are
20             doing. Also C<Astro::FITS::HdrTrans> is the only public interface
21             to the header translation.
22              
23             =cut
24              
25 61     61   2950868 use 5.006;
  53         3508  
26 62     53   640 use strict;
  54         196  
  56         1817  
27 56     62   831 use warnings;
  52         148  
  52         3483  
28 52     54   359 use Carp;
  52         215  
  52         10776  
29 52     56   27874 use Math::Trig qw/ deg2rad /;
  52         830405  
  52         5939  
30              
31 52     56   32774 use Astro::FITS::HdrTrans (); # for the generic header list
  52         183  
  52         128050  
32              
33             our $VERSION = "1.66";
34              
35             =head1 PUBLIC METHODS
36              
37             All methods in this class are CLASS METHODS. No state is retained
38             outside of the hash argument.
39              
40             =over 4
41              
42             =item B<translate_from_FITS>
43              
44             Do the header translation from FITS for the specified class.
45              
46             %generic = $class->translate_to_FITS( \%fitshdr,
47             prefix => $prefix,
48             frameset => $wcs,
49             );
50              
51             Prefix is attached to the keys in the returned hash if it
52             is defined. The frameset is an optional Starlink::AST object.
53              
54             If a translation results in an undefined value (for example, if the
55             headers can represent both imaging and spectroscopy there may be no
56             requirement for a DISPERSION header), the result is not stored in the
57             translated hash.
58              
59             A list of failed translations is available in the _UNDEFINED_TRANSLATIONS
60             key in the generic hash. This points to a reference to an array of all
61             the failed generic translations.
62              
63             The class used for the translation is stored in the key _TRANSLATION_CLASS.
64             This can then be used to reverse the translation without having to
65             re-scan the headers.
66              
67             =cut
68              
69             sub translate_from_FITS {
70 20     20 1 57 my $class = shift;
71 20         45 my $FITS = shift;
72 20         128 my %opts = @_;
73              
74 20         57 my $prefix = '';
75 20 100 66     183 if ( exists( $opts{prefix} ) &&
76             defined( $opts{prefix} ) ) {
77 1         4 $prefix = $opts{prefix};
78             }
79              
80 20         50 my $frameset;
81 20 50 33     157 if ( exists( $opts{frameset} ) &&
82             defined( $opts{frameset} ) ) {
83 0         0 $frameset = $opts{frameset};
84             }
85              
86 20 50 33     140 croak "translate_from_FITS: Not a hash reference!"
87             unless (ref($FITS) && ref($FITS) eq 'HASH');
88              
89             # Now we need to loop over the known generic headers
90             # which we obtain from Astro::FITS::HdrTrans
91 20         126 my @GEN = Astro::FITS::HdrTrans->generic_headers;
92              
93 20         67 my %generic;
94             my @failed;
95 20         55 for my $g (@GEN) {
96 2920         5202 my $method = "to_$g";
97 2920 100       27060 if ($class->can( $method )) {
98 1164         35108 my $result = $class->$method( $FITS, $frameset );
99 1164 100       12858 if (defined $result) {
100 901         3832 $generic{"$prefix$g"} = $result;
101             } else {
102 263         820 push(@failed, $g);
103             }
104             }
105             }
106              
107             # store the failed translations (if we had any)
108 20 50       135 $generic{_UNDEFINED_TRANSLATIONS} = \@failed if @failed;
109              
110             # store the translation class
111 20         72 $generic{_TRANSLATION_CLASS} = $class;
112              
113 20         1517 return %generic;
114             }
115              
116             =item B<translate_to_FITS>
117              
118             Do the header translation from generic headers to FITS
119             for the specified class.
120              
121             %fits = $class->translate_to_FITS( \%generic );
122              
123             =cut
124              
125             sub translate_to_FITS {
126 15     15 1 38 my $class = shift;
127 15         38 my $generic = shift;
128              
129 15 50 33     97 croak "translate_to_FITS: Not a hash reference!"
130             unless (ref($generic) && ref($generic) eq 'HASH');
131              
132             # Now we need to loop over the known generic headers
133             # which we obtain from Astro::FITS::HdrTrans
134 15         95 my @GEN = Astro::FITS::HdrTrans->generic_headers;
135              
136 15         41 my %FITS;
137 15         46 for my $g (@GEN) {
138 2190         3722 my $method = "from_$g";
139 2190 100       13568 if ($class->can( $method )) {
140 782         23151 %FITS = (%FITS,$class->$method( $generic ));
141             }
142              
143             }
144              
145 15         683 return %FITS;
146             }
147              
148             =back
149              
150             =head1 PROTECTED METHODS
151              
152             These methods are available to translation subclasses and should
153             not be used by external classes.
154              
155             =over 4
156              
157             =item B<can_translate>
158              
159             Returns true if the supplied headers can be handled by this class.
160              
161             $cando = $class->can_translate( \%hdrs );
162              
163             The base class version of this method returns true if either the C<INSTRUME>
164             or C<INSTRUMENT> key exist and match the value returned by the
165             C<this_instrument> method. Comparisons are case-insensitive and can use
166             regular expressions on instrument name if provided by the base class.
167              
168              
169             =cut
170              
171             sub can_translate {
172 527     527 1 1354 my $class = shift;
173 527         949 my $headers = shift;
174              
175             # get the reference instrument string
176 527         2439 my $ref = $class->this_instrument();
177 527 50       1616 return 0 unless defined $ref;
178              
179             # For consistency in subsequent algorithm convert
180             # a string to a pattern match object
181 527 100       1594 if (not ref($ref)) {
182 243         769 $ref = quotemeta($ref);
183 243         4974 $ref = qr/^$ref$/i;
184             }
185              
186             # check against the FITS and Generic versions.
187 527         999 my $inst;
188 527         1265 for my $k (qw/ INSTRUME INSTRUMENT /) {
189 540 100 66     3301 if (exists $headers->{$k} && defined $headers->{$k}) {
190 527         53976 $inst = $headers->{$k};
191 527         34162 last;
192             }
193             }
194              
195             # no recognizable instrument
196 527 50       1892 return 0 unless defined $inst;
197              
198             # Now do the test
199 527         6054 return ( $inst =~ $ref );
200             }
201              
202             =item B<this_instrument>
203              
204             Name of the instrument that can be translated by this class.
205             Defaults to an empty string. The method must be subclassed.
206              
207             $inst = $class->this_instrument();
208              
209             Can return a regular expresion object (C<qr>).
210              
211             =cut
212              
213             sub this_instrument {
214 0     0 1 0 return "";
215             }
216              
217             =item B<valid_class>
218              
219             Historically this method was used to determine whether this class can
220             handle the supplied FITS headers. The headers can be either in
221             generic form or in FITS form.
222              
223             $isvalid = $class->valid_class( \%fits );
224              
225             The base class always returns false. This is a backwards compatibility
226             method to prevent mixing of translation modules from earlier release
227             of C<Astro::FITS::HdrTrans> with the current object-oriented version.
228             See the C<can_translate> method for the new interface.
229              
230             =cut
231              
232             sub valid_class {
233 0     0 1 0 return 0;
234             }
235              
236             =item B<_generate_lookup_methods>
237              
238             We generate the unit and constant mapping methods automatically from a
239             lookup table.
240              
241             Astro::FITS::HdrTrans::UKIRT->_generate_lookup_methods( \%const, \%unit);
242              
243             This method generates all the simple internal methods. Expects two arguments,
244             both references to hashes. The first is a reference to a hash with
245             constant mapping from FITS to generic (and no reverse mapping), the
246             second is a reference to a hash with unit mappings (both from and to
247             methods are created). The methods are placed into the package given
248             by the class supplied to the method.
249              
250             Astro::FITS::HdrTrans::UKIRT->_generate_lookup_methods( \%const, \%unit, \%null);
251              
252             Additionally, an optional third argument can be used to indicate
253             methods that should be null translations. This is a reference to an array
254             of generic keywords and should be used in the rare cases when a base
255             class implementation should be nullified. This will result in undefined
256             values in the generic hash but no value in the generic to FITS mapping.
257              
258             A fourth optional argument can specify those unit mappings that should
259             use the final entry in a subheader (if a subheader is present). Mainly
260             associated with END events such as AIRMASS_END or ELEVATION_END.
261              
262             Astro::FITS::HdrTrans::UKIRT->_generate_lookup_methods( \%const, \%unit,
263             \%null, \%endobs);
264              
265             These methods will have the standard interface of
266              
267             $generic = $class->_to_GENERIC_NAME( \%fits );
268             %fits = $class->_from_GENERIC_NAME( \%generic );
269              
270             Generic unit map translations use the via_subheader() method in scalar
271             context and so will retrieve the first sub header value if the keyword
272             is not present in the primary header.
273              
274             =cut
275              
276             sub _generate_lookup_methods {
277 512     512   1612 my $class = shift;
278 512         1034 my $const = shift;
279 512         2079 my $unit = shift;
280 512         897 my $null = shift;
281 512         1003 my $endobs = shift;
282              
283             # Have to go into a different package
284 512         1266 my $p = "{\n package $class;\n";
285 512         1061 my $ep = "\n}"; # close the scope
286              
287             # Loop over the keys to the unit mapping hash
288             # The keys are the GENERIC name
289 512         2716 for my $key (keys %$unit) {
290              
291             # Get the original FITS header name
292 4876         13271 my $fhdr = $unit->{$key};
293              
294             # print "Processing $key and $ohdr and $fhdr\n";
295              
296             # First generate the code to generate Generic headers
297 4876         9720 my $subname = "to_$key";
298 4876         10444 my $sub = qq/ $p sub $subname { scalar \$_[0]->via_subheader_undef_check(\$_[1],\"$fhdr\"); } $ep /;
299 4876     1 0 578178 eval "$sub";
  1     3 0 7  
  3     1 0 44  
  1     3 0 8  
  3     1 0 25  
  1     5 0 4  
  5     5 0 29  
  5     2 0 27  
  2     4 0 14  
  4     1 0 22  
  1     5 0 7  
  5     19 0 29  
  19     3 1 110  
  3     4 1 21  
  4     13 1 25  
  13     29 1 80  
  29     15 0 209  
  15     30 0 105  
  30     25 0 195  
  25     24 0 158  
  24     16 0 166  
  16     25 0 81  
  25     27 0 136  
  27     25 0 158  
  25     13 0 160  
  13     32 0 83  
  32     17 0 218  
  17     18 0 163  
  18     13 0 175  
  13     24 1 79  
  24     31 1 222  
  31     35 1 170  
  35     18 0 230  
  18     10 0 110  
  10     7 0 47  
  7     13 0 44  
  13     28 0 75  
  28     15 0 159  
  15     16 0 97  
  16     4 0 108  
  4     20 1 38  
  20     10 1 166  
  10     3 0 65  
  3     15 0 14  
  15     10 0 534  
  10     8 0 57  
  8     3 1 42  
  3     8 1 17  
  8     42 1 46  
  42     26 1 241  
  26     7 1 152  
  7     6 1 51  
  6     10 0 30  
  10     5 0 53  
  5     1 0 30  
  1     1 0 7  
  1         3  
300             #print "Sub: $sub\n";
301              
302             # Now the from
303 4876         14158 $subname = "from_$key";
304 4876         13088 $sub = qq/ $p sub $subname { (\"$fhdr\", \$_[1]->{\"$key\"}); } $ep/;
305 4876     19 1 547120 eval "$sub";
  19     19 1 102  
  19     17 1 118  
  17     25 0 99  
  25     19 0 164  
  19     6 0 134  
  6     6 0 43  
  6     9 0 45  
  9     1 0 70  
  1     3 0 15  
  3     5 0 23  
  5     18 0 48  
  18     1 1 157  
  1     11 1 5  
  11     5 1 85  
  5     31 1 54  
  31     23 0 315  
  23     11 0 309  
  11     9 0 108  
  9     11 0 93  
  11     25 0 116  
  25     7 0 395  
  7     31 0 93  
  31     28 0 391  
  28     11 0 1484  
  11     13 0 153  
  13     20 0 216  
  20     19 0 315  
  19     14 0 317  
  14     17 1 217  
  17     24 1 305  
  24     18 1 478  
  18     11 0 398  
  11     8 0 210  
  8     5 0 184  
  5     23 0 104  
  23     4 0 487  
  4     10 0 83  
  10     12 0 264  
  12     2 0 253  
  2     7 0 34  
  7     15 0 179  
  15     2 0 399  
  2     3 0 45  
  3     11 0 59  
  11     5 0 246  
  5     11 1 108  
  11     4 1 316  
  4     17 1 98  
  17     26 1 459  
  26         606  
306             #print "Sub: $sub\n";
307              
308             }
309              
310             # and the CONSTANT mappings (only to_GENERIC_NAME)
311 512         3479 for my $key (keys %$const) {
312 793         1928 my $subname = "to_$key";
313 793         1872 my $val = $const->{$key};
314             # A method so no gain in using a null prototype
315 793         2282 my $sub = qq/ $p sub $subname { \"$val\" } $ep /;
316 793     15 1 69622 eval "$sub";
  15         337  
317             }
318              
319             # the null mappings
320 512 100       2301 if (defined $null) {
321 272         837 for my $key (@$null) {
322             # to generic
323 62         167 my $subname = "to_$key";
324 62         171 my $sub = qq/ $p sub $subname { } $ep /;
325 62         5422 eval "$sub";
326              
327             # to generic
328 62         251 $subname = "from_$key";
329 62         184 $sub = qq/ $p sub $subname { return (); } $ep /;
330 62         5610 eval "$sub";
331             }
332             }
333              
334             # the mappings that are unit mappings but from the end of a subheader
335             # group (eg ELEVATION_END)
336 512 100       2478 if (defined $endobs) {
337 30         140 for my $key (keys %$endobs) {
338              
339             # Get the original FITS header name
340 60         167 my $fhdr = $endobs->{$key};
341              
342             # print "Processing $key and $ohdr and $fhdr\n";
343              
344             # First generate the code to generate Generic headers
345 60         161 my $subname = "to_$key";
346 60         247 my $sub = qq/ $p sub $subname {
347             my \@allresults = \$_[0]->via_subheader_undef_check(\$_[1],\"$fhdr\");
348             return \$allresults[-1];
349             } $ep /;
350 60         8686 eval "$sub";
351             #print "Sub: $sub\n";
352              
353             # Now the from
354 60         210 $subname = "from_$key";
355 60         168 $sub = qq/ $p sub $subname { (\"$fhdr\", \$_[1]->{\"$key\"}); } $ep/;
356 60         6687 eval "$sub";
357             #print "Sub: $sub\n";
358              
359             }
360             }
361              
362             }
363              
364             =item B<nint>
365              
366             Return the nearest integer to a supplied floating point
367             value. 0.5 is rounded up.
368              
369             $int = Astro::FITS::HdrTrans->nint( $value );
370              
371             =cut
372              
373             sub nint {
374 36     36 1 562 my $class = shift;
375 28         259 my $value = shift;
376              
377 31 50       498 if ($value >= 0) {
378 25         297 return (int($value + 0.5));
379             } else {
380 15         110 return (int($value - 0.5));
381             }
382             }
383              
384             =item B<_parse_iso_date>
385              
386             Converts a UT date in form YYYY-MM-DDTHH:MM:SS.sss into a date
387             object (Time::Piece).
388              
389             $object = $trans->_parse_iso_date( $date );
390              
391             =cut
392              
393             sub _parse_iso_date {
394 83     85   283 my $self = shift;
395 76         256 my $datestr = shift;
396 84         308 my $return;
397 67 50       212 if (defined $datestr) {
398             # Not part of standard but we can deal with it
399 65         246 $datestr =~ s/Z//g;
400             # Time::Piece can not do fractional seconds. Should switch to DateTime
401 65         251 $datestr =~ s/\.\d+$//;
402             # parse
403 65         528 $return = Time::Piece->strptime( $datestr, "%Y-%m-%dT%T" );
404             }
405 65         3430 return $return;
406             }
407              
408             =item B<_parse_yyyymmdd_date>
409              
410             Converts a UT date in format YYYYMMDD into a date object.
411              
412             $ojbect = $trans->_parse_yyyymmdd_date( $date, $sep );
413              
414             Where $sep is the separator string and can be an empty string.
415             This allows 20090215, 2009-02-15 and 2009:02:15 to be parsed
416             by the same routine by using '', '-' and ':' respectively.
417              
418             =cut
419              
420             sub _parse_yyyymmdd_date {
421 8     31   20 my $self = shift;
422 8         27 my $datestr = shift;
423 8         17 my $sep = shift;
424 8 50       23 $sep = '' unless defined $sep;
425              
426             # OSX Leopard has a completely broken strptime that can not
427             # handle %Y%m%d. We need to change the string to make it
428             # into a parseable form (or switch to DateTime).
429 8 100       25 if (!$sep) {
430 1         4 $sep = "-";
431 1         7 $datestr = join($sep, substr($datestr,0,4),
432             substr($datestr,4,2),
433             substr($datestr,6));
434             }
435              
436 8         69 return Time::Piece->strptime( $datestr,join($sep,'%Y','%m','%d') );
437             }
438              
439             =item B<_add_seconds>
440              
441             Add the supplied number of seconds to the supplied time object
442             and return a new object.
443              
444             $new = $trans->_add_seconds( $base, $delta );
445              
446             =cut
447              
448             sub _add_seconds {
449 0     23   0 my $self = shift;
450 0         0 my $base = shift;
451 0         0 my $delta = shift;
452 0         0 return ($base + Time::Seconds->new( $delta ) );
453             }
454              
455             =item B<_utdate_to_object>
456              
457             Converts a UT date in YYYYMMDD format to a date object at midnight.
458              
459             $obj = $trans->_utdate_to_object( $YYYYMMDD );
460              
461             =cut
462              
463             sub _utdate_to_object {
464 0     8   0 my $self = shift;
465 0         0 my $utdate = shift;
466 0         0 my $year = substr($utdate, 0, 4);
467 0         0 my $month= substr($utdate, 4, 2);
468 0         0 my $day = substr($utdate, 6, 2);
469 0         0 my $basedate = $self->_parse_iso_date( $year."-".$month ."-".$day.
470             "T00:00:00");
471 0         0 return $basedate;
472             }
473              
474             =item B<cosdeg>
475              
476             Return the cosine of the angle. The angle must be in degrees.
477              
478             =cut
479              
480             sub cosdeg {
481 0     17 1 0 my $self = shift;
482 0         0 my $deg = shift;
483 0         0 cos( deg2rad($deg) );
484             }
485              
486             =item B<sindeg>
487              
488             Return the sine of the angle. The angle must be in degrees.
489              
490             =cut
491              
492             sub sindeg {
493 0     10 1 0 my $self = shift;
494 0         0 my $deg = shift;
495 0         0 sin( deg2rad($deg) );
496             }
497              
498             =item B<via_subheader>
499              
500             For the supplied FITS header item, first check the primary header
501             for existence, then check SUBHEADERS, then check "In" named subheaders.
502              
503             In scalar context returns the first value that matches.
504              
505             $value = $trans->via_subheader( $FITS_headers, $keyword );
506              
507             In list context returns all the available values in order.
508              
509             @values = $trans->via_subheader( $FITS_headers, $keyword );
510              
511             =cut
512              
513             sub via_subheader {
514 975     985 1 1650 my $self = shift;
515 975         1620 my $FITS_headers = shift;
516 975         1768 my $keyword = shift;
517              
518 975         1580 my @values;
519 975 100 100     4729 if (exists $FITS_headers->{$keyword}
    50 66        
    50 33        
520             && defined $FITS_headers->{$keyword}) {
521              
522 717 50       77087 if ( ref( $FITS_headers->{$keyword} ) eq 'ARRAY' ) {
523 0         0 @values = @{$FITS_headers->{$keyword}};
  0         0  
524             } else {
525 717         49428 push (@values,$FITS_headers->{$keyword});
526             }
527             } elsif ( $FITS_headers->{SUBHEADERS}
528             && exists $FITS_headers->{SUBHEADERS}->[0]->{$keyword}) {
529 0         0 my @subs = @{$FITS_headers->{SUBHEADERS}};
  0         0  
530 0         0 for my $s (@subs) {
531 0 0 0     0 if (exists $s->{$keyword} && defined $s->{$keyword}) {
532 0         0 push(@values, $s->{$keyword});
533             }
534             }
535             } elsif (exists $FITS_headers->{I1}
536             && exists $FITS_headers->{I1}->{$keyword}) {
537             # need to find out how many In we have
538 0         0 my $i = 1;
539 0         0 while (exists $FITS_headers->{"I$i"}) {
540 0         0 push(@values, $FITS_headers->{"I$i"}->{$keyword});
541 0         0 $i++;
542             }
543             }
544              
545 975 50       76062 return (wantarray ? @values : $values[0] );
546             }
547              
548             =item B<via_subheader_undef_check>
549              
550             Version of via_subheader that removes undefined values from the list before
551             returning the answer. Useful for SCUBA-2 where the first dark may not include
552             the TCS information.
553              
554             Same interface as via_subheader.
555              
556             =cut
557              
558             sub via_subheader_undef_check {
559 901     906 1 1754 my $self = shift;
560 901         2422 my @values = $self->via_subheader( @_ );
561              
562             # completely filter out undefs
563 901         1806 @values = grep { defined $_ } @values;
  665         2018  
564 901 100       4031 return (wantarray ? @values : $values[0] );
565             }
566              
567             =back
568              
569             =head1 PROTECTED IMPORTS
570              
571             Not all translation methods warrant a full blown inheritance. For
572             cases where one or two translation routines should be imported
573             (e.g. reading DATE-OBS FITS standard headers without importing the
574             additional FITS methods) a special import routine can be used when
575             using the class.
576              
577             use Astro::FITS::HdrTrans::FITS qw/ ROTATION /;
578              
579             This will load the from_ROTATION and to_ROTATION methods into
580             the namespace.
581              
582             =cut
583              
584             sub import {
585 35     36   101 my $class = shift;
586              
587             # this is where we are going to install the methods
588 35         111 my $callpkg = caller();
589              
590             # Prepend the from_ and to_ prefixes
591 35         63504 for my $key (@_) {
592             # The key can be fully specified with from_ and to_ already
593             # In that case we do not want to loop over from_ and to_
594 20         60 my @directions = qw/ from_ to_ /;
595 20 50 33     146 if ($key =~ /^from_/ || $key =~ /^to_/) {
596 0         0 @directions = ( '' ); # empty prefix
597             }
598              
599 20         46 for my $dir (@directions) {
600 40         94 my $method = $dir . $key;
601             #print "Importing method $method\n";
602 52     52   554 no strict 'refs';
  52         289  
  52         12852  
603              
604 40 50       88 if (!defined *{"$class\::$method"}) {
  40         217  
605 0         0 croak "Method $method is not available for export from class $class";
606             }
607              
608             # assign it
609 40         69 *{"$callpkg\::$method"} = \&{"$class\::$method"};
  40         11806  
  40         123  
610             }
611             }
612              
613             }
614              
615             =head1 WRITING A TRANSLATION CLASS
616              
617             In order to create a translation class for a new instrument it is
618             first necessary to work out the different types of translations that
619             are required; whether they are unit mappings (a simple change of
620             keyword but no change in value), constant mappings (a constant is
621             returned independently of the FITS header), mappings that already
622             exist in another class or complex mappings that have to be explicitly
623             coded.
624              
625             All translation classes must ultimately inherit from
626             C<Astro::FITS::HdrTrans::Base>.
627              
628             The first step in creation of a class is to handle the "can this class
629             translate the supplied headers" query that will be requested from
630             the C<Astro::FITS::HdrTrans> package. If the instrument name is present
631             in the standard "INSTRUME" FITS header then this can be achieved simply
632             by writing a C<this_instrument> method in the subclass that will return
633             the name of the instrument that can be translated. If a more complex
634             decision is required it will be necessary to subclass the C<can_translate>
635             method. This takes the headers that are to be translated (either in FITS
636             or generic form since the method is queried for either direction) and
637             returns a boolean indicating whether the class can be used.
638              
639             Once the class can declare it's translation instrument the next
640             step is to write the actual translation methods themselves. If any
641             unit- or constant-mappings are required they can be setup by defining
642             the %UNIT_MAP and %CONST_MAP (the names are unimportant) hashes
643             and calling the base class automated method constructor:
644              
645             __PACKAGE__->_generate_lookup_methods( \%CONST_MAP, \%UNIT_MAP );
646              
647             If your translations are very similar to an existing set of translations
648             you can inherit from that class instead of C<Astro::FITS::HdrTrans::Base>.
649             Multiple inheritance is supported if, for example, you need to
650             inherit from both the standard FITS translations (eg for DATE-OBS
651             processing) and from a more telescope-specific set of translations.
652              
653             If inheritance causes some erroneous mappings to leak through it is
654             possible to disable a specific mapping by specifying a @NULL_MAP
655             array to the method generation. This is an array of generic keywords.
656              
657             __PACKAGE__->_generate_lookup_methods( \%CONST_MAP, \%UNIT_MAP,
658             \@NULL_MAP );
659              
660             If a subset of translation methods are required from another class
661             but there is no desire to inherit the full set of methods then it
662             is possible to import specific translation methods from other classes.
663              
664             use Astro::FITS::HdrTrans::FITS qw/ UTSTART UTEND /;
665              
666             would import just the DATE-OBS and DATE-END handling functions from
667             the FITS class. Note that both the from- and to- translations will
668             be imported.
669              
670             At some point you may want to write your own more complex translations.
671             To do this you must write to- and from- methods. The API for all
672             the from_FITS translations is identical:
673              
674             $translation = $class->to_GENERIC_KEYWORD( \%fits_headers );
675              
676             ie given a reference to a hash of FITS headers (which can be
677             a tied C<Astro::FITS::Header> object), return a scalar value which
678             is the translated value.
679              
680             To convert from generic to FITS the interface is:
681              
682             %fits_subset = $class->from_GENERIC_KEYWORD( \%generic_header );
683              
684             ie multiple FITS keywords and values can be returned since in some
685             cases a single generic keyword is obtained by combining information
686             from multiple FITS headers.
687              
688             Finally, if this translation module is to be part of the
689             C<Astro::FITS::HdrTrans> distribution the default list of translation
690             classes must be updated in C<Astro::FITS::HdrTrans>. If this is to be
691             a runtime plugin, then the list of classes can be expanded at
692             runtime. For example, it should be possible for
693             C<Astro::FITS::HdrTrans::MyNewInst> to automatically append itself to
694             the list of known classes if the module is explicitly loaded by the
695             user (rather than dynamically loaded to test the headers).
696              
697             Some generic keywords actually return scalar objects. Any new instruments
698             must consistently return compatible objects. For example, UTDATE,
699             UTSTART and UTEND return (currently) Time::Piece objects.
700              
701             =head1 SEE ALSO
702              
703             C<Astro::FITS::HdrTrans>
704              
705             =head1 AUTHOR
706              
707             Tim Jenness E<lt>t.jenness@jach.hawaii.eduE<gt>
708              
709             =head1 COPYRIGHT
710              
711             Copyright (C) 2003-2005 Particle Physics and Astronomy Research Council.
712             All Rights Reserved.
713              
714             This program is free software; you can redistribute it and/or modify it under
715             the terms of the GNU General Public License as published by the Free Software
716             Foundation; either version 2 of the License, or (at your option) any later
717             version.
718              
719             This program is distributed in the hope that it will be useful,but WITHOUT ANY
720             WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
721             PARTICULAR PURPOSE. See the GNU General Public License for more details.
722              
723             You should have received a copy of the GNU General Public License along with
724             this program; if not, write to the Free Software Foundation, Inc., 59 Temple
725             Place,Suite 330, Boston, MA 02111-1307, USA
726              
727             =cut
728              
729             1;