-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
134 Adds hyper spectral imaging to qt3scan #140
Changes from 61 commits
d63d9ea
1750923
f4e6e59
90111aa
f209204
3b42079
5bbbd1b
2260671
1e58858
d0dc344
ecc1adc
312d30d
5f078c9
3dff2a7
051bace
a0fa646
477497f
0f27199
fd25ca5
19abba7
c3c9ef0
cdf4264
baacd7e
7b65937
c5b9de6
f50e041
176b0d3
a5d7a26
6b866f8
9a55f04
4b65555
6a223e5
af0a744
c48f6cc
82b6c47
ad6a39f
5249317
18d200d
fb068cf
ae49baa
5632e56
7725275
17ac15d
ddc2f5e
f1dee2f
6e13ee9
1ee6ae4
f5868f2
542e68a
b8885e3
08af994
db59737
e003c2c
288b60b
dd11d95
c504733
d18e059
1a311c5
67656fd
19903f5
7599bfb
e5b1afe
fcbb586
00ce9f5
affc6fb
bd992d6
2987715
a9a2cf1
8eb1233
898664b
1fda476
5202c90
e1703c3
3899efa
7102a31
b03a875
071617f
b4ada4f
a99e4ef
02db2a2
2d3f125
127b9da
e68690b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
import numpy as np | ||
import tkinter as tk | ||
import logging | ||
from typing import Tuple | ||
|
||
import qt3utils.datagenerators.princeton as princeton | ||
|
||
|
||
class QT3ScanPrincetonSpectrometerController: | ||
""" | ||
Implements qt3utils.applications.qt3scan.interface.QT3ScanSpectrometerDAQControllerInterface | ||
""" | ||
def __init__(self, logger_level: int): | ||
self.logger = logging.getLogger(__name__) | ||
self.logger.setLevel(logger_level) | ||
|
||
self.spectrometer = princeton.Spectrometer() | ||
self.last_config_dict = {} | ||
|
||
self.wave_start = None | ||
self.wave_end = None | ||
self.last_measured_spectrum = None | ||
self.last_wavelength_array = None | ||
|
||
@property | ||
def clock_rate(self) -> float: | ||
try: | ||
_t = self.spectrometer.exposure_time / 1000.0 # Converting from milliseconds to seconds. | ||
except Exception as e: | ||
self.logger.error(e) | ||
_t = 2 # TODO: better default behavior. Should this be -1? 1? or should Spectrometer be changed. | ||
return 1.0 / _t | ||
|
||
def start(self) -> None: | ||
""" | ||
Nothing to be done in this method. All acquisition is happening in the "sample_spectrum" method. | ||
""" | ||
self.logger.debug('calling QT3ScanPrincetonSpectrometerController start') | ||
Comment on lines
+34
to
+38
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then why is it here? Or why not implement the sample_spectrum in here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vasilisniaouris there's some detritus left-over from my initial pass at refactoring last fall. The definition of the interface requires this method. It could be better named to 'initialize_before_acquisition' perhaps. Or something. Depending on the long-term plans for this code, this would be a good cleanup task -- go through the interfaces and qt3scan, understand what's happening and then rename methods appropriately. |
||
|
||
def stop(self) -> None: | ||
""" | ||
Implementations should do necessary steps to stop acquiring data. | ||
""" | ||
#TODO: Need to implement a feature to pause scan here. If there is a way to interrupt data acquistion, do that here. Otherwise, do nothing | ||
self.logger.debug('calling QT3ScanPrincetonSpectrometerController stop') | ||
|
||
def close(self) -> None: | ||
self.spectrometer.finalize() | ||
|
||
def sample_spectrum(self) -> Tuple[np.ndarray, np.ndarray]: | ||
self.last_measured_spectrum, self.last_wavelength_array = (self.spectrometer.acquire_step_and_glue([self.wave_start, self.wave_end])) | ||
cmordi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
self.logger.debug(f'acquired spectrum from {self.last_wavelength_array[0]} to {self.last_wavelength_array[-1]} nm') | ||
return self.last_measured_spectrum, self.last_wavelength_array | ||
|
||
def configure(self, config_dict: dict) -> None: | ||
""" | ||
This method is used to configure the spectrometer with the provided settings. | ||
""" | ||
self.logger.debug("Calling configure on the Princeton Spectrometer data controller") | ||
self.last_config_dict.update(config_dict) | ||
|
||
#NOTE: If you dont type cast these then you will get serialization errors | ||
cmordi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
self.spectrometer.experiment_name = str(config_dict.get('experiment_name', self.spectrometer.experiment_name)) | ||
self.spectrometer.exposure_time = float(config_dict.get('exposure_time', self.spectrometer.exposure_time)) | ||
self.spectrometer.center_wavelength = float(config_dict.get('center_wavelength', self.spectrometer.center_wavelength)) | ||
self.spectrometer.temperature_sensor_setpoint = float(config_dict.get('temperature_sensor_setpoint', self.spectrometer.temperature_sensor_setpoint)) | ||
self.spectrometer.grating = str(config_dict.get('grating', self.spectrometer.grating)) | ||
self.wave_start = float(config_dict.get('wave_start', self.wave_start)) | ||
self.wave_end = float(config_dict.get('wave_end', self.wave_end)) | ||
|
||
def configure_view(self, gui_root: tk.Toplevel) -> None: | ||
""" | ||
This method launches a GUI window to configure the data controller. | ||
""" | ||
config_win = tk.Toplevel(gui_root) | ||
config_win.grab_set() | ||
config_win.title('Princeton Spectrometer Settings') | ||
|
||
row = 0 | ||
tk.Label(config_win, text="Experiment Name)").grid(row=row, column=0, padx=10) | ||
experiment_name_var = tk.StringVar(value=str(self.spectrometer.experiment_name)) | ||
tk.Entry(config_win, textvariable=experiment_name_var).grid(row=row, column=1) | ||
|
||
row += 1 | ||
tk.Label(config_win, text="Exposure Time (ms)").grid(row=row, column=0, padx=10) | ||
exposure_time_var = tk.DoubleVar(value=self.spectrometer.exposure_time) | ||
tk.Entry(config_win, textvariable=exposure_time_var).grid(row=row, column=1) | ||
|
||
row += 1 | ||
tk.Label(config_win, text="Center Wavelength (nm)").grid(row=row, column=0, padx=10) | ||
center_wavelength_var = tk.DoubleVar(value=self.spectrometer.center_wavelength) | ||
tk.Entry(config_win, textvariable=center_wavelength_var).grid(row=row, column=1) | ||
|
||
row += 1 | ||
tk.Label(config_win, text="Temperature Sensor Setpoint (°C)").grid(row=row, column=0, padx=10) | ||
temperature_sensor_setpoint_var = tk.DoubleVar(value=self.spectrometer.temperature_sensor_setpoint) | ||
tk.Entry(config_win, textvariable=temperature_sensor_setpoint_var).grid(row=row, column=1) | ||
|
||
row += 1 | ||
tk.Label(config_win, text="Grating").grid(row=row, column=0, padx=10) | ||
grating_var = tk.StringVar(value=self.spectrometer.grating) | ||
cmordi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
tk.Entry(config_win, textvariable=grating_var).grid(row=row, column=1) | ||
|
||
row += 1 | ||
tk.Label(config_win, text="Wavelength Start (nm)").grid(row=row, column=0, padx=10) | ||
wave_start_var = tk.DoubleVar(value=self.wave_start) | ||
tk.Entry(config_win, textvariable=wave_start_var).grid(row=row, column=1) | ||
|
||
row += 1 | ||
tk.Label(config_win, text="Wavelength End (nm)").grid(row=row, column=0, padx=10) | ||
wave_end_var = tk.DoubleVar(value=self.wave_end) | ||
tk.Entry(config_win, textvariable=wave_end_var).grid(row=row, column=1) | ||
|
||
# Pack variables into a dictionary to pass to the _set_from_gui method | ||
gui_info = { | ||
'experiment_name': experiment_name_var, | ||
'exposure_time': exposure_time_var, | ||
'center_wavelength': center_wavelength_var, | ||
'temperature_sensor_setpoint': temperature_sensor_setpoint_var, | ||
'grating': grating_var, | ||
'wave_start': wave_start_var, | ||
'wave_end': wave_end_var, | ||
} | ||
|
||
row += 1 | ||
tk.Button(config_win, text='Set', command=lambda: self._set_from_gui(gui_info)).grid(row=row, column=0) | ||
tk.Button(config_win, text='Close', command=config_win.destroy).grid(row=row, column=1) | ||
|
||
def _set_from_gui(self, gui_vars: dict) -> None: | ||
""" | ||
Sets the spectrometer configuration from the GUI. | ||
""" | ||
config_dict = {k:v.get() if v.get() not in ['None', ''] else None for k, v in gui_vars.items()} # code to handle the edge case where there are "None" value | ||
self.logger.info(config_dict) | ||
self.configure(config_dict) | ||
|
||
def print_config(self) -> None: | ||
print("Princeton Spectrometer config") | ||
print(self.last_config_dict) #NOTE: We dont' use the logger because we want to be sure this is printed to stdout |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
QT3Scan: | ||
DAQController: | ||
import_path : qt3utils.applications.controllers.princeton_spectrometer | ||
class_name : QT3ScanPrincetonSpectrometerController | ||
configure : | ||
exposure_time : 2000 # This is in ms | ||
center_wavelength : 700 # This is in nm | ||
temperature_sensor_setpoint : -70 # This is in Celsius | ||
grating : "[500nm,300][2][0]" # Varies based on spectrometer type | ||
wave_start : 600 | ||
wave_end : 850 | ||
experiment_name: "LF_Control" | ||
cmordi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
PositionController: | ||
import_path : qt3utils.applications.controllers.nidaqpiezocontroller | ||
class_name : QT3ScanNIDAQPositionController | ||
configure : | ||
daq_name : Dev1 # NI DAQ Device Name | ||
write_channels : ao0,ao1,ao2 # NI DAQ analog output channels to use for writing position | ||
read_channels : ai0,ai1,ai2 # NI DAQ analog input channels to use for reading position | ||
scale_microns_per_volt : 8 # conversion factor from volts to microns, can also supply a list [8,8,8] or [6,4.2,5] | ||
zero_microns_volt_offset: 0 # the voltage value that defines the position 0,0,0, can also supply a list [0,0,0] or [5,5,5] | ||
minimum_allowed_position : 0 # microns | ||
maximum_allowed_position : 80 # microns | ||
settling_time_in_seconds : 0.001 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe np.nan is better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure-- @cmordi we could revisit this and find out if this try block is still needed or if we fixed the problem. The problem relates to how the isinstance function of python works at this line:
qt3-utils/src/qt3utils/applications/qt3scan/main.py
Line 482 in a99e4ef