File Coverage

blib/lib/Astro/FITS/HdrTrans/SOFI.pm
Criterion Covered Total %
statement 18 167 10.7
branch 0 106 0.0
condition 0 66 0.0
subroutine 7 20 35.0
pod 2 14 14.2
total 27 373 7.2


line stmt bran cond sub pod time code
1              
2             =head1 NAME
3              
4             Astro::FITS::HdrTrans::SOFI - ESO SOFI translations
5              
6             =head1 SYNOPSIS
7              
8             use Astro::FITS::HdrTrans::SOFI;
9              
10             %gen = Astro::FITS::HdrTrans::SOFI->translate_from_FITS( %hdr );
11              
12             =head1 DESCRIPTION
13              
14             This class provides a generic set of translations that are specific to
15             the SOFI camera of the European Southern Observatory.
16              
17             =cut
18              
19             use 5.006;
20 10     10   663455 use warnings;
  10         37  
21 10     10   43 use strict;
  10         17  
  10         312  
22 10     10   42 use Carp;
  10         16  
  10         201  
23 10     10   39  
  10         16  
  10         670  
24             # Inherit from ESO
25             use base qw/ Astro::FITS::HdrTrans::ESO /;
26 10     10   58  
  10         23  
  10         4318  
27             use vars qw/ $VERSION /;
28 10     10   60  
  10         20  
  10         12174  
29             $VERSION = "1.65";
30              
31             # for a constant mapping, there is no FITS header, just a generic
32             # header that is constant
33             my %CONST_MAP = (
34             POLARIMETRY => 0,
35             );
36              
37             # NULL mappings used to override base class implementations
38             my @NULL_MAP = qw/ /;
39              
40             # unit mapping implies that the value propogates directly
41             # to the output with only a keyword name change
42              
43             my %UNIT_MAP = (
44             );
45              
46              
47             # Create the translation methods
48             __PACKAGE__->_generate_lookup_methods( \%CONST_MAP, \%UNIT_MAP, \@NULL_MAP );
49              
50             =head1 METHODS
51              
52             =over 4
53              
54             =item B<this_instrument>
55              
56             The name of the instrument required to match (case insensitively)
57             against the INSTRUME/INSTRUMENT keyword to allow this class to
58             translate the specified headers. Called by the default
59             C<can_translate> method.
60              
61             $inst = $class->this_instrument();
62              
63             Returns "SOFI".
64              
65             =cut
66              
67             return "SOFI";
68             }
69 20     20 1 52  
70             =back
71              
72             =head1 COMPLEX CONVERSIONS
73              
74             =over 4
75              
76             =cut
77              
78             # If the telescope ofset exists in arcsec, then use it. Otherwise
79             # convert the Cartesian offsets to equatorial offsets.
80             my $self = shift;
81             my $FITS_headers = shift;
82             my $decoffset = 0.0;
83 0     0 0   if ( exists $FITS_headers->{"HIERARCH.ESO.SEQ.CUMOFFSETD"} ) {
84 0           $decoffset = $FITS_headers->{"HIERARCH.ESO.SEQ.CUMOFFSETD"};
85 0            
86 0 0 0       } elsif ( exists $FITS_headers->{"HIERARCH.ESO.SEQ.CUMOFFSETX"} ||
    0          
87 0           exists $FITS_headers->{"HIERARCH.ESO.SEQ.CUMOFFSETY"} ) {
88              
89             # Obtain the x-y offsets in arcsecs.
90             my ($x_as, $y_as) = $self->xy_offsets( $FITS_headers );
91              
92             # Define degrees to radians conversion and obtain the rotation angle.
93 0           my $dtor = atan2( 1, 1 ) / 45.0;
94              
95             my $rotangle = $self->rotation( $FITS_headers );
96 0           my $cosrot = cos( $rotangle * $dtor );
97             my $sinrot = sin( $rotangle * $dtor );
98 0            
99 0           # Apply the rotation matrix to obtain the equatorial pixel offset.
100 0           $decoffset = -$x_as * $sinrot + $y_as * $cosrot;
101             }
102              
103 0           # The sense is reversed compared with UKIRT, as these measure the
104             # place on the sky, not the motion of the telescope.
105             return -1.0 * $decoffset;
106             }
107              
108 0           # Filter positions 1 and 2 used.
109             my $self = shift;
110             my $FITS_headers = shift;
111             my $filter = "";
112             my $filter1 = "open";
113 0     0 0   if ( exists $FITS_headers->{"HIERARCH.ESO.INS.FILT1.ID"} ) {
114 0           $filter1 = $FITS_headers->{"HIERARCH.ESO.INS.FILT1.ID"};
115 0           }
116 0            
117 0 0         my $filter2 = "open";
118 0           if ( exists $FITS_headers->{"HIERARCH.ESO.INS.FILT2.ID"} ) {
119             $filter2 = $FITS_headers->{"HIERARCH.ESO.INS.FILT2.ID"};
120             }
121 0            
122 0 0         if ( $filter1 eq "open" ) {
123 0           $filter = $filter2;
124             }
125              
126 0 0         if ( $filter2 eq "open" ) {
127 0           $filter = $filter1;
128             }
129              
130 0 0         if ( ( $filter1 eq "blank" ) ||
131 0           ( $filter2 eq "blank" ) ) {
132             $filter = "blank";
133             }
134 0 0 0       return $filter;
135             }
136 0            
137             =item B<to_GAIN>
138 0            
139             Fixed values for the gain depend on the camera (SW or LW), and for LW
140             the readout mode. This implementation returns a single number.
141              
142             =cut
143              
144             my $self = shift;
145             my $gain = 5.4;
146             return $gain;
147             }
148              
149 0     0 1   # Dispersion in microns per pixel.
150 0           my $self = shift;
151 0           my $FITS_headers = shift;
152             my $dispersion = 0.0;
153             my $order = 0;
154             if ( exists $FITS_headers->{"HIERARCH.ESO.INS.GRAT.ORDER"} ) {
155             $order = $FITS_headers->{"HIERARCH.ESO.INS.GRAT.ORDER"};
156 0     0 0   }
157 0           if ( $self->to_GRATING_NAME($FITS_headers) eq "LR" ) {
158 0           if ( lc( $order ) eq "blue" || $self->to_FILTER($FITS_headers) eq "GBF" ) {
159 0           $dispersion = 6.96e-4;
160 0 0         } else {
161 0           $dispersion = 1.022e-3;
162             }
163 0 0          
    0          
164 0 0 0       # Medium dispersion
165 0           } elsif ( $self->to_GRATING_NAME($FITS_headers) eq "MR" ) {
166             if ( $order == 8 ) {
167 0           $dispersion = 1.58e-4;
168             } elsif ( $order == 7 ) {
169             $dispersion = 1.87e-4;
170             } elsif ( $order == 6 ) {
171             $dispersion = 2.22e-5;
172 0 0         } elsif ( $order == 5 ) {
    0          
    0          
    0          
    0          
    0          
173 0           $dispersion = 2.71e-5;
174             } elsif ( $order == 4 ) {
175 0           $dispersion = 3.43e-5;
176             } elsif ( $order == 3 ) {
177 0           $dispersion = 4.62e-5;
178             }
179 0           }
180             return $dispersion;
181 0           }
182              
183 0           my $self = shift;
184             my $FITS_headers = shift;
185             my $name = "MR";
186 0           if ( exists $FITS_headers->{"HIERARCH.ESO.INS.GRAT.NAME"} ) {
187             $name = $FITS_headers->{"HIERARCH.ESO.INS.GRAT.NAME"};
188              
189             # Name is missing for low resolution.
190 0     0 0   } elsif ( $self->to_FILTER( $FITS_headers ) =~ /^G[BR]F/ ) {
191 0           $name = "LR";
192 0           }
193 0 0         return $name;
    0          
194 0           }
195              
196             my $self = shift;
197             my $FITS_headers = shift;
198 0           my $wavelength = 0;
199             if ( exists $FITS_headers->{"HIERARCH.ESO.INS.GRAT.WLEN"} ) {
200 0           $wavelength = $FITS_headers->{"HIERARCH.ESO.INS.GRAT.WLEN"};
201              
202             # Wavelength is missing for low resolution.
203             } elsif ( $self->to_FILTER( $FITS_headers ) =~ /^GBF/ ) {
204 0     0 0   $wavelength = 1.3;
205 0           } elsif ( $self->to_FILTER( $FITS_headers ) =~ /^GRF/ ) {
206 0           $wavelength = 2.0;
207 0 0         }
    0          
    0          
208 0           return $wavelength;
209             }
210              
211             my $self = shift;
212 0           my $FITS_headers = shift;
213             my $number = 2;
214 0           if ( exists $FITS_headers->{"HIERARCH.ESO.DET.NCORRS"} ) {
215             $number = $FITS_headers->{"HIERARCH.ESO.DET.NCORRS"};
216 0           }
217             return $number;
218             }
219              
220 0     0 0   # FLAT and DARK need no change.
221 0           my $self = shift;
222 0           my $FITS_headers = shift;
223 0 0         my $type = $FITS_headers->{"HIERARCH.ESO.DPR.TYPE"};
224 0           $type = exists( $FITS_headers->{"HIERARCH.ESO.DPR.TYPE"} ) ? $FITS_headers->{"HIERARCH.ESO.DPR.TYPE"} : "OBJECT";
225              
226 0           my $cat = $FITS_headers->{"HIERARCH.ESO.DPR.CATG"};
227             $cat = exists( $FITS_headers->{"HIERARCH.ESO.DPR.CATG"} ) ? $FITS_headers->{"HIERARCH.ESO.DPR.CATG"} : "SCIENCE";
228              
229             if ( uc( $cat ) eq "TEST" ) {
230             $type = "TEST";
231 0     0 0   } elsif ( uc( $type ) eq "STD" || uc( $cat ) eq "SCIENCE" ) {
232 0           $type = "OBJECT";
233 0           } elsif ( uc( $type ) eq "SKY,FLAT" || uc( $type ) eq "FLAT,SKY" ||
234 0 0         uc( $cat ) eq "OTHER" ) {
235             $type = "SKY";
236 0           } elsif ( uc( $type ) eq "LAMP,FLAT" || uc( $type ) eq "FLAT,LAMP" ||
237 0 0         uc( $type ) eq "FLAT" ) {
238             $type = "LAMP";
239 0 0 0       } elsif ( uc( $type ) eq "LAMP" ) {
    0 0        
    0 0        
    0 0        
    0 0        
    0          
240 0           $type = "ARC";
241             } elsif ( uc( $type ) eq "OTHER" ) {
242 0           $type = "OBJECT";
243             }
244             return $type;
245 0           }
246              
247             # If the telescope offset exists in arcsec, then use it. Otherwise
248 0           # convert the Cartesian offsets to equatorial offsets.
249             my $self = shift;
250 0           my $FITS_headers = shift;
251             my $raoffset = 0.0;
252 0           if ( exists $FITS_headers->{"HIERARCH.ESO.SEQ.CUMOFFSETA"} ) {
253             $raoffset = $FITS_headers->{"HIERARCH.ESO.SEQ.CUMOFFSETA"};
254 0            
255             } elsif ( exists $FITS_headers->{"HIERARCH.ESO.SEQ.CUMOFFSETX"} ||
256             exists $FITS_headers->{"HIERARCH.ESO.SEQ.CUMOFFSETY"} ) {
257              
258             # Obtain the x-y offsets in arcsecs.
259             my ($x_as, $y_as) = $self->xy_offsets( $FITS_headers );
260 0     0 0    
261 0           # Define degrees to radians conversion and obtain the rotation angle.
262 0           my $dtor = atan2( 1, 1 ) / 45.0;
263 0 0 0        
    0          
264 0           my $rotangle = $self->rotation( $FITS_headers );
265             my $cosrot = cos( $rotangle * $dtor );
266             my $sinrot = sin( $rotangle * $dtor );
267              
268             # Apply the rotation matrix to obtain the equatorial pixel offset.
269             $raoffset = -$x_as * $cosrot + $y_as * $sinrot;
270 0           }
271              
272             # The sense is reversed compared with UKIRT, as these measure the
273 0           # place on the sky, not the motion of the telescope.
274             return -1.0 * $raoffset;
275 0           }
276 0            
277 0           # Derive the translation between observing template and recipe name.
278             my $self = shift;
279             my $FITS_headers = shift;
280 0           my $recipe = "QUICK_LOOK";
281              
282             # Obtain the observing template. These are equivalent
283             # to the UKIRT OT science programmes and their tied DR recipes.
284             # However, there are some wrinkles and variations to be tested.
285 0           my $template = $FITS_headers->{"HIERARCH.ESO.TPL.ID"};
286             my $seq = $FITS_headers->{"HIERARCH.ESO.TPL.PRESEQ"};
287             my $type = $FITS_headers->{"HIERARCH.ESO.DPR.TYPE"};
288              
289             if ( $template eq "SOFI_img_obs_AutoJitter" ||
290 0     0 0   $template eq "SOFI_img_obs_Jitter" ||
291 0           $template eq "SOFI_img_obs_GenericOffset" ) {
292 0           if ( $type eq "STD" ) {
293             $recipe = "JITTER_SELF_FLAT_APHOT";
294             } else {
295             $recipe = "JITTER_SELF_FLAT";
296             }
297 0            
298 0           } elsif ( $template eq "SOFI_img_cal_StandardStar" ||
299 0           $template eq "SOFI_img_tec_Zp" ||
300             $seq eq "SOFI_img_cal_StandardStar" ) {
301 0 0 0       $recipe = "JITTER_SELF_FLAT_APHOT";
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0          
302              
303             } elsif ( $template eq "SOFI_img_obs_AutoJitterOffset" ||
304 0 0         $template eq "SOFI_img_obs_JitterOffset" ) {
305 0           $recipe = "CHOP_SKY_JITTER";
306              
307 0           } elsif ( $template eq "SOFI_img_cal_Darks" ||
308             $seq eq "SOFI_img_cal_Darks" ) {
309             $recipe = "REDUCE_DARK";
310              
311             } elsif ( $template eq "SOFI_img_cal_DomeFlats" ) {
312             $recipe = "DOME_FLAT";
313 0            
314             } elsif ( $template eq "SOFI_img_cal_SpecialDomeFlats" ) {
315             $recipe = "SPECIAL_DOME_FLAT";
316              
317 0           # Imaging spectroscopy. There appears to be no distinction
318             # for flats from target, hence no division into POL_JITTER and
319             # SKY_FLAT_POL.
320             } elsif ( $template eq "SOFI_img_obs_Polarimetry" ||
321 0           $template eq "SOFI_img_cal_Polarimetry" ) {
322             $recipe = "POL_JITTER";
323              
324 0           # Spectroscopy. EXTENDED_SOURCE may be more appropriate for
325             # the SOFISW_spec_obs_GenericOffset template.
326             } elsif ( $template eq "SOFI_spec_obs_AutoNodOnSlit" ||
327 0           $template eq "SOFI_spec_obs_AutoNodNonDestr" ) {
328             $recipe = "POINT_SOURCE";
329              
330             } elsif ( $template eq "SOFI_spec_cal_StandardStar" ||
331             $template eq "SOFI_spec_cal_AutoNodOnSlit" ) {
332             $recipe = "STANDARD_STAR";
333              
334 0           } elsif ( $template eq "SOFI_spec_cal_NightCalib" ) {
335             $recipe = "REDUCE_SINGLE_FRAME";
336              
337             } elsif ( $template eq "SOFI_spec_cal_Arcs" ||
338             $seq eq "SOFI_spec_cal_Arcs" ) {
339             $recipe = "REDUCE_ARC";
340 0            
341             } elsif ( $template eq "SOFI_spec_cal_DomeFlats" ||
342             $template eq "SOFI_spec_cal_NonDestrDomeFlats" ) {
343             $recipe = "LAMP_FLAT";
344 0           }
345             return $recipe;
346             }
347 0            
348             # Fixed value for the gain.
349             my $self = shift;
350             my $FITS_headers = shift;
351 0           my $spd_gain = "Normal";
352             return $spd_gain;
353             }
354              
355 0           # Translate to the SLALIB name for reference frame in spectroscopy.
356             my $self = shift;
357 0           my $FITS_headers = shift;
358             my $telescope = "ESONTT";
359             if ( exists $FITS_headers->{TELESCOP} ) {
360             my $scope = $FITS_headers->{TELESCOP};
361             if ( defined( $scope ) ) {
362 0     0 0   $telescope = $scope;
363 0           $telescope =~ s/-U//g;
364 0           $telescope =~ s/-//;
365 0           }
366             }
367             return $telescope;
368             }
369              
370 0     0 0   # Supplementary methods for the translations
371 0           # ------------------------------------------
372 0           my $self = shift;
373 0 0         my $FITS_headers = shift;
374 0           my $pixscale = 0.144;
375 0 0         if ( exists $FITS_headers->{"HIERARCH.ESO.INS.PIXSCALE"} ) {
376 0           $pixscale = $FITS_headers->{"HIERARCH.ESO.INS.PIXSCALE"};
377 0           }
378 0            
379             # Sometimes the first imaging cumulative offsets are non-zero contrary
380             # to the documentation.
381 0           my $expno = 1;
382             if ( exists $FITS_headers->{"HIERARCH.ESO.TPL.EXPNO"} ) {
383             $expno = $FITS_headers->{"HIERARCH.ESO.TPL.EXPNO"};
384             }
385             my $x_as = 0.0;
386             my $y_as = 0.0;
387 0     0 0   my $mode = uc( $self->get_instrument_mode( $FITS_headers ) );
388 0           if ( !( $expno == 1 && ( $mode eq "IMAGE" || $mode eq "POLARIMETRY" ) ) ) {
389 0           if ( exists $FITS_headers->{"HIERARCH.ESO.SEQ.CUMOFFSETX"} ) {
390 0 0         $x_as = $FITS_headers->{"HIERARCH.ESO.SEQ.CUMOFFSETX"} * $pixscale;
391 0           }
392             if ( exists $FITS_headers->{"HIERARCH.ESO.SEQ.CUMOFFSETY"} ) {
393             $y_as = $FITS_headers->{"HIERARCH.ESO.SEQ.CUMOFFSETY"} * $pixscale;
394             }
395             }
396 0           return ($x_as, $y_as);
397 0 0         }
398 0            
399              
400 0           =back
401 0            
402 0           =head1 SEE ALSO
403 0 0 0        
      0        
404 0 0         C<Astro::FITS::HdrTrans>, C<Astro::FITS::HdrTrans::UKIRT>.
405 0            
406             =head1 AUTHOR
407 0 0          
408 0           Malcolm J. Currie E<lt>mjc@star.rl.ac.ukE<gt>
409             Brad Cavanagh E<lt>b.cavanagh@jach.hawaii.eduE<gt>,
410             Tim Jenness E<lt>t.jenness@jach.hawaii.eduE<gt>.
411 0            
412             =head1 COPYRIGHT
413              
414             Copyright (C) 2008 Science and Technology Facilities Council.
415             Copyright (C) 2003-2005 Particle Physics and Astronomy Research Council.
416             All Rights Reserved.
417              
418             This program is free software; you can redistribute it and/or modify it under
419             the terms of the GNU General Public License as published by the Free Software
420             Foundation; either Version 2 of the License, or (at your option) any later
421             version.
422              
423             This program is distributed in the hope that it will be useful,but WITHOUT ANY
424             WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
425             PARTICULAR PURPOSE. See the GNU General Public License for more details.
426              
427             You should have received a copy of the GNU General Public License along with
428             this program; if not, write to the Free Software Foundation, Inc., 59 Temple
429             Place, Suite 330, Boston, MA 02111-1307, USA.
430              
431             =cut
432              
433             1;