"""Tools for noise calculation"""
from silx.gui import qt
from est.gui.larch.utils import _OptionalQDoubleSpinBox
from est.gui.XasObjectViewer import SpectrumViewer
from est.gui.XasObjectViewer import (
_plot_noise_savgol,
_plot_raw_noise_savgol,
)
class _QOddSpinBox(qt.QSpinBox):
"""
SpinBox dedicated to odd numbers
"""
oddValueChanged = qt.Signal(int)
def __init__(self, parent=None):
super().__init__(parent=parent)
self.valueChanged.connect(self.onValueChanged)
self.before_value = self.value()
def onValueChanged(self, i):
if not self.isValid(i):
self.setValue(self.before_value)
else:
self.oddValueChanged.emit(i)
self.before_value = i
def isValid(self, value):
if (value % 2) == 1:
return True
return False
[docs]
class SavitskyGolayNoiseOpts(qt.QWidget):
sigChanged = qt.Signal()
"""signal emitted when parameters change"""
DEFAULT_E_START = 150
DEFAULT_E_STOP = None
def __init__(self, parent=None):
qt.QWidget.__init__(self, parent)
self.setLayout(qt.QFormLayout())
# Inputs
self.layout().addRow(qt.QLabel("Inputs", self))
# add window size
self._windowSize = _QOddSpinBox(self)
self._windowSize.setValue(5)
self._windowSize.setSingleStep(2)
self.layout().addRow(
qt.QLabel(" - number of point (window size)", self), self._windowSize
)
self._windowSize.setToolTip("Number of points to compute average noise_savgol")
# add polynomial order
self._polynomialOrder = qt.QSpinBox(self)
self._polynomialOrder.setValue(2)
self.layout().addRow(
qt.QLabel(" - polynomial order", self), self._polynomialOrder
)
self._polynomialOrder.setToolTip(
"window size to provide to the Savitsky-Golay algorithm"
)
# E start
self._eStart = qt.QDoubleSpinBox(self)
self._eStart.setRange(-999999999999999, 999999999999999)
self._eStart.setSuffix("eV")
self._eStart.setValue(self.DEFAULT_E_START)
self.layout().addRow(qt.QLabel(" - E start", self), self._eStart)
# E end
self._eEnd = _OptionalQDoubleSpinBox(self)
self._eEnd.setRange(-999999999999999, 999999999999999)
self._eEnd.setSuffix("eV")
self._eEnd.setValue(500) # avoid to have 0 by default
self._eEnd.setValue(self.DEFAULT_E_STOP)
self._eEnd.setToolTip(
"energy end to compute noise average value. If not set will take energy max value"
)
self.layout().addRow(qt.QLabel(" - E end", self), self._eEnd)
# Outputs
self.layout().addRow(qt.QLabel("Outputs", self))
# raw data average noise
self._rawData = qt.QLineEdit("", self)
self._rawData.setReadOnly(True)
self.layout().addRow(
qt.QLabel(" - Raw data average noise (blue)", self), self._rawData
)
self._rawData.setToolTip(
"Raw data average noise is calculated as the difference between "
"the raw spectrum and the spectrum smoothed with Savitsky-Golay "
"algorithm (with provided window size and polynomial order)"
)
# edge step
self._edgeStep = qt.QLineEdit("", self)
self._edgeStep.setReadOnly(True)
self.layout().addRow(qt.QLabel(" - edge step", self), self._edgeStep)
self._edgeStep.setToolTip(
"Edge step is defined during the normalization (pymca) or the "
"pre-edge step (larch)"
)
# normalized noise
self._normalizedNoise = qt.QLineEdit("", self)
self._normalizedNoise.setReadOnly(True)
self.layout().addRow(
qt.QLabel(" - Normalized noise (red)", self), self._normalizedNoise
)
self._normalizedNoise.setToolTip("Average noise divided by edge jump")
# connect signal / slot
self._windowSize.oddValueChanged.connect(self._changed)
self._polynomialOrder.valueChanged.connect(self._changed)
self._eStart.valueChanged.connect(self._changed)
self._eEnd.sigChanged.connect(self._changed)
[docs]
def getWindowSize(self):
return self._windowSize.value()
[docs]
def setWindowSize(self, size):
if size < 1:
return
self._windowSize.setValue(size)
[docs]
def getPolynomialOrder(self):
return self._polynomialOrder.value()
[docs]
def setPolynomialOrder(self, order):
if order < 1:
return
self._polynomialOrder.setValue(order)
[docs]
def getEStart(self):
return self._eStart.value()
[docs]
def setEStart(self, e_min):
self._eStart.setValue(e_min)
[docs]
def getEEnd(self):
return self._eEnd.getValue()
[docs]
def setEEnd(self, e_max):
self._eEnd.setValue(e_max)
[docs]
def getParameters(self):
return {
"window_size": self.getWindowSize(),
"polynomial_order": self.getPolynomialOrder(),
"e_min": self.getEStart(),
"e_max": self.getEEnd(),
}
[docs]
def setParameters(self, config):
if "window_size" in config:
self.setWindowSize(config["window_size"])
if "polynomial_order" in config:
self.setPolynomialOrder(config["polynomial_order"])
if "e_min" in config:
self.setEStart(config["e_min"])
if "e_max" in config:
self.setEEnd(config["e_max"])
def _changed(self, *args, **kwargs):
self.sigChanged.emit()
[docs]
def setSpectrum(self, spectrum):
if spectrum.raw_noise_savgol is not None:
self._rawData.setText("{:.5E}".format(spectrum.raw_noise_savgol))
else:
self._rawData.clear()
if hasattr(spectrum, "edge_step"):
self._edgeStep.setText("{:.3f}".format(spectrum.edge_step))
else:
self._edgeStep.setText("?")
if spectrum.norm_noise_savgol is not None:
self._normalizedNoise.setText("{:.5E}".format(spectrum.norm_noise_savgol))
else:
self._normalizedNoise.clear()
[docs]
class SavitskyGolayNoise(qt.QMainWindow):
"""
Widget to tune SavitskyGolayNoise and display the results
"""
sigChanged = qt.Signal()
"""signal emitted when parameters change"""
def __init__(self, parent):
qt.QMainWindow.__init__(self, parent)
self.setWindowFlags(qt.Qt.Widget)
# define centre widget
self._plot = SpectrumViewer()
self._plot.setYAxisLogarithmic(True)
for ope in (
_plot_noise_savgol,
_plot_raw_noise_savgol,
):
self._plot.addCurveOperation(ope)
self.setCentralWidget(self._plot)
# options
self._options = SavitskyGolayNoiseOpts(parent=self)
self._optionsDW = qt.QDockWidget(self)
self._optionsDW.setWidget(self._options)
self._optionsDW.setAllowedAreas(
qt.Qt.RightDockWidgetArea | qt.Qt.LeftDockWidgetArea
)
self.addDockWidget(qt.Qt.RightDockWidgetArea, self._optionsDW)
self._optionsDW.setFeatures(qt.QDockWidget.DockWidgetMovable)
# connect signal / plot
self._options.sigChanged.connect(self._changed)
self._plot.sigSpectrumChanged.connect(self._updateSpectrumInfo)
[docs]
def setXASObj(self, xas_obj):
self._plot.setXasObject(xas_obj=xas_obj)
[docs]
def getParameters(self):
return self._options.getParameters()
[docs]
def setParameters(self, config):
self._options.setParameters(config=config)
[docs]
def getWindowSize(self):
return self._options.getWindowSize()
[docs]
def setWindowSize(self, size):
self._options.setWindowSize(size=size)
[docs]
def getPolynomialOrder(self):
return self._options.getPolynomialOrder()
[docs]
def setPolynomialOrder(self, order):
self._options.setPolynomialOrder(order=order)
def _changed(self, *args, **kwargs):
self.sigChanged.emit()
def _updateSpectrumInfo(self):
current_spectrum = self._plot.getCurrentSpectrum()
if current_spectrum is not None:
self._options.setSpectrum(spectrum=current_spectrum)