File Coverage

blib/lib/Astro/FITS/HdrTrans/SCUBA.pm
Criterion Covered Total %
statement 132 157 84.0
branch 30 76 39.4
condition 18 60 30.0
subroutine 22 22 100.0
pod 16 16 100.0
total 218 331 65.8


line stmt bran cond sub pod time code
1              
2             =head1 NAME
3              
4             Astro::FITS::HdrTrans::SCUBA - JCMT SCUBA translations
5              
6             =head1 DESCRIPTION
7              
8             Converts information contained in SCUBA FITS headers to and from
9             generic headers. See Astro::FITS::HdrTrans for a list of generic
10             headers.
11              
12             =cut
13              
14             use 5.006;
15 10     10   11168351 use warnings;
  10         36  
16 10     10   44 use strict;
  10         22  
  10         279  
17 10     10   39 use Carp;
  10         23  
  10         199  
18 10     10   37  
  10         16  
  10         667  
19             # Inherit from Base
20             use base qw/ Astro::FITS::HdrTrans::JAC /;
21 10     10   58  
  10         12  
  10         1536  
22             use vars qw/ $VERSION /;
23 10     10   51  
  10         22  
  10         15184  
24             $VERSION = "1.65";
25              
26             # for a constant mapping, there is no FITS header, just a generic
27             # header that is constant
28             my %CONST_MAP = (
29             COORDINATE_UNITS => 'sexagesimal',
30             INSTRUMENT => "SCUBA",
31             INST_DHS => 'SCUBA_SCUBA',
32             NUMBER_OF_OFFSETS => 1,
33             ROTATION => 0,
34             SLIT_ANGLE => 0,
35             SPEED_GAIN => 'normal',
36             TELESCOPE => 'JCMT',
37             );
38              
39             # NULL mappings used to override base class implementations
40             my @NULL_MAP = qw/ DETECTOR_INDEX WAVEPLATE_ANGLE /;
41              
42             # unit mapping implies that the value propogates directly
43             # to the output with only a keyword name change
44              
45             my %UNIT_MAP = (
46             AIRMASS_START => "AMSTART",
47             AIRMASS_END => "AMEND",
48             BOLOMETERS => "BOLOMS",
49             CHOP_ANGLE => "CHOP_PA",
50             CHOP_THROW => "CHOP_THR",
51             DEC_BASE => "LAT",
52             DEC_TELESCOPE_OFFSET => "MAP_Y",
53             DETECTOR_READ_TYPE => "MODE",
54             DR_RECIPE => "DRRECIPE",
55             FILENAME => "SDFFILE",
56             FILTER => "FILTER",
57             GAIN => "GAIN",
58             NUMBER_OF_EXPOSURES => "EXP_NO",
59             OBJECT => "OBJECT",
60             OBSERVATION_NUMBER => "RUN",
61             POLARIMETER => "POL_CONN",
62             PROJECT => "PROJ_ID",
63             RA_TELESCOPE_OFFSET => "MAP_X",
64             SCAN_INCREMENT => "SAM_DX",
65             SEEING => "SEEING",
66             STANDARD => "STANDARD",
67             TAU => "TAU_225",
68             X_BASE => "LONG",
69             Y_BASE => "LAT",
70             X_OFFSET => "MAP_X",
71             Y_OFFSET => "MAP_Y"
72             );
73              
74              
75             # Create the translation methods
76             __PACKAGE__->_generate_lookup_methods( \%CONST_MAP, \%UNIT_MAP, \@NULL_MAP );
77              
78             =head1 METHODS
79              
80             =over 4
81              
82             =item B<this_instrument>
83              
84             The name of the instrument required to match (case insensitively)
85             against the INSTRUME/INSTRUMENT keyword to allow this class to
86             translate the specified headers. Called by the default
87             C<can_translate> method.
88              
89             $inst = $class->this_instrument();
90              
91             Returns "SCUBA".
92              
93             =cut
94              
95             return "SCUBA";
96             }
97 21     21 1 52  
98             =item B<can_translate>
99              
100             The database tables do not include an instrument field so we need to determine
101             suitability by looking at other fields instead of using the base implementation.
102              
103             $cando = $class->can_translate( \%hdrs );
104              
105             For SCUBA we first check for BOLOMS and SCU# headers and then use the base
106             implementation that will look at the INSTRUME field.
107              
108             =cut
109              
110             my $self = shift;
111             my $headers = shift;
112              
113 21     21 1 51 if (exists $headers->{BOLOMS} && defined $headers->{BOLOMS} &&
114 21         93 exists $headers->{"SCU#"} && defined $headers->{"SCU#"}) {
115             return 1;
116 21 50 66     113 } else {
      66        
      33        
117             return $self->SUPER::can_translate( $headers );
118 0         0 }
119             }
120 21         588  
121             =back
122              
123             =head1 COMPLEX CONVERSIONS
124              
125             These methods are more complicated than a simple mapping. We have to
126             provide both from- and to-FITS conversions All these routines are
127             methods and the to_ routines all take a reference to a hash and return
128             the translated value (a many-to-one mapping) The from_ methods take a
129             reference to a generic hash and return a translated hash (sometimes
130             these are many-to-many)
131              
132             =over 4
133              
134             =item B<to_CHOP_COORDINATE_SYSTEM>
135              
136             Uses the C<CHOP_CRD> FITS header to determine the chopper coordinate
137             system, and then places that coordinate type in the C<CHOP_COORDINATE_SYSTEM>
138             generic header.
139              
140             A FITS header value of 'LO' translates to 'Tracking', 'AZ' translates to
141             'Alt/Az', and 'NA' translates to 'Focal Plane'. Any other values will return
142             undef.
143              
144             =cut
145              
146             my $self = shift;
147             my $FITS_headers = shift;
148             my $return;
149              
150 2     2 1 7 if (exists($FITS_headers->{'CHOP_CRD'})) {
151 2         4 my $fits_eq = $FITS_headers->{'CHOP_CRD'};
152 2         5 if ( $fits_eq =~ /LO/i ) {
153             $return = "Tracking";
154 2 50       9 } elsif ( $fits_eq =~ /AZ/i ) {
155 2         48 $return = "Alt/Az";
156 2 50       122 } elsif ( $fits_eq =~ /NA/i ) {
    50          
    0          
157 0         0 $return = "Focal Plane";
158             }
159 2         23 }
160             return $return;
161 0         0 }
162              
163             =item B<to_COORDINATE_TYPE>
164 2         15  
165             Uses the C<CENT_CRD> FITS header to determine the coordinate type
166             (galactic, B1950, J2000) and then places that coordinate type in
167             the C<COORDINATE_TYPE> generic header.
168              
169             =cut
170              
171             my $self = shift;
172             my $FITS_headers = shift;
173             my $return;
174             if (exists($FITS_headers->{'CENT_CRD'})) {
175             my $fits_eq = $FITS_headers->{'CENT_CRD'};
176 2     2 1 4 if ( $fits_eq =~ /RB/i ) {
177 2         5 $return = "B1950";
178 2         4 } elsif ( $fits_eq =~ /RJ/i ) {
179 2 50       6 $return = "J2000";
180 2         57 } elsif ( $fits_eq =~ /AZ/i ) {
181 2 50       126 $return = "galactic";
    50          
    50          
    50          
182 0         0 } elsif ( $fits_eq =~ /planet/i ) {
183             $return = "planet";
184 0         0 }
185             }
186 0         0 return $return;
187             }
188 2         5  
189             =item B<to_EQUINOX>
190              
191 2         7 Translates EQUINOX header into valid equinox value. The following
192             translation is done:
193              
194             =over 4
195              
196             =item * RB => 1950
197              
198             =item * RJ => 2000
199              
200             =item * RD => current
201              
202             =item * AZ => AZ/EL
203              
204             =back
205              
206             =cut
207              
208             my $self = shift;
209             my $FITS_headers = shift;
210             my $return;
211             if (exists($FITS_headers->{'CENT_CRD'})) {
212             my $fits_eq = $FITS_headers->{'CENT_CRD'};
213             if ( $fits_eq =~ /RB/i ) {
214 2     2 1 5 $return = "1950";
215 2         6 } elsif ( $fits_eq =~ /RJ/i ) {
216 2         4 $return = "2000";
217 2 50       7 } elsif ( $fits_eq =~ /RD/i ) {
218 2         45 $return = "current";
219 2 50       152 } elsif ( $fits_eq =~ /PLANET/i ) {
    50          
    50          
    50          
    0          
220 0         0 $return = "planet";
221             } elsif ( $fits_eq =~ /AZ/i ) {
222 0         0 $return = "AZ/EL";
223             }
224 0         0 }
225             return $return;
226 2         9 }
227              
228 0         0 =item B<from_EQUINOX>
229              
230             Translates generic C<EQUINOX> values into SCUBA FITS
231 2         7 equinox values for the C<CENT_CRD> header.
232              
233             =cut
234              
235             my $self = shift;
236             my $generic_headers = shift;
237             my %return_hash;
238             my $return;
239             if (exists($generic_headers->{EQUINOX}) &&
240             defined $generic_headers->{EQUINOX}) {
241             my $equinox = $generic_headers->{EQUINOX};
242 1     1 1 3 if ( $equinox =~ /1950/ ) {
243 1         2 $return = 'RB';
244 1         8 } elsif ( $equinox =~ /2000/ ) {
245             $return = 'RJ';
246 1 50 33     14 } elsif ( $equinox =~ /current/ ) {
247             $return = 'RD';
248 1         3 } elsif ( $equinox =~ /planet/ ) {
249 1 50       12 $return = 'PLANET';
    50          
    50          
    50          
    0          
250 0         0 } elsif ( $equinox =~ /AZ\/EL/ ) {
251             $return = 'AZ';
252 0         0 } else {
253             $return = $equinox;
254 0         0 }
255             }
256 1         2 $return_hash{'CENT_CRD'} = $return;
257             return %return_hash;
258 0         0 }
259              
260 0         0 =item B<to_OBSERVATION_MODE>
261              
262             Returns C<photometry> if the FITS header value for C<MODE>
263 1         4 is C<PHOTOM>, otherwise returns C<imaging>.
264 1         9  
265             =cut
266              
267             my $self = shift;
268             my $FITS_headers = shift;
269             my $return;
270             if ( defined( $FITS_headers->{'MODE'} ) &&
271             $FITS_headers->{'MODE'} =~ /PHOTOM/i ) {
272             $return = "photometry";
273             } else {
274             $return = "imaging";
275 2     2 1 7 }
276 2         4 return $return;
277 2         5 }
278 2 50 33     9  
279             =item B<to_OBSERVATION_TYPE>
280 0         0  
281             Converts the observation type. If the FITS header is equal to
282 2         210 C<PHOTOM>, C<MAP>, C<POLPHOT>, or C<POLMAP>, then the generic
283             header value is C<OBJECT>. Else, the FITS header value is
284 2         8 copied directly to the generic header value.
285              
286             =cut
287              
288             my $self = shift;
289             my $FITS_headers = shift;
290             my $return;
291             my $mode = $FITS_headers->{'MODE'};
292             if ( defined( $mode ) && $mode =~ /PHOTOM|MAP|POLPHOT|POLMAP/i) {
293             $return = "OBJECT";
294             } else {
295             $return = $mode;
296             }
297 2     2 1 5 return $return;
298 2         4 }
299 2         4  
300 2         10 =item B<to_POLARIMETRY>
301 2 50 33     113  
302 0         0 Sets the C<POLARIMETRY> generic header to 'true' if the
303             value for the FITS header C<MODE> is 'POLMAP' or 'POLPHOT',
304 2         6 otherwise sets it to 'false'.
305              
306 2         4 =cut
307              
308             my $self = shift;
309             my $FITS_headers = shift;
310             my $return;
311             my $mode = $FITS_headers->{'MODE'};
312             if (defined( $mode ) && $mode =~ /POLMAP|POLPHOT/i) {
313             $return = 1;
314             } else {
315             $return = 0;
316             }
317             return $return;
318 2     2 1 5 }
319 2         6  
320 2         4 =item B<to_UTDATE>
321 2         8  
322 2 50 33     102 Converts either the C<UTDATE> or C<DATE> header into a C<Time::Piece> object.
323 0         0  
324             =cut
325 2         4  
326             my $self = shift;
327 2         23 my $FITS_headers = shift;
328             my $return;
329             if ( exists( $FITS_headers->{'UTDATE'} ) &&
330             defined( $FITS_headers->{'UTDATE'} ) ) {
331             my $utdate = $FITS_headers->{'UTDATE'};
332             $return = $self->_parse_yyyymmdd_date( $utdate, ":" );
333             } elsif ( exists( $FITS_headers->{'DATE'} ) &&
334             defined( $FITS_headers->{'DATE'} ) ) {
335             my $utdate = $FITS_headers->{'DATE'};
336             $return = $self->_parse_iso_date( $utdate );
337 2     2 1 6 } elsif ( exists( $FITS_headers->{'DATE-OBS'} ) &&
338 2         4 defined( $FITS_headers->{'DATE-OBS'} ) ) {
339 2         3 my $utdate = $FITS_headers->{'DATE-OBS'};
340 2 50 33     9 $return = $self->_parse_iso_date( $utdate );
    0 0        
    0 0        
341             }
342 2         167 if (defined $return) {
343 2         122 $return = sprintf('%04d%02d%02d',$return->year,
344             $return->mon, $return->mday);
345             }
346 0         0 return $return;
347 0         0 }
348              
349             =item B<from_UTDATE>
350 0         0  
351 0         0 Converts UT date in C<Time::Piece> object into C<YYYY:MM:DD> format
352             for C<UTDATE> header.
353 2 50       179  
354 2         17 =cut
355              
356             my $self = shift;
357 2         41 my $generic_headers = shift;
358             my %return_hash;
359             if (exists($generic_headers->{UTDATE}) ) {
360             my $date = $generic_headers->{UTDATE};
361             $return_hash{UTDATE} = join(':',
362             substr($date,0,4),
363             substr($date,4,2),
364             substr($date,6,2));
365             }
366             return %return_hash;
367             }
368 1     1 1 3  
369 1         3 =item B<to_UTSTART>
370 1         2  
371 1 50       5 Combines C<UTDATE> and C<UTSTART> into a unified C<UTSTART>
372 1         2 generic header. If those headers do not exist, uses C<DATE>.
373 1         6  
374             =cut
375              
376             my $self = shift;
377             my $FITS_headers = shift;
378 1         11 my $return;
379             if ( exists( $FITS_headers->{'UTDATE'} ) &&
380             defined( $FITS_headers->{'UTDATE'} ) &&
381             exists $FITS_headers->{UTSTART} &&
382             defined $FITS_headers->{UTSTART} ) {
383              
384             # To convert to ISO replace colons with dashes
385             my $utdate = $FITS_headers->{UTDATE};
386             $utdate =~ s/:/\-/g;
387              
388             my $ut = $utdate . "T" . $FITS_headers->{'UTSTART'};
389 4     4 1 13 $return = $self->_parse_iso_date( $ut );
390 4         12  
391 4         9 } elsif (exists $FITS_headers->{"DATE-OBS"}) {
392 4 50 33     14 # reduced data
    0 33        
    0 33        
      0        
      0        
393             $return = $self->_parse_iso_date( $FITS_headers->{"DATE-OBS"} );
394              
395             } elsif ( exists( $FITS_headers->{'DATE'} ) &&
396             defined( $FITS_headers->{'DATE'} ) &&
397             $FITS_headers->{'DATE'} =~ /^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d/ ) {
398 4         582  
399 4         201 $return = $self->_parse_iso_date( $FITS_headers->{"DATE"} );
400              
401 4         21 }
402 4         220  
403             return $return;
404             }
405              
406 0         0 =item B<from_UTSTART>
407              
408             Converts the unified C<UTSTART> generic header into C<UTDATE>
409             and C<UTSTART> FITS headers of the form C<YYYY:MM:DD> and C<HH:MM:SS>.
410              
411             =cut
412 0         0  
413             my $self = shift;
414             my $generic_headers = shift;
415             my %return_hash;
416 4         16 if (exists($generic_headers->{UTSTART}) &&
417             UNIVERSAL::isa( $generic_headers->{UTSTART}, "Time::Piece" ) ) {
418             my $ut = $generic_headers->{UTSTART};
419             $return_hash{'UTDATE'} = join ':', $ut->year, $ut->mon, $ut->mday;
420             $return_hash{'UTSTART'} = join ':', $ut->hour, $ut->minute, $ut->second;
421             $return_hash{'DATE'} = $ut->datetime;
422             }
423             return %return_hash;
424             }
425              
426             =item B<to_UTEND>
427 1     1 1 3  
428 1         3 Converts the <UTDATE> and C<UTEND> headers into a combined
429 1         3 C<Time::Piece> object.
430 1 50 33     9  
431             =cut
432 1         3  
433 1         4 my $self = shift;
434 1         12 my $FITS_headers = shift;
435 1         12 my $return;
436              
437 1         46 if ( exists( $FITS_headers->{'UTDATE'} ) &&
438             defined( $FITS_headers->{'UTDATE'} ) &&
439             exists $FITS_headers->{UTEND} &&
440             defined $FITS_headers->{UTEND} ) {
441              
442             # need to replace colons with -
443             my $utdate = $FITS_headers->{"UTDATE"};
444             $utdate =~ s/:/\-/g;
445              
446             my $ut = $utdate . "T" . $FITS_headers->{'UTEND'};
447              
448 2     2 1 5 $return = $self->_parse_iso_date( $ut );
449 2         4  
450 2         4 } elsif (exists $FITS_headers->{"DATE-END"}) {
451             # reduced data
452 2 50 33     9 $return = $self->_parse_iso_date( $FITS_headers->{"DATE-END"} );
    0 33        
      33        
453             }
454             return $return;
455             }
456              
457             =item B<from_UTEND>
458 2         319  
459 2         98 Converts the unified C<UTEND> generic header into C<UTDATE> and
460             C<UTEND> FITS headers of the form C<YYYY:MM:DD> and C<HH:MM:SS>.
461 2         10  
462             =cut
463 2         97  
464             my $self = shift;
465             my $generic_headers = shift;
466             my %return_hash;
467 0         0 if (exists($generic_headers->{UTEND}) &&
468             UNIVERSAL::isa( $generic_headers->{UTEND}, "Time::Piece" ) ) {
469 2         7 my $ut = $generic_headers->{UTEND};
470             $generic_headers->{UTEND} =~ /(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)/;
471             $return_hash{'UTDATE'} = join ':', $ut->year, $ut->mon, $ut->mday;
472             $return_hash{'UTEND'} = join ':', $ut->hour, $ut->minute, $ut->second;
473             }
474             return %return_hash;
475             }
476              
477             =item B<to_MSBID>
478              
479             Converts the MSBID field to an MSBID. Complication is that the SCUBA
480 1     1 1 2 header and database store a blank MSBID as a single space rather than
481 1         2 an empty string and this causes difficulty in some subsystems.
482 1         2  
483 1 50 33     11 This routine replaces a single space with a null string.
484              
485 1         10 =cut
486 1         9  
487 1         31 my $self = shift;
488 1         22 my $FITS_headers = shift;
489             my $msbid = $FITS_headers->{MSBID};
490 1         33 $msbid =~ s/\s+$// if defined $msbid;
491             return $msbid;
492             }
493              
494             =back
495              
496             =head1 SEE ALSO
497              
498             C<Astro::FITS::HdrTrans>, C<Astro::FITS::HdrTrans::Base>
499              
500             =head1 AUTHOR
501              
502             Brad Cavanagh E<lt>b.cavanagh@jach.hawaii.eduE<gt>,
503             Tim Jenness E<lt>t.jenness@jach.hawaii.eduE<gt>
504 2     2 1 6  
505 2         5 =head1 COPYRIGHT
506 2         10  
507 2 50       115 Copyright (C) 2007 Science and Technology Facilities Council.
508 2         9 Copyright (C) 2003-2005 Particle Physics and Astronomy Research Council.
509             All Rights Reserved.
510              
511             This program is free software; you can redistribute it and/or modify it under
512             the terms of the GNU General Public License as published by the Free Software
513             Foundation; either version 2 of the License, or (at your option) any later
514             version.
515              
516             This program is distributed in the hope that it will be useful,but WITHOUT ANY
517             WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
518             PARTICULAR PURPOSE. See the GNU General Public License for more details.
519              
520             You should have received a copy of the GNU General Public License along with
521             this program; if not, write to the Free Software Foundation, Inc., 59 Temple
522             Place,Suite 330, Boston, MA 02111-1307, USA
523              
524             =cut
525              
526             1;