File Coverage

blib/lib/Astro/FITS/HdrTrans.pm
Criterion Covered Total %
statement 94 107 87.8
branch 28 50 56.0
condition 16 36 44.4
subroutine 14 15 93.3
pod 7 8 87.5
total 159 216 73.6


line stmt bran cond sub pod time code
1             package Astro::FITS::HdrTrans;
2              
3             =head1 NAME
4              
5             Astro::FITS::HdrTrans - Translate FITS headers to standardised form
6              
7             =head1 SYNOPSIS
8              
9             use Astro::FITS::HdrTrans qw/ translate_from_FITS
10             translate_to_FITS /;
11              
12             %generic_headers = translate_from_FITS(\%FITS_headers,
13             frameset => $frameset);
14              
15             %FITS_headers = translate_to_FITS(\%generic_headers);
16              
17             @headers = Astro::FITS::HdrTrans->generic_headers();
18             @classes = Astro::FITS::HdrTrans->translation_classes();
19              
20             =head1 DESCRIPTION
21              
22             Converts information contained in instrument-specific FITS headers to
23             and from generic headers. A list of generic headers are given at the end
24             of the module documentation.
25              
26             =cut
27              
28 53     53   82161 use 5.006;
  53         292  
29 53     53   353 use strict;
  53         117  
  53         1426  
30 53     53   300 use warnings;
  53         124  
  53         2256  
31 53     53   461 use warnings::register;
  53         126  
  53         12439  
32 53     53   434 use Carp;
  53         117  
  53         3905  
33              
34 53     53   347 use vars qw/ $VERSION $DEBUG @ISA /;
  53         154  
  53         3795  
35              
36 53     53   362 use Exporter 'import';
  53         126  
  53         81676  
37             our @EXPORT_OK = qw( translate_from_FITS translate_to_FITS );
38              
39             $VERSION = '1.64';
40             $DEBUG = 0;
41              
42             # The reference list of classes we can try This list should be
43             # extended whenever new translation tables are added. They should
44             # have a corresponding Astro::FITS::HdrTrans:: module available Note
45             # that there are more perl modules in the distribution than are listed
46             # here. This is because some perl modules provide a base set of
47             # translations shared by multiple instruments.
48              
49             my @REF_CLASS_LIST = qw/ ACSIS IRCAM CGS4New CGS4Old UIST UFTI RxH3
50             JCMT_GSD JCMT_GSD_DB MICHELLE SCUBA SCUBA2 UKIRTDB WFCAM IRIS2 SOFI
51             ISAAC NACO INGRID GMOS SPEX NIRI ClassicCam CURVE LCOSBIG_CC
52             LCOSBIG_1m0 LCOSBIG_0m4 LCOSBIG_0m8 LCOSINISTRO_1m0
53             LCOFLOYDS LCOMEROPE LCOSPECTRAL LCOFLI_1m0/;
54              
55             # This is the actual list that is currently supported. It should always
56             # default to the reference list
57             my @local_class_list = @REF_CLASS_LIST;
58              
59             =head1 CLASS METHODS
60              
61             Some class methods are available
62              
63             =over 4
64              
65             =item B<generic_headers>
66              
67             Returns a list of all the generic headers that can in principal be
68             used for header translation. Note that not all the instruments support
69             all the headers.
70              
71             @hdrs = Astro::FITS::HdrTrans->generic_headers();
72              
73             =cut
74              
75             my @generic_headers = qw(
76             AIRMASS_START
77             AIRMASS_END
78             ALTITUDE
79             AMBIENT_TEMPERATURE
80             AZIMUTH_START
81             AZIMUTH_END
82             BACKEND
83             BACKEND_SECTIONS
84             BANDWIDTH_MODE
85             BOLOMETERS
86             CAMERA
87             CAMERA_NUMBER
88             CHOP_ANGLE
89             CHOP_COORDINATE_SYSTEM
90             CHOP_FREQUENCY
91             CHOP_THROW
92             CONFIGURATION_INDEX
93             COORDINATE_SYSTEM
94             COORDINATE_UNITS
95             COORDINATE_TYPE
96             CYCLE_LENGTH
97             DATA_UNITS
98             DEC_BASE
99             DEC_SCALE
100             DEC_SCALE_UNITS
101             DEC_TELESCOPE_OFFSET
102             DETECTOR_BIAS
103             DETECTOR_INDEX
104             DETECTOR_READ_TYPE
105             DOME_OPEN
106             DR_GROUP
107             DR_RECIPE
108             ELEVATION_START
109             ELEVATION_END
110             EPOCH
111             EQUINOX
112             EXPOSURE_TIME
113             FILE_FORMAT
114             FILENAME
115             FILTER
116             FRONTEND
117             FOURIER_TRANSFORM_SPECTROMETER
118             GAIN
119             GALACTIC_LATITUDE
120             GALACTIC_LONGITUDE
121             GRATING_DISPERSION
122             GRATING_NAME
123             GRATING_ORDER
124             GRATING_WAVELENGTH
125             HUMIDITY
126             INSTRUMENT
127             INST_DHS
128             LATITUDE
129             LONGITUDE
130             MSBID
131             MSB_TRANSACTION_ID
132             NSCAN_POSITIONS
133             NUMBER_OF_COADDS
134             NUMBER_OF_CYCLES
135             NUMBER_OF_DETECTORS
136             NUMBER_OF_EXPOSURES
137             NUMBER_OF_FREQUENCIES
138             NUMBER_OF_JITTER_POSITIONS
139             NUMBER_OF_MICROSTEP_POSITIONS
140             NUMBER_OF_OFFSETS
141             NUMBER_OF_READS
142             NUMBER_OF_SUBFRAMES
143             NUMBER_OF_SUBSCANS
144             OBJECT
145             OBSERVATION_ID
146             OBSERVATION_ID_SUBSYSTEM
147             OBSERVATION_MODE
148             OBSERVATION_NUMBER
149             OBSERVATION_TYPE
150             POLARIMETER
151             POLARIMETRY
152             PROJECT
153             RA_BASE
154             RA_SCALE
155             RA_SCALE_UNITS
156             RA_TELESCOPE_OFFSET
157             RECEIVER_TEMPERATURE
158             REFERENCE_LOCATION
159             REMOTE
160             REST_FREQUENCY
161             ROTATION
162             SAMPLE_MODE
163             SAMPLING
164             SCAN_INCREMENT
165             SCAN_PATTERN
166             SEEING
167             SHIFT_TYPE
168             SLIT_ANGLE
169             SLIT_NAME
170             SLIT_WIDTH
171             SPECIES
172             SPEED_GAIN
173             STANDARD
174             SUBSYSTEM_IDKEY
175             SURVEY
176             SWITCH_MODE
177             SYSTEM_TEMPERATURE
178             SYSTEM_VELOCITY
179             TAI_UTC_CORRECTION
180             TAU
181             TELESCOPE
182             TILE_NUMBER
183             TRANSITION
184             USER_AZIMUTH_CORRECTION
185             USER_ELEVATION_CORRECTION
186             UTDATE
187             UTEND
188             UTSTART
189             UT1_UTC_CORRECTION
190             VELOCITY
191             VELOCITY_REFERENCE_FRAME
192             VELOCITY_TYPE
193             WAVEPLATE_ANGLE
194             WIND_BLIND
195             XBINNING
196             YBINNING
197             X_APERTURE
198             Y_APERTURE
199             X_BASE
200             Y_BASE
201             X_OFFSET
202             Y_OFFSET
203             X_REFERENCE_PIXEL
204             Y_REFERENCE_PIXEL
205             X_REQUESTED
206             Y_REQUESTED
207             X_SCALE
208             Y_SCALE
209             X_DIM
210             Y_DIM
211             X_LOWER_BOUND
212             X_UPPER_BOUND
213             Y_LOWER_BOUND
214             Y_UPPER_BOUND
215             ZENITH_DISTANCE_START
216             ZENITH_DISTANCE_END
217             );
218              
219             sub generic_headers {
220 35     35 1 92 my $class = shift;
221 35         707 return @generic_headers;
222             }
223              
224             =item B<translation_classes>
225              
226             Return the names of all the translation classes that will be
227             tried when translating a FITS header.
228              
229             @classes = Astro::FITS::HdrTrans->translation_classes();
230              
231             If arguments are supplied, the list of translation classes is
232             set to the supplied values.
233              
234             Astro::FITS::HdrTrans->translation_classes( @new );
235              
236             =cut
237              
238             sub translation_classes {
239 36     36 1 81 my $class = shift;
240 36 50       142 if (@_) {
241 0         0 @local_class_list = @_;
242             }
243 36         333 return @local_class_list;
244             }
245              
246             =item B<reset_translation_classes>
247              
248             Revert back to the reference list of translation classes.
249              
250             Astro::FITS::HdrTrans->reset_translation_classes;
251              
252             Useful if the list has been modified for a specific translation.
253              
254             =cut
255              
256             sub reset_classes {
257 0     0 0 0 my $class = shift;
258 0         0 @local_class_list = @REF_CLASS_LIST;
259             }
260              
261             =item B<push_class>
262              
263             Allows additional classes to be pushed on the list of valid
264             translation classes.
265              
266             Astro::FITS::HdrTrans->push_class( $class );
267              
268             The class[es] can be specified either as a list or a reference to
269             an array.
270              
271             =cut
272              
273             sub push_class {
274 2     2 1 897 my $class = shift;
275 2         4 my @new = @_;
276              
277             # check for array ref
278 2 50       8 @new = ( ref($new[0]) ? @{ $new[0] } : @new );
  0         0  
279 2         3 push(@local_class_list, @new);
280 2         5 return @local_class_list;
281             }
282              
283             =back
284              
285             =head1 FUNCTIONS
286              
287             The following functions are available. They can be exported but are
288             not exported by default.
289              
290             =over 4
291              
292             =item B<translate_from_FITS>
293              
294             Converts a hash containing instrument-specific FITS headers into a hash
295             containing generic headers.
296              
297             %generic_headers = translate_from_FITS(\%FITS_headers,
298             class => \@classes,
299             prefix => 'ORAC_',
300             frameset => $frameset,
301             );
302              
303             This method takes a reference to a hash containing untranslated headers,
304             and a hash reference containing the following optional keys:
305              
306             =over 8
307              
308             =item *
309              
310             class - A reference to a list of subclasses to try to use for header
311             translations. This list overrides the default list. If left blank, the
312             default list will be used, as returned by the C<translation_classes>
313             method. This is sometimes required to break degeneracy when you know
314             you have a limited set of valid instruments.
315              
316             =item *
317              
318             frameset - An AST FrameSet describing the WCS. The WCS in this
319             FrameSet will override any WCS information contained in the FITS
320             headers.
321              
322             =item *
323              
324             prefix - A string prefix to add to the front of every translated header name.
325             For example, if this prefix is set to 'ORAC_', then the translated header
326             for the instrument value, whose key is normally 'INSTRUMENT', will have a
327             key named 'ORAC_INSTRUMENT'. The original keys will not be in the
328             returned hash. If left blank, no prefix will be added.
329              
330             =back
331              
332             This method returns a hash of generic headers. This function dies if
333             the header translation fails in any way.
334              
335             =cut
336              
337             sub translate_from_FITS {
338 20     20 1 731968 my $FITS_header = shift;
339 20         69 my %options = @_;
340              
341             # translation classes
342 20         48 my @classes;
343 20 0 33     121 if ( exists( $options{class} ) &&
      33        
344             defined( $options{class} ) &&
345             ref( $options{class} ) eq 'ARRAY' ) {
346 0         0 @classes = @{$options{class}};
  0         0  
347             }
348              
349 20         49 my $prefix;
350 20 100 66     92 if ( exists( $options{prefix} ) &&
351             defined( $options{prefix} ) ) {
352 1         4 $prefix = $options{prefix};
353             }
354              
355 20         50 my $frameset;
356 20 50 33     82 if ( exists( $options{frameset} ) &&
357             defined( $options{frameset} ) ) {
358 0         0 $frameset = $options{frameset};
359             }
360              
361             # determine which class can be used for the translation
362 20         82 my $class = determine_class( $FITS_header, \@classes, 1 );
363              
364             # we know this class is already loaded so do the translation
365 20         329 return $class->translate_from_FITS( $FITS_header,
366             prefix => $prefix,
367             frameset => $frameset );
368              
369             }
370              
371             =item B<translate_to_FITS>
372              
373             Converts a hash containing generic headers into one containing
374             instrument-specific FITS headers.
375              
376             %FITS_headers = translate_to_FITS(\%generic_headers,
377             class => \@classes,
378             );
379              
380             This method takes a reference to a hash containing untranslated
381             headers, and a hash reference containing the following optional
382             keys:
383              
384             =over 8
385              
386             =item *
387              
388             class - A reference to a list of subclasses to try to use for header
389             translations. This list overrides the default list. If left blank, the
390             default list will be used.
391              
392             =item *
393              
394             prefix - A string prefix to remove from the generic header key
395             before doing header translation. Why you would want to do this
396             is if you've used a prefix in the C<translate_from_FITS> call, and
397             want to translate back from the generic headers returned from
398             that method. If left blank, no prefix will be removed.
399              
400             =back
401              
402             This method returns a hash of instrument-specific headers. This
403             function dies if the header translation fails in any way.
404              
405             =cut
406              
407             sub translate_to_FITS {
408 15     15 1 24517 my $generic_header = shift;
409 15         48 my %options = @_;
410              
411 15         33 my @classes;
412 15 0 33     82 if ( exists( $options{class} ) &&
      33        
413             defined( $options{class} ) &&
414             ref( $options{class} ) eq 'ARRAY' ) {
415 0         0 @classes = @{$options{class}};
  0         0  
416             }
417              
418              
419 15         29 my $prefix;
420 15 100 66     74 if ( exists( $options{prefix} ) &&
421             defined( $options{prefix} ) ) {
422 1         3 $prefix = $options{prefix};
423             } else {
424 14         37 $prefix = '';
425             }
426              
427             # We need to strip off any prefix before figuring out what
428             # class we need to use.
429 15         59 my %stripped_header = clean_prefix( $generic_header, $prefix );
430              
431             # Check the UTSTART and UTEND headers to make sure they're
432             # Time::Piece objects.
433 15         90 for my $h (qw/ UTSTART UTEND / ) {
434 30 50 33     285 if ( exists( $stripped_header{$h} ) &&
      33        
435             defined( $stripped_header{$h} ) &&
436             ! UNIVERSAL::isa( $stripped_header{$h}, "Time::Piece" ) ) {
437 0         0 warnings::warnif( "Warning: $h generic header is not a Time::Piece object" );
438             }
439             }
440              
441             # determine which class can be used for the translation
442 15         68 my $class = determine_class( \%stripped_header, \@classes, 0 );
443              
444 15         137 return $class->translate_to_FITS( \%stripped_header );
445              
446             }
447              
448             =item B<determine_class>
449              
450             Determine which class should be used for the translation (either way).
451             It is given a reference to the header hash and a reference to an array
452             of classes which can be queried.
453              
454             $class = determine_class( \%hdr, \@classes, $fromfits );
455              
456             The classes are loaded for each test. Failure to load indicates failure
457             to translate. If the classes are undefined, the default internal list
458             will be used.
459              
460             The third argument is a boolean indicating whether the class is being
461             used to translate from FITS (true) or to FITS (false). This is used
462             for error message clarity.
463              
464             This function can be useful to allow a single header translation to be
465             calculated without requiring that all translation are performed. For example,
466              
467             $class = Astro::FITS::HdrTrans::determine_class( \%hdr, undef, 1 );
468             $value = $class->to_OBSERVATION_ID( \%hdr, $frameset );
469              
470             If the key _TRANSLATION_CLASS exists and this class allows translation
471             and no override classes have been specified, that class is returned
472             without checking all classes. This key is automatically filled in when
473             a translation from fits is executed.
474              
475             =cut
476              
477             sub determine_class {
478 36     36 1 256 my $hdr = shift;
479 36         71 my $classes = shift;
480 36         79 my $fromfits = shift;
481              
482             # Default classes if empty or undef
483 36         235 my @defclasses = __PACKAGE__->translation_classes;
484 36 50 66     258 if (!defined $classes || !@$classes) {
485             # see if we have an override
486 36 100 66     255 if (exists $hdr->{_TRANSLATION_CLASS} && defined $hdr->{_TRANSLATION_CLASS}) {
487 16         38 my $class = $hdr->{_TRANSLATION_CLASS};
488 16         1278 my $loaded = eval "require $class";
489 16 50       91 if ($loaded) {
490 16 50 33     195 if ($class->can("can_translate") && $class->can_translate($hdr) ) {
491 16         93 return $class;
492             }
493             }
494             }
495             # did not have an override so use defaults
496 20         340 $classes = \@defclasses;
497             }
498              
499             # Determine the class name so we can use the appropriate subclass
500             # for header translations. We're going to use the "can_translate" method
501             # in each subclass listed in @$classes.
502 20         57 my %result = ();
503 20         48 my $base = "Astro::FITS::HdrTrans::";
504 20         62 foreach my $subclass ( @$classes ) {
505              
506 660         3746 my $class = $base.$subclass;
507              
508 660 50       1398 print "Trying class $class\n" if $DEBUG;
509              
510             # Try a class and if it fails to load, skip
511 660         36469 eval "require $class";
512 660 50       2790 if ( $@ ) {
513 0 0       0 print "Error loading class $class: $@\n" if $DEBUG;
514             }
515 660 50       1498 next if ( $@ );
516 660 50       6122 if ( $class->can("can_translate") ) {
517 660 100       2369 if ( $class->can_translate( $hdr ) ) {
518 20 50       107 print "Class $class matches\n" if $DEBUG;
519 20         106 $result{$subclass}++;
520             }
521             } else {
522             # What to do, what to do?
523             }
524             }
525              
526 20 50       113 if ( ( scalar keys %result ) > 1 ) {
527 0         0 croak "Ambiguities in determining which header translations to use (".
528             join(",",keys %result).")";
529             }
530              
531 20 50       80 if ( ( scalar keys %result ) == 0 ) {
532             # We couldn't figure out which one to use.
533 0 0       0 croak "Unable to determine header translation subclass. No matches for these headers when trying to convert "
534             . ($fromfits ? 'from' : 'to' )
535             . " FITS using the following classes: ".join(", ",@$classes);
536             }
537              
538             # The class we wanted is the only key in the hash
539 20         91 my @matched = keys %result;
540 20         66 my $class = $base . $matched[0];
541              
542 20         151 return $class;
543             }
544              
545             =item B<clean_prefix>
546              
547             If a prefix has been used and a targetted conversion is required (which will not understand
548             the prefix) the prefix must first be removed. This function will remove the preifx, only
549             returning headers that contained the prefix.
550              
551             %cleaned = clean_prefix( \%header, $prefix );
552              
553             If prefix is an empty string or undefined, returns all headers.
554              
555             =cut
556              
557             sub clean_prefix {
558 16     16 1 1312 my $href = shift;
559 16         31 my $prefix = shift;
560 16 100       502 return %$href unless $prefix;
561              
562 2         4 my %stripped_header;
563 2         4 while ( my ( $key, $value ) = each( %{$href} ) ) {
  42         118  
564 40 100       173 if ($key eq '_TRANSLATION_CLASS') {
    100          
565             # this should be retained
566 2         6 $stripped_header{$key} = $value;
567             } elsif ($key =~ /^$prefix/) {
568             # only propagate keys that contain the prefix
569 36         114 $key =~ s/^$prefix//;
570 36         97 $stripped_header{$key} = $value;
571             }
572             }
573 2         24 return %stripped_header;
574             }
575              
576             =back
577              
578             =head1 NOTES
579              
580             Individual translations can be invoked explicitly if a class name is known.
581             The syntax for conversion from a FITS header to generic value is
582              
583             $result = $class->to_GENERIC_KEYWORD( \%header, $frameset );
584              
585             Frameset information (Starlink::AST object) is optional.
586              
587             The syntax for conversion from generic to FITS headers is:
588              
589             %fits = $class->from_GENERIC_KEYWORD( \%translated_hdr );
590              
591             Note that the conversion to FITS can result in multiple header items
592             and can require more than a single generic translated header item.
593              
594             If you are using a prefix, the general paradigm for converting a
595             translated header back to FITS is:
596              
597             my %cleaned = Astro::FITS::HdrTrans::clean_prefix( \%translated_hdr, $prefix );
598             my $class = Astro::FITS::HdrTrans::determine_class( \%cleaned, undef, 0 );
599             my %fits = $class->from_DR_RECIPE( \%cleaned );
600              
601              
602             =head1 AUTHOR
603              
604             Brad Cavanagh E<lt>b.cavanagh@jach.hawaii.eduE<gt>,
605             Tim Jenness E<lt>t.jenness@jach.hawaii.eduE<gt>,
606             Malcolm J. Currie E<lt>mjc@jach.hawaii.eduE<gt>
607              
608             =head1 COPYRIGHT
609              
610             Copyright (C) 2007-2009, 2013 Science and Technology Facilities Council.
611             Copyright (C) 2003-2007 Particle Physics and Astronomy Research Council.
612             All Rights Reserved.
613              
614             This program is free software; you can redistribute it and/or modify it under
615             the terms of the GNU General Public License as published by the Free Software
616             Foundation; either Version 2 of the License, or (at your option) any later
617             version.
618              
619             This program is distributed in the hope that it will be useful, but WITHOUT ANY
620             WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
621             PARTICULAR PURPOSE. See the GNU General Public License for more details.
622              
623             You should have received a copy of the GNU General Public License along with
624             this program; if not, write to the Free Software Foundation, Inc., 59 Temple
625             Place, Suite 330, Boston, MA 02111-1307, USA.
626              
627             =cut
628              
629             1;