class Radiation::Spectrum

Attributes

activity[RW]
calibration[RW]
count_correction[RW]
livetime[RW]
peaks[RW]
source[RW]

Public Class Methods

new(options={}) click to toggle source
# File lib/radiation/spectrum.rb, line 11
def initialize(options={})
        @peaks               = options.key?(:peaks) ? options[:peaks] : []
        @source              = options.key?(:source) ? options[:source] : nil
        @calibration= options.key?(:calibration) ? options[:calibration] : [0, 1]
        @livetime    = options.key?(:livetime) ? options[:livetime] : 1
        @activity    = options.key?(:activity) ? options[:activity] : 1
        @count_correction = options.key?(:count_correction) ? options[:count_correction] : 1
end

Public Instance Methods

calibrate() click to toggle source
# File lib/radiation/spectrum.rb, line 20
def calibrate
        if @peaks.empty? or @peaks.select{|p| p.key?(:channel)}.empty?
                raise "Nothing to calibrate"
        end

        if @peaks.select{|p| p.key?(:channel) and p.key?(:energy)}.empty?
                if @calibration == [0,1] and @source.nil?
                        raise "No channel <-> energy associations. Specify a Source or a preliminary calibration to improve"
                else
                        self.guess_calibration
                end
                self.match_channels
        end

        @calibration = apply_linefit(@peaks)
        return self
end
channel_efficiency(peak) click to toggle source
# File lib/radiation/spectrum.rb, line 79
def channel_efficiency(peak)
        peak[:counts]*@count_correction/(peak[:intensity]*@livetime*@activity)
end
channel_energy(chn) click to toggle source
# File lib/radiation/spectrum.rb, line 47
def channel_energy(chn)
        @calibration[0] + @calibration[1]*chn
end
efficiencies(rounding=4) click to toggle source
# File lib/radiation/spectrum.rb, line 60
def efficiencies(rounding=4)
        self.match_channels
        @peaks.select{|p| p.key?(:intensity) and p.key?(:counts)}.each{|p| p[:efficiency] = channel_efficiency(p)}
        return self
end
guess_calibration(energies=@source.energies, rounding=4) click to toggle source
# File lib/radiation/spectrum.rb, line 38
def guess_calibration(energies=@source.energies, rounding=4)
        # Build all possible combinations of known energies and peaks
        arr = [energies, @peaks.collect{|peak| peak[:channel]}].comprehension
        # The approximate value for b in $Energy = a + b * Channel$ will be most frequent for $a \approx 0$
        freq = arr.collect{|a| (a.first.to_f/a.last.to_f).round(rounding) }.flatten.inject(Hash.new(0)) { |h,v| h[v] += 1; h }.sort_by{|k,v| v}
        @calibration = [0, freq.last[0] ]
        return self
end
match_channels(source=@source, rounding=4) click to toggle source
# File lib/radiation/spectrum.rb, line 66
def match_channels(source=@source, rounding=4)
        @peaks.each do |peak|
                source.transitions.each do |transition|
                        if channel_energy(peak[:channel]).to_f.approx_equal?(transition[:energy], rounding)
                                peak[:energy] = transition[:energy]
                                peak[:intensity] = transition[:intensity] if transition[:intensity] > 0
                        end
                end
        end
        return self
end
parse_hdtv(file) click to toggle source
# File lib/radiation/spectrum.rb, line 51
def parse_hdtv(file)
        xml = XmlSimple.xml_in(file, { 'KeyAttr' => 'name' })
        @peaks = xml["fit"].collect{|p| p["peak"]}.flatten.collect{|p| p["uncal"]}.flatten.collect do |p|
                {:channel => p["pos"].first["value"].first.to_f.pm(p["pos"].first["error"].first.to_f),
              :counts => p["vol"].first["value"].first.to_f.pm(p["vol"].first["error"].first.to_f) }
        end
        return self
end

Private Instance Methods

apply_linefit(peaks) click to toggle source
# File lib/radiation/spectrum.rb, line 84
def apply_linefit(peaks)
        #calibrate using linefit
        mpeaks = peaks.delete_if{|p| p[:energy] == nil}
        x = mpeaks.collect{|p| p[:channel]}
        y = mpeaks.collect{|p| p[:energy]}
        lineFit = LineFit.new

        # Use weighted calibration when possible
        if mpeaks.count{|p| p[:intensity] and p[:intensity] > 0} == mpeaks.count
                weights = mpeaks.collect{|p| p[:intensity].nil? ? 0 : p[:intensity]}
                lineFit.setData(x,y,weights)
        else
                lineFit.setData(x,y)
        end

        intercept, slope = lineFit.coefficients
        varianceIntercept, varianceSlope = lineFit.varianceOfEstimates
        return [intercept.pm(Math.sqrt(varianceIntercept.abs)), slope.pm(Math.sqrt(varianceSlope.abs))]
end