-
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
Adding PLE scanner application #1
Changes from 24 commits
462c146
100ef17
c498850
6c81ffb
77ff889
1f01a32
c889e96
bb454d1
be61a0c
3597169
410b77c
7e48fac
0762439
6807e54
ffd29d2
78257c6
656ddf6
653a028
f78c11e
8f20f31
de64e7e
874fa24
5331c54
be5db14
15d980d
39a1980
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,31 @@ | ||
import logging | ||
import numpy as np | ||
|
||
import nidaqmx | ||
|
||
class Lockin: | ||
def __init__(self, logger_level) -> None: | ||
self.logger = logging.getLogger(__name__) | ||
self.logger.setLevel(logger_level) | ||
self.device_name = "" | ||
self.signal_channel = "" | ||
self.sample_number = 20 | ||
self.rate = 20.0 | ||
self.timeout = 10 | ||
|
||
def read(self) -> np.ndarray: | ||
with nidaqmx.Task() as task: | ||
task.ai_channels.add_ai_voltage_chan(self.device_name + '/' + self.signal_channel, max_val=10) | ||
task.timing.cfg_samp_clk_timing(rate=self.rate, samps_per_chan=self.sample_number) | ||
c = task.read(number_of_samples_per_channel=self.sample_number, timeout=self.timeout) | ||
return np.array(c) | ||
|
||
def configure(self, config_dict: dict) -> None: | ||
""" | ||
This method is used to configure the data controller. | ||
""" | ||
self.device_name = config_dict.get('daq_name', self.device_name) | ||
self.signal_channel = config_dict.get('signal_channels', self.signal_channel) | ||
self.sample_number = config_dict.get('sample_number', self.sample_number) | ||
self.rate = config_dict.get('rate', self.rate) | ||
self.timeout = config_dict.get('timeout', self.timeout) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
QT3PLE: | ||
ApplicationController: | ||
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. Why is this implemented this way? Other piece of hardware (nidaq and spectrometers) doesn't have an application controller in the yaml file. 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. I think it is probably better to implement YAML files separately for each application since the logic of the generic " Alternatively we could make a "super"-config file which has all hardware and applications in one file, but it'll probably be a bit messy. |
||
import_path : qt3utils.datagenerators.plescanner | ||
class_name : PleScanner | ||
configure : | ||
controller: VoltageController | ||
readers : | ||
daq_reader : DAQCounter | ||
auxiliary_controllers : | ||
daq_writer : RepumpController | ||
|
||
DAQCounter: | ||
import_path : qt3utils.applications.controllers.nidaqedgecounter | ||
class_name : QT3PleNIDAQEdgeCounterController | ||
configure : | ||
daq_name : Dev1 # NI DAQ Device Name | ||
signal_terminal : PFI0 # NI DAQ terminal connected to input digital TTL signal | ||
clock_terminal : # Specifies the digital input terminal to the NI DAQ to use for a clock. If left blank, interprets as None or NULL | ||
clock_rate: 100000 # NI DAQ clock rate in Hz | ||
sample_time_in_seconds : 1 | ||
read_write_timeout : 10 # timeout in seconds for read/write operations | ||
signal_counter : ctr2 # NI DAQ counter to use for counting the input signal, e.g. ctr0, ctr1, ctr2, or ctr3 | ||
|
||
VoltageController: | ||
import_path : qt3utils.nidaq.customcontrollers | ||
class_name : VControl | ||
configure : | ||
daq_name : Dev1 # NI DAQ Device Name | ||
write_channels : ao3 # NI DAQ analog output channels to use for writing position | ||
read_channels : ai3 # NI DAQ analog input channels to use for reading position | ||
min_position: -3 # conversion factor from volts to microns, can also supply a list [8,8,8] or [6,4.2,5] | ||
max_position: 5 # the voltage value that defines the position 0,0,0, can also supply a list [0,0,0] or [5,5,5] | ||
scale_nm_per_volt: 1 # microns | ||
|
||
RepumpController: | ||
import_path : qt3utils.nidaq.customcontrollers | ||
class_name : ArbitraryDAQVoltageController | ||
configure : | ||
daq_name : Dev2 # NI DAQ Device Name | ||
write_channels : ao0 # NI DAQ analog output channels to use for writing position | ||
read_channels : # NI DAQ analog input channels to use for reading position | ||
min_voltage: 0 # conversion factor from volts to microns, can also supply a list [8,8,8] or [6,4.2,5] | ||
max_voltage: 1 # the voltage value that defines the position 0,0,0, can also supply a list [0,0,0] or [5,5,5] | ||
|
||
|
||
# notes | ||
|
||
# clock_rate: | ||
# Specifies the clock rate in Hz. If using an external clock, | ||
# you should specifiy the clock rate here so that the correct counts per | ||
# second are displayed. If using the internal NI DAQ clock (default behavior), | ||
# this value specifies the clock rate to use. Per the NI DAQ manual, | ||
# use a suitable clock rate for the device for best performance, which is an integer | ||
# multiple downsample of the digital sample clock. | ||
|
||
# clock_terminal: | ||
# Specifies the digital input terminal to the NI DAQ to use for a clock. | ||
# If None, the internal NI DAQ clock is used. Otherwise, a string value | ||
# specifies the terminal to use for the clock. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
QT3PLE: | ||
ApplicationController: | ||
import_path : qt3utils.datagenerators.plescanner | ||
class_name : PleScanner | ||
configure : | ||
controller: VoltageController | ||
readers : | ||
daq_readers: | ||
daq_reader : DAQReader | ||
wm_readers: | ||
wm_reader : WavemeterDllController | ||
|
||
DAQReader: | ||
import_path : qt3utils.applications.controllers.lockin_controller | ||
class_name : Lockin | ||
configure : | ||
daq_name : Silicon_DAQ # NI DAQ Device Name | ||
signal_channels : ai21 # NI DAQ analog input channels to use for reading position | ||
sample_number: 20 # Specifies the sampling rate in samples per channel per second | ||
rate: 20.0 #Specifies the number of samples to acquire or generate for each channel in the task | ||
timeout: 20 # Specifies the amount of time in seconds to wait for samples to become available | ||
|
||
VoltageController: | ||
import_path : qt3utils.nidaq.customcontrollers | ||
class_name : VControl | ||
configure : | ||
daq_name : Silicon_DAQ # NI DAQ Device Name | ||
write_channels : ao3 # NI DAQ analog output channels to use for writing voltage | ||
read_channels : ai0 # NI DAQ analog input channels to use for reading voltage | ||
min_position: -10 # conversion factor from volts to microns, can also supply a list [8,8,8] or [6,4.2,5] | ||
max_position: 10 # the voltage value that defines the position 0,0,0, can also supply a list [0,0,0] or [5,5,5] | ||
scale_nm_per_volt: 1 # microns | ||
num_measurements_per_batch: 10 | ||
|
||
WavemeterDllController: | ||
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. I think it would make sense to remove this whole file and have users make their own. Or have users input their own file paths for the wavemeter they are using 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. I will probably remove these extra files as the initial conception of the code was overly general. |
||
import_path : qt3utils.applications.controllers.wavemeter_controller | ||
class_name : WavemeterDllController | ||
configure : | ||
dll_path : C:/Users/Fulab/Downloads/00392-2-06-A_CustomerCD621/00392-2-06-A_Customer CD 621/Programming Interface/x64/CLDevIFace.dll |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -136,10 +136,8 @@ def print_config(self) -> None: | |
print(self.last_config_dict) # we dont' use the logger because we want to be sure this is printed to stdout | ||
|
||
|
||
|
||
class QT3ScanNIDAQEdgeCounterController(QT3ScopeNIDAQEdgeCounterController): | ||
""" | ||
Implements the qt3utils.applications.qt3scan.interface.QT3ScanCounterDAQControllerInterface for a NIDAQ edge counter. | ||
""" | ||
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. Can we add some description of this function back? |
||
|
||
def __init__(self, logger_level): | ||
super().__init__(logger_level) | ||
|
@@ -148,8 +146,73 @@ def __init__(self, logger_level): | |
def clock_rate(self) -> float: | ||
return self.data_generator.clock_rate | ||
|
||
def sample_counts(self, num_batches: int) -> np.ndarray: | ||
def sample_counts(self, num_batches: int, sample_time: float=-1) -> np.ndarray: | ||
if sample_time > 0: | ||
self.data_generator.sample_time = sample_time | ||
return self.data_generator.sample_counts(num_batches) | ||
|
||
def sample_count_rate(self, data_counts: np.ndarray) -> np.ndarray: | ||
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 | ||
|
||
|
||
class QT3PleNIDAQEdgeCounterController(QT3ScopeNIDAQEdgeCounterController): | ||
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. this is good b/c it's in a seperate class |
||
|
||
def __init__(self, logger_level): | ||
super().__init__(logger_level) | ||
|
||
# Change the data generator to the timed input rate counter | ||
self.data_generator = daqsamplers.NiDaqTimedDigitalInputRateCounter() | ||
|
||
# Rewrite the configure function for the timed data generator | ||
def configure(self, config_dict: dict) -> None: | ||
""" | ||
This method is used to configure the data controller. | ||
""" | ||
self.logger.debug("calling configure on the nidaq edge counter data controller") | ||
self.last_config_dict.update(config_dict) | ||
|
||
self.data_generator.daq_name = config_dict.get('daq_name', self.data_generator.daq_name) | ||
self.data_generator.signal_terminal = config_dict.get('signal_terminal', self.data_generator.signal_terminal) | ||
self.data_generator.clock_terminal = config_dict.get('clock_terminal', self.data_generator.clock_terminal) | ||
self.data_generator.clock_rate = config_dict.get('clock_rate', self.data_generator.clock_rate) | ||
self.data_generator.sample_time_in_seconds = config_dict.get('sample_time_in_seconds', self.data_generator.sample_time_in_seconds) | ||
self.data_generator.read_write_timeout = config_dict.get('read_write_timeout', self.data_generator.read_write_timeout) | ||
self.data_generator.signal_counter = config_dict.get('signal_counter', self.data_generator.signal_counter) | ||
|
||
|
||
def sample_counts(self, num_batches: int, sample_time: float=-1) -> np.ndarray: | ||
if sample_time > 0: | ||
self.data_generator.sample_time = sample_time | ||
return self.data_generator.sample_counts(num_batches) | ||
|
||
def sample_count_rate(self, data_counts: np.ndarray) -> np.ndarray: | ||
return self.data_generator.sample_count_rate(data_counts) | ||
|
||
|
||
@property | ||
def clock_rate(self) -> float: | ||
return self.data_generator.clock_rate | ||
|
||
def sample_counts(self, num_batches: int, sample_time: float=-1) -> np.ndarray: | ||
if sample_time > 0: | ||
self.data_generator.sample_time = sample_time | ||
return self.data_generator.sample_counts(num_batches) | ||
|
||
def sample_count_rate(self, data_counts: np.ndarray) -> np.floating: | ||
def sample_count_rate(self, data_counts: np.ndarray) -> np.ndarray: | ||
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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import abc | ||
import ctypes | ||
import logging | ||
|
||
|
||
class WavemeterController(abc.ABC): | ||
""" | ||
Base class for other types of wavemeter controllers to inherit from | ||
""" | ||
def __init__(self, logger_level): | ||
self.logger = logging.getLogger(__name__) | ||
self.logger.setLevel(logger_level) | ||
|
||
@abc.abstractmethod | ||
def open(self): | ||
""" | ||
Override this method to open and initialize wavemeter | ||
""" | ||
pass | ||
|
||
@abc.abstractmethod | ||
def read_wavemeter(self): | ||
""" | ||
Override this method to read the value from the wavemeter | ||
""" | ||
pass | ||
|
||
@abc.abstractmethod | ||
def close_wavemeter(self): | ||
""" | ||
Override this method to close the connection to the wavemeter | ||
""" | ||
pass | ||
|
||
@abc.abstractmethod | ||
def configure(self, config_dict: dict): | ||
""" | ||
Override this method to configure the wavemeter from a dict set via yaml file | ||
""" | ||
pass | ||
|
||
|
||
class WavemeterDllController(WavemeterController): | ||
""" | ||
Class for interfacing with wavemeter hardware | ||
""" | ||
def __init__(self, logger_level, dll_path=""): | ||
super(WavemeterDllController, self).__init__(logger_level) | ||
self.dll_path = dll_path | ||
if not dll_path == "": | ||
self.open(self.dll_path) | ||
self.last_config_dict = {} | ||
|
||
def open(self, dll_path) -> None: | ||
""" | ||
Set the path to the dll used for interfacing with the wavemeter | ||
""" | ||
self._mydll = ctypes.cdll.LoadLibrary(dll_path) | ||
self._mydll.CLGetLambdaReading.restype = ctypes.c_double | ||
self._dh = self._mydll.CLOpenUSBSerialDevice(4) | ||
if self._dh == -1: | ||
raise Exception("Failed to connect to wave meter.") | ||
|
||
def read(self) -> float: | ||
""" | ||
Return the value from the wavemeter via the dll | ||
""" | ||
return self._mydll.CLGetLambdaReading(self._dh) | ||
|
||
def close(self) -> None: | ||
""" | ||
Close the connection to the wavemeter via the dll | ||
""" | ||
ending = self._mydll.CLCloseDevice(self._dh) | ||
if ending == -1: | ||
raise Exception("Failed to properly close connection to wave meter.") | ||
else: | ||
device_handle=None | ||
|
||
def configure(self, config_dict: dict) -> None: | ||
""" | ||
This method is used to configure the data controller. | ||
""" | ||
self.logger.debug("calling configure on the wave meter controller") | ||
self.dll_path = config_dict.get('dll_path', self.dll_path) | ||
self.open(self.dll_path) | ||
|
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.
Is this script specific to a certain lock-in device/model?
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.
I believe this was an addition from @LaneUW at an earlier stage of the code (where we were trying to be universal). I suspect that we should remove this for now and let people implement this on their own (or add support for specific hardware when addressing #4).
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.
If this is general, needs documentation for the params that need to be set so that anyone can hook up any lock-in and use this code. Some testing should be done and documented somewhere to show that it works.
Some guidelines that I found helpful when dealing with any pull request:
When you issue a pull request, be very clear and verbose about the changes you are making. New code must be reviewed by another colleague before it gets merged to master. Your pull request should include things like
I think it would be useful to follow these especially when pulling on the main branch of qdl-utils