File Coverage

blib/lib/Astro/FITS/HdrTrans/JCMT_GSD.pm
Criterion Covered Total %
statement 24 145 16.5
branch 1 70 1.4
condition 2 114 1.7
subroutine 8 17 47.0
pod 10 10 100.0
total 45 356 12.6


line stmt bran cond sub pod time code
1              
2             =head1 NAME
3              
4             Astro::FITS::HdrTrans::JCMT_GSD - JCMT GSD Header translations
5              
6             =head1 DESCRIPTION
7              
8             Converts information contained in JCMT heterodyne instrument headers
9             to and from generic headers. See Astro::FITS::HdrTrans for a list of
10             generic headers.
11              
12             =cut
13              
14             use 5.006;
15 10     10   27958385 use warnings;
  10         29  
16 10     10   46 use strict;
  10         17  
  10         281  
17 10     10   51 use Carp;
  10         24  
  10         202  
18 10     10   42  
  10         21  
  10         628  
19             use Time::Piece;
20 10     10   525  
  10         8728  
  10         81  
21             # Inherit from Base
22             use base qw/ Astro::FITS::HdrTrans::Base /;
23 10     10   728  
  10         16  
  10         1497  
24             use vars qw/ $VERSION /;
25 10     10   52  
  10         17  
  10         14518  
26             $VERSION = "1.65";
27              
28             # for a constant mapping, there is no FITS header, just a generic
29             # header that is constant
30             my %CONST_MAP = (
31             INST_DHS => 'HET_GSD',
32             COORDINATE_UNITS => 'decimal',
33             EQUINOX => 'current',
34             TELESCOPE => 'JCMT',
35             );
36              
37             # NULL mappings used to override base class implementations
38             my @NULL_MAP = ();
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             AMBIENT_TEMPERATURE => "C5AT",
45             APERTURE => "C7AP",
46             AZIMUTH_START => "C4AZ",
47             BACKEND => "C1BKE",
48             BACKEND_SECTIONS => "C3NRS",
49             CHOP_FREQUENCY => "C4FRQ",
50             CHOP_THROW => "C4THROW",
51             COORDINATE_SYSTEM => "C4CSC",
52             COORDINATE_TYPE => "C4LSC",
53             CYCLE_LENGTH => "C3CL",
54             # DEC_BASE => "",
55             ELEVATION_START => "C4EL",
56             # FILENAME => "GSDFILE",
57             FILTER => "C7FIL",
58             FREQUENCY_RESOLUTION => "C12FR",
59             FRONTEND => "C1RCV",
60             HUMIDITY => "C5RH",
61             NUMBER_OF_CYCLES => "C3NCI",
62             NUMBER_OF_SUBSCANS => "C3NIS",
63             OBJECT => "C1SNA1",
64             OBSERVATION_MODE => "C6ST",
65             OBSERVATION_NUMBER => "C1SNO",
66             PROJECT => "C1PID",
67             RA_BASE => "C4RADATE",
68             RECEIVER_TEMPERATURE => "C12RT",
69             ROTATION => "CELL_V2Y",
70             REST_FREQUENCY => "C12RF",
71             SEEING => "C7SEEING",
72             SWITCH_MODE => "C6MODE",
73             SYSTEM_TEMPERATURE => "C12SST",
74             TAU => "C7TAU225",
75             USER_AZ_CORRECTION => "UAZ",
76             USER_EL_CORRECTION => "UEL",
77             VELOCITY => "C7VR",
78             VELOCITY_REFERENCE_FRAME => "C12VREF",
79             VELOCITY_TYPE => "C12VDEF",
80             X_BASE => "C4RX",
81             Y_BASE => "C4RY",
82             X_DIM => "C6XNP",
83             Y_DIM => "C6YNP",
84             X_REQUESTED => "C4SX",
85             Y_REQUESTED => "C4SY",
86             X_SCALE => "C6DX",
87             Y_SCALE => "C6DY",
88             );
89              
90             # Create the translation methods
91             __PACKAGE__->_generate_lookup_methods( \%CONST_MAP, \%UNIT_MAP, \@NULL_MAP );
92              
93             =head1 METHODS
94              
95             =over 4
96              
97             =item B<can_translate>
98              
99             Returns true if the supplied headers can be handled by this class.
100              
101             $cando = $class->can_translate( \%hdrs );
102              
103             For this class, the method will return true if the B<C1RCV> header exists
104             and matches the regular expression C</^rx(a|b|w)/i>.
105              
106             =cut
107              
108             my $self = shift;
109             my $headers = shift;
110 20     20 1 47  
111 20         42 if ( exists( $headers->{'C1RCV'} ) &&
112             defined( $headers->{'C1RCV'} ) &&
113 20 50 33     116 ( $headers->{'C1RCV'} =~ /^rx(a|b|w)/i ||
      0        
      33        
114             $headers->{'C1RCV'} =~ /^fts/i ) ) {
115             return 1;
116             } else {
117 0         0 return 0;
118             }
119 20         295 }
120              
121             =back
122              
123             =head1 COMPLEX CONVERSIONS
124              
125             These methods are more complicated than a simple mapping. We have to
126             provide both from- and to-FITS conversions All these routines are
127             methods and the to_ routines all take a reference to a hash and return
128             the translated value (a many-to-one mapping) The from_ methods take a
129             reference to a generic hash and return a translated hash (sometimes
130             these are many-to-many)
131              
132             =over 4
133              
134             =item B<to_INSTRUMENT>
135              
136             Sets the C<INSTRUMENT> generic header. For RxA3i, sets the value
137             to RXA3. For RxB, sets the value to RXB3.
138              
139             =cut
140              
141             my $self = shift;
142             my $FITS_headers = shift;
143             my $return;
144 0     0 1    
145 0           if ( exists( $FITS_headers->{'C1RCV'} ) ) {
146 0           $return = $FITS_headers->{'C1RCV'};
147             if ( $return =~ /^rxa3/i ) {
148 0 0         $return = "RXA3";
149 0           } elsif ( $return =~ /^rxb/i ) {
150 0 0         $return = "RXB3";
    0          
151 0           }
152             }
153 0           return $return;
154             }
155              
156 0           =item B<to_OBSERVATION_ID>
157              
158             Calculate a unique Observation ID.
159              
160             =cut
161              
162             # Note this routine is generic for JCMT heterodyne instrumentation.
163             # Would be completely generic if BACKEND was not used in preference to instrument.
164              
165             my $self = shift;
166             my $FITS_headers = shift;
167             my $backend = lc( $self->to_BACKEND( $FITS_headers ) );
168             my $obsnum = $self->to_OBSERVATION_NUMBER( $FITS_headers );
169 0     0 1   my $dateobs = $self->to_UTSTART( $FITS_headers );
170 0           my $datetime = $dateobs->datetime;
171 0           $datetime =~ s/-//g;
172 0           $datetime =~ s/://g;
173 0            
174 0           my $obsid = join('_', $backend, $obsnum, $datetime);
175 0           return $obsid;
176 0           }
177              
178 0           =item B<to_UTDATE>
179 0            
180             Translates the C<C3DAT> header into a YYYYMMDD integer.
181              
182             =cut
183              
184             my $self = shift;
185             my $FITS_headers = shift;
186             my $return;
187              
188             if ( exists( $FITS_headers->{'C3DAT'} ) ) {
189 0     0 1   $FITS_headers->{'C3DAT'} =~ /(\d{4})\.(\d\d)(\d{1,2})/;
190 0           my $day = (length($3) == 2) ? $3 : $3 . "0";
191 0           my $ut = "$1-$2-$day";
192             $return = sprintf("%04d%02d%02d", $1, $2, $3);
193 0 0         }
194 0           return $return;
195 0 0         }
196 0            
197 0           =item B<from_UTDATE>
198              
199 0           Translates YYYYMMDD integer to C3DAT header.
200              
201             =cut
202              
203             my $self = shift;
204             my $generic_headers = shift;
205             my %return_hash;
206             if (exists $generic_headers->{UTDATE}) {
207             my $date = $generic_headers->{UTDATE};
208             return () unless defined $date;
209 0     0 1   $return_hash{UTDATE} = sprintf("%04d.%02d%02d",
210 0           substr($date,0,4),
211 0           substr($date,4,2),
212 0 0         substr($date,6,2));
213 0           }
214 0 0         return %return_hash;
215 0           }
216              
217             =item B<to_UTSTART>
218              
219             Translates the C<C3DAT> and C<C3UT> headers into a C<Time::Piece> object.
220 0            
221             =cut
222              
223             my $self = shift;
224             my $FITS_headers = shift;
225              
226             my $return;
227             if ( exists( $FITS_headers->{'C3DAT'} ) && defined( $FITS_headers->{'C3DAT'} ) &&
228             exists( $FITS_headers->{'C3UT'} ) && defined( $FITS_headers->{'C3UT'} ) ) {
229             my $hour = int( $FITS_headers->{'C3UT'} );
230 0     0 1   my $minute = int ( ( $FITS_headers->{'C3UT'} - $hour ) * 60 );
231 0           my $second = int ( ( ( ( $FITS_headers->{'C3UT'} - $hour ) * 60 ) - $minute ) * 60 );
232             $FITS_headers->{'C3DAT'} =~ /(\d{4})\.(\d\d)(\d{1,2})/;
233 0           my $day = (length($3) == 2) ? $3 : $3 . "0";
234 0 0 0       $return = Time::Piece->strptime(sprintf("%4u-%02u-%02uT%02u:%02u:%02u", $1, $2, $day, $hour, $minute, $second ),
      0        
      0        
235             "%Y-%m-%dT%T");
236 0           }
237 0           return $return;
238 0           }
239 0            
240 0 0         =item B<to_UTEND>
241 0            
242             Translates the C<C3DAT>, C<C3UT>, C<C3NIS>, C<C3CL>, C<C3NCP>, and C<C3NCI> headers
243             into a C<Time::Piece> object.
244 0            
245             =cut
246              
247             my $self = shift;
248             my $FITS_headers = shift;
249             my ($t, $expt);
250             if ( exists( $FITS_headers->{'C3DAT'} ) && defined( $FITS_headers->{'C3DAT'} ) &&
251             exists( $FITS_headers->{'C3UT'} ) && defined( $FITS_headers->{'C3UT'} ) ) {
252             my $hour = int( $FITS_headers->{'C3UT'} );
253             my $minute = int ( ( $FITS_headers->{'C3UT'} - $hour ) * 60 );
254             my $second = int ( ( ( ( $FITS_headers->{'C3UT'} - $hour ) * 60 ) - $minute ) * 60 );
255 0     0 1   $FITS_headers->{'C3DAT'} =~ /(\d{4})\.(\d\d)(\d{1,2})/;
256 0           my $day = (length($3) == 2) ? $3 : $3 . "0";
257 0           $t = Time::Piece->strptime(sprintf("%4u-%02u-%02uT%02u:%02u:%02u", $1, $2, $day, $hour, $minute, $second ),
258 0 0 0       "%Y-%m-%dT%T");
      0        
      0        
259             }
260 0            
261 0           $expt = $self->to_EXPOSURE_TIME( $FITS_headers );
262 0            
263 0           $t += $expt;
264 0 0          
265 0           return $t;
266              
267             }
268              
269 0           =item B<to_BANDWIDTH_MODE>
270              
271 0           Uses the C3NRS (number of backend sections), C3NFOC (number of
272             frontend output channels) and C3NCH (number of channels) to form a
273 0           string that is of the format 250MHzx2048. To obtain this, the
274             bandwidth (250MHz in this example) is calculated as 125MHz * C3NRS /
275             C3NFOC. The number of channels is taken directly and not manipulated
276             in any way.
277              
278             If appropriate, the bandwidth may be given in GHz.
279              
280             =cut
281              
282             my $self = shift;
283             my $FITS_headers = shift;
284              
285             my $return;
286              
287             if ( exists( $FITS_headers->{'C3NRS'} ) && defined( $FITS_headers->{'C3NRS'} ) &&
288             exists( $FITS_headers->{'C3NFOC'} ) && defined( $FITS_headers->{'C3NFOC'} ) &&
289             exists( $FITS_headers->{'C3NCH'} ) && defined( $FITS_headers->{'C3NCH'} ) ) {
290              
291 0     0 1   my $bandwidth = 125 * $FITS_headers->{'C3NRS'} / $FITS_headers->{'C3NFOC'};
292 0            
293             if ( $bandwidth >= 1000 ) {
294 0           $bandwidth /= 1000;
295             $return = sprintf( "%dGHzx%d", $bandwidth, $FITS_headers->{'C3NCH'} );
296 0 0 0       } else {
      0        
      0        
      0        
      0        
297             $return = sprintf( "%dMHzx%d", $bandwidth, $FITS_headers->{'C3NCH'} );
298             }
299             }
300 0            
301             return $return;
302 0 0          
303 0           }
304 0            
305             =item B<to_EXPOSURE_TIME>
306 0            
307             =cut
308              
309             my $self = shift;
310 0           my $FITS_headers = shift;
311             my $expt = 0;
312              
313             if ( exists( $FITS_headers->{'C6ST'} ) && defined( $FITS_headers->{'C6ST'} ) ) {
314              
315             my $c6st = uc( $FITS_headers->{'C6ST'} );
316              
317             if ( $c6st eq 'RASTER' ) {
318              
319 0     0 1   if ( exists( $FITS_headers->{'C3NSAMPL'} ) && defined( $FITS_headers->{'C3NSAMPL'} ) &&
320 0           exists( $FITS_headers->{'C3CL'} ) && defined( $FITS_headers->{'C3CL'} ) &&
321 0           exists( $FITS_headers->{'C3NPP'} ) && defined( $FITS_headers->{'C3NPP'} ) ) {
322              
323 0 0 0       my $c3nsampl = $FITS_headers->{'C3NSAMPL'};
324             my $c3cl = $FITS_headers->{'C3CL'};
325 0           my $c3npp = $FITS_headers->{'C3NPP'};
326              
327 0 0 0       # raster.
    0          
328             $expt = 15 + $c3nsampl * $c3cl * ( 1 + 1 / sqrt( $c3npp ) ) * 1.4;
329 0 0 0       }
      0        
      0        
      0        
      0        
330             } elsif ( $c6st eq 'PATTERN' or $c6st eq 'GRID' ) {
331              
332             my $c6mode = '';
333 0            
334 0           if ( exists( $FITS_headers->{'C6MODE'} ) && defined( $FITS_headers->{'C6MODE'} ) ) {
335 0           $c6mode = $FITS_headers->{'C6MODE'};
336             } else {
337             $c6mode = 'BEAMSWITCH';
338 0           }
339              
340             if ( exists( $FITS_headers->{'C3NSAMPL'} ) && defined( $FITS_headers->{'C3NSAMPL'} ) &&
341             exists( $FITS_headers->{'C3NCYCLE'} ) && defined( $FITS_headers->{'C3NCYCLE'} ) &&
342 0           exists( $FITS_headers->{'C3CL'} ) && defined( $FITS_headers->{'C3CL'} ) ) {
343              
344 0 0 0       my $c3nsampl = $FITS_headers->{'C3NSAMPL'};
345 0           my $c3ncycle = $FITS_headers->{'C3NCYCLE'};
346             my $c3cl = $FITS_headers->{'C3CL'};
347 0            
348             if ( $c6mode eq 'POSITION_SWITCH' ) {
349              
350 0 0 0       # position switch pattern/grid.
      0        
      0        
      0        
      0        
351             $expt = 6 + $c3nsampl * $c3ncycle * $c3cl * 1.35;
352              
353             } elsif ( $c6mode eq 'BEAMSWITCH' ) {
354 0            
355 0           # beam switch pattern/grid.
356 0           $expt = 6 + $c3nsampl * $c3ncycle * $c3cl * 1.35;
357              
358 0 0         } elsif ( $c6mode eq 'CHOPPING' ) {
    0          
    0          
359             if ( exists( $FITS_headers->{'C1RCV'} ) && defined( $FITS_headers->{'C1RCV'} ) ) {
360             my $c1rcv = uc( $FITS_headers->{'C1RCV'} );
361 0           if ( $c1rcv eq 'RXA3I' ) {
362              
363             # fast frequency switch pattern/grid, receiver A.
364             $expt = 15 + $c3nsampl * $c3ncycle * $c3cl * 1.20;
365              
366 0           } elsif ( $c1rcv eq 'RXB' ) {
367              
368             # slow frequency switch pattern/grid, receiver B.
369 0 0 0       $expt = 18 + $c3nsampl * $c3ncycle * $c3cl * 1.60;
370 0            
371 0 0         }
    0          
372             }
373             }
374 0           }
375             } else {
376              
377             my $c6mode;
378             if ( exists( $FITS_headers->{'C6MODE'} ) && defined( $FITS_headers->{'C6MODE'} ) ) {
379 0           $c6mode = $FITS_headers->{'C6MODE'};
380             } else {
381             $c6mode = 'BEAMSWITCH';
382             }
383              
384             if ( exists( $FITS_headers->{'C3NSAMPL'} ) && defined( $FITS_headers->{'C3NSAMPL'} ) &&
385             exists( $FITS_headers->{'C3NCYCLE'} ) && defined( $FITS_headers->{'C3NCYCLE'} ) &&
386             exists( $FITS_headers->{'C3CL'} ) && defined( $FITS_headers->{'C3CL'} ) ) {
387 0            
388 0 0 0       my $c3nsampl = $FITS_headers->{'C3NSAMPL'};
389 0           my $c3ncycle = $FITS_headers->{'C3NCYCLE'};
390             my $c3cl = $FITS_headers->{'C3CL'};
391 0            
392             if ( $c6mode eq 'POSITION_SWITCH' ) {
393              
394 0 0 0       # position switch sample.
      0        
      0        
      0        
      0        
395             $expt = 4.8 + $c3nsampl * $c3ncycle * $c3cl * 1.10;
396              
397             } elsif ( $c6mode eq 'BEAMSWITCH' ) {
398 0            
399 0           # beam switch sample.
400 0           $expt = 4.8 + $c3nsampl * $c3ncycle * $c3cl * 1.25;
401              
402 0 0         } elsif ( $c6mode eq 'CHOPPING' ) {
    0          
    0          
403             if ( exists( $FITS_headers->{'C1RCV'} ) && defined( $FITS_headers->{'C1RCV'} ) ) {
404             my $c1rcv = uc( $FITS_headers->{'C1RCV'} );
405 0           if ( $c1rcv eq 'RXA3I' ) {
406              
407             # fast frequency switch sample, receiver A.
408             $expt = 3 + $c3nsampl * $c3ncycle * $c3cl * 1.10;
409              
410 0           } elsif ( $c1rcv eq 'RXB' ) {
411              
412             # slow frequency switch sample, receiver B.
413 0 0 0       $expt = 3 + $c3nsampl * $c3ncycle * $c3cl * 1.40;
414 0           }
415 0 0         }
    0          
416             }
417             }
418 0           }
419             }
420              
421             return $expt;
422             }
423 0            
424             =item B<to_SYSTEM_VELOCITY>
425              
426             Translate the C<C12VREF> and C<C12VDEF> headers into one combined header.
427              
428             =cut
429              
430             my $self = shift;
431 0           my $FITS_headers = shift;
432             my $return;
433             if ( exists( $FITS_headers->{'C12VREF'} ) && defined( $FITS_headers->{'C12VREF'} ) &&
434             exists( $FITS_headers->{'C12VDEF'} ) && defined( $FITS_headers->{'C12VDEF'} ) ) {
435             $return = substr( $FITS_headers->{'C12VDEF'}, 0, 3 ) . substr( $FITS_headers->{'C12VREF'}, 0, 3 );
436             }
437             return $return;
438             }
439              
440             =back
441 0     0 1    
442 0           =head1 AUTHOR
443 0            
444 0 0 0       Brad Cavanagh E<lt>b.cavanagh@jach.hawaii.eduE<gt>,
      0        
      0        
445             Tim Jenness E<lt>t.jenness@jach.hawaii.eduE<gt>
446 0            
447             =head1 COPYRIGHT
448 0            
449             Copyright (C) 2008 Science and Technology Facilities Council.
450             Copyright (C) 2003-2007 Particle Physics and Astronomy Research Council.
451             All Rights Reserved.
452              
453             This program is free software; you can redistribute it and/or modify it under
454             the terms of the GNU General Public License as published by the Free Software
455             Foundation; either version 2 of the License, or (at your option) any later
456             version.
457              
458             This program is distributed in the hope that it will be useful,but WITHOUT ANY
459             WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
460             PARTICULAR PURPOSE. See the GNU General Public License for more details.
461              
462             You should have received a copy of the GNU General Public License along with
463             this program; if not, write to the Free Software Foundation, Inc., 59 Temple
464             Place,Suite 330, Boston, MA 02111-1307, USA
465              
466             =cut
467              
468             1;