sf_ashley has asked for the wisdom of the Perl Monks concerning the following question:
Dear Monks,
Apologies for the long nature of this post. Unfortunately, I couldn't find a more succinct way of posing the following problem. I am the midst of writing a reasonably voluminous code to perform some reasonably straight-forward data analysis. One part of the code involves 'rebinning' spectra where the bin width of a spectrum is linearly interpolated to a format defined by the user (and is of a greater width than the original format). A couple of errors arise, that I do not know how to deal with and were problems that I would like to iron out. The subroutine code is given below, commented as appropriate and the error messages are as follows:
Firstly, I get the following warnings in this order: uninitialized value in addition on line 1153, unin. val in multiplication (line 1158), unin. val in subtraction (line 1162), unin. val. in numeric ge (>=) in line 1148. I also get a host of Invalid conversion warnings at line 1188, which I'll come to later. Now, @energy_before and @energy_rebin are defined as I have tested this by printing the values into an output file. Also, the values of @counts_rebin are also not entirely NULL, as I do end up getting a rebinned spectrum that is reasonably accurate. However, I don't know why it is coming up with these warnings and would like to be able to eradicate these. Any ideas as to what is causing this?
Secondly, when I say the spectra produced are reasonably accurate, the input file is definitely of a little-endian VAX format, as I had generated this earlier in the code. However, after rebinning, the spectra should be of a floating-point form. Now, when I have in line 1188, the expression 'V' for a VAX output, the code compiles but with the aforementioned warnings. If, I replace this with an 'f', I end up with a fatal error 'Modification of a read-only value attempted at line 1188'. Now, this has completely puzzled me. I think I understand that the invalid conversion should arise as I have non-integer values being parsed as integers. But I cannot see why there is a fatal exception here. Also, when I run this, I have extra digits in the invalid conversion warning, i.e. with 'V', it would just say invalid conversion in printf: at line 1188, <STDIN> line 15; with 'f' it would say (as an example) invalid conversion in printf: %\015 at line 1188 <STDIN> line 15. Why is this error arising? In the unpack command, is the data only treated as integers or does perl switch from handling these as integers to floats? Also, is there a specific format for a 4 byte little- or big-endian floating point value? I presumed this would be 'f'.
Anyway, I hope my errors aren't too trivial or are from bad programming form and suggestions regarding the aforementioned warnings/exceptions/errors would be very much appreciated. If further tests or information is required, please do not hesitate to ask.
Kind regards,
Steve
sub rebinner_interp{ #Rebinning portion of analysis code. Define local variables used in t +his subroutine my $idx, my $buf, my $i, my $j, my @calibcoeff; my @energy_rebin, my @values, my @energy_before; my $chn; my @acoeff, my @bcoeff, my @ccoeff; #Note: $spec, %slope[] and %intercept[] are global variables that have + been defined earlier #User to input their bin-width of choice, define new energy binning st +ructure that is universal for all spectra printf STDOUT "Please enter the energy width (in keV) of each bin\ +n"; chomp (my $rebin_width = <STDIN>); for ($i=0; $i<16834; $i++){ $energy_rebin[$i] = ($rebin_width * $i); } #For each spectrum, $idx, read in original spectrum in VAX format to a +rray @rawchannel for ($idx=0; $idx<=$spec; $idx++){ my @rawchannel; open (INFILE, "<", "filename.spc") or die "Can't find spectrum: filename.spc"; while (read(INFILE, $buf, 4) == 4) { if (length($buf) > 0 && length ($buf) < 4){ next; } else{ push @rawchannel, scalar unpack('V',$buf); } } close INFILE; #For $idx, determine the energy binning before from pre-determined cal +ibration $chn = 0; $i = 1; for ($chn=0;$chn<16384;$chn++){ if ($i == $calib_points){ $energy_before[$chn] = ($slope{$idx}[$i]*${chn})+$intercept{$i +dx}[$i]; } if (($chn<=$channel{$idx}[$i])&&($i<$calib_points)){ $energy_before[$chn] = ($slope{$idx}[$i]*$chn)+$intercept{$idx +}[$i]; } elsif (($chn > $channel{$idx}[$i]) && ($i < $calib_points)){ $i += 1; $energy_before[$chn] = ($slope{$idx}[$i]*$chn)+$intercept{$idx +}[$i]; } } #Originally, test script placed here to check the output of each @ener +gy_rebin and @energy_before. So the script should be fine up to here +. #Define @counts_rebin for each spectrum which will contain the number +of counts per channel for each rebinned spectrum $j=0; my $k = 0; my @counts_rebin; my $value; #Loops to generate rebinned spectrum. First part intended to define a + value for $counts_rebin[$j] if undefined. Then depending on the ord +er add all or part of the bin as fit. Note, from test $k is always l +ess than 16382. for($j=0; $j<=16831; $j++){ if(($energy_rebin[$k] >= $energy_before[$j])){ # l:1148 if ((defined($counts_rebin[$k]))==0){ $counts_rebin[$k]=0; } $value = $rawchannel[$j]; $counts_rebin[$k] = ($counts_rebin[$k] + $value); # l:1153 next; } if(($energy_rebin[$k] < $energy_before[$j]) && $j!=0){ my $totcount = $rawchannel[$j]; my $split1 = (($energy_before[$j]-$energy_rebin[$k])*$totcount +)/($energy_before[$j]-$energy_before[${j}-1]); # l:1158 if ((defined($counts_rebin[$k]))==0){ $counts_rebin[$k]=0; } $counts_rebin[$k]=($counts_rebin[$k]+($totcount-$split1)); # l +:1162 $counts_rebin[$k+1]=$split1; $k+=1; next; } if(($energy_rebin[$k]<$energy_before[$j]) && $j==0){ until ($energy_rebin[$k]>=$energy_before[$j]){ $k += 1; } $value = $rawchannel[$j]; $counts_rebin[$k] = $counts_rebin[$k] + $value; next; } } #As $counts_rebin[$j] only partially filled, define the rest of the ar +ray (up to 16382) as undefined $j=0; for ($j=0;$j<=16381;$j++){ if (defined($counts_rebin[$j])){ next; } else{ $counts_rebin[$j]=0; } } #Output the file in a little-endian VAX format open (REBINOUT, ">", "filename.spr"); binmode REBINOUT, ":raw"; for ($i=0;$i<=16831;$i++){ printf REBINOUT pack('V',$counts_rebin[$i]); # l: 1188 } close REBINOUT; } }
|
|---|