File Coverage

blib/lib/Astro/FITS/HdrTrans/SOFI.pm
Criterion Covered Total %
statement 15 164 9.1
branch 0 106 0.0
condition 0 66 0.0
subroutine 6 19 31.5
pod 2 14 14.2
total 23 369 6.2


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