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   4164389 use 5.006;
  53         228  
29 53     53   442 use strict;
  53         122  
  53         1966  
30 53     53   311 use warnings;
  53         173  
  53         3750  
31 53     53   425 use warnings::register;
  53         135  
  53         20746  
32 53     53   419 use Carp;
  53         116  
  53         5025  
33              
34 53     53   406 use vars qw/ $VERSION $DEBUG @ISA /;
  53         114  
  53         6232  
35              
36 53     53   928 use Exporter 'import';
  53         120  
  53         102156  
37             our @EXPORT_OK = qw( translate_from_FITS translate_to_FITS );
38              
39             $VERSION = '1.66';
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             OBSERVED_SIDEBAND
151             POLARIMETER
152             POLARIMETRY
153             PROJECT
154             RA_BASE
155             RA_SCALE
156             RA_SCALE_UNITS
157             RA_TELESCOPE_OFFSET
158             RECEIVER_TEMPERATURE
159             REFERENCE_LOCATION
160             REMOTE
161             REST_FREQUENCY
162             ROTATION
163             SAMPLE_MODE
164             SAMPLING
165             SCAN_INCREMENT
166             SCAN_PATTERN
167             SEEING
168             SHIFT_TYPE
169             SIDEBAND_MODE
170             SLIT_ANGLE
171             SLIT_NAME
172             SLIT_WIDTH
173             SPECIES
174             SPECTRUM_NUMBER
175             SPEED_GAIN
176             STANDARD
177             SUBSYSTEM_NUMBER
178             SUBSYSTEM_IDKEY
179             SURVEY
180             SWITCH_MODE
181             SYSTEM_TEMPERATURE
182             SYSTEM_VELOCITY
183             TAI_UTC_CORRECTION
184             TAU
185             TELESCOPE
186             TILE_NUMBER
187             TRACKING_SIDEBAND
188             TRANSITION
189             USER_AZIMUTH_CORRECTION
190             USER_ELEVATION_CORRECTION
191             UTDATE
192             UTEND
193             UTSTART
194             UT1_UTC_CORRECTION
195             VELOCITY
196             VELOCITY_REFERENCE_FRAME
197             VELOCITY_TYPE
198             WAVEPLATE_ANGLE
199             WIND_BLIND
200             XBINNING
201             YBINNING
202             X_APERTURE
203             Y_APERTURE
204             X_BASE
205             Y_BASE
206             X_OFFSET
207             Y_OFFSET
208             X_REFERENCE_PIXEL
209             Y_REFERENCE_PIXEL
210             X_REQUESTED
211             Y_REQUESTED
212             X_SCALE
213             Y_SCALE
214             X_DIM
215             Y_DIM
216             X_LOWER_BOUND
217             X_UPPER_BOUND
218             Y_LOWER_BOUND
219             Y_UPPER_BOUND
220             ZENITH_DISTANCE_START
221             ZENITH_DISTANCE_END
222             );
223              
224             sub generic_headers {
225 35     35 1 78 my $class = shift;
226 35         1209 return @generic_headers;
227             }
228              
229             =item B<translation_classes>
230              
231             Return the names of all the translation classes that will be
232             tried when translating a FITS header.
233              
234             @classes = Astro::FITS::HdrTrans->translation_classes();
235              
236             If arguments are supplied, the list of translation classes is
237             set to the supplied values.
238              
239             Astro::FITS::HdrTrans->translation_classes( @new );
240              
241             =cut
242              
243             sub translation_classes {
244 36     36 1 81 my $class = shift;
245 36 50       168 if (@_) {
246 0         0 @local_class_list = @_;
247             }
248 36         449 return @local_class_list;
249             }
250              
251             =item B<reset_translation_classes>
252              
253             Revert back to the reference list of translation classes.
254              
255             Astro::FITS::HdrTrans->reset_translation_classes;
256              
257             Useful if the list has been modified for a specific translation.
258              
259             =cut
260              
261             sub reset_classes {
262 0     0 0 0 my $class = shift;
263 0         0 @local_class_list = @REF_CLASS_LIST;
264             }
265              
266             =item B<push_class>
267              
268             Allows additional classes to be pushed on the list of valid
269             translation classes.
270              
271             Astro::FITS::HdrTrans->push_class( $class );
272              
273             The class[es] can be specified either as a list or a reference to
274             an array.
275              
276             =cut
277              
278             sub push_class {
279 2     2 1 2269 my $class = shift;
280 2         6 my @new = @_;
281              
282             # check for array ref
283 2 50       8 @new = ( ref($new[0]) ? @{ $new[0] } : @new );
  0         0  
284 2         4 push(@local_class_list, @new);
285 2         7 return @local_class_list;
286             }
287              
288             =back
289              
290             =head1 FUNCTIONS
291              
292             The following functions are available. They can be exported but are
293             not exported by default.
294              
295             =over 4
296              
297             =item B<translate_from_FITS>
298              
299             Converts a hash containing instrument-specific FITS headers into a hash
300             containing generic headers.
301              
302             %generic_headers = translate_from_FITS(\%FITS_headers,
303             class => \@classes,
304             prefix => 'ORAC_',
305             frameset => $frameset,
306             );
307              
308             This method takes a reference to a hash containing untranslated headers,
309             and a hash reference containing the following optional keys:
310              
311             =over 8
312              
313             =item *
314              
315             class - A reference to a list of subclasses to try to use for header
316             translations. This list overrides the default list. If left blank, the
317             default list will be used, as returned by the C<translation_classes>
318             method. This is sometimes required to break degeneracy when you know
319             you have a limited set of valid instruments.
320              
321             =item *
322              
323             frameset - An AST FrameSet describing the WCS. The WCS in this
324             FrameSet will override any WCS information contained in the FITS
325             headers.
326              
327             =item *
328              
329             prefix - A string prefix to add to the front of every translated header name.
330             For example, if this prefix is set to 'ORAC_', then the translated header
331             for the instrument value, whose key is normally 'INSTRUMENT', will have a
332             key named 'ORAC_INSTRUMENT'. The original keys will not be in the
333             returned hash. If left blank, no prefix will be added.
334              
335             =back
336              
337             This method returns a hash of generic headers. This function dies if
338             the header translation fails in any way.
339              
340             =cut
341              
342             sub translate_from_FITS {
343 20     20 1 951764 my $FITS_header = shift;
344 20         96 my %options = @_;
345              
346             # translation classes
347 20         46 my @classes;
348 20 0 33     175 if ( exists( $options{class} ) &&
      33        
349             defined( $options{class} ) &&
350             ref( $options{class} ) eq 'ARRAY' ) {
351 0         0 @classes = @{$options{class}};
  0         0  
352             }
353              
354 20         67 my $prefix;
355 20 100 66     98 if ( exists( $options{prefix} ) &&
356             defined( $options{prefix} ) ) {
357 1         5 $prefix = $options{prefix};
358             }
359              
360 20         60 my $frameset;
361 20 50 33     94 if ( exists( $options{frameset} ) &&
362             defined( $options{frameset} ) ) {
363 0         0 $frameset = $options{frameset};
364             }
365              
366             # determine which class can be used for the translation
367 20         88 my $class = determine_class( $FITS_header, \@classes, 1 );
368              
369             # we know this class is already loaded so do the translation
370 20         415 return $class->translate_from_FITS( $FITS_header,
371             prefix => $prefix,
372             frameset => $frameset );
373              
374             }
375              
376             =item B<translate_to_FITS>
377              
378             Converts a hash containing generic headers into one containing
379             instrument-specific FITS headers.
380              
381             %FITS_headers = translate_to_FITS(\%generic_headers,
382             class => \@classes,
383             );
384              
385             This method takes a reference to a hash containing untranslated
386             headers, and a hash reference containing the following optional
387             keys:
388              
389             =over 8
390              
391             =item *
392              
393             class - A reference to a list of subclasses to try to use for header
394             translations. This list overrides the default list. If left blank, the
395             default list will be used.
396              
397             =item *
398              
399             prefix - A string prefix to remove from the generic header key
400             before doing header translation. Why you would want to do this
401             is if you've used a prefix in the C<translate_from_FITS> call, and
402             want to translate back from the generic headers returned from
403             that method. If left blank, no prefix will be removed.
404              
405             =back
406              
407             This method returns a hash of instrument-specific headers. This
408             function dies if the header translation fails in any way.
409              
410             =cut
411              
412             sub translate_to_FITS {
413 15     15 1 39704 my $generic_header = shift;
414 15         51 my %options = @_;
415              
416 15         49 my @classes;
417 15 0 33     78 if ( exists( $options{class} ) &&
      33        
418             defined( $options{class} ) &&
419             ref( $options{class} ) eq 'ARRAY' ) {
420 0         0 @classes = @{$options{class}};
  0         0  
421             }
422              
423              
424 15         35 my $prefix;
425 15 100 66     75 if ( exists( $options{prefix} ) &&
426             defined( $options{prefix} ) ) {
427 1         5 $prefix = $options{prefix};
428             } else {
429 14         42 $prefix = '';
430             }
431              
432             # We need to strip off any prefix before figuring out what
433             # class we need to use.
434 15         71 my %stripped_header = clean_prefix( $generic_header, $prefix );
435              
436             # Check the UTSTART and UTEND headers to make sure they're
437             # Time::Piece objects.
438 15         124 for my $h (qw/ UTSTART UTEND / ) {
439 30 50 33     286 if ( exists( $stripped_header{$h} ) &&
      33        
440             defined( $stripped_header{$h} ) &&
441             ! UNIVERSAL::isa( $stripped_header{$h}, "Time::Piece" ) ) {
442 0         0 warnings::warnif( "Warning: $h generic header is not a Time::Piece object" );
443             }
444             }
445              
446             # determine which class can be used for the translation
447 15         69 my $class = determine_class( \%stripped_header, \@classes, 0 );
448              
449 15         164 return $class->translate_to_FITS( \%stripped_header );
450              
451             }
452              
453             =item B<determine_class>
454              
455             Determine which class should be used for the translation (either way).
456             It is given a reference to the header hash and a reference to an array
457             of classes which can be queried.
458              
459             $class = determine_class( \%hdr, \@classes, $fromfits );
460              
461             The classes are loaded for each test. Failure to load indicates failure
462             to translate. If the classes are undefined, the default internal list
463             will be used.
464              
465             The third argument is a boolean indicating whether the class is being
466             used to translate from FITS (true) or to FITS (false). This is used
467             for error message clarity.
468              
469             This function can be useful to allow a single header translation to be
470             calculated without requiring that all translation are performed. For example,
471              
472             $class = Astro::FITS::HdrTrans::determine_class( \%hdr, undef, 1 );
473             $value = $class->to_OBSERVATION_ID( \%hdr, $frameset );
474              
475             If the key _TRANSLATION_CLASS exists and this class allows translation
476             and no override classes have been specified, that class is returned
477             without checking all classes. This key is automatically filled in when
478             a translation from fits is executed.
479              
480             =cut
481              
482             sub determine_class {
483 36     36 1 99 my $hdr = shift;
484 36         82 my $classes = shift;
485 36         76 my $fromfits = shift;
486              
487             # Default classes if empty or undef
488 36         294 my @defclasses = __PACKAGE__->translation_classes;
489 36 50 66     266 if (!defined $classes || !@$classes) {
490             # see if we have an override
491 36 100 66     262 if (exists $hdr->{_TRANSLATION_CLASS} && defined $hdr->{_TRANSLATION_CLASS}) {
492 16         40 my $class = $hdr->{_TRANSLATION_CLASS};
493 16         1705 my $loaded = eval "require $class";
494 16 50       108 if ($loaded) {
495 16 50 33     217 if ($class->can("can_translate") && $class->can_translate($hdr) ) {
496 16         115 return $class;
497             }
498             }
499             }
500             # did not have an override so use defaults
501 20         353 $classes = \@defclasses;
502             }
503              
504             # Determine the class name so we can use the appropriate subclass
505             # for header translations. We're going to use the "can_translate" method
506             # in each subclass listed in @$classes.
507 20         64 my %result = ();
508 20         143 my $base = "Astro::FITS::HdrTrans::";
509 20         81 foreach my $subclass ( @$classes ) {
510              
511 660         4286 my $class = $base.$subclass;
512              
513 660 50       1703 print "Trying class $class\n" if $DEBUG;
514              
515             # Try a class and if it fails to load, skip
516 660         58018 eval "require $class";
517 660 50       3967 if ( $@ ) {
518 0 0       0 print "Error loading class $class: $@\n" if $DEBUG;
519             }
520 660 50       1947 next if ( $@ );
521 660 50       8701 if ( $class->can("can_translate") ) {
522 660 100       2700 if ( $class->can_translate( $hdr ) ) {
523 20 50       73 print "Class $class matches\n" if $DEBUG;
524 20         132 $result{$subclass}++;
525             }
526             } else {
527             # What to do, what to do?
528             }
529             }
530              
531 20 50       123 if ( ( scalar keys %result ) > 1 ) {
532 0         0 croak "Ambiguities in determining which header translations to use (".
533             join(",",keys %result).")";
534             }
535              
536 20 50       122 if ( ( scalar keys %result ) == 0 ) {
537             # We couldn't figure out which one to use.
538 0 0       0 croak "Unable to determine header translation subclass. No matches for these headers when trying to convert "
539             . ($fromfits ? 'from' : 'to' )
540             . " FITS using the following classes: ".join(", ",@$classes);
541             }
542              
543             # The class we wanted is the only key in the hash
544 20         80 my @matched = keys %result;
545 20         65 my $class = $base . $matched[0];
546              
547 20         196 return $class;
548             }
549              
550             =item B<clean_prefix>
551              
552             If a prefix has been used and a targetted conversion is required (which will not understand
553             the prefix) the prefix must first be removed. This function will remove the preifx, only
554             returning headers that contained the prefix.
555              
556             %cleaned = clean_prefix( \%header, $prefix );
557              
558             If prefix is an empty string or undefined, returns all headers.
559              
560             =cut
561              
562             sub clean_prefix {
563 16     16 1 2108 my $href = shift;
564 16         33 my $prefix = shift;
565 16 100       601 return %$href unless $prefix;
566              
567 2         5 my %stripped_header;
568 2         5 while ( my ( $key, $value ) = each( %{$href} ) ) {
  42         140  
569 40 100       251 if ($key eq '_TRANSLATION_CLASS') {
    100          
570             # this should be retained
571 2         6 $stripped_header{$key} = $value;
572             } elsif ($key =~ /^$prefix/) {
573             # only propagate keys that contain the prefix
574 36         958 $key =~ s/^$prefix//;
575 36         131 $stripped_header{$key} = $value;
576             }
577             }
578 2         41 return %stripped_header;
579             }
580              
581             =back
582              
583             =head1 NOTES
584              
585             Individual translations can be invoked explicitly if a class name is known.
586             The syntax for conversion from a FITS header to generic value is
587              
588             $result = $class->to_GENERIC_KEYWORD( \%header, $frameset );
589              
590             Frameset information (Starlink::AST object) is optional.
591              
592             The syntax for conversion from generic to FITS headers is:
593              
594             %fits = $class->from_GENERIC_KEYWORD( \%translated_hdr );
595              
596             Note that the conversion to FITS can result in multiple header items
597             and can require more than a single generic translated header item.
598              
599             If you are using a prefix, the general paradigm for converting a
600             translated header back to FITS is:
601              
602             my %cleaned = Astro::FITS::HdrTrans::clean_prefix( \%translated_hdr, $prefix );
603             my $class = Astro::FITS::HdrTrans::determine_class( \%cleaned, undef, 0 );
604             my %fits = $class->from_DR_RECIPE( \%cleaned );
605              
606              
607             =head1 AUTHOR
608              
609             Brad Cavanagh E<lt>b.cavanagh@jach.hawaii.eduE<gt>,
610             Tim Jenness E<lt>t.jenness@jach.hawaii.eduE<gt>,
611             Malcolm J. Currie E<lt>mjc@jach.hawaii.eduE<gt>
612              
613             =head1 COPYRIGHT
614              
615             Copyright (C) 2007-2009, 2013 Science and Technology Facilities Council.
616             Copyright (C) 2003-2007 Particle Physics and Astronomy Research Council.
617             All Rights Reserved.
618              
619             This program is free software; you can redistribute it and/or modify it under
620             the terms of the GNU General Public License as published by the Free Software
621             Foundation; either Version 2 of the License, or (at your option) any later
622             version.
623              
624             This program is distributed in the hope that it will be useful, but WITHOUT ANY
625             WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
626             PARTICULAR PURPOSE. See the GNU General Public License for more details.
627              
628             You should have received a copy of the GNU General Public License along with
629             this program; if not, write to the Free Software Foundation, Inc., 59 Temple
630             Place, Suite 330, Boston, MA 02111-1307, USA.
631              
632             =cut
633              
634             1;