Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
29 changes: 25 additions & 4 deletions src/navigate/model/device_startup_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1151,20 +1151,41 @@ def start_lasers(
Trigger class.
"""

analog, digital, modulation = None, None, None

if is_synthetic:
device_type = "SyntheticLaser"

else:
device_type = configuration["configuration"]["microscopes"][microscope_name][
analog = configuration["configuration"]["microscopes"][microscope_name][
"lasers"
][id]["power"]["hardware"]["type"]
][id]["power"]["hardware"].get("type", None)

if device_type == "NI":
digital = configuration["configuration"]["microscopes"][microscope_name][
"lasers"
][id]["onoff"]["hardware"].get("type", None)

device_type = analog

if analog == "NI" or digital == "NI":
if device_connection is not None:
return device_connection
from navigate.model.devices.lasers.ni import LaserNI

return LaserNI(microscope_name, device_connection, configuration, id)
if analog == "NI" and digital == "NI":
modulation = "mixed"
elif analog == "NI":
modulation = "analog"
elif digital == "NI":
modulation = "digital"

return LaserNI(
microscope_name=microscope_name,
device_connection=device_connection,
configuration=configuration,
laser_id=id,
modulation_type=modulation,
)

elif device_type.lower() == "syntheticlaser" or device_type.lower() == "synthetic":
if device_connection is not None:
Expand Down
142 changes: 97 additions & 45 deletions src/navigate/model/devices/lasers/ni.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ class LaserNI(LaserBase):
This class is used to control a laser connected to a National Instruments DAQ.
"""

def __init__(self, microscope_name, device_connection, configuration, laser_id):
def __init__(self, microscope_name, device_connection, configuration,
laser_id, modulation_type="digital"):
"""Initialize the LaserNI class.

Parameters
Expand All @@ -67,13 +68,75 @@ def __init__(self, microscope_name, device_connection, configuration, laser_id):
The device configuration.
laser_id : str
The laser id.
modulation_type : str
The modulation type of the laser - Analog, Digital, or Mixed.
"""
super().__init__(microscope_name, device_connection, configuration, laser_id)

#: str: The modulation type of the laser - Analog, Digital, or Mixed.
self.modulation_type = modulation_type

#: str: Modulation type of the laser - Analog or Digital.
self.on_off_type = None
self.digital_port_type = None

#: float: The minimum digital modulation voltage.
self.laser_min_do = None

#: float: The maximum digital modulation voltage.
self.laser_max_do = None

#: nidaqmx.Task: The laser digital modulation task.
self.laser_do_task = None

#: float: The minimum analog modulation voltage.
self.laser_min_ao = None

#: float: The maximum analog modulation voltage.
self.laser_max_ao = None

#: nidaqmx.Task: The laser analog modulation task.
self.laser_ao_task = None

#: float: Current laser intensity.
self._current_intensity = 0

# Initialize the laser modulation type.
if self.modulation_type == "mixed":
self.initialize_digital_modulation()
self.initialize_analog_modulation()
logger.info(f"{str(self)} initialized with mixed modulation.")

elif self.modulation_type == "analog":
self.initialize_analog_modulation()
logger.info(f"{str(self)} initialized with analog modulation.")

elif self.modulation_type == "digital":
self.initialize_digital_modulation()
logger.info(f"{str(self)} initialized with digital modulation.")

def initialize_analog_modulation(self):
try:
laser_ao_port = self.device_config["power"]["hardware"]["channel"]

#: float: The minimum analog modulation voltage.
self.laser_min_ao = self.device_config["power"]["hardware"]["min"]

#: float: The maximum analog modulation voltage.
self.laser_max_ao = self.device_config["power"]["hardware"]["max"]

#: object: The laser analog modulation task.
self.laser_ao_task = nidaqmx.Task()
self.laser_ao_task.ao_channels.add_ao_voltage_chan(
laser_ao_port, min_val=self.laser_min_ao,
max_val=self.laser_max_ao
)
except DaqError as e:
logger.debug(
f"{str(self)} error:, {e}, {e.error_type}, {e.error_code}")
print(f"{str(self)} error:, {e}, {e.error_type}, {e.error_code}")

# Digital out (if using mixed modulation mode)
def initialize_digital_modulation(self):
"""Initialize the digital modulation of the laser."""
try:
laser_do_port = self.device_config["onoff"]["hardware"]["channel"]

Expand All @@ -87,18 +150,20 @@ def __init__(self, microscope_name, device_connection, configuration, laser_id):
self.laser_do_task = nidaqmx.Task()

if "/ao" in laser_do_port:
# Artificial Digital Modulation via an Analog Port
# Perform the digital modulation with an analog output port.
self.laser_do_task.ao_channels.add_ao_voltage_chan(
laser_do_port, min_val=self.laser_min_do, max_val=self.laser_max_do
laser_do_port, min_val=self.laser_min_do,
max_val=self.laser_max_do
)
self.on_off_type = "analog"
self.digital_port_type = "analog"

else:
# Digital Modulation via a Digital Port
self.laser_do_task.do_channels.add_do_chan(
laser_do_port, line_grouping=LineGrouping.CHAN_FOR_ALL_LINES
laser_do_port,
line_grouping=LineGrouping.CHAN_FOR_ALL_LINES
)
self.on_off_type = "digital"
self.digital_port_type = "digital"
except (KeyError, DaqError) as e:
self.laser_do_task = None
if isinstance(e, DaqError):
Expand All @@ -108,28 +173,6 @@ def __init__(self, microscope_name, device_connection, configuration, laser_id):
print(e.error_code)
print(e.error_type)

#: float: Current laser intensity.
self._current_intensity = 0

# Analog out
try:
laser_ao_port = self.device_config["power"]["hardware"]["channel"]

#: float: The minimum analog modulation voltage.
self.laser_min_ao = self.device_config["power"]["hardware"]["min"]

#: float: The maximum analog modulation voltage.
self.laser_max_ao = self.device_config["power"]["hardware"]["max"]

#: object: The laser analog modulation task.
self.laser_ao_task = nidaqmx.Task()
self.laser_ao_task.ao_channels.add_ao_voltage_chan(
laser_ao_port, min_val=self.laser_min_ao, max_val=self.laser_max_ao
)
except DaqError as e:
logger.debug(f"{str(self)} error:, {e}, {e.error_type}, {e.error_code}")
print(f"{str(self)} error:, {e}, {e.error_type}, {e.error_code}")

def set_power(self, laser_intensity):
"""Sets the laser power.

Expand All @@ -138,6 +181,8 @@ def set_power(self, laser_intensity):
laser_intensity : float
The laser intensity.
"""
if self.laser_ao_task is None:
return
try:
scaled_laser_voltage = (int(laser_intensity) / 100) * self.laser_max_ao
self.laser_ao_task.write(scaled_laser_voltage, auto_start=True)
Expand All @@ -147,34 +192,41 @@ def set_power(self, laser_intensity):

def turn_on(self):
"""Turns on the laser."""
# set ao power
self.set_power(self._current_intensity)

if self.laser_do_task is None:
return
try:
self.set_power(self._current_intensity)
if self.laser_do_task is not None:
if self.on_off_type == "digital":
self.laser_do_task.write(True, auto_start=True)
elif self.on_off_type == "analog":
self.laser_do_task.write(self.laser_max_do, auto_start=True)
if self.digital_port_type == "digital":
self.laser_do_task.write(True, auto_start=True)
elif self.digital_port_type == "analog":
self.laser_do_task.write(self.laser_max_do, auto_start=True)
except DaqError as e:
logger.exception(e)

def turn_off(self):
"""Turns off the laser."""
# set ao power to zero
tmp = self._current_intensity
self.set_power(0)
self._current_intensity = tmp

if self.laser_do_task is None:
return
try:
tmp = self._current_intensity
self.set_power(0)
self._current_intensity = tmp
if self.laser_do_task is not None:
if self.on_off_type == "digital":
self.laser_do_task.write(False, auto_start=True)
elif self.on_off_type == "analog":
self.laser_do_task.write(self.laser_min_do, auto_start=True)
if self.digital_port_type == "digital":
self.laser_do_task.write(False, auto_start=True)
elif self.digital_port_type == "analog":
self.laser_do_task.write(self.laser_min_do, auto_start=True)
except DaqError as e:
logger.exception(e)

def close(self):
"""Close the NI Task before exit."""
try:
self.laser_ao_task.close()
if self.laser_ao_task is not None:
self.laser_ao_task.close()
if self.laser_do_task is not None:
self.laser_do_task.close()
except DaqError as e:
Expand Down
2 changes: 2 additions & 0 deletions src/navigate/model/microscope.py
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,8 @@ def prepare_next_channel(self, update_daq_task_flag=True):
self.lasers[str(self.laser_wavelength[self.current_laser_index])].set_power(
channel["laser_power"]
)
logger.info(f"{self.laser_wavelength[self.current_laser_index]} "
f"nm laser power set to {channel['laser_power']}")
# self.lasers[str(self.laser_wavelength[self.current_laser_index])].turn_on()

# stop daq before writing new waveform
Expand Down
9 changes: 5 additions & 4 deletions test/model/devices/lasers/test_laser_ni.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ def setUp(self) -> None:
device_connection=self.device_connection,
configuration=self.configuration,
laser_id=laser_id,
modulation_type="mixed",
)

def tearDown(self):
Expand All @@ -67,11 +68,11 @@ def test_set_power(self):
assert self.laser._current_intensity == self.current_intensity

def test_turn_on(self):
self.laser.on_off_type = "digital"
self.laser.digital_port_type = "digital"
self.laser.turn_on()
self.laser.laser_do_task.write.assert_called_with(True, auto_start=True)

self.laser.on_off_type = "analog"
self.laser.digital_port_type = "analog"
self.laser.turn_on()
self.laser.laser_do_task.write.assert_called_with(
self.laser.laser_max_do, auto_start=True
Expand All @@ -81,13 +82,13 @@ def test_turn_off(self):
self.current_intensity = random.randint(1, 100)
self.laser._current_intensity = self.current_intensity

self.laser.on_off_type = "digital"
self.laser.digital_port_type = "digital"
self.laser.turn_off()
self.laser.laser_do_task.write.assert_called_with(False, auto_start=True)

assert self.laser._current_intensity == self.current_intensity

self.laser.on_off_type = "analog"
self.laser.digital_port_type = "analog"
self.laser.turn_off()
self.laser.laser_do_task.write.assert_called_with(
self.laser.laser_min_do, auto_start=True
Expand Down
Loading