#!/usr/bin/perl # this should be used by octave to create an # equivalent to MATLAB's cdfread function. use warnings; use strict; use CDF; use File::Basename; use File::stat; use Time::localtime; use Getopt::Long; use Pod::Usage; my $verbose = 0; my $debug = 0; my $help = 0; my $badnameCTR = 0; GetOptions ( 'verbose' => \$verbose, 'vb=i' => \$verbose, 'debug+' => \$debug, 'help|?' => \$help, 'octave' => sub { open(FILE,">ocdfread.m"); print FILE "function [data info] = ocdfread(filename)\n"; print FILE "stdout = system([\"cdfread.pl \" filename]);\n"; print FILE "eval(stdout);\n"; print FILE "endfunction\n"; close FILE; print "created ocdfread.m\n"; exit; } ); if ($help || (@ARGV != 1)) { pod2usage( -msg => "CDFREAD.PL - allows Octave to read CDF files", -verbose => $verbose, ); } my ($filename) = @ARGV; $filename .= ".cdf" if ($filename !~ /.cdf$/); my ($fileID, $numDims, @dimSizes, $encoding, $majority, $majoritySTRING, $maxRec, $numVars, $numAttrs); &CDF::CDFopen($filename,\$fileID); &CDF::CDFinquire($fileID,\$numDims, address@hidden, \$encoding, \$majority, \$maxRec, \$numVars, \$numAttrs); # force the second dimension in $dimSizes # if not specified, then the dimensionality is 1; $dimSizes[1] = 1 unless ($dimSizes[1]); if ($debug) { print "%dims = $numDims, dimsizes = @dimSizes, encoding = $encoding, ". "majority = $majority, maxRec = $maxRec, numvars = $numVars, ". "numattr = $numAttrs\n"; } ################ #### HEADER #### ################ my ($cType,@cParms,$cSize,$uSize); &CDF::CDFlib(GET_,CDF_INFO_,$filename,\$cType,address@hidden,\$cSize,\$uSize); print "%type=$cType, address@hidden, csize=$cSize, usize=$uSize\n" if $debug; print "info.Filename = '".basename($filename)."';\n"; print "info.FileModDate = '".ctime(stat($filename)->mtime)."';\n"; print "info.FileSize = $uSize;\n"; print "info.Format = 'CDF';\n"; my ($version, $release, $increment); &CDF::CDFlib(GET_,CDF_VERSION_,\$version); &CDF::CDFlib(GET_,CDF_RELEASE_,\$release); &CDF::CDFlib(GET_,CDF_INCREMENT_,\$increment); print "info.FormatVersion = '$version.$release.$increment';\n"; # LIMITATION: single files only!! print "info.FileSettings.Format = 'Single-file';\n"; my $cPct; &CDF::CDFlib(GET_,CDF_COMPRESSION_,\$cType,address@hidden,\$cPct); print "info.FileSettings.Compression = '$cType';\n"; print "info.FileSettings.CompressionParam = '@cParms';\n"; print "info.FileSettings.CompressionPercent = $cPct;\n"; print "info.FileSettings.Encoding = '".getCDFencoding($encoding)."';\n"; for ($majority) { if ($_ == COL_MAJOR) {$majoritySTRING = "Column";} elsif ($_ == ROW_MAJOR) {$majoritySTRING = "Row";} else {$majoritySTRING = "unknown";} } print "info.FileSettings.Majority = '$majoritySTRING';\n"; #&CDF::CDFlib(GET_,CDF_COPYRIGHT_,\$cdfcopyright); #print "info.FileSettings.Copyright = '$cdfcopyright';\n"; print "info.Subfiles = {};\n"; ############################# ### READ IN THE VARIABLES ### ############################# my @varnames; for (my $jvar=0;$jvar<$numVars;$jvar++) { my ($varName, $dataType, $numElements, $recVariance, @dimVariances); &CDF::CDFvarInquire($fileID, $jvar, \$varName, \$dataType, \$numElements, \$recVariance, address@hidden); if ($varName =~ /address@hidden&*()+|\\:;]/) { $varName =~ tr/ address@hidden&*()+|\\:;/_/; $varName .= sprintf("_%03d",++$badnameCTR); } if ($debug) { print "%Var[$jvar]: name=$varName type=$dataType n=$numElements ". "var=$recVariance address@hidden"; } my $dataTypeSTRING = getCDFtype($dataType); die "%Presently, rVariables must be numeric\n" if ($dataTypeSTRING =~ /char/); my $VarianceCode = getCDFvariances($recVariance, @dimVariances); $varnames[$jvar] = $varName; # store name for INFO structure my $VAR = $jvar + 1; print "info.Variables{$VAR,1} = '$varName';\n"; print "info.Variables{$VAR,2} = address@hidden;\n"; print "info.Variables{$VAR,3} = ".($maxRec+1).";\n"; print "info.Variables{$VAR,4} = '$dataTypeSTRING';\n"; print "info.Variables{$VAR,5} = '$VarianceCode';\n"; print "info.Variables{$VAR,6} = 'Full';\n"; for (my $kRec=0;$kRec<=$maxRec;$kRec++) { my $row = $kRec + 1; # ALT SYNTAX: data{$row,$VAR}($recrow,$reccol) = $value;\n print "data{$row,$VAR} = ["; # loop indices are named $recD#. Read as "record dimension #" # hopefully ,this makes extending to > 2 dimensions easy. for (my $recD1=0;$recD1<$dimSizes[0];$recD1++) { for (my $recD2=0;$recD2<$dimSizes[1];$recD2++) { my @indices = ($recD1, $recD2); my $value; &CDF::CDFvarGet($fileID, $jvar, $kRec, address@hidden, \$value); print "$value "; } # only print the trailing ';' if there's another row coming print "; " if ($recD1 + 1 < $dimSizes[0]); } print "];\n"; } } ############################## ### READ IN THE ATTRIBUTES ### ############################## for (my $jattr=0;$jattr<$numAttrs;$jattr++) { my ($attrName, $attrScope, $attrScopeSTRING, $maxEntry); &CDF::CDFattrInquire($fileID, $jattr, \$attrName, \$attrScope, \$maxEntry); if ($attrName =~ /address@hidden&*()+|\\:;]/) { $attrName =~ tr/ address@hidden&*()+|\\:;/_/; $attrName .= sprintf("_%03d",++$badnameCTR); } print "%Attr[$jattr]: name=$attrName ". "type=$attrScope n=$maxEntry\n" if $debug; for ($attrScope) { if ($_ == GLOBAL_SCOPE) {$attrScopeSTRING = "Global";} elsif ($_ == VARIABLE_SCOPE) {$attrScopeSTRING = "Variable";} else {$attrScopeSTRING = "unknown";} } my $entryCTR = 1; for (my $jEntry=0;$jEntry<=$maxEntry;$jEntry++) { my ($dataType, $dataTypeSTRING, $numElements); unless (&CDF::CDFattrEntryInquire($fileID, $jattr, $jEntry, \$dataType, \$numElements)) { $dataTypeSTRING = getCDFtype($dataType); print "info.VariableAttributes.$attrName\{$entryCTR,1\} ". "= '$varnames[$jEntry]';\n" if ($attrScope == VARIABLE_SCOPE) ; print "%AttrEntry[$jEntry]: type = $dataTypeSTRING, ". "n = $numElements\n" if $debug; if ($dataTypeSTRING !~ /char/ && $numElements > 1) { print "info.ERROR = 'INPUT ERROR with attribute ''$attrName''. ". "Cannot handle multiple elements in numeric attributes. ". "See CDF::CDFattrGet'\n"; } my $value; &CDF::CDFattrGet($fileID, $jattr, $jEntry, \$value); $value =~ s/(.*?)\n?$/'$1'/ if ($dataTypeSTRING =~ /char/) ; print "info.$attrScopeSTRING"."Attributes.$attrName". "{$entryCTR,$attrScope} = $value;\n"; $entryCTR++; } } } &CDF::CDFclose($fileID); ######################################################################## ## SUBROUTINES ## ######################################################################## sub getCDFtype { my ($dataType) = @_; my $dataTypeSTRING = qq//; for ($dataType) { if ($_ == CDF_BYTE) {$dataTypeSTRING = "byte";} elsif ($_ == CDF_INT1) {$dataTypeSTRING = "int8";} elsif ($_ == CDF_INT2) {$dataTypeSTRING = "int16";} elsif ($_ == CDF_INT4) {$dataTypeSTRING = "int32";} elsif ($_ == CDF_UINT1) {$dataTypeSTRING = "uint1";} elsif ($_ == CDF_UINT2) {$dataTypeSTRING = "uint2";} elsif ($_ == CDF_UINT4) {$dataTypeSTRING = "uint4";} elsif ($_ == CDF_REAL4) {$dataTypeSTRING = "float";} elsif ($_ == CDF_REAL8) {$dataTypeSTRING = "double";} elsif ($_ == CDF_FLOAT) {$dataTypeSTRING = "float";} elsif ($_ == CDF_DOUBLE){$dataTypeSTRING = "double";} elsif ($_ == CDF_CHAR) {$dataTypeSTRING = "char";} elsif ($_ == CDF_UCHAR) {$dataTypeSTRING = "uchar";} elsif ($_ == CDF_EPOCH) {$dataTypeSTRING = "CDF_EPOCH";} else {$dataTypeSTRING = "unknown($dataType)";} } return $dataTypeSTRING; } sub getCDFencoding { my ($encoding) = @_; my $encodingSTRING = qq//; for ($encoding) { if ($_ == NETWORK_ENCODING) {$encodingSTRING = "Network";} elsif ($_ == SUN_ENCODING) {$encodingSTRING = "Sun";} elsif ($_ == VAX_ENCODING) {$encodingSTRING = "VAX";} elsif ($_ == DECSTATION_ENCODING) {$encodingSTRING = "DECstation";} elsif ($_ == SGi_ENCODING) {$encodingSTRING = "SGi";} elsif ($_ == IBMPC_ENCODING) {$encodingSTRING = "IBMPC";} elsif ($_ == IBMRS_ENCODING) {$encodingSTRING = "IBMRS";} elsif ($_ == HOST_ENCODING) {$encodingSTRING = "Host";} elsif ($_ == MAC_ENCODING) {$encodingSTRING = "Mac";} elsif ($_ == HP_ENCODING) {$encodingSTRING = "HP";} elsif ($_ == NeXT_ENCODING) {$encodingSTRING = "NeXT";} elsif ($_ == ALPHAOSF1_ENCODING) {$encodingSTRING = "ALPHAOSF1";} elsif ($_ == ALPHAVMSd_ENCODING) {$encodingSTRING = "ALPHAVMSd";} elsif ($_ == ALPHAVMSg_ENCODING) {$encodingSTRING = "ALPHAVMSg";} elsif ($_ == ALPHAVMSi_ENCODING) {$encodingSTRING = "ALPHAVMSi";} else {$encodingSTRING = "unknown";} } return $encodingSTRING; } sub getCDFvariances { my ($rec, @dim) = @_; my $VarianceCode; for ($rec) { if ($_ == VARY) {$VarianceCode = "T/";} elsif ($_ == NOVARY) {$VarianceCode = "F/";} else {$VarianceCode = "$rec\?/";} } if (@dim > 1 || $dim[0] > 1) { foreach (@dim) { if ($_ == VARY) {$VarianceCode .= "T";} elsif ($_ == NOVARY) {$VarianceCode .= "F";} else {$VarianceCode .= "$rec\?";} } } return $VarianceCode; } =head1 NAME F - Reads a CDF file and makes output readable by octave or matlab. Along with a short .m file, one can simulate the action of Matlab's CDFREAD in Octave. This was written to read MegaSIMS cdf files and has many limitations. You are welcomed to enhance the code. =head1 SYNOPSIS F [-B] [-B] [-B] [-B] I C C generates OCDFREAD.M, an Octave m-file that simulates Matlab's CDFREAD. C produces this message C produces a longer help message =head1 DESCRIPTION Reads a CDF data file and writes output readable by Octave. On the command line, the '.cdf' extension is optional. =head1 OPTIONS =over =item -B runs in verbose mode =item -B shows execution options =item DEFAULTS: not verbose, no debug, writes to STDOUT =back =head1 ARGUMENTS This program expects exactly one argument for command-line execution. =head1 SEE ALSO L,L, L, L, L =head1 KNOWN BUGS This should work fine as long as you stick to the MegaSIMS restricted CDF format. =over Doesn't import the CDF copyright statement into the data structure. Only reads single-file CDF data sets. Won't read attributes beyond the first element in an entry. This seems to be a CDF Perl package limitation. Dimensionality of a record is limited to 2. Only reads numeric variable data. No support for zVariables. Conversion from funny characters (non alphanumerics) to `_' is probably incomplete. We've learned to avoid those anyways. =back =head1 AUTHOR MegaSIMS group, UCLA, Eaddress@hidden. Peter H. Mao, Eaddress@hidden =head1 COPYRIGHT AND LICENSE F - CDF reader for Octave. Copyright (C) 2007 MegaSIMS, University of California Regents This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. To receive a copy of the GNU General Public License, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. =cut