File Coverage

blib/lib/Astro/FITS/HdrTrans/MICHELLE.pm
Criterion Covered Total %
statement 84 103 81.5
branch 10 24 41.6
condition 6 27 22.2
subroutine 17 18 94.4
pod 11 11 100.0
total 128 183 69.9


line stmt bran cond sub pod time code
1              
2             =head1 NAME
3              
4             Astro::FITS::HdrTrans::MICHELLE - UKIRT Michelle translations
5              
6             =head1 SYNOPSIS
7              
8             use Astro::FITS::HdrTrans::MICHELLE;
9              
10             %gen = Astro::FITS::HdrTrans::MICHELLE->translate_from_FITS( %hdr );
11              
12             =head1 DESCRIPTION
13              
14             This class provides a generic set of translations that are specific to
15             the MICHELLE camera and spectrometer of the United Kingdom Infrared
16             Telescope.
17              
18             =cut
19              
20             use 5.006;
21 10     10   16917313 use warnings;
  10         30  
22 10     10   44 use strict;
  10         15  
  10         318  
23 10     10   40 use Carp;
  10         22  
  10         189  
24 10     10   46  
  10         22  
  10         690  
25             # Inherit from UKIRT
26             # UKIRTNew must come first because of DATE-OBS handling
27             use base qw/ Astro::FITS::HdrTrans::UKIRTNew /;
28 10     10   58  
  10         22  
  10         1450  
29             use vars qw/ $VERSION /;
30 10     10   53  
  10         15  
  10         8023  
31             $VERSION = "1.65";
32              
33             # for a constant mapping, there is no FITS header, just a generic
34             # header that is constant
35             my %CONST_MAP = (
36              
37             );
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             # Michelle Specific
44             CHOP_ANGLE => "CHPANGLE",
45             CHOP_THROW => "CHPTHROW",
46             GRATING_DISPERSION => "GRATDISP",
47             GRATING_NAME => "GRATNAME",
48             GRATING_ORDER => "GRATORD",
49             GRATING_WAVELENGTH => "GRATPOS",
50             SAMPLING => "SAMPLING",
51             SLIT_ANGLE => "SLITANG",
52              
53             # CGS4 compatible
54             NSCAN_POSITIONS => "DETNINCR",
55             SCAN_INCREMENT => "DETINCR",
56              
57             # UIST compatible
58             NUMBER_OF_READS => "NREADS",
59             POLARIMETRY => "POLARISE",
60             SLIT_NAME => "SLITNAME",
61              
62             # UIST + WFCAM compatible
63             EXPOSURE_TIME => "EXP_TIME",
64              
65             # UFTI + IRCAM compatible
66             SPEED_GAIN => "SPD_GAIN",
67              
68             # CGS4 + UIST + WFCAM
69             CONFIGURATION_INDEX => 'CNFINDEX',
70             );
71              
72             # Derived from end entry in subheader
73             my %ENDOBS_MAP = (
74             DETECTOR_INDEX => 'DINDEX',
75             );
76              
77              
78             # Create the translation methods
79             __PACKAGE__->_generate_lookup_methods( \%CONST_MAP, \%UNIT_MAP, undef, \%ENDOBS_MAP );
80              
81             =head1 METHODS
82              
83             =over 4
84              
85             =item B<this_instrument>
86              
87             The name of the instrument required to match (case insensitively)
88             against the INSTRUME/INSTRUMENT keyword to allow this class to
89             translate the specified headers. Called by the default
90             C<can_translate> method.
91              
92             $inst = $class->this_instrument();
93              
94             Returns "MICHELLE".
95              
96             =cut
97              
98             return "MICHELLE";
99             }
100 20     20 1 55  
101             =back
102              
103             =head1 COMPLEX CONVERSIONS
104              
105             =over 4
106              
107             =item B<to_DEC_TELESCOPE_OFFSET>
108              
109             Declination offsets need to be handled differently for spectroscopy
110             mode because of the new nod iterator.
111              
112             =cut
113              
114             my $self = shift;
115             my $FITS_headers = shift;
116             my $decoff;
117 1     1 1 3  
118 1         4 # Determine the observation mode, e.g. spectroscopy or imaging.
119 1         2 my $mode = $self->to_OBSERVATION_MODE($FITS_headers);
120             if ( $mode eq 'spectroscopy' ) {
121              
122 1         5 # If the nod iterator is used, then telescope offsets always come out
123 1 50       5 # as 0,0. We need to check if we're in the B beam (the nodded
124             # position) to figure out what the offset is using the chop angle
125             # and throw.
126             if ( exists( $FITS_headers->{CHOPBEAM} ) &&
127             $FITS_headers->{CHOPBEAM} =~ /^B/ &&
128             exists( $FITS_headers->{CHPANGLE} ) &&
129 1 50 33     3 exists( $FITS_headers->{CHPTHROW} ) ) {
      33        
      33        
130              
131             my $pi = 4 * atan2( 1, 1 );
132             my $throw = $FITS_headers->{CHPTHROW};
133             my $angle = $FITS_headers->{CHPANGLE} * $pi / 180.0;
134 1         142 $decoff = $throw * cos( $angle );
135 1         4 } else {
136 1         54 $decoff = $FITS_headers->{TDECOFF};
137 1         55 }
138              
139 0         0 # Imaging.
140             } else {
141             $decoff = $FITS_headers->{TDECOFF};
142             }
143              
144 0         0 return $decoff;
145             }
146              
147 1         5 =item B<from_DEC_TELESCOPE_OFFSET>
148              
149             If we are nodding TDECOFF always comes out as 0.0. We always return
150             zero for spectroscopy and TDECOFF otherwise. It's possible that this
151             is incorrect and should only occur for the specific case of a B
152             chop beam. The chopbeam is not stored in the generic headers.
153              
154             =cut
155              
156             my $self = shift;
157             my $generic_headers = shift;
158             my $tdecoff;
159             if ($generic_headers->{OBSERVATION_MODE} eq 'spectroscopy') {
160 1     1 1 3 $tdecoff = 0.0;
161 1         2 } else {
162 1         1 $tdecoff = $generic_headers->{DEC_TELESCOPE_OFFSET};
163 1 50       4 }
164 1         3 return ("TDECOFF",$tdecoff);
165             }
166 0         0  
167             =item B<to_DETECTOR_READ_TYPE>
168 1         6  
169             Usually DET_MODE but in some older data it can be DETMODE.
170              
171             =cut
172              
173             my $self = shift;
174             my $FITS_headers = shift;
175              
176             # cut off date is 20040206
177             my $read_type;
178 1     1 1 3 for my $k (qw/ DET_MODE DETMODE /) {
179 1         10 if (exists $FITS_headers->{$k}) {
180             $read_type = $FITS_headers->{$k};
181             last;
182 1         2 }
183 1         4 }
184 1 50       6 return $read_type;
185 1         29 }
186 1         97  
187             =item B<to_NUMBER_OF_OFFSETS>
188              
189 1         3 Cater for early data with missing headers. Normally the NOFFSETS
190             header is available.
191              
192             =cut
193              
194             my $self = shift;
195             my $FITS_headers = shift;
196              
197             # It's normally a ABBA pattern. Add one for the final offset to 0,0.
198             my $noffsets = 5;
199              
200 1     1 1 4 # Look for a defined header containing integers.
201 1         2 if ( exists $FITS_headers->{NOFFSETS} ) {
202             my $noff = $FITS_headers->{NOFFSETS};
203             if ( defined $noff && $noff =~ /\d+/ ) {
204 1         2 $noffsets = $noff;
205             }
206             }
207 1 50       5 return $noffsets;
208 1         26 }
209 1 50 33     83  
210 1         3 =item B<to_OBSERVATION_MODE>
211              
212             Normally use INSTMODE header but for older data use CAMERA.
213 1         2  
214             =cut
215              
216             my $self = shift;
217             my $FITS_headers = shift;
218              
219             my $mode;
220             # 20040206
221             for my $k (qw/ INSTMODE CAMERA /) {
222             if (exists $FITS_headers->{$k}) {
223 2     2 1 5 $mode = $FITS_headers->{$k};
224 2         6 last;
225             }
226 2         5 }
227             return $mode;
228 2         8 }
229 2 50       11  
230 2         52 =item B<to_RA_TELESCOPE_OFFSET>
231 2         106  
232             Right-ascension offsets need to be handled differently for spectroscopy
233             mode because of the new nod iterator.
234 2         5  
235             =cut
236              
237             my $self = shift;
238             my $FITS_headers = shift;
239             my $raoff;
240              
241             # Determine the observation mode, e.g. spectroscopy or imaging.
242             my $mode = $self->to_OBSERVATION_MODE($FITS_headers);
243             if ( $mode eq 'spectroscopy' ) {
244              
245 0     0   0 # If the nod iterator is used, then telescope offsets always come out
246 0         0 # as 0,0. We need to check if we're in the B beam (the nodded
247 0         0 # position) to figure out what the offset is using the chop angle
248             # and throw.
249             if ( exists( $FITS_headers->{CHOPBEAM} ) &&
250 0         0 $FITS_headers->{CHOPBEAM} =~ /^B/ &&
251 0 0       0 exists( $FITS_headers->{CHPANGLE} ) &&
252             exists( $FITS_headers->{CHPTHROW} ) ) {
253             my $pi = 4 * atan2( 1, 1 );
254             my $throw = $FITS_headers->{CHPTHROW};
255             my $angle = $FITS_headers->{CHPANGLE} * $pi / 180.0;
256             $raoff = $throw * sin( $angle );
257 0 0 0     0  
      0        
      0        
258             } else {
259             $raoff = $FITS_headers->{TRAOFF};
260             }
261 0         0  
262 0         0 # Imaging.
263 0         0 } else {
264 0         0 $raoff = $FITS_headers->{TRAOFF};
265             }
266             return $raoff;
267 0         0 }
268              
269             =item B<from_TELESCOPE>
270              
271             For data taken before 20010906, return 'UKATC'. For data taken on and
272 0         0 after 20010906, return 'UKIRT'. Returned header is C<TELESCOP>.
273              
274 0         0 =cut
275              
276             my $self = shift;
277             my $generic_headers = shift;
278             my $utdate = $generic_headers->{'UTDATE'};
279             if ( $utdate < 20010906 ) {
280             return( "TELESCOP", "UKATC" );
281             } else {
282             return( "TELESCOP", "UKIRT" );
283             }
284             }
285 1     1 1 2  
286 1         3 =item B<to_X_REFERENCE_PIXEL>
287 1         4  
288 1 50       6 Specify the reference pixel, which is normally near the frame centre.
289 0         0 Note that offsets for polarimetry are undefined.
290              
291 1         13 =cut
292              
293             my $self = shift;
294             my $FITS_headers = shift;
295             my $xref;
296              
297             # Use the average of the bounds to define the centre.
298             if ( exists $FITS_headers->{RDOUT_X1} && exists $FITS_headers->{RDOUT_X2} ) {
299             my $xl = $FITS_headers->{RDOUT_X1};
300             my $xu = $FITS_headers->{RDOUT_X2};
301             $xref = $self->nint( ( $xl + $xu ) / 2 );
302              
303 1     1 1 3 # Use a default of the centre of the full array.
304 1         3 } else {
305 1         2 $xref = 161;
306             }
307             return $xref;
308 1 50 33     4 }
309 1         54  
310 1         52 =item B<from_X_REFERENCE_PIXEL>
311 1         57  
312             Always returns the value '1' as CRPIX1.
313              
314             =cut
315 0         0  
316             my $self = shift;
317 1         3 return ("CRPIX1", 1.0);
318             }
319              
320             =item B<to_Y_REFERENCE_PIXEL>
321              
322             Specify the reference pixel, which is normally near the frame centre.
323             Note that offsets for polarimetry are undefined.
324              
325             =cut
326              
327 1     1 1 3 my $self = shift;
328 1         14 my $FITS_headers = shift;
329             my $yref;
330              
331             # Use the average of the bounds to define the centre.
332             if ( exists $FITS_headers->{RDOUT_Y1} && exists $FITS_headers->{RDOUT_Y2} ) {
333             my $yl = $FITS_headers->{RDOUT_Y1};
334             my $yu = $FITS_headers->{RDOUT_Y2};
335             $yref = $self->nint( ( $yl + $yu ) / 2 );
336              
337             # Use a default of the centre of the full array.
338             } else {
339 1     1 1 3 $yref = 121;
340 1         3 }
341 1         2 return $yref;
342             }
343              
344 1 50 33     26 =item B<from_Y_REFERENCE_PIXEL>
345 1         48  
346 1         53 Always returns the value '1' as CRPIX2.
347 1         159  
348             =cut
349              
350             my $self = shift;
351 0         0 return ("CRPIX2", 1.0);
352             }
353 1         4  
354             =back
355              
356             =head1 SEE ALSO
357              
358             C<Astro::FITS::HdrTrans>, C<Astro::FITS::HdrTrans::UKIRT>.
359              
360             =head1 AUTHOR
361              
362             Malcolm J. Currie E<lt>mjc@star.rl.ac.ukE<gt>
363 1     1 1 3 Brad Cavanagh E<lt>b.cavanagh@jach.hawaii.eduE<gt>,
364 1         14 Tim Jenness E<lt>t.jenness@jach.hawaii.eduE<gt>.
365              
366             =head1 COPYRIGHT
367              
368             Copyright (C) 2008 Science and Technology Facilities Council.
369             Copyright (C) 2006-2007 Particle Physics and Astronomy Research Council.
370             ACopyright (C) 2003-2005 Particle Physics and Astronomy Research Council.
371             All Rights Reserved.
372              
373             This program is free software; you can redistribute it and/or modify it under
374             the terms of the GNU General Public License as published by the Free Software
375             Foundation; either Version 2 of the License, or (at your option) any later
376             version.
377              
378             This program is distributed in the hope that it will be useful,but WITHOUT ANY
379             WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
380             PARTICULAR PURPOSE. See the GNU General Public License for more details.
381              
382             You should have received a copy of the GNU General Public License along with
383             this program; if not, write to the Free Software Foundation, Inc., 59 Temple
384             Place, Suite 330, Boston, MA 02111-1307, USA.
385              
386             =cut
387              
388             1;