Source code for trspectrometer.utils

# Copyright 2021 Patrick C. Tapping
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program.  If not, see <http://www.gnu.org/licenses/>.

"""
Module for storing miscellaneous utility functions etc.
"""

import re
from datetime import datetime

import numpy as np

import configuration as config


[docs]def no_infs(x, copy=True): """ Convenience function to convert all ``+inf`` and ``-inf`` in a numpy array to ``nan``. :param x: Numpy array. :param copy: Create a copy of ``x`` (default, ``True``), or convert in-place (``False``). :returns: The array ``x``, with all infinite values converted to ``nan``. """ return np.nan_to_num(x, copy=copy, nan=np.nan, posinf=np.nan, neginf=np.nan)
[docs]def mask_outliers(data, axis:int=0, sensitivity:float=0.1, copy:bool=True): """ Convert outlying points in a numpy array to NaNs. The ``axis`` parameter determines the array axis in which to compute the median values and detect the outliers. The ``sensitivity`` parameter would typically range between 0.0 and 1.0, where 0.0 will return the original data unmodified, and larger values will remove a greater number of outliers. Values greater than 1.0 are permitted. By default the operation is performed on a copy of the original data. To modify the original data in-place, the ``copy`` parameter can be set to ``False``. :param data: Numpy array containing data to process. :param axis: Index of array axis in which to detect outliers. :param sensitivity: Sensitivity of outlier detection. :param copy: Operate on a copy of the input data. :returns: Numpy array containing the processed data. """ if sensitivity > 0.0: data_delta = np.abs(data - np.nanmedian(data, axis=axis)) med_dev = np.nanmedian(data_delta, axis=axis) if copy: data = data.copy() data[(data_delta/med_dev)>(1.0/sensitivity)] = np.nan return data
[docs]class AcquisitionError(RuntimeError): """ An exception to identify some error which occurred during the acquisition process. :param message: String describing the error. """ def __init__(self, message="Unknown acquisition error."): super().__init__(message)
[docs]class AcquisitionAbortedWarning(UserWarning): """ An exception to indicate the acquisition process was stopped manually. Since the stop was manually triggered, this is a warning rather than an error, but may still want to be acted upon by the listeners to the completion callback. :param message: String describing the warning. """ def __init__(self, message="Acquisition interrupted by user."): super().__init__(message)
[docs]def status_message(message: str) -> None: """ Display a message on the main window's status bar. This may be called from outside the Qt UI thread. :param message: String to display on the status bar. """ try: config.mainwindow.show_message.emit(message) except: pass
[docs]def clean_filename(filename, allowed_symbols=["-", "_", "+", "."]): """ Clean up a filename by removing symbols and replacing spaces with underscores. The list of ``allowed_symbols`` will not be removed. :param filename: Input filename to clean up. :returns: The cleaned filename. """ # Perform a few substitutions first filename = filename.rstrip() filename = re.subn("(?<=[0-9]) nm", "nm", filename)[0] filename = re.subn("(?<=[0-9]) mW", "mW", filename)[0] filename = filename.replace(" ", "_") filename = filename.replace("&", "+") return "".join(c for c in filename if c.isalnum() or c in allowed_symbols)
[docs]def si_unit_factor(unit: str): """ Get the scaling factor for a given SI unit prefix. The current implementation works for time-based units (in seconds, s), and wavelength units (in metres, m) for ranges applicable for this application. For example, the scaling factor for ``"ps"`` is 1e-12. :param unit: String of the SI unit. :returns: Scaling factor of the SI prefix. """ return { # Time units "hours": 3600.0, "hr": 3600.0, "minutes": 60.0, "min": 60.0, "s" : 1.0, "ms": 1e-3, "us": 1e-6, "µs": 1e-6, # micro sign UxB5 "μs": 1e-6, # greek small letter mu Ux3BC "ns": 1e-9, "ps": 1e-12, "fs": 1e-15, "as": 1e-18, # Wavelength units "m" : 1.0, "mm": 1e-3, "um": 1e-6, "µm": 1e-6, # micro sign UxB5 "μm": 1e-6, # greek small letter mu Ux3BC "nm": 1e-9, "pm": 1e-12, "Å" : 1e-10, # Angstrom sign Ux212B "Å" : 1e-10, # capital letter A with ring above UxC5 "A" : 1e-10, "fm": 1e-15, "am": 1e-18, }[unit.strip()]
[docs]def now_string(): """ Get the current local time as an ISO8601 formatted string. This format is useful for embedding into the metadata of data files. To convert the string back into a python ``datetime`` object, use :meth:`datetime.fromisoformat`. :returns: String representation of current time. """ return datetime.now().astimezone().isoformat()