pychemstation 0.4.7.dev1__py3-none-any.whl → 0.4.7.dev2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pychemstation/control/__init__.py +3 -2
- hein-analytical-control/devices/Agilent/hplc.py → pychemstation/control/comm.py +21 -181
- pychemstation/control/method.py +232 -0
- pychemstation/control/sequence.py +140 -0
- pychemstation/control/table_controller.py +75 -0
- pychemstation/utils/__init__.py +0 -2
- {ag_hplc_macro/control → pychemstation/utils}/chromatogram.py +2 -1
- pychemstation/utils/constants.py +1 -1
- hein_analytical_control/devices/Agilent/hplc_param_types.py → pychemstation/utils/macro.py +5 -69
- pychemstation/utils/method_types.py +44 -0
- pychemstation/utils/sequence_types.py +33 -0
- pychemstation/utils/table_types.py +60 -0
- {pychemstation-0.4.7.dev1.dist-info → pychemstation-0.4.7.dev2.dist-info}/METADATA +13 -12
- pychemstation-0.4.7.dev2.dist-info/RECORD +30 -0
- ag_hplc_macro/__init__.py +0 -3
- ag_hplc_macro/analysis/__init__.py +0 -1
- ag_hplc_macro/analysis/base_spectrum.py +0 -509
- ag_hplc_macro/analysis/spec_utils.py +0 -304
- ag_hplc_macro/analysis/utils.py +0 -63
- ag_hplc_macro/control/__init__.py +0 -5
- ag_hplc_macro/control/hplc.py +0 -673
- ag_hplc_macro/generated/__init__.py +0 -56
- ag_hplc_macro/generated/dad_method.py +0 -367
- ag_hplc_macro/generated/pump_method.py +0 -519
- ag_hplc_macro/utils/__init__.py +0 -2
- ag_hplc_macro/utils/constants.py +0 -15
- ag_hplc_macro/utils/hplc_param_types.py +0 -185
- hein-analytical-control/__init__.py +0 -3
- hein-analytical-control/analysis/__init__.py +0 -1
- hein-analytical-control/analysis/base_spectrum.py +0 -509
- hein-analytical-control/analysis/spec_utils.py +0 -304
- hein-analytical-control/analysis/utils.py +0 -63
- hein-analytical-control/devices/Agilent/__init__.py +0 -3
- hein-analytical-control/devices/Agilent/chemstation.py +0 -290
- hein-analytical-control/devices/Agilent/chromatogram.py +0 -129
- hein-analytical-control/devices/Agilent/hplc_param_types.py +0 -141
- hein-analytical-control/devices/Magritek/Spinsolve/__init__.py +0 -0
- hein-analytical-control/devices/Magritek/Spinsolve/commands.py +0 -495
- hein-analytical-control/devices/Magritek/Spinsolve/spectrum.py +0 -822
- hein-analytical-control/devices/Magritek/Spinsolve/spinsolve.py +0 -425
- hein-analytical-control/devices/Magritek/Spinsolve/utils/__init__.py +0 -5
- hein-analytical-control/devices/Magritek/Spinsolve/utils/connection.py +0 -168
- hein-analytical-control/devices/Magritek/Spinsolve/utils/constants.py +0 -8
- hein-analytical-control/devices/Magritek/Spinsolve/utils/exceptions.py +0 -25
- hein-analytical-control/devices/Magritek/Spinsolve/utils/parser.py +0 -340
- hein-analytical-control/devices/Magritek/Spinsolve/utils/shimming.py +0 -55
- hein-analytical-control/devices/Magritek/Spinsolve/utils/spinsolve_logging.py +0 -43
- hein-analytical-control/devices/Magritek/__init__.py +0 -0
- hein-analytical-control/devices/OceanOptics/IR/NIRQuest512.py +0 -90
- hein-analytical-control/devices/OceanOptics/IR/__init__.py +0 -0
- hein-analytical-control/devices/OceanOptics/IR/ir_spectrum.py +0 -191
- hein-analytical-control/devices/OceanOptics/Raman/__init__.py +0 -0
- hein-analytical-control/devices/OceanOptics/Raman/raman_control.py +0 -46
- hein-analytical-control/devices/OceanOptics/Raman/raman_spectrum.py +0 -148
- hein-analytical-control/devices/OceanOptics/UV/QEPro2192.py +0 -90
- hein-analytical-control/devices/OceanOptics/UV/__init__.py +0 -0
- hein-analytical-control/devices/OceanOptics/UV/uv_spectrum.py +0 -227
- hein-analytical-control/devices/OceanOptics/__init__.py +0 -0
- hein-analytical-control/devices/OceanOptics/oceanoptics.py +0 -115
- hein-analytical-control/devices/__init__.py +0 -15
- hein-analytical-control/generated/__init__.py +0 -56
- hein-analytical-control/generated/dad_method.py +0 -367
- hein-analytical-control/generated/pump_method.py +0 -519
- hein_analytical_control/__init__.py +0 -3
- hein_analytical_control/analysis/__init__.py +0 -1
- hein_analytical_control/analysis/base_spectrum.py +0 -509
- hein_analytical_control/analysis/spec_utils.py +0 -304
- hein_analytical_control/analysis/utils.py +0 -63
- hein_analytical_control/devices/Agilent/__init__.py +0 -3
- hein_analytical_control/devices/Agilent/chemstation.py +0 -290
- hein_analytical_control/devices/Agilent/chromatogram.py +0 -129
- hein_analytical_control/devices/Agilent/hplc.py +0 -436
- hein_analytical_control/devices/Magritek/Spinsolve/__init__.py +0 -0
- hein_analytical_control/devices/Magritek/Spinsolve/commands.py +0 -495
- hein_analytical_control/devices/Magritek/Spinsolve/spectrum.py +0 -822
- hein_analytical_control/devices/Magritek/Spinsolve/spinsolve.py +0 -425
- hein_analytical_control/devices/Magritek/Spinsolve/utils/__init__.py +0 -5
- hein_analytical_control/devices/Magritek/Spinsolve/utils/connection.py +0 -168
- hein_analytical_control/devices/Magritek/Spinsolve/utils/constants.py +0 -8
- hein_analytical_control/devices/Magritek/Spinsolve/utils/exceptions.py +0 -25
- hein_analytical_control/devices/Magritek/Spinsolve/utils/parser.py +0 -340
- hein_analytical_control/devices/Magritek/Spinsolve/utils/shimming.py +0 -55
- hein_analytical_control/devices/Magritek/Spinsolve/utils/spinsolve_logging.py +0 -43
- hein_analytical_control/devices/Magritek/__init__.py +0 -0
- hein_analytical_control/devices/OceanOptics/IR/NIRQuest512.py +0 -90
- hein_analytical_control/devices/OceanOptics/IR/__init__.py +0 -0
- hein_analytical_control/devices/OceanOptics/IR/ir_spectrum.py +0 -191
- hein_analytical_control/devices/OceanOptics/Raman/__init__.py +0 -0
- hein_analytical_control/devices/OceanOptics/Raman/raman_control.py +0 -46
- hein_analytical_control/devices/OceanOptics/Raman/raman_spectrum.py +0 -148
- hein_analytical_control/devices/OceanOptics/UV/QEPro2192.py +0 -90
- hein_analytical_control/devices/OceanOptics/UV/__init__.py +0 -0
- hein_analytical_control/devices/OceanOptics/UV/uv_spectrum.py +0 -227
- hein_analytical_control/devices/OceanOptics/__init__.py +0 -0
- hein_analytical_control/devices/OceanOptics/oceanoptics.py +0 -115
- hein_analytical_control/devices/__init__.py +0 -15
- hein_analytical_control/generated/__init__.py +0 -56
- hein_analytical_control/generated/dad_method.py +0 -367
- hein_analytical_control/generated/pump_method.py +0 -519
- pychemstation-0.4.7.dev1.dist-info/RECORD +0 -109
- /ag_hplc_macro/utils/chemstation.py → /pychemstation/utils/parsing.py +0 -0
- {pychemstation-0.4.7.dev1.dist-info → pychemstation-0.4.7.dev2.dist-info}/LICENSE +0 -0
- {pychemstation-0.4.7.dev1.dist-info → pychemstation-0.4.7.dev2.dist-info}/WHEEL +0 -0
- {pychemstation-0.4.7.dev1.dist-info → pychemstation-0.4.7.dev2.dist-info}/top_level.txt +0 -0
@@ -1,191 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
.. module:: ir_spectrum
|
3
|
-
:synopsis: Module for representing an IR spectrum
|
4
|
-
:platforms: Unix, Windows
|
5
|
-
|
6
|
-
.. moduleauthor:: Graham Keenan (Cronin Lab 2020)
|
7
|
-
|
8
|
-
.. note:: Edit as needed, this is a skeletal implementation that can be improved
|
9
|
-
"""
|
10
|
-
|
11
|
-
import json
|
12
|
-
import time
|
13
|
-
import numpy as np
|
14
|
-
import matplotlib.pyplot as plt
|
15
|
-
|
16
|
-
|
17
|
-
def _write_json(data: dict, filename: str):
|
18
|
-
"""Write data out to a JSON file
|
19
|
-
|
20
|
-
Args:
|
21
|
-
data (dict): Data to write
|
22
|
-
filename (str): Path to save JSON file
|
23
|
-
"""
|
24
|
-
|
25
|
-
with open(filename, "w") as f_d:
|
26
|
-
json.dump(data, f_d, indent=4)
|
27
|
-
|
28
|
-
|
29
|
-
def _load_json(filename: str) -> dict:
|
30
|
-
"""Loads a JSON file from disk
|
31
|
-
|
32
|
-
Args:
|
33
|
-
filename (str): Name of the file to load
|
34
|
-
|
35
|
-
Returns:
|
36
|
-
dict: JSON data
|
37
|
-
"""
|
38
|
-
|
39
|
-
with open(filename) as f_d:
|
40
|
-
return json.load(f_d)
|
41
|
-
|
42
|
-
|
43
|
-
def _ensure_serializable(data: dict) -> dict:
|
44
|
-
"""Ensures the output data is serializable
|
45
|
-
|
46
|
-
Args:
|
47
|
-
data (Dict): Data to check
|
48
|
-
|
49
|
-
Returns:
|
50
|
-
Dict: Serializable data
|
51
|
-
"""
|
52
|
-
|
53
|
-
for k, v in data.items():
|
54
|
-
if isinstance(v, np.ndarray):
|
55
|
-
data[k] = v.tolist()
|
56
|
-
elif isinstance(v, dict):
|
57
|
-
data[k] = _ensure_serializable(v)
|
58
|
-
return data
|
59
|
-
|
60
|
-
|
61
|
-
def trim_data(self, data: list, lower_range: int, upper_range: int) -> np.ndarray:
|
62
|
-
"""Trims data within a certain range
|
63
|
-
Returns valid indexes that can be used to trim X and Y data
|
64
|
-
|
65
|
-
Args:
|
66
|
-
data (list): Data to trim within the range
|
67
|
-
lower_range (int): Lower end of the range (Minimum value)
|
68
|
-
upper_range (int): Upper end of the range (Maximum value)
|
69
|
-
|
70
|
-
Returns:
|
71
|
-
np.ndarray: Indexes to use for trimming data
|
72
|
-
"""
|
73
|
-
|
74
|
-
above_ind = np.array(data) > lower_range
|
75
|
-
below_ind = np.array(data) < upper_range
|
76
|
-
|
77
|
-
return np.logical_and(above_ind, below_ind)
|
78
|
-
|
79
|
-
|
80
|
-
class IRSpectrum:
|
81
|
-
"""Class representing an IR spectrum.
|
82
|
-
Spectrum can be a reference spectrum or a measured spectrum
|
83
|
-
|
84
|
-
Args:
|
85
|
-
wavelengths (list): Wavelengths of the spectrum
|
86
|
-
intensities (list): Intensities of the spectrum
|
87
|
-
ref (dict, optional): Reference spectrum if available
|
88
|
-
"""
|
89
|
-
|
90
|
-
def __init__(self, wavelengths: list, intensities: list, ref: dict = {}):
|
91
|
-
self.wavelengths = wavelengths
|
92
|
-
self.intensities = intensities
|
93
|
-
self.reference = ref
|
94
|
-
self.wavenumbers = []
|
95
|
-
self.transmittance = []
|
96
|
-
|
97
|
-
if self.reference:
|
98
|
-
self.convert_intensities_to_transmittance()
|
99
|
-
self.convert_wavelength_to_wavenumber()
|
100
|
-
|
101
|
-
@classmethod
|
102
|
-
def load_spectrum(cls, filepath: str):
|
103
|
-
"""Class method for loading spectrum data from a file
|
104
|
-
|
105
|
-
Args:
|
106
|
-
filepath (str): Path to spectrum data
|
107
|
-
|
108
|
-
Returns:
|
109
|
-
IRSpectrum: IRSpectrum with spectral data from file
|
110
|
-
"""
|
111
|
-
|
112
|
-
data = _load_json(filepath)
|
113
|
-
return cls(data["wavelength"], data["intensities"], ref=data["reference"])
|
114
|
-
|
115
|
-
def convert_intensities_to_transmittance(self):
|
116
|
-
"""Converts intensities to transmittance"""
|
117
|
-
|
118
|
-
self.transmittance = list(
|
119
|
-
reversed(
|
120
|
-
[
|
121
|
-
(s / r) * 100
|
122
|
-
for (s, r) in zip(self.intensities, self.reference["intensities"])
|
123
|
-
]
|
124
|
-
)
|
125
|
-
)
|
126
|
-
|
127
|
-
def convert_wavelength_to_wavenumber(self):
|
128
|
-
"""Converts Wavelengths to Wavenumbers"""
|
129
|
-
|
130
|
-
self.wavenumbers = list(reversed([(10**7) / nm for nm in self.wavelengths]))
|
131
|
-
|
132
|
-
def plot_spectrum(
|
133
|
-
self, display: bool = False, savepath: str = "", limits: tuple = ()
|
134
|
-
):
|
135
|
-
"""Plots the spectral data
|
136
|
-
|
137
|
-
Args:
|
138
|
-
display (bool, optional): Display the spectrum on screen. Defaults to False.
|
139
|
-
savepath (str, optional): Path to save the spectrum graph to disk. Defaults to "".
|
140
|
-
"""
|
141
|
-
|
142
|
-
# Clear any previous plots/data
|
143
|
-
plt.clf()
|
144
|
-
plt.cla()
|
145
|
-
|
146
|
-
# TODO::GAK -- Implement limit checks
|
147
|
-
|
148
|
-
# Already have a reference set, measured spectrum
|
149
|
-
if self.reference:
|
150
|
-
plt.xlabel("Wavenumber (cm-1)")
|
151
|
-
plt.ylabel("Transmittance (%)")
|
152
|
-
plt.title("IR Spectrum")
|
153
|
-
plt.plot(self.wavenumbers, self.transmittance, color="black", linewidth=0.5)
|
154
|
-
|
155
|
-
# No reference set, reference spectrum
|
156
|
-
else:
|
157
|
-
plt.xlabel("Wavelength (nm)")
|
158
|
-
plt.ylabel("Intensities")
|
159
|
-
plt.title("Reference IR Spectrum")
|
160
|
-
plt.plot(self.wavelengths, self.intensities, color="black", linewidth=0.5)
|
161
|
-
|
162
|
-
# Set limits after plotting
|
163
|
-
plt.gca().set_ylim(bottom=0)
|
164
|
-
|
165
|
-
# Save plot to disk
|
166
|
-
if savepath:
|
167
|
-
plt.savefig(savepath)
|
168
|
-
|
169
|
-
# Display the plot on screen
|
170
|
-
if display:
|
171
|
-
plt.show()
|
172
|
-
|
173
|
-
def dump_spectrum(self, filename: str):
|
174
|
-
"""Dump spectral data out to disk
|
175
|
-
|
176
|
-
Args:
|
177
|
-
filename (str): Path to save file (JSON)
|
178
|
-
"""
|
179
|
-
|
180
|
-
out = {
|
181
|
-
"wavenumbers": self.wavenumbers,
|
182
|
-
"transmittance": self.transmittance,
|
183
|
-
"wavelength": self.wavelengths,
|
184
|
-
"intensities": self.intensities,
|
185
|
-
"reference": self.reference,
|
186
|
-
"timestamp": time.strftime("%d_%m_%Y_%H:%M:%S"),
|
187
|
-
}
|
188
|
-
|
189
|
-
out = _ensure_serializable(out)
|
190
|
-
|
191
|
-
_write_json(out, filename)
|
File without changes
|
@@ -1,46 +0,0 @@
|
|
1
|
-
"""Module for interfacing with OceanOptics Raman spectrometer
|
2
|
-
|
3
|
-
.. moduleauthor:: Artem Leonov, Graham Keenan
|
4
|
-
"""
|
5
|
-
import time
|
6
|
-
import logging
|
7
|
-
|
8
|
-
from ..oceanoptics import OceanOpticsSpectrometer
|
9
|
-
from .raman_spectrum import RamanSpectrum
|
10
|
-
|
11
|
-
|
12
|
-
class OceanOpticsRaman(OceanOpticsSpectrometer):
|
13
|
-
"""Operational class for interfacing with the OceanOptics Raman spectrometer
|
14
|
-
|
15
|
-
Inherits:
|
16
|
-
OceanOpticsSpectrometer: Base Spectrometer class
|
17
|
-
"""
|
18
|
-
|
19
|
-
def __init__(self, name=None, path=None):
|
20
|
-
super().__init__("RAMAN", name)
|
21
|
-
self.last_spectrum = None
|
22
|
-
self.spectrum = RamanSpectrum(path)
|
23
|
-
self.start_time = time.time()
|
24
|
-
self.logger = logging.getLogger("oceanoptics.spectrometer.raman")
|
25
|
-
|
26
|
-
def get_spectrum(self):
|
27
|
-
"""Obtains spectrum and performs basic processing"""
|
28
|
-
|
29
|
-
spec = self.scan()
|
30
|
-
timestamp = round((time.time() - self.start_time), 2)
|
31
|
-
self.logger.debug("Spectrum obtained at %s", timestamp)
|
32
|
-
self.spectrum.load_spectrum(spec, timestamp, reference=False)
|
33
|
-
# self.last_spectrum = self.spectrum.default_process()
|
34
|
-
# return np.array(
|
35
|
-
# [
|
36
|
-
# self.last_spectrum['wavelengths'],
|
37
|
-
# self.last_spectrum['intensities']
|
38
|
-
# ]
|
39
|
-
# )
|
40
|
-
|
41
|
-
def obtain_reference_spectrum(self):
|
42
|
-
"""Obtains the reference spectrum for further use"""
|
43
|
-
|
44
|
-
spec = self.scan()
|
45
|
-
self.start_time = time.time()
|
46
|
-
self.spectrum.load_spectrum(spec, 0, reference=True)
|
@@ -1,148 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Module for handling Raman spectroscopic data
|
3
|
-
|
4
|
-
.. moduleauthor:: Artem Leonov, Graham Keenan
|
5
|
-
"""
|
6
|
-
import logging
|
7
|
-
import os
|
8
|
-
|
9
|
-
import numpy as np
|
10
|
-
from scipy import signal
|
11
|
-
|
12
|
-
from ....analysis import AbstractSpectrum
|
13
|
-
from ....analysis.utils import interpolate_to_index
|
14
|
-
|
15
|
-
LASER_POWER = 785
|
16
|
-
|
17
|
-
MIN_X = 780
|
18
|
-
MAX_X = 1006
|
19
|
-
|
20
|
-
|
21
|
-
def _convert_wavelength_to_wavenumber(data):
|
22
|
-
"""Converts x from spectrometer to Raman shift in wavenumbers
|
23
|
-
|
24
|
-
Arguments:
|
25
|
-
data (iterable): Wavelength data to convert
|
26
|
-
|
27
|
-
Returns:
|
28
|
-
(:obj: np.array): Wavenumbers data
|
29
|
-
"""
|
30
|
-
|
31
|
-
wavenumbers = [(10**7 / LASER_POWER) - (10**7 / wv) for wv in data]
|
32
|
-
|
33
|
-
return np.array(wavenumbers)
|
34
|
-
|
35
|
-
|
36
|
-
class RamanSpectrum(AbstractSpectrum):
|
37
|
-
"""Defines methods for Raman spectroscopic data handling
|
38
|
-
|
39
|
-
Args:
|
40
|
-
path(str, optional): Valid path to save the spectral data.
|
41
|
-
If not provided, uses .//raman_data
|
42
|
-
"""
|
43
|
-
|
44
|
-
AXIS_MAPPING = {
|
45
|
-
"x": "wavelength",
|
46
|
-
"y": "intensities",
|
47
|
-
}
|
48
|
-
|
49
|
-
INTERNAL_PROPERTIES = {
|
50
|
-
"reference",
|
51
|
-
"original",
|
52
|
-
"baseline",
|
53
|
-
}
|
54
|
-
|
55
|
-
def __init__(self, path=None, autosaving=True):
|
56
|
-
|
57
|
-
if path is not None:
|
58
|
-
path = os.path.join(".", "raman_data")
|
59
|
-
|
60
|
-
self.logger = logging.getLogger("oceanoptics.spectrometer.raman.spectrum")
|
61
|
-
|
62
|
-
super().__init__(path, autosaving)
|
63
|
-
|
64
|
-
def find_peaks_iteratively(self, limit=10, steps=100):
|
65
|
-
"""Finds all peaks iteratively moving the threshold
|
66
|
-
|
67
|
-
Args:
|
68
|
-
limit (int): Max number of peaks found at each iteration to stop
|
69
|
-
after.
|
70
|
-
steps (int): Max number of iterations.
|
71
|
-
"""
|
72
|
-
|
73
|
-
gradient = np.linspace(self.y.max(), self.y.min(), steps)
|
74
|
-
pl = [
|
75
|
-
0,
|
76
|
-
] # peaks length
|
77
|
-
|
78
|
-
# Looking for peaks and decreasing height
|
79
|
-
for i, n in enumerate(gradient):
|
80
|
-
peaks, _ = signal.find_peaks(self.y, height=n)
|
81
|
-
pl.append(len(peaks))
|
82
|
-
diff = pl[-1] - pl[-2]
|
83
|
-
if diff > limit:
|
84
|
-
self.logger.debug(
|
85
|
-
"Stopped at iteration %s, with height %s, \
|
86
|
-
diff - %s",
|
87
|
-
i + 1,
|
88
|
-
n,
|
89
|
-
diff,
|
90
|
-
)
|
91
|
-
break
|
92
|
-
|
93
|
-
# Final peaks at previous iteration
|
94
|
-
peaks, _ = signal.find_peaks(self.y, height=gradient[i - 1])
|
95
|
-
|
96
|
-
# Updating widths
|
97
|
-
pw = signal.peak_widths(self.y, peaks, rel_height=0.95)
|
98
|
-
peak_xs = self.x.copy()[peaks][:, np.newaxis]
|
99
|
-
peak_ys = self.y.copy()[peaks][:, np.newaxis]
|
100
|
-
peaks_ids = np.around(peak_xs)
|
101
|
-
peaks_left_ids = interpolate_to_index(self.x, pw[2])[:, np.newaxis]
|
102
|
-
peaks_right_ids = interpolate_to_index(self.x, pw[3])[:, np.newaxis]
|
103
|
-
|
104
|
-
# Packing for peak array
|
105
|
-
self.peaks = np.hstack(
|
106
|
-
(
|
107
|
-
peaks_ids,
|
108
|
-
peak_xs,
|
109
|
-
peak_ys,
|
110
|
-
peaks_left_ids,
|
111
|
-
peaks_right_ids,
|
112
|
-
)
|
113
|
-
)
|
114
|
-
|
115
|
-
return peaks_ids
|
116
|
-
|
117
|
-
def load_spectrum(self, spectrum, timestamp, reference=False):
|
118
|
-
"""Loads the spectral data
|
119
|
-
|
120
|
-
Args:
|
121
|
-
spectrum (tuple): Tuple containing spectrum x and y as numpy arrays.
|
122
|
-
Example: (array(x), array(y))
|
123
|
-
timestamp (float): time.time() for the measured spectra
|
124
|
-
reference (bool, optional): True if the supplied spectra should be
|
125
|
-
stored as a reference (background)
|
126
|
-
"""
|
127
|
-
|
128
|
-
super().load_spectrum(spectrum[0], spectrum[1], timestamp)
|
129
|
-
self.original = spectrum[1]
|
130
|
-
if reference:
|
131
|
-
self.reference = spectrum[1]
|
132
|
-
|
133
|
-
def subtract_reference(self):
|
134
|
-
"""Subtracts reference spectrum and updates the current one"""
|
135
|
-
|
136
|
-
if self.reference is None:
|
137
|
-
raise ValueError("Please upload the reference first")
|
138
|
-
|
139
|
-
self.y -= self.reference
|
140
|
-
|
141
|
-
def default_processing(self):
|
142
|
-
"""Dummy method for quick processing. Returns spectral data!"""
|
143
|
-
|
144
|
-
self.correct_baseline()
|
145
|
-
self.smooth_spectrum()
|
146
|
-
self.find_peaks_iteratively()
|
147
|
-
|
148
|
-
return self.x, self.y, self.timestamp
|
@@ -1,90 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
.. module:: QEPro2192
|
3
|
-
:synopsis: Module representing the UV QEPro2192 spectrometer
|
4
|
-
:platforms: Unix, Windows
|
5
|
-
|
6
|
-
.. moduleauthor:: Graham Keenan (Cronin Lab 2020)
|
7
|
-
|
8
|
-
.. note:: Feel free to edit. This is a skeleton for minimal functionality.
|
9
|
-
|
10
|
-
"""
|
11
|
-
|
12
|
-
import json
|
13
|
-
from pathlib import Path
|
14
|
-
from .uv_spectrum import UVSpectrum
|
15
|
-
from typing import Optional, Union, Dict
|
16
|
-
from ..oceanoptics import OceanOpticsSpectrometer
|
17
|
-
|
18
|
-
|
19
|
-
class NoReferenceException(Exception):
|
20
|
-
"""Exception for calling spectrum without a reference"""
|
21
|
-
|
22
|
-
|
23
|
-
class QEPro2192(OceanOpticsSpectrometer):
|
24
|
-
"""Class representing the QEPro2192 spectrometer
|
25
|
-
|
26
|
-
Inherits:
|
27
|
-
OceanOpticsSpectrometer
|
28
|
-
"""
|
29
|
-
|
30
|
-
def __init__(self):
|
31
|
-
super().__init__("UV", name="QEPro2192 UV Spectrometer")
|
32
|
-
self.reference = {}
|
33
|
-
self.__ref_called = False
|
34
|
-
|
35
|
-
def load_reference(self, ref: Union[str, Dict]):
|
36
|
-
"""Loads a pre-existing reference from disk or from dict.
|
37
|
-
|
38
|
-
Args:
|
39
|
-
ref (Union[str, Dict]): Reference as either a dictionary
|
40
|
-
or JSON filepath
|
41
|
-
"""
|
42
|
-
|
43
|
-
# Filepath, load and set
|
44
|
-
if isinstance(ref, str) or isinstance(ref, Path):
|
45
|
-
with open(ref) as fd:
|
46
|
-
self.reference = json.load(fd)
|
47
|
-
self.__ref_called = True
|
48
|
-
|
49
|
-
# Dict, set
|
50
|
-
elif isinstance(ref, dict):
|
51
|
-
self.reference = ref
|
52
|
-
self.__ref_called = True
|
53
|
-
|
54
|
-
# Not supported
|
55
|
-
else:
|
56
|
-
self.logger.warning(f"Reference {ref} is unsupported. Not loading")
|
57
|
-
|
58
|
-
def obtain_reference_spectrum(self) -> UVSpectrum:
|
59
|
-
"""Obtain a reference spectrum.
|
60
|
-
|
61
|
-
Returns:
|
62
|
-
UVSpectrum: Reference UV spectrum
|
63
|
-
"""
|
64
|
-
|
65
|
-
wavelengths, intensities = self.scan()
|
66
|
-
self.reference["wavelength"] = wavelengths
|
67
|
-
self.reference["intensities"] = intensities
|
68
|
-
self.__ref_called = True
|
69
|
-
|
70
|
-
return UVSpectrum(wavelengths, intensities)
|
71
|
-
|
72
|
-
def obtain_spectrum(self) -> UVSpectrum:
|
73
|
-
"""Obtain a UV spectrum of a sample.
|
74
|
-
|
75
|
-
Raises:
|
76
|
-
NoReferenceException: Attempting to measure a sample without
|
77
|
-
a reference
|
78
|
-
|
79
|
-
Returns:
|
80
|
-
UVSpectrum: Sample UV spectrum.
|
81
|
-
"""
|
82
|
-
|
83
|
-
if not self.__ref_called:
|
84
|
-
raise NoReferenceException(
|
85
|
-
"Attempting to call a spectrum without a valid reference\
|
86
|
-
spectrum"
|
87
|
-
)
|
88
|
-
|
89
|
-
wavelengths, intensities = self.scan()
|
90
|
-
return UVSpectrum(wavelengths, intensities, ref=self.reference)
|
File without changes
|
@@ -1,227 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
.. module:: uv_spectrum
|
3
|
-
:synopsis: Module representing a UV spectrum
|
4
|
-
:platforms: Unix, Windows
|
5
|
-
|
6
|
-
.. moduleauthor:: Graham Keenan (Cronin Lab 2020)
|
7
|
-
|
8
|
-
.. note:: Feel free to edit. This is a skeletal implementation.
|
9
|
-
|
10
|
-
"""
|
11
|
-
|
12
|
-
import time
|
13
|
-
import json
|
14
|
-
import numpy as np
|
15
|
-
import matplotlib.pyplot as plt
|
16
|
-
|
17
|
-
|
18
|
-
def _write_json(data: dict, filename: str):
|
19
|
-
"""Writes out data to a JSON file
|
20
|
-
|
21
|
-
Args:
|
22
|
-
data (dict): Data to write
|
23
|
-
filename (str): Path to save to
|
24
|
-
"""
|
25
|
-
|
26
|
-
with open(filename, "w") as f_d:
|
27
|
-
json.dump(data, f_d, indent=4)
|
28
|
-
|
29
|
-
|
30
|
-
def _load_json(filename: str) -> dict:
|
31
|
-
"""Load JSON file from disk
|
32
|
-
|
33
|
-
Args:
|
34
|
-
filename (str): Path to JSON file
|
35
|
-
|
36
|
-
Returns:
|
37
|
-
dict: JSON data
|
38
|
-
"""
|
39
|
-
|
40
|
-
with open(filename) as f_d:
|
41
|
-
return json.load(f_d)
|
42
|
-
|
43
|
-
|
44
|
-
def _ensure_serializable(data: dict) -> dict:
|
45
|
-
"""Ensures the output data is serializable
|
46
|
-
|
47
|
-
Args:
|
48
|
-
data (Dict): Data to check
|
49
|
-
|
50
|
-
Returns:
|
51
|
-
Dict: Serializable data
|
52
|
-
"""
|
53
|
-
|
54
|
-
for k, v in data.items():
|
55
|
-
if isinstance(v, np.ndarray):
|
56
|
-
data[k] = v.tolist()
|
57
|
-
elif isinstance(v, dict):
|
58
|
-
data[k] = _ensure_serializable(v)
|
59
|
-
return data
|
60
|
-
|
61
|
-
|
62
|
-
def trim_data(data: list, lower_range: int, upper_range: int) -> np.ndarray:
|
63
|
-
"""Trims data within a certain range
|
64
|
-
Returns valid indexes that can be used to trim X and Y data
|
65
|
-
|
66
|
-
Args:
|
67
|
-
data (list): Data to trim within the range
|
68
|
-
lower_range (int): Lower end of the range (Minimum value)
|
69
|
-
upper_range (int): Upper end of the range (Maximum value)
|
70
|
-
|
71
|
-
Returns:
|
72
|
-
np.ndarray: Indexes to use for trimming data
|
73
|
-
"""
|
74
|
-
|
75
|
-
above_ind = np.array(data) > lower_range
|
76
|
-
below_ind = np.array(data) < upper_range
|
77
|
-
|
78
|
-
return np.logical_and(above_ind, below_ind)
|
79
|
-
|
80
|
-
|
81
|
-
class UVSpectrum:
|
82
|
-
"""Class representing a UV spectrum.
|
83
|
-
Spectrum can be a reference spectrum or measured spectrum.
|
84
|
-
|
85
|
-
Args:
|
86
|
-
wavelengths (list): Wavelengths of the spectrum
|
87
|
-
intensities (list): Intensities of the spectrum
|
88
|
-
ref (dict): Reference spectrum data (Defaults to {})
|
89
|
-
|
90
|
-
"""
|
91
|
-
|
92
|
-
def __init__(self, wavelengths: list, intensities: list, ref: dict = {}):
|
93
|
-
self.wavelengths = np.array(wavelengths)
|
94
|
-
self.intensities = np.array(intensities)
|
95
|
-
self.reference = ref
|
96
|
-
self.absorbances = []
|
97
|
-
self.max_peak = 0
|
98
|
-
|
99
|
-
# If we have a reference, generate absorbances using Beer-Lambert relation
|
100
|
-
if self.reference:
|
101
|
-
self.beer_lambert()
|
102
|
-
|
103
|
-
@classmethod
|
104
|
-
def load_spectrum(cls, filepath: str):
|
105
|
-
"""Class method for loading spectrum data from file
|
106
|
-
|
107
|
-
Args:
|
108
|
-
filepath (str): Path to spectrum JSON file
|
109
|
-
|
110
|
-
Returns:
|
111
|
-
UVSpectrum: UVSpectrum instance
|
112
|
-
"""
|
113
|
-
|
114
|
-
data = _load_json(filepath)
|
115
|
-
return cls(data["wavelength"], data["intensities"], ref=data["reference"])
|
116
|
-
|
117
|
-
def beer_lambert(self):
|
118
|
-
"""Beer-Lambert relation.
|
119
|
-
Converts intensities to absorbances for a UV spectrum
|
120
|
-
"""
|
121
|
-
|
122
|
-
for ref, measured in zip(self.reference["intensities"], self.intensities):
|
123
|
-
try:
|
124
|
-
if ref == 0 or measured == 0:
|
125
|
-
continue
|
126
|
-
self.absorbances.append(np.log10(ref / measured))
|
127
|
-
except Exception:
|
128
|
-
break
|
129
|
-
|
130
|
-
# Set absorbances
|
131
|
-
self.absorbances = np.array(self.absorbances)
|
132
|
-
|
133
|
-
# Find the maximum peak
|
134
|
-
self.max_peak = self.wavelengths[np.argmax(self.absorbances)]
|
135
|
-
|
136
|
-
def plot_spectrum(
|
137
|
-
self,
|
138
|
-
display: bool = False,
|
139
|
-
legend: bool = False,
|
140
|
-
savepath: str = "",
|
141
|
-
limits: tuple = (),
|
142
|
-
):
|
143
|
-
"""Plot spectrum.
|
144
|
-
|
145
|
-
Args:
|
146
|
-
display (bool, optional): Display the spectra on screen. Defaults to False.
|
147
|
-
legend (bool, optional): Display a legend or not for maximum peak. Defaults to False.
|
148
|
-
savepath (str, optional): Path to save the image to. Defaults to "".
|
149
|
-
limits (tuple, optional): Trims data within a certain range. Defaults to ().
|
150
|
-
"""
|
151
|
-
|
152
|
-
# Clear any previous plots/data
|
153
|
-
plt.clf()
|
154
|
-
plt.cla()
|
155
|
-
|
156
|
-
# Set X label
|
157
|
-
plt.xlabel("Wavelength (nm)")
|
158
|
-
|
159
|
-
# Trim data within a certain range
|
160
|
-
if limits:
|
161
|
-
trimmed = trim_data(self.wavelengths, *limits)
|
162
|
-
self.wavelengths = self.wavelengths[trimmed]
|
163
|
-
self.intensities = self.intensities[trimmed]
|
164
|
-
|
165
|
-
# Trim absorbances if they're present
|
166
|
-
if len(self.absorbances) > 0:
|
167
|
-
self.absorbances = self.absorbances[trimmed]
|
168
|
-
|
169
|
-
# Display different labels for Measured spectrum
|
170
|
-
# We have a reference spectrum already, means this is a measured sample
|
171
|
-
if self.reference:
|
172
|
-
plt.ylabel("Absorbances")
|
173
|
-
plt.title("UV/Vis Reference Spectrum")
|
174
|
-
plt.plot(self.wavelengths, self.absorbances, color="black", linewidth=0.5)
|
175
|
-
|
176
|
-
# Display different labels for Reference
|
177
|
-
# No reference means this should be a reference
|
178
|
-
else:
|
179
|
-
plt.ylabel("Intensities")
|
180
|
-
plt.title("UV/Vis Spectrum")
|
181
|
-
plt.plot(self.wavelengths, self.intensities, color="black", linewidth=0.5)
|
182
|
-
|
183
|
-
# Display legend
|
184
|
-
if legend:
|
185
|
-
leg_label = f"Maximum Peak: {self.max_peak:.2f}nm"
|
186
|
-
plt.vlines(
|
187
|
-
x=self.max_peak,
|
188
|
-
ymin=0,
|
189
|
-
ymax=max(self.absorbances),
|
190
|
-
color="g",
|
191
|
-
zorder=3,
|
192
|
-
label=leg_label,
|
193
|
-
)
|
194
|
-
leg = plt.legend()
|
195
|
-
for legobj in leg.legendHandles:
|
196
|
-
legobj.set_linewidth(5.0)
|
197
|
-
|
198
|
-
# Set limits after plotting
|
199
|
-
plt.gca().set_ylim(bottom=0)
|
200
|
-
|
201
|
-
# Save if savepath is defined
|
202
|
-
if savepath:
|
203
|
-
plt.savefig(savepath)
|
204
|
-
|
205
|
-
# SHow the spectrum on screen
|
206
|
-
if display:
|
207
|
-
plt.show()
|
208
|
-
|
209
|
-
def dump_spectrum(self, filename: str):
|
210
|
-
"""Dump spectrum data to JSON file
|
211
|
-
|
212
|
-
Args:
|
213
|
-
filename (str): Path to save JSON file
|
214
|
-
"""
|
215
|
-
|
216
|
-
out = {
|
217
|
-
"wavelength": self.wavelengths,
|
218
|
-
"absorbances": self.absorbances,
|
219
|
-
"intensities": self.intensities,
|
220
|
-
"reference": self.reference,
|
221
|
-
"max_peak": self.max_peak,
|
222
|
-
"timestamp": time.strftime("%d_%m_%Y_%H:%M:%S"),
|
223
|
-
}
|
224
|
-
|
225
|
-
out = _ensure_serializable(out)
|
226
|
-
|
227
|
-
_write_json(out, filename)
|
File without changes
|