Skip to content

Commit

Permalink
Merge pull request #1 from UW-Quantum-Defect-Lab/feature-clean-ple
Browse files Browse the repository at this point in the history
Adding PLE scanner application
  • Loading branch information
ethan-r-hansen authored Oct 24, 2024
2 parents e9e1a11 + 39a1980 commit 7d46145
Show file tree
Hide file tree
Showing 12 changed files with 2,430 additions and 45 deletions.
31 changes: 31 additions & 0 deletions src/qt3utils/applications/controllers/lockin_controller.py
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)
59 changes: 59 additions & 0 deletions src/qt3utils/applications/controllers/nidaq_rate_counter.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
QT3PLE:
ApplicationController:
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.
39 changes: 39 additions & 0 deletions src/qt3utils/applications/controllers/nidaq_wm_ple.yaml
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:
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
61 changes: 60 additions & 1 deletion src/qt3utils/applications/controllers/nidaqedgecounter.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,11 @@ 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.
"""

def __init__(self, logger_level):
super().__init__(logger_level)

Expand All @@ -153,3 +153,62 @@ def sample_counts(self, num_batches: int) -> np.ndarray:

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



class QT3PleNIDAQEdgeCounterController(QT3ScopeNIDAQEdgeCounterController):
"""
Implements the timed NIDAQ edge counter for PLE scans
"""

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.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
87 changes: 87 additions & 0 deletions src/qt3utils/applications/controllers/wavemeter_controller.py
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)

Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 7d46145

Please sign in to comment.