File Coverage

blib/lib/Astro/FITS/HdrTrans/NACO.pm
Criterion Covered Total %
statement 15 149 10.0
branch 0 98 0.0
condition 0 63 0.0
subroutine 6 16 37.5
pod 1 11 9.0
total 22 337 6.5


line stmt bran cond sub pod time code
1             package Astro::FITS::HdrTrans::NACO;
2              
3             =head1 NAME
4              
5             Astro::FITS::HdrTrans::NACO - ESO NACO translations
6              
7             =head1 SYNOPSIS
8              
9             use Astro::FITS::HdrTrans::NACO;
10              
11             %gen = Astro::FITS::HdrTrans::NACO->translate_from_FITS( %hdr );
12              
13             =head1 DESCRIPTION
14              
15             This class provides a generic set of translations that are specific to
16             the NACO camera of the European Southern Observatory.
17              
18             =cut
19              
20 10     10   35783793 use 5.006;
  10         56  
21 10     10   79 use warnings;
  10         37  
  10         822  
22 10     10   109 use strict;
  10         34  
  10         290  
23 10     10   74 use Carp;
  10         19  
  10         1318  
24              
25             # Inherit from ESO
26 10     10   69 use base qw/ Astro::FITS::HdrTrans::ESO /;
  10         36  
  10         22417  
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 "NAOS+CONICA".
63              
64             =cut
65              
66             sub this_instrument {
67 20     20 1 74 return "NAOS+CONICA";
68             }
69              
70             =back
71              
72             =head1 COMPLEX CONVERSIONS
73              
74             =over 4
75              
76             =cut
77              
78             sub to_DEC_SCALE {
79 0     0 0   my $self = shift;
80 0           my $FITS_headers = shift;
81 0           my $scale;
82 0           my $scale_def = 0.0271;
83 0 0         if ( exists ( $FITS_headers->{CDELT2} ) ) {
    0          
84 0           $scale = $FITS_headers->{CDELT2};
85             } elsif ( exists ( $FITS_headers->{"HIERARCH.ESO.INS.PIXSCALE"} ) ) {
86 0           $scale = $FITS_headers->{"HIERARCH.ESO.INS.PIXSCALE"} / 3600.0;
87             }
88 0 0         $scale = defined( $scale ) ? $scale: $scale_def;
89 0           return $scale;
90             }
91              
92             # If the telescope ofset exists in arcsec, then use it. Otherwise
93             # convert the Cartesian offsets to equatorial offsets.
94             sub to_DEC_TELESCOPE_OFFSET {
95 0     0 0   my $self = shift;
96 0           my $FITS_headers = shift;
97 0           my $decoffset = 0.0;
98 0 0 0       if ( exists $FITS_headers->{"HIERARCH.ESO.SEQ.CUMOFFSETD"} ) {
    0          
99 0           $decoffset = $FITS_headers->{"HIERARCH.ESO.SEQ.CUMOFFSETD"};
100              
101             } elsif ( exists $FITS_headers->{"HIERARCH.ESO.SEQ.CUMOFFSETX"} &&
102             exists $FITS_headers->{"HIERARCH.ESO.SEQ.CUMOFFSETY"} ) {
103              
104 0           my $pixscale = 0.0271;
105 0 0         if ( exists $FITS_headers->{"HIERARCH.ESO.INS.PIXSCALE"} ) {
106 0           $pixscale = $FITS_headers->{"HIERARCH.ESO.INS.PIXSCALE"};
107             }
108              
109             # Sometimes the first cumulative offsets are non-zero contrary to the
110             # documentation.
111 0           my $expno = 1;
112 0 0         if ( exists $FITS_headers->{"HIERARCH.ESO.TPL.EXPNO"} ) {
113 0           $expno = $FITS_headers->{"HIERARCH.ESO.TPL.EXPNO"};
114             }
115 0           my ( $x_as, $y_as );
116 0 0         if ( $expno == 1 ) {
117 0           $x_as = 0.0;
118 0           $y_as = 0.0;
119             } else {
120 0           $x_as = $FITS_headers->{"HIERARCH.ESO.SEQ.CUMOFFSETX"} * $pixscale;
121 0           $y_as = $FITS_headers->{"HIERARCH.ESO.SEQ.CUMOFFSETY"} * $pixscale;
122             }
123              
124             # Define degrees to radians conversion and obtain the rotation angle.
125 0           my $dtor = atan2( 1, 1 ) / 45.0;
126              
127 0           my $rotangle = $self->rotation($FITS_headers);
128 0           my $cosrot = cos( $rotangle * $dtor );
129 0           my $sinrot = sin( $rotangle * $dtor );
130              
131             # Apply the rotation matrix to obtain the equatorial pixel offset.
132 0           $decoffset = -$x_as * $sinrot + $y_as * $cosrot;
133             }
134              
135             # The sense is reversed compared with UKIRT, as these measure the
136             # place on the sky, not the motion of the telescope.
137 0           return -1.0 * $decoffset;
138             }
139              
140             # Derive the translation between observing template and recipe name.
141             sub to_DR_RECIPE {
142 0     0 0   my $self = shift;
143 0           my $FITS_headers = shift;
144 0           my $recipe = "QUICK_LOOK";
145              
146             # Obtain the observing template. These are equivalent
147             # to the UKIRT OT science programmes and their tied DR recipes.
148             # However, there are some wrinkles and variations to be tested.
149 0           my $template = $FITS_headers->{"HIERARCH.ESO.TPL.ID"};
150 0           my $seq = $FITS_headers->{"HIERARCH.ESO.TPL.PRESEQ"};
151              
152 0 0 0       if ( $template =~ /_img_obs_AutoJitter/ ||
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
      0        
153             $template =~ /_img_obs_GenericOffset/ ) {
154 0           $recipe = "JITTER_SELF_FLAT";
155              
156             } elsif ( $template =~ /_img_cal_StandardStar/ ||
157             $template =~ /_img_cal_StandardStarOff/ ||
158             $template =~ /_img_tec_Zp/ ||
159             $template =~ /_img_tec_ZpNoChop/ ||
160             $seq =~ /_img_cal_StandardStar/ ||
161             $seq =~ /_img_cal_StandardStarOff/ ) {
162 0           $recipe = "JITTER_SELF_FLAT_APHOT";
163              
164             } elsif ( $template =~ /_img_obs_AutoJitterOffset/ ||
165             $template =~ /_img_obs_FixedSkyOffset/ ) {
166 0           $recipe = "CHOP_SKY_JITTER";
167              
168             # The following two perhaps should be using NOD_CHOP and a variant of
169             # NOD_CHOP_APHOT to cope with the three source images (central double
170             # flux) rather than four.
171             } elsif ( $template =~ /_img_obs_AutoChopNod/ ||
172             $seq =~ /_img_obs_AutoChopNod/ ) {
173 0           $recipe = "NOD_SELF_FLAT_NO_MASK";
174              
175             } elsif ( $template =~ /_img_cal_ChopStandardStar/ ) {
176 0           $recipe = "NOD_SELF_FLAT_NO_MASK_APHOT";
177              
178             } elsif ( $template =~ /_cal_Darks/ ||
179             $seq =~ /_cal_Darks/ ) {
180 0           $recipe = "REDUCE_DARK";
181              
182             } elsif ( $template =~ /_img_cal_TwFlats/ ||
183             $template =~ /_img_cal_SkyFlats/ ) {
184 0           $recipe = "SKY_FLAT_MASKED";
185              
186             } elsif ( $template =~ /_img_cal_LampFlats/ ) {
187 0           $recipe = "LAMP_FLAT";
188              
189             # Imaging spectroscopy. There appears to be no distinction
190             # for flats from target, hence no division into POL_JITTER and
191             # SKY_FLAT_POL.
192             } elsif ( $template =~ /_pol_obs_GenericOffset/ ||
193             $template =~ /_pol_cal_StandardStar/ ) {
194 0           $recipe = "POL_JITTER";
195              
196             } elsif ( $template =~ /_pol_obs_AutoChopNod/ ||
197             $template =~ /_pol_cal_ChopStandardStar/ ) {
198 0           $recipe = "POL_NOD_CHOP";
199              
200             } elsif ( $template =~ /_pol_cal_LampFlats/ ) {
201 0           $recipe = "POL_JITTER";
202              
203             # Spectroscopy. EXTENDED_SOURCE may be more appropriate for
204             # the NACO_spec_obs_GenericOffset template.
205             } elsif ( $template =~ /_spec_obs_AutoNodOnSlit/ ||
206             $template =~ /_spec_obs_GenericOffset/ ||
207             $template =~ /_spec_obs_AutoChopNod/ ) {
208 0           $recipe = "POINT_SOURCE";
209              
210             } elsif ( $template =~ /_spec_cal_StandardStar/ ||
211             $template =~ /_spec_cal_StandardStarNod/ ||
212             $template =~ /_spec_cal_AutoNodOnSlit/ ) {
213 0           $recipe = "STANDARD_STAR";
214              
215             } elsif ( $template =~ /_spec_cal_NightCalib/ ) {
216 0           $recipe = "REDUCE_SINGLE_FRAME";
217              
218             } elsif ( $template =~ /_spec_cal_Arcs/ ||
219             $seq =~ /_spec_cal_Arcs/ ) {
220 0           $recipe = "REDUCE_ARC";
221              
222             } elsif ( $template =~ /_spec_cal_LampFlats/ ) {
223 0           $recipe = "LAMP_FLAT";
224             }
225 0           return $recipe;
226             }
227              
228              
229             # Filters appear to be in wheels 4 to 6. It appears the filter
230             # in just one of the three.
231             sub to_FILTER {
232 0     0 0   my $self = shift;
233 0           my $FITS_headers = shift;
234 0           my $filter = "empty";
235              
236 0           my $id = 4;
237 0   0       while ( $filter eq "empty" && $id < 7 ) {
238 0 0         if ( exists $FITS_headers->{"HIERARCH.ESO.INS.OPTI${id}.NAME"} ) {
239 0           $filter = $FITS_headers->{"HIERARCH.ESO.INS.OPTI${id}.NAME"};
240             }
241 0           $id++;
242             }
243 0           return $filter;
244             }
245              
246             # Fixed value for the gain, as that's all the documentation gives.
247             # the readout mode.
248             sub to_GAIN {
249 0     0 0   10;
250             }
251              
252             # Using Table 10 of the NACO USer's Guide.
253             sub to_GRATING_DISPERSION {
254 0     0 0   my $self = shift;
255 0           my $FITS_headers = shift;
256 0           my $dispersion = 0.0;
257 0 0         if ( exists $FITS_headers->{CDELT1} ) {
258 0           $dispersion = $FITS_headers->{CDELT1};
259             } else {
260 0 0 0       if ( exists $FITS_headers->{"HIERARCH.ESO.INS.GRAT.NAME"} &&
261             exists $FITS_headers->{"HIERARCH.ESO.OPTI7.NAME"} ) {
262 0           my $order = $FITS_headers->{"HIERARCH.ESO.INS.GRAT.ORDER"};
263 0           my $camera = $FITS_headers->{"HIERARCH.ESO.OPTI7.NAME"};
264              
265 0 0         if ( $camera eq "S54" ) {
    0          
    0          
266 0 0         if ( $order == 1 ) {
    0          
    0          
267 0           $dispersion = 1.98e-3;
268             } elsif ( $order == 2 ) {
269 0           $dispersion = 6.8e-4;
270             } elsif ( $order == 3 ) {
271 0           $dispersion = 9.7e-4;
272             }
273              
274             } elsif ( $camera eq "L54" ) {
275 0           $dispersion = 3.20e-3;
276              
277             } elsif ( $camera eq "S27" ) {
278 0 0         if ( $order == 1 ) {
    0          
279 0           $dispersion = 9.5e-4;
280             } elsif ( $order == 2 ) {
281 0           $dispersion = 5.0e-4;
282             }
283             }
284             }
285             }
286 0           return $dispersion;
287             }
288              
289             sub to_RA_SCALE {
290 0     0 0   my $self = shift;
291 0           my $FITS_headers = shift;
292 0           my $scale;
293 0           my $scale_def = -0.0271;
294 0 0         if ( exists ( $FITS_headers->{CDELT1} ) ) {
    0          
295 0           $scale = $FITS_headers->{CDELT1};
296             } elsif ( exists ( $FITS_headers->{"HIERARCH.ESO.INS.PIXSCALE"} ) ) {
297 0           $scale = - $FITS_headers->{"HIERARCH.ESO.INS.PIXSCALE"} / 3600.0;
298             }
299 0 0         $scale = defined( $scale ) ? $scale: $scale_def;
300 0           return $scale;
301             }
302              
303             # If the telescope offset exists in arcsec, then use it. Otherwise
304             # convert the Cartesian offsets to equatorial offsets.
305             sub to_RA_TELESCOPE_OFFSET {
306 0     0 0   my $self = shift;
307 0           my $FITS_headers = shift;
308 0           my $raoffset = 0.0;
309 0 0 0       if ( exists $FITS_headers->{"HIERARCH.ESO.SEQ.CUMOFFSETA"} ) {
    0          
310 0           $raoffset = $FITS_headers->{"HIERARCH.ESO.SEQ.CUMOFFSETA"};
311              
312             } elsif ( exists $FITS_headers->{"HIERARCH.ESO.SEQ.CUMOFFSETX"} &&
313             exists $FITS_headers->{"HIERARCH.ESO.SEQ.CUMOFFSETY"} ) {
314              
315 0           my $pixscale = 0.0271;
316 0 0         if ( exists $FITS_headers->{"HIERARCH.ESO.INS.PIXSCALE"} ) {
317 0           $pixscale = $FITS_headers->{"HIERARCH.ESO.INS.PIXSCALE"};
318             }
319              
320             # Sometimes the first cumulative offsets are non-zero contrary to the
321             # documentation.
322 0           my $expno = 1;
323 0 0         if ( exists $FITS_headers->{"HIERARCH.ESO.TPL.EXPNO"} ) {
324 0           $expno = $FITS_headers->{"HIERARCH.ESO.TPL.EXPNO"};
325             }
326 0           my ( $x_as, $y_as );
327 0 0         if ( $expno == 1 ) {
328 0           $x_as = 0.0;
329 0           $y_as = 0.0;
330             } else {
331 0           $x_as = $FITS_headers->{"HIERARCH.ESO.SEQ.CUMOFFSETX"} * $pixscale;
332 0           $y_as = $FITS_headers->{"HIERARCH.ESO.SEQ.CUMOFFSETY"} * $pixscale;
333             }
334              
335             # Define degrees to radians conversion and obtain the rotation angle.
336 0           my $dtor = atan2( 1, 1 ) / 45.0;
337              
338 0           my $rotangle = $self->rotation($FITS_headers);
339 0           my $cosrot = cos( $rotangle * $dtor );
340 0           my $sinrot = sin( $rotangle * $dtor );
341              
342             # Apply the rotation matrix to obtain the equatorial pixel offset.
343 0           $raoffset = -$x_as * $cosrot + $y_as * $sinrot;
344             }
345              
346             # The sense is reversed compared with UKIRT, as these measure the
347             # place on the sky, not the motion of the telescope.
348 0           return -1.0 * $raoffset;
349             }
350              
351             # Just translate to shorter strings for ease and to fit within the
352             # night log.
353             sub to_SPEED_GAIN {
354 0     0 0   my $self = shift;
355 0           my $FITS_headers = shift;
356 0           my $spd_gain = "HighSens";
357             my $detector_mode = exists( $FITS_headers->{"HIERARCH.ESO.DET.MODE.NAME"} ) ?
358 0 0         $FITS_headers->{"HIERARCH.ESO.DET.MODE.NAME"} : $spd_gain;
359 0 0         if ( $detector_mode eq "HighSensitivity" ) {
    0          
    0          
360 0           $spd_gain = "HighSens";
361             } elsif ( $detector_mode eq "HighDynamic" ) {
362 0           $spd_gain = "HighDyn";
363             } elsif ( $detector_mode eq "HighBackground" ) {
364 0           $spd_gain = "HighBack";
365             }
366 0           return $spd_gain;
367             }
368              
369             # Translate to the SLALIB name for reference frame in spectroscopy.
370             sub to_TELESCOPE {
371 0     0 0   my $self = shift;
372 0           my $FITS_headers = shift;
373 0           my $telescope = "VLT4";
374 0 0         if ( exists $FITS_headers->{TELESCOP} ) {
375 0           my $scope = $FITS_headers->{TELESCOP};
376 0 0         if ( defined( $scope ) ) {
377 0           $telescope = $scope;
378 0           $telescope =~ s/ESO-//;
379 0           $telescope =~ s/-U//g;
380             }
381             }
382 0           return $telescope;
383             }
384              
385             =back
386              
387             =head1 SEE ALSO
388              
389             C<Astro::FITS::HdrTrans>, C<Astro::FITS::HdrTrans::UKIRT>.
390              
391             =head1 AUTHOR
392              
393             Malcolm J. Currie E<lt>mjc@star.rl.ac.ukE<gt>
394             Brad Cavanagh E<lt>b.cavanagh@jach.hawaii.eduE<gt>,
395             Tim Jenness E<lt>t.jenness@jach.hawaii.eduE<gt>.
396              
397             =head1 COPYRIGHT
398              
399             Copyright (C) 2008 Science and Technology Facilities Council.
400             Copyright (C) 2003-2005 Particle Physics and Astronomy Research Council.
401             All Rights Reserved.
402              
403             This program is free software; you can redistribute it and/or modify it under
404             the terms of the GNU General Public License as published by the Free Software
405             Foundation; either Version 2 of the License, or (at your option) any later
406             version.
407              
408             This program is distributed in the hope that it will be useful,but WITHOUT ANY
409             WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
410             PARTICULAR PURPOSE. See the GNU General Public License for more details.
411              
412             You should have received a copy of the GNU General Public License along with
413             this program; if not, write to the Free Software Foundation, Inc., 59 Temple
414             Place, Suite 330, Boston, MA 02111-1307, USA.
415              
416             =cut
417              
418             1;