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