~ubuntu-branches/ubuntu/lucid/pdl/lucid

« back to all changes in this revision

Viewing changes to IO/NDF/NDF.pm.PL

  • Committer: Bazaar Package Importer
  • Author(s): Ben Gertzfield
  • Date: 2002-04-08 18:47:16 UTC
  • Revision ID: james.westby@ubuntu.com-20020408184716-0hf64dc96kin3htp
Tags: upstream-2.3.2
ImportĀ upstreamĀ versionĀ 2.3.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*-perl-*-
 
2
#
 
3
# The contents depend on whether we have bad-value support in PDL.
 
4
# (there's only a small amount of code that uses bad values)
 
5
#
 
6
# - this is called by Makefile.PL to ensure the file exists 
 
7
#   when the makefile is written
 
8
#
 
9
 
 
10
use strict;
 
11
 
 
12
use Config;
 
13
use File::Basename qw(&basename &dirname);
 
14
 
 
15
# check for bad value support
 
16
use vars qw( $bvalflag $usenan );
 
17
use File::Spec;
 
18
require File::Spec->catfile( File::Spec->updir, File::Spec->updir, "Basic", "Core", "badsupport.p" );
 
19
 
 
20
# This forces PL files to create target in same directory as PL file.
 
21
# This is so that make depend always knows where to find PL derivatives.
 
22
chdir(dirname($0));
 
23
my $file;
 
24
($file = basename($0)) =~ s/\.PL$//;
 
25
$file =~ s/\.pl$//
 
26
        if ($Config{'osname'} eq 'VMS' or
 
27
            $Config{'osname'} eq 'OS2');  # "case-forgiving"
 
28
 
 
29
if ( $bvalflag ) {
 
30
    print "Extracting $file (WITH bad value support)\n";
 
31
} else {
 
32
    print "Extracting $file (NO bad value support)\n";
 
33
}
 
34
 
 
35
open OUT,">$file" or die "Can't create $file: $!";
 
36
chmod 0644, $file;
 
37
 
 
38
#########################################################
 
39
 
 
40
print OUT <<"!WITH!SUBS!";
 
41
 
 
42
# This file is automatically created by NDF.pm.PL
 
43
# - bad value support = $bvalflag
 
44
!WITH!SUBS!
 
45
 
 
46
print OUT <<'!NO!SUBS!';
 
47
 
 
48
package PDL::IO::NDF;
 
49
 
 
50
=head1 NAME
 
51
 
 
52
PDL::IO::NDF - PDL Module for reading and writing Starlink
 
53
N-dimensional data structures as PDLs.
 
54
 
 
55
=head1 SYNOPSIS
 
56
 
 
57
 use PDL::IO::NDF;
 
58
 
 
59
 $a = PDL->rndf($file);
 
60
 
 
61
 $a = rndf('test_image');
 
62
 $a = rndf('test_image', 1);
 
63
 
 
64
 $a->wndf($file);
 
65
 wndf($a, 'out_image');
 
66
 
 
67
 propndfx($a, 'template', 'out_image');
 
68
 
 
69
=head1 DESCRIPTION
 
70
 
 
71
This module adds the ability to read and write Starlink N-dimensional data
 
72
files as N-dimensional PDLs.
 
73
 
 
74
=head1 FUNCTIONS
 
75
 
 
76
=cut
 
77
 
 
78
@EXPORT_OK = qw/rndf wndf/;
 
79
%EXPORT_TAGS = (Func=>[@EXPORT_OK]);
 
80
 
 
81
@ISA    = qw( PDL::Exporter );
 
82
 
 
83
use PDL::Core;
 
84
use PDL::Types;
 
85
use Carp;
 
86
use strict;
 
87
 
 
88
# Starlink data type conversion
 
89
use vars qw/%pdltypes %startypes $ndf_loaded $VERSION $EXTNAME/;
 
90
 
 
91
$VERSION = '1.02';
 
92
 
 
93
# Set PDL -> Starlink data types
 
94
%pdltypes = ("$PDL_B"  => "_UBYTE",     # was "_BYTE"
 
95
             "$PDL_S"  => "_WORD",
 
96
             "$PDL_US" => "_UWORD",
 
97
             "$PDL_L"  => "_INTEGER",
 
98
             "$PDL_F"  => "_REAL",
 
99
             "$PDL_D"  => "_DOUBLE"
 
100
            );
 
101
 
 
102
# Set Starlink -> PDL data types
 
103
%startypes = ("_BYTE"    =>$PDL_B,
 
104
              "_UBYTE"   =>$PDL_B,
 
105
              "_WORD"    =>$PDL_S,
 
106
              "_UWORD"   =>$PDL_US,
 
107
              "_INTEGER" =>$PDL_L,
 
108
              "_REAL"    =>$PDL_F,
 
109
              "_DOUBLE"  =>$PDL_D
 
110
             );
 
111
 
 
112
# This is the name I use in the PDL header hash that contains the
 
113
# NDF extensions so that they can be recreated
 
114
 
 
115
$EXTNAME = 'NDF_EXT';
 
116
 
 
117
# load NDF library
 
118
sub _init_NDF {
 
119
    return if $ndf_loaded++; # a bit pointless increasing each time?
 
120
    eval 'use NDF';
 
121
    croak 'Cannot use NDF library' if $@ ne "";
 
122
}
 
123
 
 
124
!NO!SUBS!
 
125
 
 
126
    if ( $bvalflag ) {
 
127
        print OUT <<'!NO!SUBS!';
 
128
 
 
129
use PDL::Bad;
 
130
 
 
131
# internal functions for checking bad values
 
132
 
 
133
my $starbad = 0;  # flag
 
134
 
 
135
use vars qw( @starbad );
 
136
 
 
137
sub _init_starbad {
 
138
    return if $starbad;
 
139
    _init_NDF();  # just to be safe
 
140
    @starbad = eval 
 
141
          "( &NDF::VAL__BADUB,
 
142
             &NDF::VAL__BADW,
 
143
             &NDF::VAL__BADUW,
 
144
             &NDF::VAL__BADI,
 
145
             &NDF::VAL__BADF,
 
146
             &NDF::VAL__BADD );
 
147
";
 
148
    croak 'Unable to initialise bad values from NDF library' if $@ ne "";
 
149
    $starbad = 1;
 
150
}
 
151
 
 
152
sub starlink_bad_value ($) { 
 
153
    _init_starbad();
 
154
    return $starbad[$_[0]]; 
 
155
}
 
156
 
 
157
# given a piddle, check if the Starlink bad value matches
 
158
# the bad value used for this type
 
159
sub compare_bad_values ($) { 
 
160
    _init_starbad();
 
161
    return ($starbad[$_[0]] == badvalue($_[0])); 
 
162
}
 
163
 
 
164
!NO!SUBS!
 
165
 
 
166
} # if: $bvalflag
 
167
 
 
168
print OUT <<'!NO!SUBS!';
 
169
 
 
170
=head2 rndf()
 
171
 
 
172
=for ref
 
173
 
 
174
Reads a piddle from a NDF format data file.
 
175
 
 
176
=for example
 
177
 
 
178
 $pdl = rndf('file.sdf');
 
179
 $pdl = rndf('file.sdf',1);
 
180
 
 
181
The '.sdf' suffix is optional. The optional second argument turns off
 
182
automatic quality masking and returns a quality array as well.
 
183
 
 
184
BUG? C<rndf('bob.sdf',1)> calls ndf_sqmf(1,...), which means that
 
185
the quality array is turned into bad pixels - ie the I<opposite> of
 
186
above. Or am I confused?
 
187
 
 
188
Header information and NDF Extensions are stored in the piddle as a hash
 
189
which can be retreived with the C<$pdl-E<gt>gethdr> command.
 
190
Array extensions are stored in the header as follows:
 
191
 
 
192
 $a - the base DATA_ARRAY
 
193
 
 
194
If C<$hdr = $a-E<gt>gethdr>;
 
195
 
 
196
then:
 
197
 
 
198
 %{$hdr}        contains all the FITS headers plus:
 
199
 $$hdr{Error}   contains the Error/Variance PDL
 
200
 $$hdr{Quality} The quality byte array (if reqeusted)
 
201
 @{$$hdr{Axis}} Is an array of piddles containing the information
 
202
                for axis 0, 1, etc.
 
203
 $$hdr{NDF_EXT} Contains all the NDF extensions
 
204
 $$hdr{Hist}    Contains the history information
 
205
 $$hdr{NDF_EXT}{_TYPES}
 
206
                Data types for non-PDL NDF extensions so that
 
207
                wndf can reconstruct a NDF.
 
208
 
 
209
All extension information is stored in the header hash array.
 
210
Extension structures are preserved in hashes, so that the PROJ_PARS
 
211
component of the IRAS.ASTROMETRY extension is stored in
 
212
C<$$hdr{NDF_EXT}{IRAS}{ASTROMETRY}{'PROJ_PARS'}>. All array structures are
 
213
stored as arrays in the Hdr: numeric arrays are stored as PDLs,
 
214
logical and character arrays are stored as plain Perl arrays. FITS
 
215
arrays are a special case and are expanded as scalars into the header.
 
216
 
 
217
PDL does not have a signed byte datatype, so any '_BYTE' data
 
218
is read into a C<byte> (unsigned) piddle and a warning is printed
 
219
to C<STDOUT>.
 
220
 
 
221
!NO!SUBS!
 
222
 
 
223
    if ( $bvalflag ) {
 
224
        print OUT <<'!NO!SUBS!';
 
225
=for bad
 
226
 
 
227
If the starlink bad flag is set, then the bad flag on the output
 
228
piddle is also set.  Starlink bad values are converted to the
 
229
current bad value used by the piddle type (if they are different).
 
230
 
 
231
!NO!SUBS!
 
232
} # if: $bvalflag
 
233
 
 
234
print OUT <<'!NO!SUBS!';
 
235
=cut
 
236
 
 
237
# This is one form of the new command
 
238
 
 
239
sub rndf {PDL->rndf(@_)}
 
240
 
 
241
# And this is the real form
 
242
# Allows the command to be called in OO form or as a function
 
243
sub PDL::rndf {  # Read a piddle from a NDF file
 
244
 
 
245
  my $class = shift;
 
246
  barf 'Usage: $a = rndf($file,[$nomask]); $a = PDL->rndf(...) '
 
247
    if ($#_!=0 && $#_!=1);
 
248
 
 
249
  # ensure we have the NDF module
 
250
  _init_NDF();
 
251
 
 
252
  # Setup the Header Hash
 
253
  my $header = {};
 
254
 
 
255
  # Read in the filename
 
256
  # And setup the new PDL
 
257
  my $file = shift; 
 
258
  my $pdl = $class->new;
 
259
  my $nomask = shift if $#_ > -1;
 
260
 
 
261
  my ($infile, $status, $indf, $entry, @info, $value);
 
262
 
 
263
  # Strip trailing .sdf if one is present
 
264
  # remove the trailing extension names
 
265
  # (anything after a `.' UNLESS it's part of a directory
 
266
  # name, hence the check for ^/)
 
267
  $file =~ s/\.sdf$//;
 
268
  $infile = $file;
 
269
  $infile =~ s!\.[^/]*$!!;
 
270
 
 
271
  # If file is not there
 
272
  croak "Cannot find $infile.sdf" unless -e "$infile.sdf";
 
273
 
 
274
  # Set status
 
275
  $status = &NDF::SAI__OK;
 
276
 
 
277
  # Begin NDF and ERR session
 
278
  err_begin($status);
 
279
  ndf_begin();
 
280
 
 
281
  # Open the file
 
282
  ndf_find(&NDF::DAT__ROOT, $file, $indf, $status);
 
283
 
 
284
  # unset automatic quality masking if $nomask is defined
 
285
  # I think the logic here is wrong (ie 0 and 1 should be swapped)
 
286
  $nomask = 0 unless (defined $nomask);
 
287
  $nomask = 1 if $nomask != 0;
 
288
  ndf_sqmf($nomask, $indf, $status);
 
289
 
 
290
  # Read the data array (need to pass the header as well)
 
291
  my $badflag;
 
292
  ( $status, $badflag ) = rdata($indf, $pdl, $nomask, $header, $class, $status);
 
293
 
 
294
  # Read the axes
 
295
  raxes($indf, $pdl, $header, $class, $status);
 
296
 
 
297
  # Read the header
 
298
  rhdr($indf, $pdl, $header, $class, $status);
 
299
 
 
300
  # Read history information
 
301
  rhist($indf, $pdl, $header, $status);
 
302
 
 
303
  # Read labels
 
304
  @info = ('Label', 'Title', 'Units');
 
305
  for $entry (@info) {
 
306
    $value = 'NULL';
 
307
    ndf_cget($indf, $entry, $value, $status);
 
308
    $$header{"$entry"} = $value if $value ne 'NULL';
 
309
    print "$entry is $value\n" if ($PDL::verbose && $value ne 'NULL');
 
310
    undef $value;
 
311
  }
 
312
 
 
313
  # Tidy up
 
314
  ndf_annul($indf, $status);
 
315
  ndf_end($status);
 
316
 
 
317
  if ($status != &NDF::SAI__OK) {
 
318
    err_flush($status);         # Flush the error system
 
319
    err_end($status);
 
320
    croak("rndf: obtained NDF error");
 
321
  }
 
322
 
 
323
  err_end($status);
 
324
 
 
325
  # Put the header into the main pdl
 
326
  $pdl->sethdr($header);
 
327
 
 
328
!NO!SUBS!
 
329
 
 
330
    if ( $bvalflag ) {
 
331
        print OUT <<'!NO!SUBS!';
 
332
  # set the bad flag if it was set in the input file
 
333
  print "Bad flag status is: $badflag\n" if $PDL::verbose;
 
334
  $pdl->badflag(1) if $badflag;
 
335
 
 
336
!NO!SUBS!
 
337
 
 
338
} # if; $bvalflag
 
339
 
 
340
  print OUT <<'!NO!SUBS!';
 
341
  return $pdl;
 
342
}
 
343
 
 
344
=head2 wndf()
 
345
 
 
346
=for ref
 
347
 
 
348
Writes a piddle to a NDF format file:
 
349
 
 
350
=for example
 
351
 
 
352
   $pdl->wndf($file);
 
353
   wndf($pdl,$file);
 
354
 
 
355
wndf can be used for writing PDLs to NDF files. 
 
356
The '.sdf' suffix is optional. All the extensions
 
357
created by rndf are supported by wndf.  This means that error, axis
 
358
and quality arrays will be written if they exist. Extensions are also
 
359
reconstructed by using their name (ie FIGARO.TEST would be expanded as
 
360
a FIGARO extension and a TEST component). Hdr keywords Label, Title
 
361
and Units are treated as special cases and are written to the label,
 
362
title and units fields of the NDF.
 
363
 
 
364
Header information is written to corresponding NDF extensions.
 
365
NDF extensions can also be created in the {NDF} hash by using a key
 
366
containing '.', ie {NDF}{'IRAS.DATA'} would write the information to
 
367
an IRAS.DATA extension in the NDF. rndf stores this as
 
368
C<$$hdr{NDF}{IRAS}{DATA}> and the two systems are interchangeable.
 
369
 
 
370
rndf stores type information in {NDF}{'_TYPES'} and below so that
 
371
wndf can reconstruct the data type of non-PDL extensions. If no
 
372
entry exists in _TYPES, wndf chooses between characters, integer and
 
373
double on a best guess basis.  Any perl arrays are written as CHAR
 
374
array extensions (on the assumption that numeric arrays will exist as
 
375
PDLs).
 
376
 
 
377
!NO!SUBS!
 
378
 
 
379
    if ( $bvalflag ) {
 
380
        print OUT <<'!NO!SUBS!';
 
381
=for bad
 
382
 
 
383
The bad flag is written to the file, if set. 
 
384
 
 
385
!NO!SUBS!
 
386
 
 
387
} # if: $bvalflag
 
388
 
 
389
print OUT<<'!NO!SUBS!';
 
390
=cut
 
391
 
 
392
# This is one form of the new command
 
393
# OO version.
 
394
 
 
395
*wndf = \&PDL::wndf;
 
396
 
 
397
# And this is the real form
 
398
# Allows the command to be called in OO form or as a function
 
399
sub PDL::wndf {  # Write a PDL to an NDF format file
 
400
 
 
401
  barf 'Usage: wndf($pdl,$file)' if $#_!=1;
 
402
 
 
403
  # ensure we have the NDF module
 
404
  _init_NDF();
 
405
 
 
406
  my ($indf, $place, $status, $outndf);
 
407
  my (@lbnd, @ubnd);
 
408
 
 
409
  my ($pdl, $outfile) = @_;
 
410
 
 
411
  # Check that we are dealing with a PDL
 
412
  croak 'Argument is not a PDL variable' unless $pdl->isa('PDL');
 
413
 
 
414
  # Set status
 
415
  $status = &NDF::SAI__OK;
 
416
 
 
417
  # Strip trailing .sdf if one is present
 
418
  $outfile =~ s/\.sdf$//;
 
419
 
 
420
  # Begin context
 
421
  ndf_begin();
 
422
  err_begin($status);
 
423
 
 
424
  # Open the new file
 
425
  ndf_place(&NDF::DAT__ROOT(), $outfile, $place, $status);
 
426
 
 
427
  # Need to create data_array component
 
428
  # Change the bounds to match the PDL
 
429
 
 
430
  @ubnd = $pdl->dims;
 
431
  @lbnd = ();
 
432
  @lbnd = map { 1 } (0..$#ubnd);
 
433
 
 
434
  ndf_new($pdltypes{$pdl->get_datatype}, $#ubnd+1, \@lbnd, \@ubnd, $place,
 
435
          $outndf, $status);
 
436
 
 
437
  # Set the application name
 
438
  ndf_happn('PDL::wndf', $status);
 
439
 
 
440
  # Write the data to this file
 
441
  wdata($outndf, $pdl, $status);
 
442
 
 
443
  # Write the header and title
 
444
  whdr($outndf, $pdl, $status);
 
445
 
 
446
  # Create a history extension
 
447
  ndf_hcre($outndf, $status);
 
448
 
 
449
  # Annul the identifier
 
450
  ndf_annul($outndf, $status);
 
451
 
 
452
  # End context
 
453
  ndf_end($status);
 
454
 
 
455
  if ($status != &NDF::SAI__OK) {
 
456
    err_flush($status);
 
457
    croak "Bad STATUS in wndf\n";
 
458
  }
 
459
 
 
460
  err_end($status);
 
461
 
 
462
  return 1;
 
463
}
 
464
 
 
465
=head2 propndfx()
 
466
 
 
467
Routine to write a PDL to an NDF by copying the extension information
 
468
from an existing NDF and writing DATA,VARIANCE, QUALITY and AXIS info
 
469
from a PDL (if they exist).
 
470
 
 
471
Extensions, labels and history are propogated from the old NDF.
 
472
No new extension information is written.
 
473
 
 
474
This command has been superseded by L<wndf()|/wndf()>.
 
475
 
 
476
=cut
 
477
 
 
478
*propndfx = \&PDL::propndfx;
 
479
 
 
480
sub PDL::propndfx {  # Write a PDL to a NDF format file
 
481
 
 
482
  barf 'Usage: propndfx($pdl, $infile, $outfile)' if $#_!=2;
 
483
 
 
484
  # ensure we have the NDF module
 
485
  _init_NDF();
 
486
 
 
487
  my ($indf, $in_place, $status, $outplace, $outndf);
 
488
 
 
489
  my ($pdl, $infile, $outfile) = @_;
 
490
 
 
491
  # Set status
 
492
  $status = &NDF::SAI__OK;
 
493
 
 
494
  # Strip trailing .sdf if one is present
 
495
  $outfile =~ s/\.sdf$//;
 
496
  $infile =~ s/\.sdf$//;
 
497
  # File is the first thing before a .
 
498
  my $file = (split(/\./,$infile))[0];
 
499
 
 
500
  croak "$file does not exist\n" unless -e "$file.sdf";
 
501
 
 
502
  # Begin context
 
503
  ndf_begin();
 
504
  err_begin($status);
 
505
 
 
506
  # Open the old file
 
507
  ndf_find(&NDF::DAT__ROOT(), $infile, $indf, $status);
 
508
 
 
509
  # Set the application name
 
510
  ndf_happn('PDL::propndfx', $status);
 
511
 
 
512
  # Open the new file
 
513
  ndf_place(&NDF::DAT__ROOT(), $outfile, $outplace, $status);
 
514
 
 
515
  # Copy the extension information to outfile
 
516
  ndf_scopy($indf, ' ', $outplace, $outndf, $status);
 
517
 
 
518
  # Annul the input file identifier
 
519
  ndf_annul($indf, $status);
 
520
 
 
521
  # Write the data to this file
 
522
  wdata($outndf, $pdl, $status);
 
523
 
 
524
  # Annul the identifier
 
525
  ndf_annul($outndf, $status);
 
526
 
 
527
  # End context
 
528
  ndf_end($status);
 
529
 
 
530
  if ($status != &NDF::SAI__OK) {
 
531
    err_flush($status);
 
532
    croak "Bad STATUS in propndfx\n";
 
533
  }
 
534
 
 
535
  err_end($status);
 
536
 
 
537
  return 1;
 
538
}
 
539
 
 
540
=head1 NOTES
 
541
 
 
542
The perl NDF module must be available. This is available from the 
 
543
author or from Starlink (http://www.starlink.rl.ac.uk).
 
544
 
 
545
If an NDF is read which contains AST World Coordinate information
 
546
(a .WCS component) this information is currently ignored. Currently
 
547
WCS information can only be written and stored using standard FITS headers.
 
548
See http://rlspc5.bnsc.rl.ac.uk/star/docs/sun211.htx/sun211.html#xref_
 
549
for more information on AST.
 
550
 
 
551
=head1 AUTHOR
 
552
 
 
553
This module was written by Tim Jenness E<lt>t.jenness@jach.hawaii.eduE<gt>.
 
554
Copyright (C) Tim Jenness 1997-2000. All Rights Reserved.
 
555
 
 
556
=head1 SEE ALSO
 
557
 
 
558
L<PDL::FAQ> for general information on the Perl Data language,
 
559
L<NDF> for information on the NDF module.
 
560
 
 
561
=cut
 
562
 
 
563
 
 
564
#######################################################################
 
565
 
 
566
# These are the generic routines used by the rndf command
 
567
 
 
568
#  RDATA
 
569
#    Read Data, Quality and Variance
 
570
 
 
571
# returns ( $status, $badflag ), where $badflag is:
 
572
#
 
573
#   1 - the piddle may contain bad data
 
574
#   0 - the piddle contains no bad data
 
575
#
 
576
# This reports starlink's bad-data flag (ie it's valid
 
577
# even in bad value support is not available in PDL)
 
578
#
 
579
sub rdata {
 
580
  my ($indf, $pdl, $nomask, $header, $class, $status) = @_;
 
581
 
 
582
  return ( $status, undef ) if $status != &NDF::SAI__OK;
 
583
 
 
584
  my ($maxdims, $ndim, @dim, @comps, $dcomp, $tcomp, $exist);
 
585
  my ($type, $data_pntr, $el, $temppdl, $nbytes, $badbit, $dref);
 
586
 
 
587
  ####################################################################
 
588
  #                      D  I  M  S                                  #
 
589
  ####################################################################
 
590
 
 
591
  # Find the dimensions of the data array
 
592
  $maxdims = 100;               # Maximum number of dimensions allowed
 
593
 
 
594
  &ndf_dim($indf, $maxdims, \@dim, $ndim, $status);
 
595
  @dim = @dim[0..$ndim-1];
 
596
 
 
597
  print "Dims = @dim\n" if $PDL::verbose;
 
598
 
 
599
  $$header{Extensions} = [];
 
600
 
 
601
  ####################################################################
 
602
  #    D A T A  +  V  A  R  I  A  N  C  E  +  Q U A L I T Y          #
 
603
  ####################################################################
 
604
 
 
605
  my $badflag = 0;
 
606
 
 
607
  @comps = ('Data','Error');
 
608
  push(@comps, 'Quality') if $nomask;
 
609
 
 
610
  for $dcomp (@comps) {
 
611
 
 
612
    $tcomp = $dcomp;
 
613
    $tcomp = 'Variance' if $tcomp eq 'Error';
 
614
 
 
615
    ndf_state($indf, $tcomp, $exist, $status);
 
616
 
 
617
    if ($exist && ($status == &NDF::SAI__OK)) {
 
618
 
 
619
      # Find the type of the data array
 
620
      ndf_type($indf, $tcomp, $type, $status);
 
621
 
 
622
      # Map the data array
 
623
      ndf_map($indf, $dcomp, $type, 'READ', $data_pntr, $el, $status);
 
624
 
 
625
      # Check the bad-data status of this component
 
626
      my $this_badflag = 0;
 
627
      ndf_bad($indf, $dcomp, 0, $this_badflag, $status );
 
628
 
 
629
      if ($status == &NDF::SAI__OK) {
 
630
 
 
631
        print "Reading $dcomp array...\n" if $PDL::verbose;
 
632
 
 
633
        # combine the bad data flag with the "global" one for
 
634
        # the file
 
635
        $badflag |= ($this_badflag != 0);
 
636
 
 
637
        # Create a temporary PDL to deal with separate components
 
638
        if ($dcomp eq 'Data') {
 
639
          $temppdl = $pdl;
 
640
        } else {
 
641
          $dcomp = 'Errors' if $dcomp eq 'Error';
 
642
          $$header{"$dcomp"} = $class->new;
 
643
          $temppdl = $$header{"$dcomp"};
 
644
        }
 
645
 
 
646
        # Set up the PDL
 
647
        $temppdl->set_datatype($startypes{$type});
 
648
        $temppdl->setdims([@dim]);
 
649
        $dref = $temppdl->get_dataref;
 
650
 
 
651
        # warning about data loss
 
652
        if ( $type eq "_BYTE" ) {
 
653
            print "Warning: NDF type (signed byte) converted to PDL's unsigned byte.\n";
 
654
        }
 
655
 
 
656
        # How many bytes in this data type?
 
657
        $type =~ s/^_|^_U//;
 
658
        $nbytes = &NDF::byte_size($type);
 
659
 
 
660
        mem2string($data_pntr, $nbytes*$el, $$dref);
 
661
 
 
662
        push(@{$$header{Extensions}}, $dcomp) if $dcomp ne 'Data';
 
663
 
 
664
        if ($dcomp eq 'Quality') {
 
665
          # Quality bad bit mask
 
666
          ndf_bb($indf, $badbit, $status);
 
667
 
 
668
          my $qhdr = {};
 
669
          $$qhdr{BADBIT} = $badbit;
 
670
          $temppdl->sethdr($qhdr);
 
671
        }
 
672
 
 
673
!NO!SUBS!
 
674
 
 
675
    if ( $bvalflag ) {
 
676
        print OUT <<'!NO!SUBS!';
 
677
        # if badflag is true, convert any STARLINK bad values
 
678
        # to PDL values
 
679
        if ( $badflag and 
 
680
             !compare_bad_values($temppdl->get_datatype) ) {
 
681
            my $star_bad = starlink_bad_value($temppdl->get_datatype);
 
682
            print "Converting from STARLINK bad value ($star_bad)...\n" if $PDL::verbose;
 
683
            $temppdl->inplace->setvaltobad( $star_bad );
 
684
        }
 
685
 
 
686
!NO!SUBS!
 
687
 
 
688
} # if: $bvalflag
 
689
 
 
690
print OUT <<'!NO!SUBS!';
 
691
        # Flush and Clear temporary PDL
 
692
        $temppdl->upd_data();
 
693
        undef $temppdl;
 
694
      }
 
695
 
 
696
      ndf_unmap($indf, $tcomp, $status);
 
697
    }
 
698
  }
 
699
  err_flush($status) if $status != &NDF::SAI__OK;
 
700
 
 
701
  return ( $status, $badflag );
 
702
}
 
703
 
 
704
 
 
705
 
 
706
  ####################################################################
 
707
  #                      A  X  I  S                                  #
 
708
  ####################################################################
 
709
 
 
710
# Axes are stored as follows:
 
711
#  Array of PDL axes as an array in header of main pdl (called 'Axis')
 
712
#  Header of this PDL contains label and errors and units etc.
 
713
 
 
714
sub raxes {
 
715
  my ($indf, $pdl, $header, $class, $status) = @_;
 
716
 
 
717
  return $status if $status != &NDF::SAI__OK;
 
718
 
 
719
  my ($there, $naxis, @dims, $axcomp, $exist, $axtype, $axpntr, $el);
 
720
  my ($nbytes, $temppdl, $tcomp, $daxref, $dref);
 
721
  my ($axhdr, $ndims);
 
722
 
 
723
  # Read in axis information
 
724
  ndf_state($indf, 'Axis', $there, $status);
 
725
 
 
726
  $ndims = $pdl->getndims; # Find number of dimensions
 
727
 
 
728
  if ($there) {
 
729
 
 
730
    # Label from 0..$#dims to match array index handling
 
731
    # Want to put the axes into an array of axes
 
732
    # And update the extensions field
 
733
    $$header{Axis} = [];
 
734
    push(@{$$header{Extensions}}, "Axis");
 
735
 
 
736
    for $naxis (0..$ndims-1) {
 
737
      # Does a CENTRE component exist
 
738
      $axcomp = 'CENTRE';
 
739
      ndf_astat($indf, $axcomp, $naxis+1, $exist, $status);
 
740
 
 
741
      if ($exist && ($status == &NDF::SAI__OK)) {
 
742
 
 
743
        print "Reading Axis $naxis...\n" if $PDL::verbose;
 
744
        ndf_atype($indf, $axcomp, $naxis+1, $axtype, $status);
 
745
 
 
746
        ndf_amap($indf, $axcomp, $naxis+1, $axtype, 'READ', $axpntr,
 
747
                 $el, $status);
 
748
 
 
749
        # Set up new PDL for axis info if map was okay
 
750
        if ($status == &NDF::SAI__OK) {
 
751
          print "Number of elements: $el\n" if $PDL::verbose;
 
752
          $$header{Axis}[$naxis] = $class->new;
 
753
          $temppdl = $$header{Axis}[$naxis];
 
754
          $temppdl->set_datatype($startypes{$axtype});
 
755
          $temppdl->setdims([$el]);
 
756
          $dref = $temppdl->get_dataref;
 
757
 
 
758
 
 
759
          # How many bytes?
 
760
          $axtype =~ s/^_|^_U//;
 
761
          $nbytes = &NDF::byte_size($axtype);
 
762
 
 
763
          mem2string($axpntr, $nbytes*$el, $$dref);
 
764
          $temppdl->upd_data();
 
765
 
 
766
          # Header info for axis
 
767
          $axhdr = {};   # somewhere to put the pdl
 
768
 
 
769
          # Read VARIANCE array if there
 
770
          $axcomp = 'Error';
 
771
          $tcomp = 'Variance';
 
772
          ndf_astat($indf, $tcomp, $naxis+1, $exist, $status);
 
773
 
 
774
          if ($exist && ($status == &NDF::SAI__OK)) {
 
775
            print "Reading Axis $naxis errors...\n" if $PDL::verbose;
 
776
            ndf_atype($indf, $tcomp, $naxis+1, $axtype, $status);
 
777
 
 
778
            ndf_amap($indf, $axcomp, $naxis+1, $axtype, 'READ', $axpntr,
 
779
                     $el, $status);
 
780
 
 
781
            # Set up new PDL for axis info if map was okay
 
782
            if ($status == &NDF::SAI__OK) {
 
783
 
 
784
              $$axhdr{Errors} = $class->new;
 
785
              $$axhdr{Extensions} = ["Errors"];
 
786
 
 
787
              $$axhdr{Errors}->set_datatype($startypes{$axtype});
 
788
              $$axhdr{Errors}->setdims([$el]);
 
789
              $daxref = $$axhdr{Errors}->get_dataref;
 
790
 
 
791
              # How many bytes?
 
792
              $axtype =~ s/^_|^_U//;
 
793
              $nbytes = &NDF::byte_size($axtype);
 
794
 
 
795
              mem2string($axpntr, $nbytes*$el, $$daxref);
 
796
              $$axhdr{Errors}->upd_data();
 
797
 
 
798
            }
 
799
          }
 
800
 
 
801
          # Get label and units
 
802
          for my $entry ('Units', 'Label') {
 
803
            ndf_astat($indf, $entry, $naxis+1, $exist, $status);
 
804
            if ($exist) {
 
805
              my $value = '';
 
806
              ndf_acget($indf,$entry, $naxis+1, $value, $status);
 
807
              $axhdr->{"$entry"} = $value;
 
808
            }
 
809
          }
 
810
        }
 
811
        ndf_aunmp($indf,'*',$naxis+1,$status);
 
812
 
 
813
        # Now store this header into temppdl
 
814
        $temppdl->sethdr($axhdr);
 
815
 
 
816
      } else {
 
817
        err_annul($status) unless $status == &NDF::SAI__OK;
 
818
      }
 
819
 
 
820
    }
 
821
 
 
822
  }
 
823
 
 
824
 return $status;
 
825
}
 
826
 
 
827
 
 
828
  ####################################################################
 
829
  #                      E  X  T  E  N  S  I  O  N  S                #
 
830
  ####################################################################
 
831
 
 
832
sub rhdr {
 
833
  my ($indf, $pdl, $header, $class, $status) = @_;
 
834
 
 
835
  return $status if $status != &NDF::SAI__OK;
 
836
 
 
837
  my ($nextn, $xname, $xloc, $extn, $size, @fits, $entry, $value);
 
838
  my ($comment, $item, $el);
 
839
 
 
840
  # Read in any header information from the extensions
 
841
 
 
842
  ndf_xnumb($indf, $nextn, $status);
 
843
  print "Reading in header information...\n"
 
844
    if (($nextn > 0) && $PDL::verbose);
 
845
 
 
846
  # Read in extensions one at a time
 
847
  for $extn (1..$nextn) {
 
848
 
 
849
    $xname = 'NULL';
 
850
    ndf_xname($indf, $extn, $xname, $status);
 
851
 
 
852
    undef $xloc;
 
853
    ndf_xloc($indf, $xname, 'READ', $xloc, $status);
 
854
 
 
855
    # FITS is a special case and must be expanded
 
856
    if ($xname eq 'FITS') {
 
857
      # Read in the data
 
858
      dat_size($xloc, $size, $status);
 
859
      &dat_getvc($xloc, $size, \@fits, $el, $status);
 
860
 
 
861
      if ($status == &NDF::SAI__OK) {
 
862
        print "Reading FITS header...\n" if $PDL::verbose;
 
863
        for $entry (0..$el-1) {
 
864
          ($item, $value, $comment) = &fits_get_nth_item(\@fits,$entry);
 
865
          $$header{$item} = $value;
 
866
          $$header{'_COMMENTS'}{$item} = $comment;
 
867
        }
 
868
      } else {
 
869
        print "Error mapping $xname\n";
 
870
        err_annul($status);
 
871
      }
 
872
      undef @fits;
 
873
    } else {
 
874
 
 
875
      # Read in extensions to $EXTNAME
 
876
      $status = &find_prim($xloc, \%{$$header{$EXTNAME}}, $class, $status);
 
877
    }
 
878
 
 
879
    dat_annul($xloc, $status);
 
880
  }
 
881
  return $status;
 
882
}
 
883
 
 
884
  ####################################################################
 
885
  #                      H  I  S  T  O  R  Y                         #
 
886
  ####################################################################
 
887
 
 
888
sub rhist {
 
889
  my ($indf, $pdl, $header, $status) = @_;
 
890
 
 
891
  return $status if $status != &NDF::SAI__OK;
 
892
 
 
893
  my ($nrec, $app, $date, $i);
 
894
 
 
895
  # Read HISTORY information into a Hist header
 
896
 
 
897
  ndf_hnrec($indf,$nrec,$status);
 
898
 
 
899
  if ($status == &NDF::SAI__OK && ($nrec > 0)) {
 
900
    print "Reading history information into 'Hist'...\n" if $PDL::verbose;
 
901
 
 
902
    $$header{Hist}{'Nrecords'} = $nrec;
 
903
 
 
904
    # Loop through the history components and find last "APPLICATION"
 
905
    for $i (1..$nrec) {
 
906
 
 
907
      ndf_hinfo($indf, 'APPLICATION', $i, $app, $status);
 
908
      ndf_hinfo($indf, 'DATE', $i, $date, $status);
 
909
 
 
910
      $$header{Hist}{"Application$i"} = $app;
 
911
      $$header{Hist}{"Date$i"} = $date;
 
912
 
 
913
      print "  $app on $date\n" if $PDL::verbose;
 
914
    }
 
915
  } else {
 
916
    err_annul($status);
 
917
  }
 
918
  return $status;
 
919
}
 
920
 
 
921
 
 
922
 
 
923
################ Generic routines ###########################
 
924
#################### INTERNAL ###############################
 
925
 
 
926
# Find primitive components below a given HDS locator
 
927
# FITS information is stored in Hdr
 
928
# NDF extensions stored in NDF
 
929
 
 
930
sub find_prim {
 
931
 
 
932
  my ($loc, $href, $class, $status) = @_;
 
933
  my ($prim, $type, $size, @dim, $ndims, $struct, $name, $nloc, $el);
 
934
  my ($value, @values, $item, $entry, $maxdims);
 
935
  my ($packtype, $ncomp, $packed, $comp);
 
936
  my ($pntr, $nbytes, $comment, $dref);
 
937
 
 
938
  # Return if bad status
 
939
  return  0 if $status != &NDF::SAI__OK;
 
940
 
 
941
  # Most dimensions
 
942
  $maxdims = 100;
 
943
 
 
944
  # Is it a primitive component
 
945
  dat_prim($loc, $prim, $status);
 
946
 
 
947
  # Type, size and shape
 
948
  dat_type($loc, $type, $status);
 
949
  dat_size($loc, $size, $status);
 
950
  dat_shape($loc, $maxdims, \@dim, $ndims, $status);
 
951
  dat_name($loc, $name, $status);
 
952
 
 
953
  if ($prim) {
 
954
    print "\tReading component: $name ($type) " if $PDL::verbose;
 
955
 
 
956
    # Store type of information
 
957
    $$href{"_TYPES"}{"$name"} = $type;
 
958
 
 
959
    # Read it into the header
 
960
    if (($size == 1) && ($ndims == 0)) {
 
961
      if ($type  eq '_DOUBLE') {
 
962
        # dat_get0c doesn't return full precision for doubles
 
963
        dat_get0d($loc, $value, $status);
 
964
        if ($status != &NDF::SAI__OK) {
 
965
          print "Error mapping scalar 0d $type $name\n";
 
966
          err_flush($status);
 
967
        }
 
968
 
 
969
      } else {
 
970
        dat_get0c($loc, $value, $status);
 
971
        if ($status != &NDF::SAI__OK) {
 
972
          print "Error mapping scalar 0c $name\n";
 
973
          err_flush($status);
 
974
        }
 
975
 
 
976
      }
 
977
 
 
978
      $$href{"$name"} = $value if ($status == &NDF::SAI__OK);
 
979
      undef $value;
 
980
 
 
981
    } else {
 
982
      @dim = @dim[0..$ndims-1];
 
983
 
 
984
      # Inform the user of dimensions
 
985
      print "[@dim]" if $PDL::verbose;
 
986
 
 
987
      # Map arrays (don't need to unpack them)
 
988
      if ($type =~ /CHAR|LOG/) {
 
989
 
 
990
        # Read in the data
 
991
        dat_getvc($loc, $size, \@values, $el, $status);
 
992
 
 
993
        if ($status != &NDF::SAI__OK) {
 
994
          print "Error mapping $name\n";
 
995
        } else {
 
996
          $$href{"$name"} = [@values];
 
997
          undef @values;
 
998
        }
 
999
 
 
1000
      } else {
 
1001
 
 
1002
        # map the data
 
1003
        dat_mapv($loc, $type, 'READ', $pntr, $el, $status);
 
1004
 
 
1005
        if ($status != &NDF::SAI__OK) {
 
1006
          print "Error mapping $name\n";
 
1007
        }
 
1008
 
 
1009
 
 
1010
        if ($status == &NDF::SAI__OK) {
 
1011
          # Create the pdl
 
1012
          $$href{"$name"} = $class->new;
 
1013
          $$href{"$name"}->set_datatype($startypes{$type});
 
1014
          $$href{"$name"}->setdims([@dim]);
 
1015
          $dref = $$href{"$name"}->get_dataref();
 
1016
 
 
1017
          # How many bytes in this data type?
 
1018
          $type =~ s/^_|^_U//;
 
1019
          $nbytes = &NDF::byte_size($type);
 
1020
 
 
1021
          mem2string($pntr, $nbytes*$el, $$dref);
 
1022
          $$href{"$name"}->upd_data();
 
1023
        }
 
1024
 
 
1025
        dat_unmap($loc,$status);
 
1026
      }
 
1027
    }
 
1028
    print "\n" if $PDL::verbose;
 
1029
 
 
1030
  } else {
 
1031
 
 
1032
    my ($ndimx, @dim, $ndim);
 
1033
    dat_shape($loc, 20, \@dim, $ndim, $status);
 
1034
 
 
1035
    # If we have a single dimension ($ndim=0) then
 
1036
    # proceed. Cant yet cope with multi-dimensional
 
1037
    # extensions
 
1038
    if ($ndim == 0) {
 
1039
      dat_ncomp($loc, $ncomp, $status);
 
1040
      print "$name ($type) has $ncomp components\n" if $PDL::verbose;
 
1041
 
 
1042
      # Store type of extension
 
1043
      $$href{$name}{"_TYPES"}{STRUCT}{"$name"} = $type;
 
1044
 
 
1045
      for $comp (1..$ncomp) {
 
1046
        dat_index($loc, $comp, $nloc, $status);
 
1047
        $status = &find_prim($nloc, \%{$$href{$name}}, $class, $status);
 
1048
        dat_annul($nloc, $status);
 
1049
      }
 
1050
    } else {
 
1051
      print "Extension $name is an array structure -- skipping\n"
 
1052
        if $PDL::verbose;
 
1053
    }
 
1054
 
 
1055
  }
 
1056
  return $status;
 
1057
}
 
1058
 
 
1059
 
 
1060
###### Routines for WRITING data ######################################
 
1061
 
 
1062
sub wdata {
 
1063
 
 
1064
  my ($outndf, $pdl, $status) = @_;
 
1065
  my (@bounds, $ndims, @lower, @comps, $dcomp, $temppdl, $type);
 
1066
  my ($pntr, $el, $nbytes, $axis, $axcomp, $axpntr, %hdr, %axhdr);
 
1067
  my ($entry, $value, $i, $axndims, $axtype, $tcomp, @acomps);
 
1068
 
 
1069
 
 
1070
  # Return if bad status
 
1071
  return  0 if $status != &NDF::SAI__OK;
 
1072
 
 
1073
  ####################################################################
 
1074
  #                      D  I  M  S                                  #
 
1075
  ####################################################################
 
1076
  # Change the bounds to match the PDL
 
1077
  @bounds = $pdl->dims;
 
1078
  $ndims = $#bounds;
 
1079
  @lower = map { 1 } (0..$ndims);
 
1080
 
 
1081
  &ndf_sbnd($ndims+1, \@lower, \@bounds, $outndf, $status);
 
1082
 
 
1083
 
 
1084
  ####################################################################
 
1085
  #    D A T A  +  V  A  R  I  A  N  C  E  +  Q U A L I T Y          #
 
1086
  ####################################################################
 
1087
  # Map data, variance, quality...
 
1088
  @comps = ('Data','Errors','Quality');
 
1089
 
 
1090
  # Retrieve header and check that the header is a hash ref
 
1091
  my $hdr = $pdl->gethdr;
 
1092
  if (ref($hdr) eq 'HASH') {
 
1093
    %hdr = %$hdr;
 
1094
  } else {
 
1095
    %hdr = ();
 
1096
  }
 
1097
 
 
1098
  for $dcomp (@comps) {
 
1099
 
 
1100
    if ($dcomp eq 'Data') {
 
1101
      $temppdl = $pdl;
 
1102
    } else {
 
1103
      if (exists $hdr{"$dcomp"}) {
 
1104
        $temppdl = $hdr{"$dcomp"};
 
1105
        # Check that we have a PDL
 
1106
        next unless UNIVERSAL::isa($temppdl, 'PDL');
 
1107
      } else {
 
1108
        next;                   # Skip this component
 
1109
      }
 
1110
    }
 
1111
 
 
1112
    # Check that we have some data
 
1113
    if ($temppdl->nelem > 0) {
 
1114
 
 
1115
      if (($dcomp eq 'Quality') && ($temppdl->get_datatype != $PDL_B)) {
 
1116
        # Quality must be bytes so convert to BYTE if this is not true
 
1117
        $temppdl = byte ($$pdl{"$dcomp"});
 
1118
      }
 
1119
 
 
1120
      $type = $pdltypes{$temppdl->get_datatype};
 
1121
      $type = '_UBYTE' if $dcomp eq 'Quality'; # No choice for QUALITY
 
1122
 
 
1123
      # Set the output type of the NDF
 
1124
      $tcomp = $dcomp;
 
1125
      $tcomp = 'Variance' if $tcomp eq 'Errors';
 
1126
      ndf_stype($type, $outndf, $tcomp, $status) unless $dcomp eq 'Quality';
 
1127
 
 
1128
      print "Mapping $dcomp , $type\n" if $PDL::verbose;
 
1129
      # Map NDF
 
1130
      $dcomp = 'Error' if $dcomp eq 'Errors';
 
1131
      ndf_map($outndf, $dcomp, $type, 'WRITE', $pntr, $el, $status);
 
1132
 
 
1133
      if ($status == &NDF::SAI__OK) {
 
1134
        # Number of bytes per entry
 
1135
        $nbytes = PDL::Core::howbig($temppdl->get_datatype);
 
1136
 
 
1137
        # Convert to 1D data stream
 
1138
        my $p1d = $temppdl->clump(-1);
 
1139
        # Total number of bytes
 
1140
        $nbytes *= $p1d->getdim(0);
 
1141
 
 
1142
!NO!SUBS!
 
1143
 
 
1144
    if ( $bvalflag ) {
 
1145
        print OUT <<'!NO!SUBS!';
 
1146
        # if badflag is true, convert any PDL bad values to STARLINK ones 
 
1147
        # note: we have to create a copy of the data to ensure that the
 
1148
        # bad data value of the original piddle does not get changed. 
 
1149
        # per-piddle bad values would obviate the need for this.
 
1150
        if ( $temppdl->badflag() ) {
 
1151
            print "Setting bad flag for component $dcomp ...\n" if $PDL::verbose;
 
1152
            ndf_sbad( 1, $outndf, $dcomp, $status );
 
1153
            if ( !compare_bad_values($temppdl->get_datatype) ) {
 
1154
                my $star_bad = starlink_bad_value($temppdl->get_datatype);
 
1155
                print "Converting from PDL to STARLINK bad value ($star_bad)...\n" if $PDL::verbose;
 
1156
                $temppdl = $temppdl->setbadtoval( $star_bad ); # do NOT do inplace
 
1157
            }
 
1158
        }
 
1159
 
 
1160
!NO!SUBS!
 
1161
 
 
1162
} # if: $bvalflag
 
1163
 
 
1164
print OUT <<'!NO!SUBS!';
 
1165
        # Copy to disk
 
1166
        string2mem(${$temppdl->get_dataref}, $nbytes, $pntr);
 
1167
 
 
1168
        # Write badbit mask
 
1169
        if ($dcomp eq 'Quality') {
 
1170
          ndf_sbb($$temppdl{Hdr}{BADBIT}, $outndf, $status)
 
1171
            if defined $$temppdl{Hdr}{BADBIT};
 
1172
        }
 
1173
      }
 
1174
      # Unmap Data
 
1175
      ndf_unmap($outndf, $tcomp, $status);
 
1176
 
 
1177
    }
 
1178
 
 
1179
    # free the temporary pdl
 
1180
    undef $temppdl;
 
1181
 
 
1182
  }
 
1183
 
 
1184
 
 
1185
  ####################################################################
 
1186
  #                      A  X  I  S                                  #
 
1187
  ####################################################################
 
1188
  # A X I S information is expected as an array in the header
 
1189
  # called 'Axis'. These PDLs contain the CENTRE data and any further
 
1190
  # info is stored in their header.
 
1191
  # @bounds is accessed to find expected shape
 
1192
 
 
1193
 
 
1194
  # Simply look in the header for a Axis
 
1195
  if (exists $hdr{Axis}) {
 
1196
     # Check that we have an array
 
1197
     if (ref($hdr{Axis}) eq 'ARRAY') {
 
1198
       # Now loop over axes
 
1199
       for my $i (0..$#{$hdr{Axis}} ) {
 
1200
         # Loop unless status is bad
 
1201
         last unless $status == &NDF::SAI__OK;
 
1202
 
 
1203
         # Extract the ith axis PDL from the array
 
1204
         my $axis = $hdr{Axis}->[$i];
 
1205
 
 
1206
         # If we have a PDL
 
1207
         if (UNIVERSAL::isa($axis, 'PDL')) {
 
1208
            # We now want to copy the data and if necessary the
 
1209
            # Error array. Since there are only two I will do it the
 
1210
            # long way by explcitly storing data and then error
 
1211
 
 
1212
            # Set data type
 
1213
            $axtype = $pdltypes{$axis->get_datatype};
 
1214
            ndf_astyp($axtype, $outndf, 'Centre', $i+1, $status);
 
1215
 
 
1216
            # Okay we can now map this pdl
 
1217
            ndf_amap($outndf, 'Centre', $i+1, $axtype, 'WRITE', $axpntr,
 
1218
                   $el, $status);
 
1219
 
 
1220
            # Check that we have the correct number of entries
 
1221
            my $nelem = $axis->nelem;
 
1222
            if ($el == $nelem) {
 
1223
              print "Mapping axis " , $i+1 , "\n"  if $PDL::verbose;
 
1224
            
 
1225
              # Number of bytes per entry
 
1226
              $nbytes = PDL::Core::howbig($axis->get_datatype) * $el;
 
1227
            
 
1228
              # Copy to disk
 
1229
              string2mem( $ { $axis->get_dataref }, $nbytes, $axpntr)
 
1230
                 if ($status == &NDF::SAI__OK);
 
1231
 
 
1232
            } else {
 
1233
              carp "Axis ",$i+1 .
 
1234
                   " is the wrong size ($el values required but got ".
 
1235
                   $nelem . ")- ignoring";
 
1236
            }
 
1237
            # Unmap
 
1238
            ndf_aunmp($outndf, '*', $i+1, $status);
 
1239
 
 
1240
            # Errors
 
1241
            my $axhdr = $axis->gethdr; # Retrieve and check header
 
1242
            if (ref($axhdr) eq 'HASH') {
 
1243
              %axhdr = %$axhdr;
 
1244
            } else {
 
1245
              %axhdr = ();
 
1246
            }
 
1247
 
 
1248
            # Look for an Errors component in the header hash
 
1249
            if (exists $axhdr{Errors}) {
 
1250
               my $axerr = $axhdr{Errors};
 
1251
               if (UNIVERSAL::isa($axerr, 'PDL')) {
 
1252
 
 
1253
                 # Set data type
 
1254
                 $axtype = $pdltypes{$axerr->get_datatype};
 
1255
                 ndf_astyp($axtype, $outndf, 'Variance', $i+1, $status);
 
1256
 
 
1257
                 # Okay we can now map this pdl
 
1258
                 ndf_amap($outndf, 'Errors', $i+1, $axtype, 'WRITE',
 
1259
                   $axpntr, $el, $status);
 
1260
 
 
1261
                 # Check that we have the correct number of entries
 
1262
                 my $nelem = $axerr->nelem;
 
1263
                 print "Nelem: $nelem and $el\n";
 
1264
                 if ($nelem == $el) {
 
1265
                   print "Mapping errors for axis " . $i+1 . "\n"
 
1266
                     if $PDL::verbose;
 
1267
 
 
1268
                   # Number of bytes per entry
 
1269
                   $nbytes = PDL::Core::howbig($axerr->get_datatype) * $el;
 
1270
 
 
1271
                   # Copy to disk
 
1272
                   string2mem($ {$axerr->get_dataref}, $nbytes, $axpntr)
 
1273
                     if ($status == &NDF::SAI__OK);
 
1274
 
 
1275
                 } else {
 
1276
                    carp "Error PDL for Axis ",$i+1,
 
1277
                       " is the wrong size ($el values required but got ".
 
1278
                        $axerr->nelem . ")- ignoring";
 
1279
                 }
 
1280
                 # Unmap
 
1281
                 ndf_aunmp($outndf, '*', $i+1, $status);
 
1282
 
 
1283
               }
 
1284
 
 
1285
            }
 
1286
 
 
1287
            # Now character components
 
1288
            foreach my $char (keys %axhdr) {
 
1289
              if ($char =~ /LABEL|UNITS/i) {
 
1290
                $value = $axhdr{"$char"};
 
1291
                ndf_acput($value, $outndf, $char, $i+1, $status)
 
1292
                  if length($value) > 0;
 
1293
              }
 
1294
 
 
1295
            }
 
1296
 
 
1297
         }
 
1298
 
 
1299
       }
 
1300
 
 
1301
     }
 
1302
 
 
1303
  }
 
1304
 
 
1305
}
 
1306
 
 
1307
 
 
1308
# Write header information to NDF extension
 
1309
# This routine does not reconstruct an NDF identically to one that
 
1310
# was read in. FITS extensions are made. All non-integer numeric types
 
1311
# are written as doubles (apart from PDLs which carry there own type
 
1312
# information) unless _TYPES information exists in {NDF}.
 
1313
 
 
1314
 
 
1315
sub whdr {
 
1316
 
 
1317
  my ($outndf, $pdl, $status) = @_;
 
1318
 
 
1319
  my (%header, @fitsdim, $fitsloc, $value);
 
1320
  my (%unused, @fits, $hashref, $hdr);
 
1321
 
 
1322
  # Return if bad status
 
1323
  return 0 if $status != &NDF::SAI__OK;
 
1324
 
 
1325
  print "Writing header information...\n" if $PDL::verbose;
 
1326
 
 
1327
  # Write FITS header from {Hdr}
 
1328
 
 
1329
  # Retrieve and check header from PDL
 
1330
  $hdr = $pdl->gethdr;
 
1331
  if (ref($hdr) eq 'HASH') {
 
1332
    %header = %$hdr;
 
1333
  } else {
 
1334
    %header = ();
 
1335
  }
 
1336
 
 
1337
  %unused = ();
 
1338
  @fits = ();
 
1339
 
 
1340
  foreach my $key (sort keys %header) {
 
1341
 
 
1342
    next if $key eq '_COMMENTS';
 
1343
 
 
1344
    if ($key =~ /^TITLE$|^UNITS$|^LABEL$/i) {
 
1345
      # This is not extension info
 
1346
      ndf_cput($header{$key}, $outndf, $key, $status)
 
1347
        if length($header{$key}) > 0;
 
1348
    }
 
1349
 
 
1350
    # Only write scalars
 
1351
    if (not ref($header{"$key"})) {
 
1352
 
 
1353
       push(@fits, fits_construct_string($key,$header{$key},
 
1354
            $header{'_COMMENTS'}{$key}) );
 
1355
 
 
1356
    } else {
 
1357
      # Deal with later
 
1358
      $unused{$key} = $header{$key};
 
1359
    }
 
1360
 
 
1361
  }
 
1362
 
 
1363
  # Write the FITS extension
 
1364
  if ($#fits > -1) {
 
1365
    push(@fits, "END");
 
1366
    # Write it out
 
1367
    @fitsdim = ($#fits+1);
 
1368
    ndf_xnew($outndf, 'FITS', '_CHAR*80', 1, @fitsdim, $fitsloc, $status);
 
1369
    dat_put1c($fitsloc, $#fits+1, \@fits, $status);
 
1370
    dat_annul($fitsloc, $status);
 
1371
  }
 
1372
 
 
1373
  # Loop through all NDF header info and array Hdr information
 
1374
  # Note that %unused still contains our NDF hash so
 
1375
  # we must remove that before proceeding (else we end up with
 
1376
  # an 'NDF_EXT' extension!
 
1377
 
 
1378
  delete $unused{$EXTNAME};
 
1379
 
 
1380
  foreach $hashref (\%unused, $header{$EXTNAME}) {
 
1381
     whash($hashref, $outndf, '', '', $status);
 
1382
  }
 
1383
}
 
1384
 
 
1385
 
 
1386
#-----------------------------------------------------
 
1387
# This routine writes a hash array to a NDF extension
 
1388
# Keys that have leading underscores are skipped
 
1389
# $hashname contains the extension name to use by default.
 
1390
# This is extended  by any '.' in the key.
 
1391
 
 
1392
sub whash {
 
1393
 
 
1394
  my ($hash, $outndf, $hashname, $stypes, $status) = @_;
 
1395
 
 
1396
  my ($key, $comp, @structures, $extension, $xtype, @dim, $ndims, $loc);
 
1397
  my (@locs, $there, $type, %header, $packed, $length);
 
1398
  my (@bounds, $nbytes, $pntr, $maxsize, $which, $el, $path);
 
1399
  my ($oldhash, $oldtypes, $structs);
 
1400
 
 
1401
  if (defined $hash) {
 
1402
    %header = %$hash;
 
1403
  } else {
 
1404
    %header = ();
 
1405
  }
 
1406
 
 
1407
 
 
1408
  my $root = 1 if length($hashname) == 0; # Mark a ROOT structure
 
1409
 
 
1410
  # Return if bad status
 
1411
  return 0 if $status != &NDF::SAI__OK;
 
1412
 
 
1413
  foreach $key (sort keys %header) {
 
1414
 
 
1415
    next if $key =~ /^_/;
 
1416
 
 
1417
    # If the key matches the Extensions (Axis or errors)
 
1418
    # then skip. Should probably use the Extensions array
 
1419
    # itself to work out which components to skip
 
1420
 
 
1421
    next if $key eq 'Axis';
 
1422
    next if $key eq 'Errors';
 
1423
    next if $key eq 'Extensions';
 
1424
    next if $key eq 'Hist';
 
1425
 
 
1426
    # Deal with HASH arrays early
 
1427
    ref($header{"$key"}) eq 'HASH' && do {
 
1428
      $oldhash = $hashname;
 
1429
      $oldtypes = $stypes;
 
1430
      $hashname .= ".$key";
 
1431
      $hashname =~ s/^\.//; # remove leading dot
 
1432
      $stypes .= ".".$header{"$key"}{'_TYPES'}{STRUCT}{"$key"};
 
1433
      $stypes =~ s/^\.//; # remove leading dot
 
1434
      whash(\%{$header{"$key"}}, $outndf, $hashname, $stypes, $status);
 
1435
      $hashname = $oldhash;
 
1436
      $stypes = $oldtypes;
 
1437
      next;
 
1438
    };
 
1439
 
 
1440
 
 
1441
 
 
1442
    $path = $hashname . ".$key";
 
1443
    $path =~ s/^\.//; # remove leading dot
 
1444
 
 
1445
    if ($key =~ /^TITLE$|^UNITS$|^LABEL$/i) {
 
1446
      # This is not extension info
 
1447
      ndf_cput($header{$key}, $outndf, $key, $status);
 
1448
    } else {
 
1449
 
 
1450
      # Find nested structures
 
1451
      @structures = split(/\./, uc($path));
 
1452
 
 
1453
      # Last entry in structure is the important name
 
1454
      $comp = pop(@structures);
 
1455
 
 
1456
      # Find list of structures
 
1457
      $structs = join(".", @structures);
 
1458
      $structs = 'PERLDL' unless (defined $structs && $structs =~ /./);
 
1459
      $stypes = 'PERLDL_HDR' unless (defined $stypes && $stypes =~ /./);
 
1460
 
 
1461
      $loc = mkstruct($outndf, $structs, $stypes, $status);
 
1462
      undef $stypes;
 
1463
 
 
1464
      # Now write the component
 
1465
      # $loc is a locator to the last structure component
 
1466
      $type = $header{"_TYPES"}{"$key"};
 
1467
 
 
1468
      # What is the data type?
 
1469
      unless ($type =~ /./) {
 
1470
        # number or character or array or pdl
 
1471
        # All arrays are chars - should be PDLs if nums
 
1472
        ref($header{"$key"}) eq 'ARRAY' && ($type = "_CHAR");
 
1473
 
 
1474
        ref($header{"$key"}) eq 'PDL' &&
 
1475
          ($type = $pdltypes{$header{"$key"}{"Datatype"}});
 
1476
 
 
1477
        ref($header{"$key"}) || do {
 
1478
          if ($header{"$key"} =~ /^(-?)(\d*)(\.?)(\d*)([Ee][-\+]?\d+)?$/) {
 
1479
            $header{"$key"} =~ /\.|[eE]/
 
1480
              && ($type = "_DOUBLE")
 
1481
                ||   ($type = "_INTEGER");
 
1482
          } else {
 
1483
            ($type = '_CHAR')   # Character
 
1484
          }
 
1485
        };
 
1486
 
 
1487
      }
 
1488
 
 
1489
      # Create and write component
 
1490
      # PDLs
 
1491
 
 
1492
      if (ref($header{"$key"}) eq 'PDL') {
 
1493
        my $pdl = $header{"$key"};
 
1494
        @bounds = $pdl->dims;
 
1495
        my $n = $#bounds + 1;
 
1496
        dat_new($loc, $comp, $type, $n, \@bounds, $status);
 
1497
        $nbytes = PDL::Core::howbig($pdl->get_datatype) *
 
1498
            $pdl->nelem;
 
1499
        cmp_mapv($loc, $comp, $type, 'WRITE', $pntr, $el, $status);
 
1500
        string2mem(${$pdl->get_dataref}, $nbytes, $pntr)
 
1501
          if ($status == &NDF::SAI__OK);
 
1502
        cmp_unmap($loc, $comp, $status);
 
1503
      }
 
1504
 
 
1505
      # SCALARS
 
1506
      ref($header{"$key"}) || do {
 
1507
 
 
1508
        if ($type =~ /^_CHAR/) {
 
1509
          $length = length($header{"$key"});
 
1510
          dat_new0c($loc, $comp, $length, $status);
 
1511
          cmp_put0c($loc, $comp, $header{"$key"}, $status)
 
1512
            if $length > 0;
 
1513
        } elsif ($type =~ /^_LOG/) {
 
1514
          # In this case, add a check for FALSE or TRUE as strings
 
1515
          dat_new0l($loc, $comp, $status);
 
1516
          my $value = $header{$key};
 
1517
          if ($value eq 'FALSE') {
 
1518
            $value = 0;
 
1519
          } elsif ($value eq 'TRUE') {
 
1520
            $value = 1;
 
1521
          }
 
1522
          cmp_put0l($loc, $comp, $value, $status);
 
1523
        } else {
 
1524
          no strict "refs"; # Turn off strict
 
1525
          $which = lc(substr($type, 1,1));
 
1526
          &{"dat_new0$which"}($loc, $comp, $status);
 
1527
          &{"cmp_put0$which"}($loc, $comp, $header{"$key"}, $status);
 
1528
        }
 
1529
      };
 
1530
 
 
1531
      # CHARACTER ARRAYS
 
1532
      ref($header{"$key"}) eq 'ARRAY' && do {
 
1533
        # First go through the array and remove any
 
1534
        # entries that are references. I draw the line at storing
 
1535
        # nested arrays
 
1536
        my @norefs = grep { not ref($_) } @{$header{"$key"}};
 
1537
 
 
1538
        ($maxsize, $packed) = &NDF::pack1Dchar(\@norefs);
 
1539
        $ndims = 1;
 
1540
        @dim = ($#norefs+1);
 
1541
        dat_newc($loc, $comp, $maxsize, $ndims, @dim, $status);
 
1542
        cmp_putvc($loc, $comp, $dim[0], @norefs, $status);
 
1543
      };
 
1544
 
 
1545
      # Annul all the locators
 
1546
      dat_annul($loc, $status);
 
1547
 
 
1548
    }
 
1549
  }
 
1550
 
 
1551
}
 
1552
 
 
1553
# Subroutine to make extension structures of arbritrary depth
 
1554
# Just pass dot separated string to this function
 
1555
# A locator to the final structure is returned
 
1556
 
 
1557
sub mkstruct {
 
1558
 
 
1559
  my ($outndf, $structs, $types, $status) = @_;
 
1560
  my ($extension, @locs, @dim, $ndims, $xtype, $loc, $retloc);
 
1561
  my ($set, $prmry, $there);
 
1562
 
 
1563
  my (@structures) = split('\.', $structs);
 
1564
  my (@types) = split('\.', $types);
 
1565
 
 
1566
  return &NDF::DAT__NOLOC if $#structures < 0;
 
1567
  return &NDF::DAT__NOLOC if $status != &NDF::SAI__OK;
 
1568
 
 
1569
  # Does extension exist
 
1570
 
 
1571
  $extension = shift(@structures);
 
1572
  $xtype = shift(@types);
 
1573
 
 
1574
  ndf_xstat($outndf, $extension, $there, $status);
 
1575
 
 
1576
  # Make it if necessary
 
1577
  unless ($there) {
 
1578
    print "Writing $extension extension ($xtype)...\n" if $PDL::verbose;
 
1579
    @dim = (0);         # No pdl extensions!
 
1580
    $ndims = 0;
 
1581
    ndf_xnew($outndf, $extension, $xtype, $ndims, @dim, $loc, $status);
 
1582
  } else {                      # Get the locator to the extension
 
1583
    ndf_xloc($outndf, $extension, 'UPDATE', $loc, $status);
 
1584
  }
 
1585
 
 
1586
 
 
1587
  @locs = ();           # store the locators so that they can be annulled later
 
1588
  push(@locs, $loc);
 
1589
 
 
1590
  # Make the structures, end up with a locator to the correct component
 
1591
  for (0..$#structures) {
 
1592
    dat_there($loc, $structures[$_], $there, $status);
 
1593
    unless ($there) {   # Make it if it isnt there
 
1594
      my $type = $types[$_];
 
1595
      @dim = (0);
 
1596
      dat_new($loc, $structures[$_], $type, 0, @dim, $status);
 
1597
    }
 
1598
    dat_find($loc, $structures[$_], $loc, $status);
 
1599
    push(@locs, $loc);  # Store the new locator
 
1600
  }
 
1601
 
 
1602
  # Clone the locator
 
1603
  dat_clone($loc, $retloc, $status);
 
1604
  $set = 1;
 
1605
  $prmry = 1;
 
1606
  dat_prmry($set, $retloc, $prmry, $status);
 
1607
 
 
1608
  # Annul all except the last locator
 
1609
  foreach (@locs) {
 
1610
    dat_annul($_, $status);
 
1611
  }
 
1612
 
 
1613
  return &NDF::DAT__NOLOC unless $status == &NDF::SAI__OK;
 
1614
  return $retloc;
 
1615
 
 
1616
}
 
1617
 
 
1618
# end of module
 
1619
1;
 
1620
 
 
1621
!NO!SUBS!