File Coverage

blib/lib/Astro/FITS/HdrTrans/JCMT_GSD_DB.pm
Criterion Covered Total %
statement 102 139 73.3
branch 20 66 30.3
condition 19 93 20.4
subroutine 18 18 100.0
pod 9 9 100.0
total 168 325 51.6


line stmt bran cond sub pod time code
1              
2             =head1 NAME
3              
4             Astro::FITS::HdrTrans::JCMT_GSD_DB - JCMT GSD Database header translations
5              
6             =head1 DESCRIPTION
7              
8             Converts information contained in JCMT heterodyne database 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   15756935 use warnings;
  10         78  
16 10     10   43 use strict;
  10         23  
  10         304  
17 10     10   40 use Carp;
  10         25  
  10         183  
18 10     10   39  
  10         17  
  10         621  
19             use Time::Piece;
20 10     10   735  
  10         9618  
  10         66  
21             # Inherit from Base
22             use base qw/ Astro::FITS::HdrTrans::Base /;
23 10     10   665  
  10         25  
  10         1729  
24             use vars qw/ $VERSION /;
25 10     10   54  
  10         19  
  10         12701  
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 => "TAMB",
45             APERTURE => "APERTURE",
46             AZIMUTH_START => "AZ",
47             BACKEND => "BACKEND",
48             BACKEND_SECTIONS => "NORSECT",
49             CHOP_FREQUENCY => "CHOPFREQ",
50             CHOP_THROW => "CHOPTHRW",
51             COORDINATE_SYSTEM => "COORDCD",
52             # COORDINATE_TYPE => "C4LSC",
53             CYCLE_LENGTH => "CYCLLEN",
54             # DEC_BASE => "",
55             ELEVATION_START => "EL",
56             FILENAME => "GSDFILE",
57             FILTER => "FILTER",
58             FREQUENCY_RESOLUTION => "FREQRES",
59             FRONTEND => "FRONTEND",
60             HUMIDITY => "HUMIDITY",
61             NUMBER_OF_CYCLES => "NOCYCLES",
62             NUMBER_OF_SUBSCANS => "NOSCANS",
63             OBJECT => "OBJECT",
64             OBSERVATION_MODE => "OBSMODE",
65             OBSERVATION_NUMBER => "SCAN",
66             PROJECT => "PROJID",
67             # RA_BASE => "C4RADATE",
68             RECEIVER_TEMPERATURE => "TRX",
69             ROTATION => "YPOSANG",
70             REST_FREQUENCY => "RESTFRQ1",
71             SEEING => "PHA",
72             SWITCH_MODE => "SWMODE",
73             SYSTEM_TEMPERATURE => "STSYS",
74             TAU => "TAU",
75             USER_AZ_CORRECTION => "UXPNT",
76             USER_EL_CORRECTION => "UYPNT",
77             VELOCITY => "VELOCITY",
78             VELOCITY_REFERENCE_FRAME => "VREF",
79             VELOCITY_TYPE => "VDEF",
80             X_BASE => "XREF",
81             Y_BASE => "YREF",
82             X_DIM => "NOXPTS",
83             Y_DIM => "NOYPTS",
84             X_REQUESTED => "XSOURCE",
85             Y_REQUESTED => "YSOURCE",
86             X_SCALE => "DELTAX",
87             Y_SCALE => "DELTAY",
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 "GSDFILE" header
104             exists and the "SCA#" header exist.
105              
106             =cut
107              
108             my $self = shift;
109             my $headers = shift;
110 20     20 1 45  
111 20         43 if (exists $headers->{GSDFILE} && exists $headers->{"SCA#"}) {
112             return 1;
113 20 50 66     88 }
114 1         4 return 0;
115             }
116 19         285  
117             =back
118              
119             =head1 COMPLEX CONVERSIONS
120              
121             These methods are more complicated than a simple mapping. We have to
122             provide both from- and to-FITS conversions All these routines are
123             methods and the to_ routines all take a reference to a hash and return
124             the translated value (a many-to-one mapping) The from_ methods take a
125             reference to a generic hash and return a translated hash (sometimes
126             these are many-to-many)
127              
128             =over 4
129              
130             =item B<to_INSTRUMENT>
131              
132             Sets the C<INSTRUMENT> generic header. For RxA3i, sets the value
133             to RXA3. For RxB, sets the value to RXB3.
134              
135             =cut
136              
137             my $self = shift;
138             my $FITS_headers = shift;
139             my $return;
140 1     1 1 3  
141 1         1 if ( exists( $FITS_headers->{'FRONTEND'} ) ) {
142 1         10 $return = $FITS_headers->{'FRONTEND'};
143             if ( $return =~ /^rxa3/i ) {
144 1 50       4 $return = "RXA3";
145 1         12 } elsif ( $return =~ /^rxb/i ) {
146 1 50       8 $return = "RXB3";
    50          
147 0         0 }
148             }
149 1         2 return $return;
150             }
151              
152 1         3 =item B<to_OBSERVATION_ID>
153              
154             Calculate a unique Observation ID.
155              
156             =cut
157              
158             # Note this routine is generic for JCMT heterodyne instrumentation.
159             # Would be completely generic if BACKEND was not used in preference to instrument.
160              
161             my $self = shift;
162             my $FITS_headers = shift;
163             my $backend = lc( $self->to_BACKEND( $FITS_headers ) );
164             my $obsnum = $self->to_OBSERVATION_NUMBER( $FITS_headers );
165 1     1 1 2 my $dateobs = $self->to_UTSTART( $FITS_headers );
166 1         2 my $datetime = $dateobs->datetime;
167 1         16 $datetime =~ s/-//g;
168 1         19 $datetime =~ s/://g;
169 1         5  
170 1         5 my $obsid = join('_', $backend, $obsnum, $datetime);
171 1         86 return $obsid;
172 1         12 }
173              
174 1         3 =item B<to_UTDATE>
175 1         7  
176             Translates the C<DATE_OBS> or C<LONGDATEOBS> header into a
177             YYYYMMDD integer.
178              
179             =cut
180              
181             my $self = shift;
182             my $FITS_headers = shift;
183             my $date = _mysql_convert_date( _date_header( $FITS_headers ), 1);
184             return $date->strftime('%Y%m%d');
185             }
186 1     1 1 2  
187 1         2 =item B<to_UTSTART>
188 1         3  
189 1         10 Translates the DB date header into a C<Time::Piece> object.
190              
191             =cut
192              
193             my $self = shift;
194             my $FITS_headers = shift;
195             return _mysql_convert_date( _date_header( $FITS_headers ));
196             }
197              
198             =item B<to_UTEND>
199 2     2 1 12  
200 2         4 Translates the database date header into a C<Time::Piece> object and adds
201 2         6 on the exposure time.
202              
203             =cut
204              
205             my $self = shift;
206             my $FITS_headers = shift;
207              
208             my $return = _mysql_convert_date( _date_header( $FITS_headers ) );
209             return undef unless defined $return;
210             my $expt = $self->to_EXPOSURE_TIME( $FITS_headers );
211              
212 1     1 1 3 $return += $expt;
213 1         2 return $return;
214             }
215 1         2  
216 1 50       4 =item B<to_BANDWIDTH_MODE>
217 1         3  
218             Uses the NORSECT (number of backend sections), NOFCHAN (number of
219 1         5 frontend output channels) and NOBCHAN (number of channels) to form a
220 1         67 string that is of the format 250MHzx2048. To obtain this, the
221             bandwidth (250MHz in this example) is calculated as 125MHz * NORSECT /
222             NOFCHAN. The number of channels is taken directly and not manipulated
223             in any way.
224              
225             If appropriate, the bandwidth may be given in GHz.
226              
227             =cut
228              
229             my $self = shift;
230             my $FITS_headers = shift;
231              
232             my $return;
233              
234             if ( exists( $FITS_headers->{'NORSECT'} ) && defined( $FITS_headers->{'NORSECT'} ) &&
235             exists( $FITS_headers->{'NOFCHAN'} ) && defined( $FITS_headers->{'NOFCHAN'} ) &&
236             exists( $FITS_headers->{'NOBCHAN'} ) && defined( $FITS_headers->{'NOBCHAN'} ) ) {
237 1     1 1 3  
238 1         1 my $bandwidth = 125 * $FITS_headers->{'NORSECT'} / $FITS_headers->{'NOFCHAN'};
239              
240 1         2 if ( $bandwidth >= 1000 ) {
241             $bandwidth /= 1000;
242 1 50 33     14 $return = sprintf( "%dGHzx%d", $bandwidth, $FITS_headers->{'NOBCHAN'} );
      33        
      33        
      33        
      33        
243             } else {
244             $return = sprintf( "%dMHzx%d", $bandwidth, $FITS_headers->{'NOBCHAN'} );
245             }
246 1         11 }
247              
248 1 50       8 return $return;
249 0         0  
250 0         0 }
251              
252 1         6  
253             =item B<to_EXPOSURE_TIME>
254              
255             =cut
256 1         4  
257             my $self = shift;
258             my $FITS_headers = shift;
259             my $expt = 0;
260              
261             if ( exists( $FITS_headers->{'OBSMODE'} ) && defined( $FITS_headers->{'OBSMODE'} ) ) {
262              
263             my $obsmode = uc( $FITS_headers->{'OBSMODE'} );
264              
265             if ( $obsmode eq 'RASTER' ) {
266 2     2 1 4  
267 2         3 if ( exists( $FITS_headers->{'NSCAN'} ) && defined( $FITS_headers->{'NSCAN'} ) &&
268 2         2 exists( $FITS_headers->{'CYCLLEN'} ) && defined( $FITS_headers->{'CYCLLEN'} ) &&
269             exists( $FITS_headers->{'NOCYCPTS'} ) && defined( $FITS_headers->{'NOCYCPTS'} ) ) {
270 2 50 33     42  
271             my $nscan = $FITS_headers->{'NSCAN'};
272 2         7 my $cycllen = $FITS_headers->{'CYCLLEN'};
273             my $nocycpts = $FITS_headers->{'NOCYCPTS'};
274 2 50 33     16  
    50          
275             # raster.
276 0 0 0     0 $expt = 15 + $nscan * $cycllen * ( 1 + 1 / sqrt( $nocycpts ) ) * 1.4;
      0        
      0        
      0        
      0        
277             }
278             } elsif ( $obsmode eq 'PATTERN' or $obsmode eq 'GRID' ) {
279              
280 0         0 my $swmode = '';
281 0         0  
282 0         0 if ( exists( $FITS_headers->{'SWMODE'} ) && defined( $FITS_headers->{'SWMODE'} ) ) {
283             $swmode = $FITS_headers->{'SWMODE'};
284             } else {
285 0         0 $swmode = 'BEAMSWITCH';
286             }
287              
288             if ( exists( $FITS_headers->{'NSCAN'} ) && defined( $FITS_headers->{'NSCAN'} ) &&
289 0         0 exists( $FITS_headers->{'NCYCLE'} ) && defined( $FITS_headers->{'NCYCLE'} ) &&
290             exists( $FITS_headers->{'CYCLLEN'} ) && defined( $FITS_headers->{'CYCLLEN'} ) ) {
291 0 0 0     0  
292 0         0 my $nscan = $FITS_headers->{'NSCAN'};
293             my $ncycle = $FITS_headers->{'NCYCLE'};
294 0         0 my $cycllen = $FITS_headers->{'CYCLLEN'};
295              
296             if ( $swmode eq 'POSITION_SWITCH' ) {
297 0 0 0     0  
      0        
      0        
      0        
      0        
298             # position switch pattern/grid.
299             $expt = 6 + $nscan * $ncycle * $cycllen * 1.35;
300              
301 0         0 } elsif ( $swmode eq 'BEAMSWITCH' ) {
302 0         0  
303 0         0 # beam switch pattern/grid.
304             $expt = 6 + $nscan * $ncycle * $cycllen * 1.35;
305 0 0       0  
    0          
    0          
306             } elsif ( $swmode eq 'CHOPPING' ) {
307             if ( exists( $FITS_headers->{'FRONTEND'} ) && defined( $FITS_headers->{'FRONTEND'} ) ) {
308 0         0 my $frontend = uc( $FITS_headers->{'FRONTEND'} );
309             if ( $frontend eq 'RXA3I' ) {
310              
311             # fast frequency switch pattern/grid, receiver A.
312             $expt = 15 + $nscan * $ncycle * $cycllen * 1.20;
313 0         0  
314             } elsif ( $frontend eq 'RXB' ) {
315              
316 0 0 0     0 # slow frequency switch pattern/grid, receiver B.
317 0         0 $expt = 18 + $nscan * $ncycle * $cycllen * 1.60;
318 0 0       0  
    0          
319             }
320             }
321 0         0 }
322             }
323             } else {
324              
325             my $swmode;
326 0         0 if ( exists( $FITS_headers->{'SWMODE'} ) && defined( $FITS_headers->{'SWMODE'} ) ) {
327             $swmode = uc( $FITS_headers->{'SWMODE'} );
328             } else {
329             $swmode = 'BEAMSWITCH';
330             }
331              
332             if ( exists( $FITS_headers->{'NSCAN'} ) && defined( $FITS_headers->{'NSCAN'} ) &&
333             exists( $FITS_headers->{'NCYCLE'} ) && defined( $FITS_headers->{'NCYCLE'} ) &&
334 2         4 exists( $FITS_headers->{'CYCLLEN'} ) && defined( $FITS_headers->{'CYCLLEN'} ) ) {
335 2 50 33     8  
336 2         6 my $nscan = $FITS_headers->{'NSCAN'};
337             my $ncycle = $FITS_headers->{'NCYCLE'};
338 0         0 my $cycllen = $FITS_headers->{'CYCLLEN'};
339              
340             if ( $swmode eq 'POSITION_SWITCH' ) {
341 2 50 33     23  
      33        
      33        
      33        
      33        
342             # position switch sample.
343             $expt = 4.8 + $nscan * $ncycle * $cycllen * 1.10;
344              
345 2         3 } elsif ( $swmode eq 'BEAMSWITCH' ) {
346 2         3  
347 2         3 # beam switch sample.
348             $expt = 4.8 + $nscan * $ncycle * $cycllen * 1.25;
349 2 50       6  
    50          
    0          
350             } elsif ( $swmode eq 'CHOPPING' ) {
351             if ( exists( $FITS_headers->{'FRONTEND'} ) && defined( $FITS_headers->{'FRONTEND'} ) ) {
352 0         0 my $frontend = uc( $FITS_headers->{'FRONTEND'} );
353             if ( $frontend eq 'RXA3I' ) {
354              
355             # fast frequency switch sample, receiver A.
356             $expt = 3 + $nscan * $ncycle * $cycllen * 1.10;
357 2         6  
358             } elsif ( $frontend eq 'RXB' ) {
359              
360 0 0 0     0 # slow frequency switch sample, receiver B.
361 0         0 $expt = 3 + $nscan * $ncycle * $cycllen * 1.40;
362 0 0       0 }
    0          
363             }
364             }
365 0         0 }
366             }
367             }
368              
369             return $expt;
370 0         0 }
371              
372             =item B<to_SYSTEM_VELOCITY>
373              
374             Translate the C<VREF> and C<C12VDEF> headers into one combined header.
375              
376             =cut
377              
378 2         5 my $self = shift;
379             my $FITS_headers = shift;
380             my $return;
381             if ( exists( $FITS_headers->{'VREF'} ) && defined( $FITS_headers->{'VREF'} ) &&
382             exists( $FITS_headers->{'VDEF'} ) && defined( $FITS_headers->{'VDEF'} ) ) {
383             $return = uc( substr( $FITS_headers->{'VDEF'}, 0, 3 ) . substr( $FITS_headers->{'VREF'}, 0, 3 ) );
384             }
385             return $return;
386             }
387              
388 1     1 1 2 =back
389 1         2  
390 1         7 =begin __PRIVATE
391 1 50 33     14  
      33        
      33        
392             =over 4
393 1         4  
394             =item B<_date_header>
395 1         4  
396             Works out which header corresponds to the date field in MySQL format and returns the value.
397              
398             $value = _date_header( $FITS_headers );
399              
400             Returns undef if none found.
401              
402             =cut
403              
404             my $FITS_headers = shift;
405             # For compatability with Sybase database, also accept LONGDATEOBS LONGDATE
406             for my $key (qw/ DATE_OBS LONGDATEOBS LONGDATE / ) {
407             if (exists $FITS_headers->{$key} && defined $FITS_headers->{$key}) {
408             return $FITS_headers->{$key};
409             }
410             }
411             return;
412             }
413              
414             =item B<_mysql_convert_date>
415 4     4   5  
416             Converts a MySQL date to Time::Piece object.
417 4         7  
418 4 50 33     14 $date = _mysql_convert_date( $string );
419 4         18  
420             Optional flag can be set to true to drop Hours, minutes and seconds from parse.
421              
422 0         0 $utday = _mysql_convert_date( $string, 1);
423              
424             Returns undef if no string is supplied.
425              
426             =cut
427              
428             my $date = shift;
429             my $drophms = shift;
430             return undef unless $date;
431              
432             # The UT header is in MySQL format, which is something like
433             # "2002-03-15 07:04:35".
434             #
435             # Also support the Sybase format, which is something like
436             # "Mar 15 2002 7:04:35:234AM ". We first need to remove the number
437             # of milliseconds, then the whitespace at the end, then use the
438             # "%b%t%d%t%Y%t%I:%M:%S%p" format.
439              
440 4     4   7 # General cleanup
441 4         7 $date =~ s/\s*$//;
442 4 50       8  
443             my $return;
444              
445             if ($date =~ /^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d/) {
446             if ($drophms) {
447             $date =~ s/\s*\d\d?:\d\d:\d\d$//;
448             $return = Time::Piece->strptime( $date,
449             "%Y-%m-%d" );
450             } else {
451             $return = Time::Piece->strptime( $date,
452             "%Y-%m-%d %T" );
453 4         20 }
454             } else {
455 4         13 $date =~ s/:\d\d\d//;
456              
457 4 50       12 if ($drophms) {
458 4 100       7 $date =~ s/\s*\d\d?:\d\d:\d\d[A|P]M$//;
459 1         5 $return = Time::Piece->strptime( $date,
460 1         6 "%b %e %Y" );
461             } else {
462             $return = Time::Piece->strptime( $date,
463 3         15 "%b %e %Y %l:%M:%S%p" );
464             }
465             }
466             return $return;
467 0         0 }
468              
469 0 0       0 =back
470 0         0  
471 0         0 =end __PRIVATE
472              
473             =head1 AUTHOR
474 0         0  
475             Brad Cavanagh E<lt>b.cavanagh@jach.hawaii.eduE<gt>,
476             Tim Jenness E<lt>t.jenness@jach.hawaii.eduE<gt>
477              
478 4         254 =head1 COPYRIGHT
479              
480             Copyright (C) 2008 Science and Technology Facilities Council.
481             Copyright (C) 2003-2007 Particle Physics and Astronomy Research Council.
482             All Rights Reserved.
483              
484             This program is free software; you can redistribute it and/or modify it under
485             the terms of the GNU General Public License as published by the Free Software
486             Foundation; either version 2 of the License, or (at your option) any later
487             version.
488              
489             This program is distributed in the hope that it will be useful,but WITHOUT ANY
490             WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
491             PARTICULAR PURPOSE. See the GNU General Public License for more details.
492              
493             You should have received a copy of the GNU General Public License along with
494             this program; if not, write to the Free Software Foundation, Inc., 59 Temple
495             Place,Suite 330, Boston, MA 02111-1307, USA
496              
497             =cut
498              
499             1;