File Coverage

blib/lib/Astro/FITS/HdrTrans/JCMT_GSD.pm
Criterion Covered Total %
statement 21 142 14.7
branch 1 70 1.4
condition 2 114 1.7
subroutine 7 16 43.7
pod 10 10 100.0
total 41 352 11.6


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