Skip to content
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

Closed
wants to merge 83 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
d63d9ea
implemented the spectrometer controller file
cmordi Dec 25, 2023
1750923
implemented spectrometer yaml
cmordi Dec 25, 2023
f4e6e59
changed naming
cmordi Dec 26, 2023
90111aa
WIP -- this is work in progress. Do not use this commit in final PR.
gadamc Dec 26, 2023
f209204
Adds necessary specification to yaml file.
gadamc Dec 29, 2023
3b42079
Removes num_data_samples_per_batch from interface.
gadamc Dec 29, 2023
5bbbd1b
WIP-Removes clock_rate property from qt3scan interface.
gadamc Dec 29, 2023
2260671
removes unused method
gadamc Dec 30, 2023
1e58858
adds some docstrings to methods
gadamc Dec 30, 2023
d0dc344
puts the clock_rate back as a required property of the qt3scan daq co…
gadamc Dec 30, 2023
ecc1adc
Adds docstrings to interface to help with development.
gadamc Dec 31, 2023
312d30d
Removes unnecessary method from randon data generator DAQ controller
gadamc Dec 31, 2023
5f078c9
WIP -- trying to implement spectrometer DAQ class.
gadamc Dec 31, 2023
3dff2a7
WIP
gadamc Dec 31, 2023
051bace
WIP - made some progress to figure out why
gadamc Dec 31, 2023
a0fa646
Fixes QT3ScanPrincetonSpectrometerController to
gadamc Dec 31, 2023
477497f
Fixes QT3ScanPrincetonSpectrometerController to
gadamc Dec 31, 2023
0f27199
Merge branch '134sub-changes-to-interface' of https://github.com/qt3u…
cmordi Dec 31, 2023
fd25ca5
Merge branch '134sub-changes-to-interface' of https://github.com/qt3u…
cmordi Dec 31, 2023
19abba7
Fixes QT3ScanPrincetonSpectrometerController to
gadamc Dec 31, 2023
c3c9ef0
Merge branch '134sub-changes-to-interface' of https://github.com/qt3u…
cmordi Jan 2, 2024
cdf4264
replace quotation for unicode error
cmordi Jan 8, 2024
baacd7e
fixed wave start and end bugs
cmordi Jan 9, 2024
7b65937
Adds pythonnet as a depedency.
cmordi Jan 24, 2024
c5b9de6
Adds functionality to acquire data from spectrometer.
cmordi Jan 24, 2024
f50e041
Implements hyper-spectral image application controller
gadamc Jan 31, 2024
176b0d3
Adds random spectrometer class.
gadamc Jan 31, 2024
a5d7a26
reorders DAQ options to put random DAQs at bottom of list.
gadamc Jan 31, 2024
6b866f8
removes some logging statements
gadamc Jan 31, 2024
9a55f04
Adds code to display a randomly selected spectrum
gadamc Jan 31, 2024
4b65555
cleaned up code and fixed bugs
cmordi Feb 1, 2024
6a223e5
added grid and reverted the wave issue in RandGenerator
cmordi Feb 1, 2024
af0a744
This commit adds the princeton spectrometer to the qt3scan
gadamc Dec 26, 2023
c48f6cc
Adds necessary specification to yaml file.
gadamc Dec 29, 2023
82b6c47
Removes num_data_samples_per_batch from interface.
gadamc Dec 29, 2023
ad6a39f
This commit removed clock_rate and get_completed_scan_range
gadamc Dec 29, 2023
5249317
removes unused method
gadamc Dec 30, 2023
18d200d
adds some docstrings to methods
gadamc Dec 30, 2023
fb068cf
puts the clock_rate back as a required property of the qt3scan daq co…
gadamc Dec 30, 2023
ae49baa
Adds docstrings to interface to help with development.
gadamc Dec 31, 2023
5632e56
Removes unnecessary method from randon data generator DAQ controller
gadamc Dec 31, 2023
7725275
Initial pass to implement QT3ScanPrincetonSpectrometerController class
gadamc Dec 31, 2023
17ac15d
Sidesteps qt3scan.main check for objects to pass isisntance
gadamc Dec 31, 2023
ddc2f5e
Intermediate hack to resolve issue related to clock_rate
gadamc Dec 31, 2023
f1dee2f
Fixes QT3ScanPrincetonSpectrometerController to
gadamc Dec 31, 2023
6e13ee9
Fixes QT3ScanPrincetonSpectrometerController to
gadamc Dec 31, 2023
1ee6ae4
Fixes QT3ScanPrincetonSpectrometerController to
gadamc Dec 31, 2023
f5868f2
replace quotation for unicode error
cmordi Jan 8, 2024
542e68a
fixed wave start and end bugs
cmordi Jan 9, 2024
b8885e3
Adds pythonnet as a depedency.
cmordi Jan 24, 2024
08af994
Adds functionality to acquire data from spectrometer.
cmordi Jan 24, 2024
db59737
Implements hyper-spectral image application controller
gadamc Jan 31, 2024
e003c2c
Adds random spectrometer class.
gadamc Jan 31, 2024
288b60b
reorders DAQ options to put random DAQs at bottom of list.
gadamc Jan 31, 2024
dd11d95
removes some logging statements
gadamc Jan 31, 2024
c504733
Adds code to display a randomly selected spectrum
gadamc Jan 31, 2024
d18e059
cleaned up code and fixed bugs
cmordi Feb 1, 2024
1a311c5
added grid and reverted the wave issue in RandGenerator
cmordi Feb 1, 2024
67656fd
Adds simulation of finding NV centers in sample.
gadamc Feb 2, 2024
19903f5
Merge branch '134sub-changes-to-interface' of https://github.com/qt3u…
cmordi Feb 3, 2024
7599bfb
working on fixes adam highlighted
cmordi Feb 4, 2024
e5b1afe
small tweaks in acquire method
cmordi Feb 4, 2024
fcbb586
added a lot of error checks and removed return statements
cmordi Feb 5, 2024
00ce9f5
need to catch step and glue wavelength error early
cmordi Feb 5, 2024
affc6fb
removed redundancy and add blocks for errors
cmordi Feb 6, 2024
bd992d6
redid structure of the spectrometer datagenerator
cmordi Feb 6, 2024
2987715
working on file names and a few format changes
cmordi Feb 7, 2024
a9a2cf1
fixed the structure of acquire
cmordi Feb 9, 2024
8eb1233
WIP - added spe file
cmordi Feb 10, 2024
898664b
added type hints and removed spe reader - will handle in issue
cmordi Feb 10, 2024
1fda476
Added a dropdown for gratings
cmordi Feb 11, 2024
5202c90
Fixed file name collisions
cmordi Feb 11, 2024
e1703c3
removes comment and updates spacing
gadamc Feb 11, 2024
3899efa
removes comment and updates spacing
gadamc Feb 11, 2024
7102a31
Fixes selection of correct hyperspectral image
gadamc Feb 2, 2024
b03a875
update version number
gadamc Feb 11, 2024
071617f
fixed variable names and type hints
cmordi Feb 11, 2024
b4ada4f
Fixed the type and .Net serialization errors that would occur
cmordi Feb 13, 2024
a99e4ef
Added pause/stop hyperspectral scan feature
cmordi Feb 13, 2024
02db2a2
WIP-vasilis-suggestions
cmordi Feb 14, 2024
2d3f125
updated the readme
cmordi Feb 15, 2024
127b9da
note for stop scan
cmordi Feb 17, 2024
e68690b
new note for stop scan
cmordi Feb 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions src/qt3utils/applications/controllers/princeton_spectrometer.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,12 @@ def configure(self, config_dict: dict) -> None:
self.logger.debug("Calling configure on the Princeton Spectrometer data controller")
self.last_config_dict.update(config_dict)

self.spectrometer.experiment_name = config_dict.get('experiment_name', self.spectrometer.experiment_name)
self.spectrometer.center_wavelength = float(config_dict.get('center_wavelength', self.spectrometer.center_wavelength))
#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.num_frames = float(config_dict.get('num_frames', self.spectrometer.num_frames))
self.spectrometer.grating = config_dict.get('grating', self.spectrometer.grating)
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))

Expand All @@ -83,17 +83,17 @@ def configure_view(self, gui_root: tk.Toplevel) -> None:

row += 1
tk.Label(config_win, text="Exposure Time (ms)").grid(row=row, column=0, padx=10)
exposure_time_var = tk.StringVar(value=str(self.spectrometer.exposure_time))
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.StringVar(value=str(self.spectrometer.center_wavelength))
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.StringVar(value=str(self.spectrometer.temperature_sensor_setpoint))
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
Expand All @@ -103,12 +103,12 @@ def configure_view(self, gui_root: tk.Toplevel) -> None:

row += 1
tk.Label(config_win, text="Wavelength Start (nm)").grid(row=row, column=0, padx=10)
wave_start_var = tk.StringVar(value=str(self.wave_start))
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.StringVar(value=str(self.wave_end))
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
Expand All @@ -119,7 +119,7 @@ def configure_view(self, gui_root: tk.Toplevel) -> None:
'temperature_sensor_setpoint': temperature_sensor_setpoint_var,
'grating': grating_var,
'wave_start': wave_start_var,
'wave_end': wave_end_var
'wave_end': wave_end_var,
}

row += 1
Expand Down
18 changes: 5 additions & 13 deletions src/qt3utils/applications/qt3scan/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,15 +218,7 @@ class QT3ScanHyperSpectralApplicationController:
For HyperSpectral imaging, the daq_controller object will be a spectrometer that
acquires a spectrum at each position in the scan.

Note that the DAQ controller here must implement QT3ScanCounterDAQControllerInterface


The following methods are required for a daq_controller object that
are not currently in the QT3ScanDAQControllerInterface.
We should find a way to declare these methods for any spectrometer
that is used for hyperspectral imaging. This could be another interface,
or perhaps using the concept of Python MixIns. To be discussed....

Note that the DAQ controller here must implement QT3ScanSpectrometerDAQControllerInterface
"""
def __init__(self,
position_controller: QT3ScanPositionControllerInterface,
Expand Down Expand Up @@ -390,9 +382,7 @@ def scan_axis(self, axis, min, max, step_size) -> Tuple[np.ndarray, np.ndarray]:
time.sleep(self.raster_line_pause)
for val in np.arange(min, max, step_size):
self.position_controller.go_to_position(**{axis: val})

measured_spectrum, measured_wavelengths = self.daq_controller.sample_spectrum()
spectrums_in_scan.append(measured_spectrum)

if initial_spectrum_size is None:
initial_spectrum_size = len(measured_spectrum)
Expand All @@ -407,6 +397,8 @@ def scan_axis(self, axis, min, max, step_size) -> Tuple[np.ndarray, np.ndarray]:
raise QT3Error("Inconsistent wavelength array and spectrum size obtained during scan! Check your hardware.")
if np.array_equal(wavelength_array, measured_wavelengths) is False:
raise QT3Error("Inconsistent wavelength array obtained during scan! Check your hardware.")

spectrums_in_scan.append(measured_spectrum)

return np.array(spectrums_in_scan), wavelength_array

Expand Down Expand Up @@ -442,9 +434,9 @@ def set_scan_range(self, xmin: float, xmax: float, ymin: float, ymax: float) ->
def get_completed_scan_range(self) -> Tuple[float, float, float, float]:
"""
Returns a tuple of the scan range that has been completed
:return: _xmin, _xmax, _ymin, current_y
:return: xmin, xmax, ymin, current_y
"""
return self._xmin, self._xmax, self._ymin, self.current_y
return self.xmin, self.xmax, self.ymin, self.current_y

def save_scan(self, afile_name) -> None:
file_type = afile_name.split('.')[-1]
Expand Down
6 changes: 2 additions & 4 deletions src/qt3utils/applications/qt3scan/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,10 +445,8 @@ def _load_controller_from_dict(self, config: dict, a_protocol: Protocol) -> Any:
controller = cls(logger.level)

logger.debug(f"asserting {config['class_name']} of proper type {a_protocol}")
if isinstance(controller, a_protocol) is False:
logger.warning(f"\n\n{config['class_name']} does not pass isinstance check of {a_protocol}!")
logger.warning(f"Proceed with caution. It's likely that qt3scan will fail when trying to call methods of {a_protocol}\n")

assert isinstance(controller, a_protocol)

# configure the controller
# all controllers *should* have a configure method
logger.debug(f"calling {a_protocol} configure method")
Expand Down
22 changes: 11 additions & 11 deletions src/qt3utils/datagenerators/princeton.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,22 @@
import logging
import numpy as np
from time import sleep
from pathlib import Path

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

try:
import clr

lf_root = os.environ['LIGHTFIELD_ROOT']
automation_path = lf_root + '\PrincetonInstruments.LightField.AutomationV4.dll'
addin_path = lf_root + '\AddInViews\PrincetonInstruments.LightFieldViewV4.dll'
support_path = lf_root + '\PrincetonInstruments.LightFieldAddInSupportServices.dll'
lf_root = Path(os.environ['LIGHTFIELD_ROOT'])
automation_path = lf_root / 'PrincetonInstruments.LightField.AutomationV4.dll'
addin_path = lf_root / 'AddInViews' / 'PrincetonInstruments.LightFieldViewV4.dll'
support_path = lf_root / 'PrincetonInstruments.LightFieldAddInSupportServices.dll'

addin_class = clr.AddReference(addin_path)
automation_class = clr.AddReference(automation_path)
support_class = clr.AddReference(support_path)
addin_class = clr.AddReference(str(addin_path))
automation_class = clr.AddReference(str(automation_path))
support_class = clr.AddReference(str(support_path))

import PrincetonInstruments.LightField as lf

Expand All @@ -32,7 +33,6 @@

cmordi marked this conversation as resolved.
Show resolved Hide resolved
class LightfieldApp:
def __init__(self, visible):
cmordi marked this conversation as resolved.
Show resolved Hide resolved

self._addinbase = lf.AddIns.AddInBase()
self._automation = lf.Automation.Automation(visible, List[String]())
self._application = self._automation.LightFieldApplication
Expand Down Expand Up @@ -108,7 +108,7 @@ def acquire(self):
"""
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to know/specify in advance if this will be a single frame or a multiframe return?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you analyze the length of the current image and it is greater than one frame then you will know


acquisition_time = self.get(lf.AddIns.CameraSettings.ShutterTimingExposureTime) #to allow the camera to capture the desired amount of data during the specified exposure time.
num_frames = self.get(lf.AddIns.ExperimentSettings.FrameSettingsFramesToStore)
num_frames = self.get(lf.AddIns.ExperimentSettings.AcquisitionFramesToStore)
self.experiment.Acquire()

sleep(0.001 * acquisition_time * num_frames) #sleep delay that waits for the exposure duration of the camera
Expand Down Expand Up @@ -229,14 +229,14 @@ def num_frames(self):
"""
Returns the number of frames taken during the acquisition.
"""
return self.light.get(lf.AddIns.ExperimentSettings.FrameSettingsFramesToStore)
return self.light.get(lf.AddIns.ExperimentSettings.AcquisitionFramesToStore)

@num_frames.setter
def num_frames(self, num_frames):
"""
Sets the number of frames to be taken during acquisition to number.
"""
return self.light.set(lf.AddIns.ExperimentSettings.FrameSettingsFramesToStore, num_frames)
return self.light.set(lf.AddIns.ExperimentSettings.AcquisitionFramesToStore, num_frames)

@property
def exposure_time(self):
Expand Down