File Coverage

blib/lib/Astro/FITS/Header/NDF.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Astro::FITS::Header::NDF;
2              
3             =head1 NAME
4              
5             Astro::FITS::Header::NDF - Manipulate FITS headers from NDF files
6              
7             =head1 SYNOPSIS
8              
9             use Astro::FITS::Header::NDF;
10              
11             $hdr = new Astro::FITS::Header::NDF( Cards => \@cards );
12             $hdr = new Astro::FITS::Header::NDF( Items => \@items );
13             $hdr = new Astro::FITS::Header::NDF( ndfID => $indf );
14             $hdr = new Astro::FITS::Header::NDF( File => $file );
15              
16             $hdr->writehdr( $indf );
17             $hdr->writehdr( File => $file );
18              
19             =head1 DESCRIPTION
20              
21             This module makes use of the Starlink L module to read and
22             write to an NDF FITS extension or to a C<.HEADER> block in an HDS
23             container file. If the file is found to be an HDS container
24             containing multiple NDFs at the top level, either the .HEADER NDF or
25             the first NDF containing a FITS header is deemed to be the primary
26             header, and all other headers a subsidiary headers indexed by the name
27             of the NDF in the container.
28              
29             It stores information about a FITS header block in an object. Takes an
30             hash as an argument, with either an array reference pointing to an
31             array of FITS header cards, array of C
32             objects, or a filename, or (alternatively) an NDF identifier.
33              
34             Currently, subheader support is readonly.
35              
36             =cut
37              
38 1     1   694171 use strict;
  1         11  
  1         100  
39 1     1   8 use Carp;
  1         3  
  1         365  
40 1     1   11 use File::Spec;
  1         1  
  1         127  
41 1     1   2600 use NDF qw/ :ndf :dat :err :hds :msg /;
  0            
  0            
42              
43             use base qw/ Astro::FITS::Header /;
44              
45             use vars qw/ $VERSION /;
46              
47             $VERSION = '3.09';
48              
49             =head1 METHODS
50              
51             =over 4
52              
53             =item B
54              
55             Reads a FITS header from an NDF.
56              
57             $hdr->configure( Cards => \@cards );
58             $hdr->configure( ndfID => $indf );
59             $hdr->configure( File => $filename );
60              
61             Accepts an NDF identifier or a filename. If both C and C keys
62             exist, C key takes priority.
63              
64             If the file is actually an HDS container, an attempt will be made
65             to read a ".HEADER" NDF inside that container (this is the standard
66             layout of UKIRT (and some JCMT) data files). If an extension is specified
67             explicitly (that is not ".sdf") that path is treated as an explicit path
68             to an NDF. If an explicit path is specified no attempt is made to locate
69             other NDFs in the HDS container.
70              
71             If the NDF can be opened successfully but there is no .MORE.FITS
72             extension, an empty header is returned rather than throwing an error.
73              
74             =cut
75              
76             sub configure {
77             my $self = shift;
78              
79             my %args = @_;
80              
81             my ($indf, $started);
82             my $task = ref($self);
83              
84             return $self->SUPER::configure(%args)
85             if exists $args{Cards} or exists $args{Items};
86              
87             # Store the definition of good locally
88             my $status = &NDF::SAI__OK;
89             my $good = $status;
90              
91              
92             # Start error system (this may be the first time we hit
93             # starlink)
94             err_begin( $status );
95              
96             # did we start NDF
97             my $ndfstarted;
98             my $FileName = "";
99              
100             # Read the args hash
101             if (exists $args{ndfID}) {
102             $indf = $args{ndfID};
103              
104             # Need to work out the file name
105             ndf_msg( "NDF", $indf );
106             msg_load( " ", "^NDF", $FileName, my $len, $status );
107              
108             } elsif (exists $args{File}) {
109             # Remove trailing .sdf
110             my $file = $args{File};
111             $FileName = $file;
112             $file =~ s/\.sdf$//;
113              
114             # NDF currently (c.2008) has troubles with spaces in paths
115             # we work around this by changing to the directory before
116             # opening the file
117             my ($vol, $dir, $root) = File::Spec->splitpath( $file );
118             my $cwd;
119             if ($dir =~ /\s/) {
120             # only bother if there is a space
121             $cwd = File::Spec->rel2abs( File::Spec->curdir );
122             # if the chdir fails we will try to open the file
123             # with NDF anyway using the path. Otherwise we change the
124             # filename to be the root
125             if (chdir($dir)) {
126             $file = $root;
127             }
128             }
129              
130             # Start NDF
131             ndf_begin();
132             $ndfstarted = 1;
133              
134             # First we need to find whether we have an HDS container or a
135             # straight NDF. Rather than simply trying an ndf_find on both
136             # (which causes leaks in the NDF system circa 2001) we explicitly
137             # open it using HDS unless it has a "." in it.
138             if ($file =~ /\./) {
139             # an NDF
140             ndf_find(&NDF::DAT__ROOT(), $file, $indf, $status);
141             } else {
142             # Try HDS
143             hds_open( $file, 'READ', my $hdsloc, $status);
144              
145             # Find its type
146             dat_type( $hdsloc, my $type, $status);
147              
148             if ($status == $good) {
149              
150             # If we have an NDF we can simply reopen it
151             # Additionally if we have no description of the component
152             # at all we assume NDF. This overcomes a bug in the acquisition
153             # for SCUBA where a blank type field is used.
154             my $ndffile;
155             if ($type =~ /NDF/i || $type !~ /\w/) {
156             $ndffile = $file;
157             } else {
158             # For now simply assume we can find a .HEADER
159             # in future we could tweak this to default to first NDF
160             # it finds if no .HEADER
161             $ndffile = $file . ".HEADER";
162             $FileName .= ".HEADER";
163             }
164              
165             # Close the HDS file
166             dat_annul( $hdsloc, $status);
167              
168             # Open the NDF
169             ndf_find(&NDF::DAT__ROOT(), $ndffile, $indf, $status);
170              
171             # reset the directory
172             if (defined $cwd) {
173             chdir($cwd) or carp "Could not return to current working directory";
174             }
175              
176              
177             }
178             }
179              
180             } else {
181              
182             $status = &NDF::SAI__ERROR;
183             err_rep(' ',
184             "$task: Argument hash does not contain ndfID, File or Cards",
185             $status);
186              
187             }
188              
189             if ($status == $good) {
190              
191             # See if the extension exists
192             ndf_xstat( $indf, "FITS", my $there, $status);
193              
194             if ($status == $good && $there) {
195              
196             # Find the FITS extension
197             ndf_xloc($indf, 'FITS', 'READ', my $xloc, $status);
198              
199             if ($status == $good) {
200              
201             # Variables...
202             my (@dim, $ndim, $nfits, $maxdim);
203              
204             # Get the dimensions of the FITS array
205             # Should only be one-dimensional
206             $maxdim = 7;
207             dat_shape($xloc, $maxdim, @dim, $ndim, $status);
208              
209             if ($status == $good) {
210              
211             if ($ndim != 1) {
212             $status = &SAI__ERROR;
213             err_rep(' ',"$task: Dimensionality of FITS array should be 1 but is $ndim", $status);
214              
215             }
216              
217             }
218              
219             # Set the FITS array to empty
220             my @fits = (); # Note that @fits only exists in this block
221              
222             # Read the FITS extension
223             dat_get1c($xloc, $dim[0], @fits, $nfits, $status);
224              
225             # Annul the locator
226             dat_annul($xloc, $status);
227              
228             # Check status and read into hash
229             if ($status == $good) {
230              
231             # Parse the FITS array
232             $self->SUPER::configure( Cards => \@fits );
233              
234             } else {
235              
236             err_rep(' ',"$task: Error reading FITS array", $status);
237              
238             }
239              
240             } else {
241              
242             # Add my own message to status
243             err_rep(' ', "$task: Error locating FITS extension",
244             $status);
245             }
246             } elsif ($status != $good) {
247             err_rep(' ', "$task: Error determining presence of FITS extension",
248             $status);
249             } else {
250             # simply is not there but file is okay
251             }
252              
253             # Close the NDF identifier (if we opened it)
254             ndf_annul($indf, $status) if exists $args{File};
255             }
256              
257             # Shutdown
258             ndf_end($status) if $ndfstarted;
259              
260             # Handle errors
261             if ($status != $good) {
262             my ( $oplen, @errs );
263             do {
264             err_load( my $param, my $parlen, my $opstr, $oplen, $status );
265             push @errs, $opstr;
266             } until ( $oplen == 1 );
267             err_annul($status);
268             err_end( $status );
269             croak "Error during header read from NDF $FileName:\n" . join "\n", @errs;
270             }
271             err_end($status);
272              
273             # It is possible to annul the errors before exiting if we want
274             # or to flush them out.
275             return;
276              
277             }
278              
279              
280             =item B
281              
282             Write a FITS header to an NDF.
283              
284             $hdr->writehdr( ndfID => $indf );
285             $hdr->writehdr( File => $file );
286              
287             Accepts an NDF identifier or a filename. If both C and C keys
288             exist, C key takes priority.
289              
290             Throws an exception (croaks) on error.
291              
292             =cut
293              
294             sub writehdr {
295              
296             my $self = shift;
297             my %args = @_;
298              
299             # Store the definition of good locally
300             my $status = &NDF::SAI__OK;
301             my $good = $status;
302              
303              
304             # Start error system (this may be the first time we hit
305             # starlink)
306             err_begin( $status );
307              
308             # Indicate whether we have started an NDF context or not
309             my $ndfstarted;
310              
311             # Look in the args hash and open the output file if needed
312             my $ndfid;
313             if (exists $args{ndfID}) {
314             $ndfid = $args{ndfID};
315             } elsif (exists $args{File}) {
316             my $file = $args{File};
317             $file =~ s/\.sdf//;
318              
319             # Start NDF
320             ndf_begin();
321             $ndfstarted = 1;
322              
323             ndf_open(&NDF::DAT__ROOT(), $file, 'UPDATE', 'UNKNOWN',
324             $ndfid, my $place, $status);
325              
326             # If status is bad, try assuming it is a HDS container
327             # with UKIRT style .HEADER component
328             if ($status != $good or $ndfid == 0) {
329             # dont want to contaminate existing status
330             my $lstat = $good;
331             my $hdsfile = $file . ".HEADER";
332             my $useheader;
333             err_mark();
334             ndf_open(&NDF::DAT__ROOT(), $hdsfile, 'UPDATE', 'UNKNOWN',
335             $ndfid, $place, $lstat);
336             if ($lstat != $good) {
337             err_annul( $lstat );
338             } else {
339             $useheader = 1;
340             }
341             err_rlse();
342              
343             # flush bad global status if we succeeded
344             err_annul($status) if $useheader;
345              
346             }
347              
348             # KLUGE : need to get NDF__NOID from the NDF module at some point
349             if ($ndfid == 0 && $status == $good) {
350             # could create it :-)
351             $status = &NDF::SAI__ERROR;
352             err_rep(' ',"File '$file' does not exist to receive the header", $status);
353             }
354              
355             } else {
356             err_end( $status );
357             croak "Missing argument to writehdr. Must include either ndfID or File key";
358             }
359              
360             # Now need to find out whether we have a FITS header in the
361             # file already
362             ndf_xstat( $ndfid, 'FITS', my $there, $status);
363              
364             # delete it
365             ndf_xdel($ndfid, 'FITS', $status) if $there;
366              
367             # Get the fits array
368             my @cards = $self->cards;
369              
370             # Write the FITS extension
371             if ($#cards > -1) {
372              
373             # Write it out
374             my @fitsdim = (scalar(@cards));
375             ndf_xnew($ndfid, 'FITS', '_CHAR*80', 1, @fitsdim, my $fitsloc, $status);
376             dat_put1c($fitsloc, scalar(@cards), @cards, $status);
377             dat_annul($fitsloc, $status);
378             }
379              
380             # Write HISTORY information
381             my @text =("Astro::FITS::Header::NDF - write FITS header to file ^FILE",);
382             ndf_msg( "FILE", $ndfid );
383             ndf_hput("NORMAL", '', 0, scalar(@text), @text, 1, 1,1, $ndfid, $status );
384              
385             ndf_annul( $ndfid, $status );
386              
387             # Shutdown
388             ndf_end($status) if $ndfstarted;
389              
390             # Handle errors
391             if ($status != $good) {
392             my @errs;
393             my $oplen;
394             do {
395             err_load( my $param, my $parlen, my $opstr, $oplen, $status );
396             push @errs, $opstr;
397             } until ( $oplen == 1 );
398             err_annul($status);
399             err_end($status);
400             croak "Error during header write to NDF:\n" . join "\n", @errs;
401             }
402             err_end($status);
403              
404             return;
405             }
406              
407              
408             =back
409              
410             =head1 NOTES
411              
412             This module requires the Starlink L module.
413              
414             =head1 SEE ALSO
415              
416             L, L, L
417             L
418              
419             =head1 AUTHORS
420              
421             Tim Jenness Et.jenness@jach.hawaii.eduE,
422             Alasdair Allan Eaa@astro.ex.ac.ukE,
423             Brad Cavanagh Eb.cavanagh@jach.hawaii.eduE
424              
425             =head1 COPYRIGHT
426              
427             Copyright (C) 2008-2009 Science & Technology Facilities Council.
428             Copyright (C) 2001-2005 Particle Physics and Astronomy Research Council.
429             All Rights Reserved.
430              
431             This program is free software; you can redistribute it and/or modify it under
432             the terms of the GNU General Public License as published by the Free Software
433             Foundation; either version 3 of the License, or (at your option) any later
434             version.
435              
436             This program is distributed in the hope that it will be useful,but WITHOUT ANY
437             WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
438             PARTICULAR PURPOSE. See the GNU General Public License for more details.
439              
440             You should have received a copy of the GNU General Public License along with
441             this program; if not, write to the Free Software Foundation, Inc., 59 Temple
442             Place,Suite 330, Boston, MA 02111-1307, USA
443              
444             =cut
445              
446             1;