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