Needing more than a spark test?

Seems like it :)

Is their code well documented? I haven't had a lot of luck with deconvolution, personally. Or rather, I have failed to implement it successfully. I'm supposing that I didn't quite understand it sufficiently...

Edit.
I found the paper on Gaussian deconvolution at the Therimino site. The basic premise is that you know all the possible contaminating spectral lines. Suppose we do, since there's a finite number of elements, and even less of them have adjacent lines. Based on that possibility one tries to sort that all out. I need to reread it. Seems you will need quite a bit table or database of all the lines. Could principal component analysis be of help here? Or is the gaussian deconvolution sufficient?

2nd edit. Wish that Therimino wasn't in visual basic. Would have thought it would be in C or C++.
For quantitative xray spectroscopy it is quite common to use elemental references. That way your particular system's peculiarities can be taken into account. Collecting elemental samples is a "thing" these days so it isn't too difficult or expensive to come up with a set. I bought some pure manganese, iron and nickel samples off ebay for characterizing my XRF system. The iron and nickel were accompanied by cadmium and zinc -- while not useful for characterizing steel alloys, they could be used for a wider xray energy range, if one of us wants to use our XRF system for other purposes. Like checking to see if the paint on some old structure you've got has lead in it. I know, there are off-the-shelf tests for that.....but as time goes by their availability is going away. I got the last two at a local Home Depot not very long ago, and that store was the only one that had them within a 25 mile radius. An XRF system like ours should be able to detect lead like gangbusters, since the pocketgeiger's xray detector sensitivity is close to its maximum for the lead 10Kev line.

The thought occurs to me that there's little most buyers can do when it comes to verifying the veracity of those elemental sample vendors. We should be able to do that :).

Anyway, my point is that coming up with a dictionary of different elements won't be all that hard to do. The tricky bit may be making sure that the XRF system is relatively stable over time.

Regarding your comment about the use of visual basic: I agree with you. C or C++ would have been nice. I haven't looked at the pyMCA code to see if it includes deconvolution -- but given all the stuff available via scipy or numpy that seems pretty likely. At this point I would find it easier to follow the Python code compared to Visual Basic.
 
I decided to make a little mounting setup for my TFT display. It holds the display at a convenient angle for viewing and is heavy enough to keep it from moving around. I had all the material I needed to make it on hand, with the exception of a few 4-40 screws to assemble it and attach the board.

Like this:

TFT display.JPG

I'm using the display in its serial mode. For now I'm leaving the protective plastic film over the display.
 
A number of things have conspired to slow progress on my version of the XRF project. The holiday season in general, getting Covid, bad weather and attendant damage, subsequent cleanup, probably a few other things I already have forgotten all needed attention.

In my last post I showed the TFT display mounted on a little holder and today I was able to actually plot some decent looking pulses. Along the way I found that I couldn't share the built-in LED pin with the Teensy4.0's SPI clock pin -- trying to use that pin to use it as a flasher for my "geiger" mode AND the SPI clock caused the TFT library function to hang. Not sure why, but there you go. So for now the geiger mode is broken (it used the LED pin).

The other thing I ran into was some subtle problem with my approach to acquiring the entire pulse waveform once the input voltage exceeded the trigger voltage. I tried a sequential method, backing up to find the beginning of the pulse then sucking in the pulse data up to the current pointer into the circular buffer; and then following the acquisition until the pulse amplitude decayed to close to zero. Not sure why, but the subsequent pulses didn't resemble the waveforms I was seeing on my oscilloscope. Ranging from slightly wrong to _really_ wrong. Just to move forward, instead I just inserted a delay function that was long enough for a valid pulse to come and go, then processed the data in the circular buffer to extract the entire pulse My motivation for trying the other approach was efficiency: it would be possible to abort the pulse processing as soon as it became clear that it wasn't good enough, reducing the chances of missing a subsequent good pulse. I think it should work but clearly need to do a better job of analyzing how the code _actually_ works.

Anyway, at this point I can acquire decent-looking pulses, although my program is rejecting more pulses than I like. There's more debugging in the future.....

Here's a photo of my TFT display showing a pulse, sampled at 360KSPS:

NoisyPulse.JPG

I deliberately chose a photo of a fairly noisy pulse, just to show why I think a simple peak detection scheme won't work all that well. Integrating the entire pulse should produce a value that is much less affected by noise.

The plot also is auto-scaled, so this particular pulse may be lower in amplitude so the system noise is more apparent.
 
A number of things have conspired to slow progress on my version of the XRF project. The holiday season in general, getting Covid, bad weather and attendant damage, subsequent cleanup, probably a few other things I already have forgotten all needed attention.

In my last post I showed the TFT display mounted on a little holder and today I was able to actually plot some decent looking pulses. Along the way I found that I couldn't share the built-in LED pin with the Teensy4.0's SPI clock pin -- trying to use that pin to use it as a flasher for my "geiger" mode AND the SPI clock caused the TFT library function to hang. Not sure why, but there you go. So for now the geiger mode is broken (it used the LED pin).

The other thing I ran into was some subtle problem with my approach to acquiring the entire pulse waveform once the input voltage exceeded the trigger voltage. I tried a sequential method, backing up to find the beginning of the pulse then sucking in the pulse data up to the current pointer into the circular buffer; and then following the acquisition until the pulse amplitude decayed to close to zero. Not sure why, but the subsequent pulses didn't resemble the waveforms I was seeing on my oscilloscope. Ranging from slightly wrong to _really_ wrong. Just to move forward, instead I just inserted a delay function that was long enough for a valid pulse to come and go, then processed the data in the circular buffer to extract the entire pulse My motivation for trying the other approach was efficiency: it would be possible to abort the pulse processing as soon as it became clear that it wasn't good enough, reducing the chances of missing a subsequent good pulse. I think it should work but clearly need to do a better job of analyzing how the code _actually_ works.

Anyway, at this point I can acquire decent-looking pulses, although my program is rejecting more pulses than I like. There's more debugging in the future.....

Here's a photo of my TFT display showing a pulse, sampled at 360KSPS:

View attachment 431558

I deliberately chose a photo of a fairly noisy pulse, just to show why I think a simple peak detection scheme won't work all that well. Integrating the entire pulse should produce a value that is much less affected by noise.

The plot also is auto-scaled, so this particular pulse may be lower in amplitude so the system noise is more apparent.
Great progress, both on your covid recovery and pulse recovery! If the pulses all look this way, it would be great. They probably don't, but this one is very encouraging. Agree with pulse integration being helpful.

Correlation might help as well since the shape is important. How many samples is this pulse? Correlation is expensive computationally, but one could do it in the frequency domain and take the IFFT, which will reduce the burden.

How are you choosing your trigger threshold? Ideally it should be based on a fixed ratio of the noise floor. Or at least, that is what is commonly done in radar signal processing. I used a median filter to estimate the local noise floor. It runs on an M4, so might be ok for your use. If you are interested in a code sample I can send you one. I used this code in my Doppler chronograph.
 
Why would the shape of the pulse be important? I assume the decay rate would be more hardware related than related to the arriving photon? Unless you mean summation of nearly coincident detections. Then yes.
 
Why would the shape of the pulse be important? I assume the decay rate would be more hardware related than related to the arriving photon? Unless you mean summation of nearly coincident detections. Then yes.
Your second comment is what I was thinking about. We have no control over the timing of incoming pulses, and many will overlap. Using shape could help discriminate overlapping pulses a little. Sometimes the shape can also help determine if the pulse is "real", as apposed to coming from a different source. If the shape is totally wrong, then that energy can be rejected.

Sometimes the pulse has a shape due to the source, and other times due to one's electronics. Wide bandwidth systems preserve pulse shape whereas narrowband systems smear or blur those features. What's best depends on the features one is trying to extract. If the pulses didn't overlap one could use simple pulse energy. Overlapping makes things harder. There's lots of strategies, and each one has it's plusses and minuses. Pulse rejection works at the expense of identification time. It's simpler to implement but one has to wait longer, or one misses an element. In the end, it's a balance between cost, performance and time, just like any other engineering project...
 
Great progress, both on your covid recovery and pulse recovery! If the pulses all look this way, it would be great. They probably don't, but this one is very encouraging. Agree with pulse integration being helpful.

Correlation might help as well since the shape is important. How many samples is this pulse? Correlation is expensive computationally, but one could do it in the frequency domain and take the IFFT, which will reduce the burden.

How are you choosing your trigger threshold? Ideally it should be based on a fixed ratio of the noise floor. Or at least, that is what is commonly done in radar signal processing. I used a median filter to estimate the local noise floor. It runs on an M4, so might be ok for your use. If you are interested in a code sample I can send you one. I used this code in my Doppler chronograph.
I don't think that correlation would help. Each (good) pulse represents one x-ray photon, which could come from an iron or nickel or chrome or cobalt or molybdenum or vanadium or manganese atom (etc.), so attempting to combine pulses wouldn't do us any good.

The trigger is a simple fixed value. However, my ADC ISR includes a low-pass filter, whose value is subtracted from the raw ADC data before being entered into my circular buffer. But it's even a bit more complicated than that. The startup code examines the data coming out of the ADC and extracts the RMS noise, then sets a data-clip value that is equal to 3*RMS + the low pass filter value. This in turn is used to reject pulses so they don't push the low pass filter voltage around too much in the ISR.

Also, to reduce the possibility of noise triggering the pulse processor I keep a rolling average of the last 8 ADC samples and trigger on that.
 
Your second comment is what I was thinking about. We have no control over the timing of incoming pulses, and many will overlap. Using shape could help discriminate overlapping pulses a little. Sometimes the shape can also help determine if the pulse is "real", as apposed to coming from a different source. If the shape is totally wrong, then that energy can be rejected.

Sometimes the pulse has a shape due to the source, and other times due to one's electronics. Wide bandwidth systems preserve pulse shape whereas narrowband systems smear or blur those features. What's best depends on the features one is trying to extract. If the pulses didn't overlap one could use simple pulse energy. Overlapping makes things harder. There's lots of strategies, and each one has it's plusses and minuses. Pulse rejection works at the expense of identification time. It's simpler to implement but one has to wait longer, or one misses an element. In the end, it's a balance between cost, performance and time, just like any other engineering project...
For now I'm simply discriminating on the pulse width. Pulse overlap = a longer pulse. I was hoping that my 2nd order polynomial fit, performed around the pulse peak, also might help. Presumably a peak caused by the overlap of two pulses won't be as "nice" in terms of the goodness of fit. But, given the noise levels I'm seeing, I'm starting to think that my polynomial fit may not buy me much in that regard. One problem with the fitting routine is that my ADC sample rate is slow enough to limit the number of samples around the peak. I'm using 10 samples at this point. A 1MSPS ADC would more than double that number of samples, plus the ADC resolution would be much better. The Teensy ADC's ENOB is around 10 bits.
 
I don't think that correlation would help. Each (good) pulse represents one x-ray photon, which could come from an iron or nickel or chrome or cobalt or molybdenum or vanadium or manganese atom (etc.), so attempting to combine pulses wouldn't do us any good.

The trigger is a simple fixed value. However, my ADC ISR includes a low-pass filter, whose value is subtracted from the raw ADC data before being entered into my circular buffer. But it's even a bit more complicated than that. The startup code examines the data coming out of the ADC and extracts the RMS noise, then sets a data-clip value that is equal to 3*RMS + the low pass filter value. This in turn is used to reject pulses so they don't push the low pass filter voltage around too much in the ISR.

Also, to reduce the possibility of noise triggering the pulse processor I keep a rolling average of the last 8 ADC samples and trigger on that.
Correlation sometimes helps, other times it devolves into a mess. Since you currently have the means to collect and store data you are on the forefront of all this, it's obviously your judgement on how to proceed. I'm just a bystander at this point, but would like to help if I can.

How do you extract the RMS noise? What do you use for the noise estimator? I have learned that this subtraction process can come with it's own problems, namely it actually can increase noise if done without care. A simple subtraction of random variables always increases the variance of the resultant. However, if the estimate has low variance we can minimize the SNR loss.

The 3*RMS noise estimate is roughly a 10 dB SNR threshold plus your local estimate. Since the local average can be heavily biased by the energy of a local pulse, you may be inadvertently raising your threshold, which may cause you to miss detections. This is why I recommend using a median filter which tends not to be biased by noise impulses or local energy. Using a median filter for local noise estimation is the basis for OS-CFAR detection. (Ordered Statistic Constant False Alarm Rate). Anyways, just a suggestion. I have used OS-CFAR for decades in preference to other schemes as it behaves a lot better in real life than cell averaged cfar. I think what you have implemented is practically equivalent to CA-CFAR. JMHO.
 
Back
Top