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