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 61 commits
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
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ dependencies = [
"nipiezojenapy>=1.0.4",
"pulseblaster>=1.0.1",
"PyYAML>=6.0.1",
"pythonnet>=3.0.3"
]

[project.urls]
Expand Down
13 changes: 4 additions & 9 deletions src/qt3utils/applications/controllers/nidaqedgecounter.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ def print_config(self) -> None:


class QT3ScanNIDAQEdgeCounterController(QT3ScopeNIDAQEdgeCounterController):
"""
Implements the qt3utils.applications.qt3scan.interface.QT3ScanCounterDAQControllerInterface for a NIDAQ edge counter.
"""

def __init__(self, logger_level):
super().__init__(logger_level)
Expand All @@ -148,13 +151,5 @@ def clock_rate(self) -> float:
def sample_counts(self, num_batches: int) -> np.ndarray:
return self.data_generator.sample_counts(num_batches)

def sample_count_rate(self, data_counts: np.ndarray) -> np.ndarray:
def sample_count_rate(self, data_counts: np.ndarray) -> np.floating:
return self.data_generator.sample_count_rate(data_counts)

@property
def num_data_samples_per_batch(self) -> int:
return self.data_generator.num_data_samples_per_batch

@num_data_samples_per_batch.setter
def num_data_samples_per_batch(self, value: int):
self.data_generator.num_data_samples_per_batch = value
139 changes: 139 additions & 0 deletions src/qt3utils/applications/controllers/princeton_spectrometer.py
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.
Copy link
Collaborator

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?

Copy link
Collaborator Author

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:

assert isinstance(controller, a_protocol)
. The isinstance checks to see if a class implements all of the methods in a Protocol and I think for properties, it actually runs the method due to some python import reason. But we could try to remove the try/except block and see if it works. If it doesn't work without the try block, then yeah maybe np.nan works...

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
Copy link
Collaborator

Choose a reason for hiding this comment

The 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?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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
25 changes: 25 additions & 0 deletions src/qt3utils/applications/controllers/princeton_spectrometer.yaml
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
Loading