File Coverage

blib/lib/Astro/FITS/HdrTrans/SCUBA.pm
Criterion Covered Total %
statement 129 154 83.7
branch 30 76 39.4
condition 18 60 30.0
subroutine 21 21 100.0
pod 16 16 100.0
total 214 327 65.4


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