To post in-line images, login and click on the Gallery link at the top
Quote from: aducharme on April 29, 2026, 11:42:12 PMReading the deadtime correction paper, the logarithm appears to have fallen out by extrapolating the x + x^2/2 terms as the first two terms in the Taylor series of log(1-x)=\sum x^n/n. To me, those first two terms are hallmarks of the Taylor series for e^x = \sum x^n/n!. This gives another possible deadtime correction i/(2-e^(i*\tau)). It is surprisingly similar to the logarithm-based correction:
Maybe this would get rid of the squiggle? Or perhaps it's not a good model. John, would you be able to give it a shot?

Quote from: JonF on April 30, 2026, 01:59:03 AMQuote from: Ben Buse on April 29, 2026, 09:11:05 AMJohn, what do you think about loading in specified concentration maps into CalcImage? If the EDS maps were quantified elsewhere, and we loaded the maps in as specified elements to allow for correct matrix correction of the WDS elements?
Just to add my two pennies worth, the problem with this is that you would need to quant the EDS maps first, meaning that for these to be accurate, you need to quantify all of the major elements. This would be fine if you were just using the WDS to look at minor/trace phases (alongside the EDS), but if you're using the WDS to disentangle a nasty interference of major elements, then you'd potentially be loading in the wrong quant EDS data, whether by omission of the WDS-measured elements (and hence incorrect matrix corrections), or by including elements that required the WDS in the first place (and hence have a major interference issue).

import os
import numpy as np
#import DigitalMicrograph as DM
#dir=r"C:\Users\arksa\Documents\em-data\jeol-pts"
#dir=r"C:\Users\glxbb\OneDrive - University of Bristol\JEOL8530F\EpmaData\Ben\eds wds sjio pfe\eds wds sjio pfe_0004_MAP\Pos_0002\EDS"
#dir=r"C:\Users\glxbb\OneDrive - University of Bristol\JEOL8530F\EpmaData\Ben\eds test2\eds test2_0001_MAP\Pos_0003\EDS"
dir=r"C:\Users\glxbb\OneDrive - University of Bristol\JEOL8530F\EpmaData\Ben\eds wds map ga garnet\eds wds map ga garnet_0001_MAP\Pos_0001\EDS"
os.chdir(dir)
file=r"2406PTTD.PTS"
x=60
y=60
# number of energy channel of EDS.
ch=4096
# binning of energy channel in output.
# if you want enegy resolution, set 1.
# this parameter is set just for memory and disc saving.
binn=1
# cutoff energy of output.
# this is also for memory saving.
cut=2048-160
data=np.fromfile(file,dtype="u2",offset=16384)
flag=np.arange(data.size)[(data==55296) | (data==53504) | (data==53760)]
# assuming livetime stamp is 24576 = 0x6000
# assuming realtime stamp is 28672 = 0x7000
livet=24576
realt=28672
# resolution of time stamp.
# maybe 10 msec
tscale=10
#data=np.where((data==24576) | (data==28672), 1, data-45056)
data=np.where((data==livet) | (data==realt), binn*((data-livet)//(realt-livet)), data-45056)
flagsize=flag.size
print(flagsize)
si=np.zeros((x+1,y,(ch-cut-160)//binn),dtype="u2")
siTest=np.zeros((x+1,y,(ch-cut)//binn),dtype="u2")
ypos=-1
j=0
for i in range(flag.size):
#for i in range(10):
pos=flag[i]
if data[pos]==55296-45056:
ypos+=1
j=0
elif data[pos]==53760-45056:
nextpos=flag[i+1]
if data[nextpos]==53760-45056 or data[nextpos]==53504-45056:
si[j,ypos,:]=np.bincount(data[pos+1:nextpos]//binn,minlength=(ch-cut)//binn)[:(ch-cut)//binn][160:]
#si[j,ypos,:]=np.bincount(data[pos+1:nextpos])[160:2048+160]
#SiStore=np.bincount(data[pos+1:nextpos])
j+=1
si[:,:,0:2]*=tscale
si2=np.transpose(si,(2,0,1))
#siDM=DM.CreateImage(si2.copy(order="C"))
#siDM.ShowImage()
#del siDM
"""
recalibrate(s::Spectrum{T}, es::LinearEnergyScale)
Allows changing the energy scale on a spectrum from one LinearEnergyScale to another as though the spectrum were
measured on a different detector. The algorithm uses a FFT-base scheme to rescale and shift the spectral data.
This scheme allows for fractional shifts of offset and fractional changes in the width. It is limited in that
the change in width must produce an integral number of channels in the resulting spectrum. The algorithm
maintains the total spectrum integral so the new spectrum can be used for quantitative purposes.
Plotting one spectrum over the other should maintain peak position but is likely to change the channel counts.
"""
function recalibrate(s::Spectrum{T}, es::LinearEnergyScale)
les = LinearEnergyScale(3.0, 10.1)
spec = loadspectrum("\path\to\spectrum\spec.msa")
new_spec = recalibrate(spec, les)
Quote from: Nicholas Ritchie on April 30, 2026, 06:00:34 AMQuote from: Ben Buse on April 30, 2026, 03:48:51 AMCan I make a map sum a standard?I'm not exactly sure where the problem lies. It may be a overflow inMapSum = sum(hs3)
MapSum[:LiveTime]=hs3[1,1][:LiveTime]*60*60
maprefs = references([reference(n"Al",MapSum,mat"Ca3Mn57Al40Si60O240")],128)
ERROR: DomainError with -2082.385000000002:
sqrt was called with a negative real argument but will only return a complex result if called with a complex argument. Try sqrt(Complex(x)).hs3[1,1][:LiveTime]*60*60Try:float(hs3[1,1][:LiveTime])*60*60 # or
hs3[1,1][:LiveTime]*60.0*60.0
maprefs = references([reference(n"Al",hs3[1,1],mat"Al")],128)
ERROR: DomainError with -2082.385000000002:
sqrt was called with a negative real argument but will only return a complex result if called with a complex argument. Try sqrt(Complex(x)).
Stacktrace:
[1] throw_complex_domainerror(f::Symbol, x::Float64)
@ Base.Math .\math.jl:33
[2] sqrt
@ .\math.jl:627 [inlined]
[3] resolution_to_fwhm
@ C:\Users\glxbb\.julia\packages\NeXLSpectrum\eVZfj\src\detector.jl:155 [inlined]
[4] resolution
@ C:\Users\glxbb\.julia\packages\NeXLSpectrum\eVZfj\src\detector.jl:165 [inlined]
[5] resolution(eV::Float64, det::BasicEDS)
@ NeXLSpectrum C:\Users\glxbb\.julia\packages\NeXLSpectrum\eVZfj\src\detector.jl:309
[6] buildfilter(::Type{Float64}, ty::Type{G2Filter}, det::BasicEDS, a::Float64, b::Float64)
@ NeXLSpectrum C:\Users\glxbb\.julia\packages\NeXLSpectrum\eVZfj\src\filter.jl:320
[7] buildfilter
@ C:\Users\glxbb\.julia\packages\NeXLSpectrum\eVZfj\src\filter.jl:312 [inlined]
[8] references(refs::Vector{NeXLSpectrum.ReferencePacket}, det::BasicEDS; ftype::Type{Float64}, filter::Type)
@ NeXLSpectrum C:\Users\glxbb\.julia\packages\NeXLSpectrum\eVZfj\src\reference.jl:156
[9] references
@ C:\Users\glxbb\.julia\packages\NeXLSpectrum\eVZfj\src\reference.jl:146 [inlined]
[10] references(refs::Vector{NeXLSpectrum.ReferencePacket}, fwhm::Int64; kwargs::@Kwargs{})
@ NeXLSpectrum C:\Users\glxbb\.julia\packages\NeXLSpectrum\eVZfj\src\reference.jl:166
[11] top-level scope
@ REPL[255]:1
60 × 60 HyperSpectrum{UInt16,(:Y, :X)}[8 sp1 spess, -1638.3 + 10.0⋅ch eV, 2048 ch]
refs4
References[
BasicEDS[2048 chs, 0.0 + 10.0⋅ch eV, 130.0 eV @ Mn K-L3, 2 ch LLD, [Be,Sc,Ba,Pu]]
resmap4 = fit_spectrum(hs4,refs4)
ERROR: AssertionError: The detector for the hyper-spectrum must match the detector for the filtered references.
Stacktrace:
Quote from: Ben Buse on April 30, 2026, 03:48:51 AMCan I make a map sum a standard?I'm not exactly sure where the problem lies. It may be a overflow inMapSum = sum(hs3)
MapSum[:LiveTime]=hs3[1,1][:LiveTime]*60*60
maprefs = references([reference(n"Al",MapSum,mat"Ca3Mn57Al40Si60O240")],128)
ERROR: DomainError with -2082.385000000002:
sqrt was called with a negative real argument but will only return a complex result if called with a complex argument. Try sqrt(Complex(x)).
hs3[1,1][:LiveTime]*60*60
Try:float(hs3[1,1][:LiveTime])*60*60 # or
hs3[1,1][:LiveTime]*60.0*60.0
Quote from: Ben Buse on April 30, 2026, 01:11:21 AMLooking at thisLiveTime => lt,Seems live time is fixed for every pixel in the map is it possible to use an array of live time
Using Nicholas example above, I loaded the map save from hyperspy as rpl,hs = NeXLSpectrum.compress(HyperSpectrum(
LinearEnergyScale(0.0,40.0),
Dict{Symbol,Any}(
:TakeOffAngle => deg2rad(35.0),
:ProbeCurrent => 1.0,
:LiveTime => lt,
:BeamEnergy => 30.0e3,
:Name => "8 sp1 spess"
),
readrplraw(raw"C:\\Users\\glxbb\\julia\\edstry2"),
fov = [ 60u"mm", 60u"mm"], offset= [ 0.0u"mm", 0.0u"mm" ]
))
60 × 60 HyperSpectrum{UInt16,(:Y, :X)}[8 sp1 spess, 0.0 + 40.0⋅ch eV, 512 ch]
readrplraw(....),
livetime=fill(get(props, :LiveTime, 1.0), (512, 512))
)