File Coverage

blib/lib/Astro/FITS/HdrTrans/LCO.pm
Criterion Covered Total %
statement 17 166 10.2
branch 0 62 0.0
condition 0 36 0.0
subroutine 6 27 22.2
pod 16 20 80.0
total 39 311 12.5


line stmt bran cond sub pod time code
1             # -*-perl-*-
2              
3              
4             =head1 NAME
5              
6             Astro::FITS::HdrTrans::LCO - Base class for translation of LCO instruments
7              
8             =head1 SYNOPSIS
9              
10             use Astro::FITS::HdrTrans::LCO;
11              
12             =head1 DESCRIPTION
13              
14             This class provides a generic set of translations that are common to
15             instrumentation from LCO. It should not be use directly for translation of
16             instrument FITS headers.
17              
18             =cut
19              
20             use 5.006;
21 19     19   22106018 use warnings;
  19         62  
22 19     19   114 use strict;
  19         37  
  19         465  
23 19     19   91 use Carp;
  19         73  
  19         393  
24 19     19   84  
  19         37  
  19         1315  
25             # Inherit from the Base translation class and not HdrTrans itself
26             # (which is just a class-less wrapper).
27              
28             use base qw/ Astro::FITS::HdrTrans::FITS /;
29 19     19   122  
  19         42  
  19         4920  
30             use vars qw/ $VERSION /;
31 19     19   101  
  19         39  
  19         25792  
32             $VERSION = "1.65";
33              
34             # for a constant mapping, there is no FITS header, just a generic
35             # header that is constant
36             my %CONST_MAP = (
37             DETECTOR_READ_TYPE => "STARE",
38             OBSERVATION_MODE => "imaging",
39             NUMBER_OF_EXPOSURES => 1,
40             POLARIMETRY => 0,
41             SPEED_GAIN => "NORMAL"
42             );
43              
44             my %UNIT_MAP = (
45             EXPOSURE_TIME => "EXPTIME",
46             GAIN => "GAIN",
47             READNOISE => "RDNOISE",
48             INSTRUMENT => "INSTRUME",
49             OBJECT => "OBJECT",
50             );
51              
52              
53             # Create the translation methods
54             __PACKAGE__->_generate_lookup_methods( \%CONST_MAP, \%UNIT_MAP );
55              
56             =head1 COMPLEX CONVERSIONS
57              
58             These methods are more complicated than a simple mapping. We have to
59             provide both from- and to-FITS conversions All these routines are
60             methods and the to_ routines all take a reference to a hash and return
61             the translated value (a many-to-one mapping) The from_ methods take a
62             reference to a generic hash and return a translated hash (sometimes
63             these are many-to-many)
64              
65             =over 4
66              
67             =cut
68              
69             =item B<to_AIRMASS_END>
70              
71             Set's the airmass at the end of the exposure. The C<AMEND> is used if it exists,
72             otherwise C<AIRMASS> is used. In the case of neither existing, it is set to 1.0.
73              
74             =cut
75              
76             my $self = shift;
77             my $FITS_headers = shift;
78 0     0 1   my $end_airmass = 1.0;
79 0           if ( exists $FITS_headers->{"AMEND"} && $FITS_headers->{"AMEND"} !~ /^UNKNOWN/ ) {
80 0           $end_airmass = $FITS_headers->{"AMEND"};
81 0 0 0       } elsif ( exists $FITS_headers->{"AIRMASS"} && $FITS_headers->{"AIRMASS"} !~ /^UNKNOWN/ ) {
    0 0        
82 0           $end_airmass = $FITS_headers->{"AIRMASS"};
83             }
84 0           return $end_airmass;
85             }
86 0            
87             =item B<to_AIRMASS_START>
88              
89             Set's the airmass at the start of the exposure. The C<AMSTART> is used if it
90             exists, otherwise C<AIRMASS> is used. In the case of neither existing, it is set
91             to 1.0.
92              
93             =cut
94              
95             my $self = shift;
96             my $FITS_headers = shift;
97             my $start_airmass = 1.0;
98 0     0 1   if ( exists $FITS_headers->{"AMSTART"} && $FITS_headers->{"AMSTART"} !~ /^UNKNOWN/ ) {
99 0           $start_airmass = $FITS_headers->{"AMSTART"};
100 0           } elsif ( exists $FITS_headers->{"AIRMASS"} && $FITS_headers->{"AIRMASS"} !~ /^UNKNOWN/ ) {
101 0 0 0       $start_airmass = $FITS_headers->{"AIRMASS"};
    0 0        
102 0           }
103             return $start_airmass;
104 0           }
105              
106 0           =item B<to_DEC_BASE>
107              
108             Converts the base declination from sexagesimal d:m:s to decimal
109             degrees using the C<DEC> keyword, defaulting to 0.0.
110              
111             =cut
112              
113             my $self = shift;
114             my $FITS_headers = shift;
115             my $dec = 0.0;
116             my $sexa = $FITS_headers->{"DEC"};
117 0     0 1   if ( defined( $sexa ) ) {
118 0           $dec = $self->dms_to_degrees( $sexa );
119 0           }
120 0           return $dec;
121 0 0         }
122 0            
123              
124 0           =item B<to_DR_RECIPE>
125              
126             Returns the data-reduction recipe name. The selection depends on the
127             values of the C<OBJECT> and C<OBSTYPE> keywords. The default is
128             "QUICK_LOOK". A dark returns "REDUCE_DARK", and an object's recipe is
129             "JITTER_SELF_FLAT".
130              
131             =cut
132              
133             my $self = shift;
134             my $FITS_headers = shift;
135             my $recipe = "QUICK_LOOK";
136              
137             if ( exists $FITS_headers->{OBSTYPE} ) {
138 0     0 1   if ( $FITS_headers->{OBSTYPE} =~ /BIAS/i ) {
139 0           $recipe = "REDUCE_BIAS";
140 0           } elsif ( $FITS_headers->{OBSTYPE} =~ /DARK/i ) {
141             $recipe = "REDUCE_DARK";
142 0 0         } elsif ( $FITS_headers->{OBSTYPE} =~ /FLAT/i ) {
143 0 0         $recipe = "SKY_FLAT";
    0          
    0          
    0          
    0          
144 0           } elsif ( $FITS_headers->{OBSTYPE} =~ /EXPOSE/i ) {
145             # $recipe = "JITTER_SELF_FLAT";
146 0           $recipe = "OFFLINE_REDUCTION";
147             } elsif ( $FITS_headers->{OBSTYPE} =~ /STANDARD/i ) {
148 0           # $recipe = "BRIGHT_POINT_SOURCE_NCOLOUR_APHOT";
149             $recipe = "OFFLINE_REDUCTION";
150             }
151 0           }
152              
153             return $recipe;
154 0           }
155              
156             # Equinox may be absent...
157             my $self = shift;
158 0           my $FITS_headers = shift;
159             my $equinox = 2000.0;
160             if ( exists $FITS_headers->{EQUINOX} ) {
161             $equinox = $FITS_headers->{EQUINOX};
162             }
163 0     0 0   return $equinox;
164 0           }
165 0            
166 0 0         =item B<to_FILTER>
167 0            
168             Look for C<FILTER> keyword first and if not found, concatenate the individual
169 0           C<FILTERx> keywords together, minus any that say "air"
170             =cut
171              
172             my $self = shift;
173             my $FITS_headers = shift;
174             my $filter = "";
175             if (exists $FITS_headers->{"FILTER"} ) {
176             $filter = $FITS_headers->{"FILTER"};
177             } else {
178             my $filter1 = $FITS_headers->{ "FILTER1" };
179 0     0 1   my $filter2 = $FITS_headers->{ "FILTER2" };
180 0           my $filter3 = $FITS_headers->{ "FILTER3" };
181 0            
182 0 0         if ( $filter1 =~ "air" ) {
183 0           $filter = $filter2;
184             }
185 0            
186 0           if ( $filter2 =~ "air" ) {
187 0           $filter = $filter1;
188             }
189 0 0          
190 0           if ( $filter1 =~ "air" && $filter2 =~ "air" ) {
191             $filter = $filter3;
192             }
193 0 0          
194 0           if ( ( $filter1 =~ "air" ) &&
195             ( $filter2 =~ "air" ) &&
196             ( $filter3 =~ "air" ) ) {
197 0 0 0       $filter = "air";
198 0           }
199             }
200             return $filter;
201 0 0 0       }
      0        
202              
203             my $self = shift;
204 0           my $generic_headers = shift;
205             my %return_hash;
206             $return_hash{'FILTER'} = $generic_headers->{FILTER};
207 0            
208             return %return_hash;
209             }
210              
211 0     0 0    
212 0           =item B<to_NUMBER_OF_OFFSETS>
213 0            
214 0           Return the number of offsets. (dithers)
215              
216 0           =cut
217              
218             my $self = shift;
219             my $FITS_headers = shift;
220             my $ndither = ( defined( $FITS_headers->{FRMTOTAL} ) ? $FITS_headers->{FRMTOTAL} : 1 );
221              
222             return $ndither + 1;
223              
224             }
225              
226             =item B<_to_OBSERVATION_NUMBER>
227 0     0 1    
228 0           Converts to the observation number. This uses the C<FRAMENUM> keyword if it
229 0 0         exists, otherwise it is obtained from the filename
230              
231 0           =cut
232              
233             my $self = shift;
234             my $FITS_headers = shift;
235             my $obsnum = 0;
236             if ( exists ( $FITS_headers->{FRAMENUM} ) ) {
237             $obsnum = $FITS_headers->{FRAMENUM};
238             }
239              
240             return $obsnum;
241             }
242              
243 0     0 0   =item B<to_OBSERVATION_TYPE>
244 0            
245 0           Determines the observation type from the C<OBSTYPE> keyword. Almost a direct
246 0 0         mapping except "EXPOSE" which needs mapping to OBJECT. Lambert may need extra
247 0           handling in future
248              
249             =cut
250 0            
251             my $self = shift;
252             my $FITS_headers = shift;
253             my $obstype = uc( $FITS_headers->{OBSTYPE} );
254             if ( $obstype eq "EXPOSE" || $obstype eq "STANDARD" ) {
255             $obstype = "OBJECT";
256             }
257             return $obstype;
258             }
259              
260             =item B<to_RA_BASE>
261              
262 0     0 1   Converts the base right ascension from sexagesimal h:m:s to decimal degrees
263 0           using the C<RA> keyword, defaulting to 0.0.
264 0            
265 0 0 0       =cut
266 0            
267             my $self = shift;
268 0           my $FITS_headers = shift;
269             my $ra = 0.0;
270             my $sexa = $FITS_headers->{"RA"};
271             if ( defined( $sexa ) ) {
272             $ra = $self->hms_to_degrees( $sexa );
273             }
274             return $ra;
275             }
276              
277             =item B<to_STANDARD>
278              
279 0     0 1   Returns whether or not the observation is of a standard source. It is
280 0           deemed to be a standard when the C<OBSTYPE> keyword is "STANDARD".
281 0            
282 0           =cut
283 0 0          
284 0           my $self = shift;
285             my $FITS_headers = shift;
286 0           my $standard = 0;
287             my $type = $FITS_headers->{OBSTYPE};
288             if ( uc( $type ) eq "STANDARD" ) {
289             $standard = 1;
290             }
291             return $standard;
292             }
293              
294             my $self = shift;
295             my $FITS_headers = shift;
296             # use Data::Dumper;
297 0     0 1   # print Dumper $FITS_headers;
298 0           return $self->_get_UT_date( $FITS_headers );
299 0           }
300 0            
301 0 0         my $self = shift;
302 0           my $generic_headers = shift;
303             my $utend = $generic_headers->{UTEND}->strptime( '%T' );
304 0           return ( "UTEND"=> $utend );
305             }
306              
307             my $self = shift;
308 0     0 1   my $generic_headers = shift;
309 0           my $utstart = $generic_headers->{UTSTART}->strptime('%T');
310             return ( "UTSTART"=> $utstart );
311             }
312 0            
313             my $self = shift;
314             my $generic_headers = shift;
315             my $ymd = $generic_headers->{DATE};
316 0     0 1   my $dobs = substr( $ymd, 0, 4 ) . "-" . substr( $ymd, 4, 2 ) ."-" . substr( $ymd, 6, 2 );
317 0           return ( "DATE-OBS"=>$dobs );
318 0           }
319 0            
320             =item B<to_XBINNING>
321              
322             Determines the binning in the X direction of the frame. We look for C<XBINNING>
323 0     0 1   if it exists, otherwise we look for the C<CCDSUM> keyword and extract the first
324 0           part.
325 0            
326 0           =cut
327              
328             my $self = shift;
329             my $FITS_headers = shift;
330 0     0 0   my $xbinning = 2;
331 0           if ( exists ( $FITS_headers->{XBINNING} ) ) {
332 0           $xbinning = $FITS_headers->{XBINNING};
333 0           } elsif ( exists ( $FITS_headers->{CCDSUM} ) ) {
334 0           my $ccdsum = $FITS_headers->{CCDSUM};
335             my @pos = split( / /, $ccdsum );
336             $xbinning = $pos[ 0 ];
337             }
338             return $xbinning;
339             }
340              
341             =item B<to_YBINNING>
342              
343             Determines the binning in the Y direction of the frame. We look for C<YBINNING>
344             if it exists, otherwise we look for the C<CCDSUM> keyword and extract the second
345             part.
346 0     0 1    
347 0           =cut
348 0            
349 0 0         my $self = shift;
    0          
350 0           my $FITS_headers = shift;
351             my $ybinning = 2;
352 0           if ( exists ( $FITS_headers->{YBINNING} ) ) {
353 0           $ybinning = $FITS_headers->{YBINNING};
354 0           } elsif ( exists ( $FITS_headers->{CCDSUM} ) ) {
355             my $ccdsum = $FITS_headers->{CCDSUM};
356 0           my @pos = split( / /, $ccdsum );
357             $ybinning = $pos[ 1 ];
358             }
359             return $ybinning;
360             }
361              
362             # Supplementary methods for the translations
363             # ------------------------------------------
364              
365             =item B<dms_to_degrees>
366              
367             Converts a sky angle specified in d m s format into decimal degrees.
368 0     0 1   The argument is the sexagesimal-format angle.
369 0            
370 0           =cut
371 0 0          
    0          
372 0           my $self = shift;
373             my $sexa = shift;
374 0           my $dms;
375 0           if ( defined( $sexa ) ) {
376 0           if ($sexa =~ /UNKNOWN/i or $sexa eq "N/A" or $sexa eq "NaN" ) {
377             $dms = 0.0;
378 0           } else {
379             my @pos = split( /:/, $sexa );
380             $dms = abs($pos[ 0 ]) + $pos[ 1 ] / 60.0 + $pos [ 2 ] / 3600.0;
381             if ( $pos[ 0 ] =~ /-/ ) {
382             $dms = -$dms;
383             }
384             }
385             }
386             return $dms;
387             }
388              
389             =item B<hms_to_degrees>
390              
391             Converts a sky angle specified in h m s format into decimal degrees.
392 0     0 1   It takes no account of latitude. The argument is the sexagesimal
393 0           format angle.
394 0            
395 0 0         =cut
396 0 0 0        
      0        
397 0           my $self = shift;
398             my $sexa = shift;
399 0           my $hms;
400 0           if ( defined( $sexa ) ) {
401 0 0         if ($sexa =~ /UNKNOWN/i or $sexa eq "N/A" or $sexa eq "NaN" ) {
402 0           $hms = 0.0;
403             } else {
404             my @pos = split( /:/, $sexa );
405             $hms = 15.0 * ( $pos[ 0 ] + $pos[ 1 ] / 60.0 + $pos [ 2 ] / 3600.0 );
406 0           }
407             }
408             return $hms;
409             }
410              
411             # Returns the UT date in YYYYMMDD format.
412             my $self = shift;
413             my $FITS_headers = shift;
414              
415             # use Data::Dumper;print Dumper $FITS_headers;die;
416             # This is UT start and time.
417             my $dateobs = $FITS_headers->{"DATE-OBS"};
418 0     0 1   # print "DATE-OBS=$dateobs\n";
419 0           my $utdate = substr( $dateobs, 0, 4 ) . substr( $dateobs, 5, 2 ) . substr( $dateobs, 8, 2 );
420 0           # print "UTDATE=$utdate\n";
421 0 0         # Extract out the data in yyyymmdd format.
422 0 0 0       return $utdate;
      0        
423 0           }
424              
425 0           =back
426 0            
427             =head1 SEE ALSO
428              
429 0           C<Astro::FITS::HdrTrans>, C<Astro::FITS::HdrTrans::Base>.
430              
431             =head1 AUTHOR
432              
433             Tim Lister E<lt>tlister@lcogt.netE<gt>
434 0     0      
435 0           =head1 COPYRIGHT
436              
437             =cut
438              
439 0           1;