|
4 | 4 | import sys
|
5 | 5 | import threading
|
6 | 6 | import time
|
7 |
| -import pandas as pd |
8 | 7 | import json
|
9 | 8 | import shutil
|
10 | 9 | import socket
|
11 | 10 | import xmltodict
|
| 11 | +import struct |
| 12 | +import csv |
| 13 | +import subprocess |
| 14 | +import logging |
| 15 | +import serial |
| 16 | +import requests |
| 17 | +import pandas as pd |
| 18 | +import numpy as np |
| 19 | +import matplotlib.pyplot as plt |
12 | 20 | import tkinter as tk
|
13 | 21 | import customtkinter as ctk
|
14 | 22 | from tkinter import ttk, messagebox, font
|
15 | 23 | from tkinter.ttk import Treeview
|
16 |
| -import struct |
17 |
| -import numpy as np |
18 |
| -import matplotlib.pyplot as plt |
19 |
| -from dataclasses import dataclass |
20 | 24 | from matplotlib.figure import Figure
|
21 | 25 | from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
|
22 | 26 | from PIL import Image
|
23 |
| -import csv |
24 | 27 | from decimal import Decimal
|
25 | 28 | from plyer import notification as plyer_notification
|
26 |
| -import subprocess |
27 |
| -import logging |
28 |
| -import serial |
29 |
| -import requests |
| 29 | +from dataclasses import dataclass |
30 | 30 | from element_string_lists import (
|
31 | 31 | elementstr_symbolsonly,
|
32 | 32 | elementstr_namesonly,
|
33 | 33 | elementstr_symbolswithzinbrackets,
|
34 | 34 | all_xray_lines,
|
35 | 35 | )
|
36 | 36 |
|
37 |
| -versionNum = "v1.1.0" # v0.9.6 was the first GeRDA-control version |
38 |
| -versionDate = "2024/06/20" |
| 37 | +versionNum = "v1.1.1" # v0.9.6 was the first GeRDA-control version |
| 38 | +versionDate = "2024/06/26" |
39 | 39 |
|
40 | 40 |
|
41 | 41 | class BrukerInstrument:
|
@@ -113,7 +113,7 @@ def __init__(self):
|
113 | 113 | self.instr_totalspecchannels = 2048
|
114 | 114 | self.specchannelsarray = np.array(list(range(0, self.instr_totalspecchannels)))
|
115 | 115 |
|
116 |
| - self.open_tcp_connection(self.ip, self.port, instant_connect=True) |
| 116 | + self.open_tcp_connection(self.ip, self.port, instant_connect=False) |
117 | 117 |
|
118 | 118 | def open_tcp_connection(
|
119 | 119 | self, connection_ip: str, connection_port: int, instant_connect: bool = False
|
@@ -2535,54 +2535,63 @@ def getNearbyEnergies(energy, qty):
|
2535 | 2535 | printAndLog(closest, "INFO")
|
2536 | 2536 |
|
2537 | 2537 |
|
2538 |
| -def sanityCheckSpectrum( |
2539 |
| - spectrum_counts: list, spectrum_energies: list, source_voltage_in_kV: int |
2540 |
| -) -> bool: |
2541 |
| - """Checks that a spectrum is sensible, and that the listed voltage is accurate. |
2542 |
| - This is required because of a bug in Bruker pXRF instrument software, sometimes causing phases of an assay to use an incorrect voltage. |
2543 |
| - Returns TRUE if sanity check PASSES, return FALSE if not. |
2544 |
| - DEPRECATED 2024/01/08, USE sanityCheckSpectrum_SumMethod instead.""" |
2545 |
| - # Calculate the standard deviation of the spectrum |
2546 |
| - std_dev = np.std(spectrum_counts) |
2547 |
| - # print(f"spectrum std dev = {std_dev}") |
2548 |
| - |
2549 |
| - # Set a threshold for noise detection (too small might be prone to noise, too high isn't useful. starting with stddev/100. UPDATE - using /50, 100 was too sensitive in some cases. /40 now. need to come up with a better method for this, it gives a lot of false positives.) |
2550 |
| - threshold = std_dev / 40 |
2551 |
| - # print(f"{threshold=}") |
2552 |
| - |
2553 |
| - # reverse iterate list to search backwards - no zero peak to worry about, and generally should be faster. |
2554 |
| - for i in range(len(spectrum_counts) - 1, 0, -1): |
2555 |
| - if spectrum_counts[i] > threshold: |
2556 |
| - # Found a peak above the noise threshold |
2557 |
| - peak_index = i |
2558 |
| - break |
2559 |
| - else: |
2560 |
| - # No peak above the noise threshold found |
2561 |
| - peak_index = None |
2562 |
| - |
2563 |
| - if peak_index is not None: |
2564 |
| - # print(f"Latest point with a peak above noise: energy={spectrum_energies[peak_index]}, counts={spectrum_counts[peak_index]}") |
2565 |
| - if spectrum_energies[peak_index] < source_voltage_in_kV: |
2566 |
| - # this point should be LOWER than source voltage *almost* always. some exclusions, incl. sum peaks, but those should be niche. |
2567 |
| - return True |
2568 |
| - else: |
2569 |
| - printAndLog( |
2570 |
| - f"Failed Sanity Check Details: highest meaningful energy present={spectrum_energies[peak_index]:.2f}, meaningful counts threshold={threshold:.0f}, reported source voltage={source_voltage_in_kV:.0f}" |
2571 |
| - ) |
2572 |
| - return False |
2573 |
| - |
2574 |
| - else: |
2575 |
| - # No peak above noise detected - flat spectra? |
2576 |
| - return False |
| 2538 | +# def sanityCheckSpectrum( |
| 2539 | +# spectrum_counts: list, spectrum_energies: list, source_voltage_in_kV: int |
| 2540 | +# ) -> bool: |
| 2541 | +# """Checks that a spectrum is sensible, and that the listed voltage is accurate. |
| 2542 | +# This is required because of a bug in Bruker pXRF instrument software, sometimes causing phases of an assay to use an incorrect voltage. |
| 2543 | +# Returns TRUE if sanity check PASSES, return FALSE if not. |
| 2544 | +# DEPRECATED 2024/01/08, USE sanityCheckSpectrum_SumMethod instead.""" |
| 2545 | +# # Calculate the standard deviation of the spectrum |
| 2546 | +# std_dev = np.std(spectrum_counts) |
| 2547 | +# # print(f"spectrum std dev = {std_dev}") |
| 2548 | + |
| 2549 | +# # Set a threshold for noise detection (too small might be prone to noise, too high isn't useful. starting with stddev/100. UPDATE - using /50, 100 was too sensitive in some cases. /40 now. need to come up with a better method for this, it gives a lot of false positives.) |
| 2550 | +# threshold = std_dev / 40 |
| 2551 | +# # print(f"{threshold=}") |
| 2552 | + |
| 2553 | +# # reverse iterate list to search backwards - no zero peak to worry about, and generally should be faster. |
| 2554 | +# for i in range(len(spectrum_counts) - 1, 0, -1): |
| 2555 | +# if spectrum_counts[i] > threshold: |
| 2556 | +# # Found a peak above the noise threshold |
| 2557 | +# peak_index = i |
| 2558 | +# break |
| 2559 | +# else: |
| 2560 | +# # No peak above the noise threshold found |
| 2561 | +# peak_index = None |
| 2562 | + |
| 2563 | +# if peak_index is not None: |
| 2564 | +# # print(f"Latest point with a peak above noise: energy={spectrum_energies[peak_index]}, counts={spectrum_counts[peak_index]}") |
| 2565 | +# if spectrum_energies[peak_index] < source_voltage_in_kV: |
| 2566 | +# # this point should be LOWER than source voltage *almost* always. some exclusions, incl. sum peaks, but those should be niche. |
| 2567 | +# return True |
| 2568 | +# else: |
| 2569 | +# printAndLog( |
| 2570 | +# f"Failed Sanity Check Details: highest meaningful energy present={spectrum_energies[peak_index]:.2f}, meaningful counts threshold={threshold:.0f}, reported source voltage={source_voltage_in_kV:.0f}" |
| 2571 | +# ) |
| 2572 | +# return False |
| 2573 | + |
| 2574 | +# else: |
| 2575 | +# # No peak above noise detected - flat spectra? |
| 2576 | +# return False |
2577 | 2577 |
|
2578 | 2578 |
|
2579 | 2579 | def sanityCheckSpectrum_SumMethod(
|
2580 |
| - spectrum_counts: list, spectrum_energies: list, source_voltage_in_kV: int |
| 2580 | + spectrum_counts: list[int], spectrum_energies: list[float], source_voltage_in_kV: int |
2581 | 2581 | ) -> bool:
|
2582 |
| - """Checks that a spectrum is sensible, and that the listed voltage is accurate. |
2583 |
| - This is required because of a bug in Bruker pXRF instrument software, sometimes causing phases of an assay to use an incorrect voltage. |
| 2582 | + """Checks that a spectrum is 'sensible', and that the communicated voltage is accurate. |
| 2583 | + This is required because of a 'voltage bug' in Bruker pXRF instrument software (or NSI tube firmware), |
| 2584 | + sometimes causing phases of an assay to use an incorrect voltage from a previous phase. |
| 2585 | + This algorithm operates by working backwards through the list of counts (starting from the right-hand-side of the spectrum), |
| 2586 | + and summing those counts it passes until it reaches the point where 2% of the total counts of the spectrum have been passed. |
| 2587 | + At this point on the spectrum, the energy of that channel should be below the source voltage used for the spectrum |
| 2588 | + (assuming no sum-peaks, which CAN give a false-positive). The Bremsstrahlung in the spectrum *should* drop down |
| 2589 | + to near 0 counts beyond the source voltage (in keV). |
| 2590 | + |
| 2591 | + `spectrum_counts` is a ordered list of 2048 integers representing the counts from each channel/bin of the detector.\n |
| 2592 | + `spectrum_energies` is a ordered list of 2048 floats representing the energy (keV) of each channel/bin of the detector.\n |
| 2593 | + `source_voltage_in_kV` is the voltage of the source for the given spectrum, AS REPORTED BY THE OEM API AND IN THE PDZ FILE.\n |
2584 | 2594 | Returns TRUE if sanity check passed, return FALSE if not.
|
2585 |
| - An UPDATED version of the sanityCheckSpectrum function that should be less prone to false positives, and faster. |
2586 | 2595 | NOW ALSO CHECKS FOR NULL SPECTRA, i.e. zero-peak only.
|
2587 | 2596 | """
|
2588 | 2597 | counts_sum = np.sum(spectrum_counts)
|
|
0 commit comments