BioImager  3.9.1
A .NET microscopy imaging library. Supports various microscopes by using imported libraries & GUI automation. Supported libraries include PriorĀ® & ZeissĀ® & all devices supported by Micromanager 2.0 and python-microscope.
Loading...
Searching...
No Matches
sapphire.py
1#!/usr/bin/env python3
2
3## Copyright (C) 2020 David Miguel Susano Pinto <carandraug@gmail.com>
4## Copyright (C) 2020 Ian Dobbie <ian.dobbie@bioch.ox.ac.uk>
5## Copyright (C) 2020 Mick Phillips <mick.phillips@gmail.com>
6## Copyright (C) 2020 Tiago Susano Pinto <tiagosusanopinto@gmail.com>
7##
8## This file is part of Microscope.
9##
10## Microscope is free software: you can redistribute it and/or modify
11## it under the terms of the GNU General Public License as published by
12## the Free Software Foundation, either version 3 of the License, or
13## (at your option) any later version.
14##
15## Microscope is distributed in the hope that it will be useful,
16## but WITHOUT ANY WARRANTY; without even the implied warranty of
17## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18## GNU General Public License for more details.
19##
20## You should have received a copy of the GNU General Public License
21## along with Microscope. If not, see <http://www.gnu.org/licenses/>.
22
23import logging
24
25import serial
26
28import microscope.abc
29
30
31_logger = logging.getLogger(__name__)
32
33
38):
39 """Coherent Sapphire laser.
40
41 The Sapphire is a diode-pumped solid-state laser and only supports
42 `TriggerMode.SOFTWARE`.
43
44 """
45
46 laser_status = {
47 b"1": "Start up",
48 b"2": "Warmup",
49 b"3": "Standby",
50 b"4": "Laser on",
51 b"5": "Laser ready",
52 b"6": "Error",
53 }
54
55 def __init__(self, com=None, baud=19200, timeout=0.5, **kwargs):
56 # laser controller must run at 19200 baud, 8+1 bits,
57 # no parity or flow control
58 # timeout is recomended to be over 0.5
59 super().__init__(**kwargs)
60 self.connectionconnection = serial.Serial(
61 port=com,
62 baudrate=baud,
63 timeout=timeout,
64 stopbits=serial.STOPBITS_ONE,
65 bytesize=serial.EIGHTBITS,
66 parity=serial.PARITY_NONE,
67 )
68 # Turning off command prompt
69 self.send(b">=0")
70
71 # The sapphire laser turns on as soon as the key is switched
72 # on. So turn radiation off before we start.
73 self.send(b"L=0")
74
75 # Head ID value is a float point value,
76 # but only the integer part is significant
77 headID = int(float(self.send(b"?hid")))
78 _logger.info("Sapphire laser serial number: [%s]", headID)
79
80 self._max_power_mw = float(self.send(b"?maxlp"))
81 self._min_power = float(self.send(b"?minlp")) / self._max_power_mw
82
84
85 def _write(self, command):
86 count = super()._write(command)
87 # This device always writes backs something. If echo is on,
88 # it's the whole command, otherwise just an empty line. Read
89 # it and throw it away.
90 self._readline()
91 return count
92
93 def send(self, command):
94 """Send command and retrieve response."""
95 self._write_write(command)
96 return self._readline()
97
98 @microscope.abc.SerialDeviceMixin.lock_comms
99 def clearFault(self):
100 self.flush_buffer()
101 return self.get_statusget_status()
102
103 def flush_buffer(self):
104 line = b" "
105 while len(line) > 0:
106 line = self._readline()
107
108 @microscope.abc.SerialDeviceMixin.lock_comms
109 def get_status(self):
110 result = []
111
112 status_code = self.send(b"?sta")
113 result.append(
114 (
115 "Laser status: "
116 + self.laser_status.get(status_code, "Undefined")
117 )
118 )
119
120 for cmd, stat in [
121 (b"?l", "Ligh Emission on?"),
122 (b"?t", "TEC Servo on?"),
123 (b"?k", "Key Switch on?"),
124 (b"?sp", "Target power:"),
125 (b"?p", "Measured power:"),
126 (b"?hh", "Head operating hours:"),
127 ]:
128 result.append(stat + " " + self.send(cmd).decode())
129
130 self._write_write(b"?fl")
131 faults = self._readline()
132 response = self._readline()
133 while response:
134 faults += b" " + response
135 response = self._readline()
136
137 result.append(faults.decode())
138 return result
139
140 @microscope.abc.SerialDeviceMixin.lock_comms
141 def _do_shutdown(self) -> None:
142 # Disable laser.
143 self._write_write(b"l=0")
144 self.flush_buffer()
145
146 # Initialization to do when cockpit connects.
147 @microscope.abc.SerialDeviceMixin.lock_comms
148 def initialize(self):
149 self.flush_buffer()
150
151 # Turn the laser ON. Return True if we succeeded, False otherwise.
152 @microscope.abc.SerialDeviceMixin.lock_comms
153 def _do_enable(self):
154 _logger.info("Turning laser ON.")
155 # Turn on emission.
156 response = self.send(b"l=1")
157 _logger.info("l=1: [%s]", response.decode())
158
159 # Enabling laser might take more than 500ms (default timeout)
160 prevTimeout = self.connectionconnection.timeout
161 self.connectionconnection.timeout = max(1, prevTimeout)
162 isON = self.get_is_onget_is_on()
163 self.connectionconnection.timeout = prevTimeout
164
165 if not isON:
166 # Something went wrong.
167 _logger.error("Failed to turn on. Current status:\r\n")
168 _logger.error(self.get_statusget_status())
169 return isON
170
171 # Turn the laser OFF.
172 @microscope.abc.SerialDeviceMixin.lock_comms
173 def disable(self):
174 _logger.info("Turning laser OFF.")
175 return self._write_write(b"l=0")
176
177 # Return True if the laser is currently able to produce light.
178 @microscope.abc.SerialDeviceMixin.lock_comms
179 def get_is_on(self):
180 return self.send(b"?l") == b"1"
181
182 @microscope.abc.SerialDeviceMixin.lock_comms
183 def _get_power_mw(self):
184 return float(self.send(b"?p"))
185
186 @microscope.abc.SerialDeviceMixin.lock_comms
187 def _set_power_mw(self, mW):
188 mW_str = "%.3f" % mW
189 _logger.info("Setting laser power to %s mW.", mW_str)
190 # using send instead of _write, because
191 # if laser is not on, warning is returned
192 return self.send(b"p=%s" % mW_str.encode())
193
194 def _do_set_power(self, power: float) -> None:
195 # power is already clipped to the [0 1] range but we need to
196 # clip it again since the min power we actually can do is 0.2
197 # and we get an error from the laser if we set it to lower.
198 power = max(self._min_power, power)
199 self._set_power_mw(power * self._max_power_mw)
200
201 def _do_get_power(self) -> float:
202 return self._get_power_mw() / self._max_power_mw
None initialize(self)
Definition: abc.py:339
bool get_is_on(self)
Definition: abc.py:1212
typing.List[str] get_status(self)
Definition: abc.py:1206
int _write(self, bytes command)
Definition: abc.py:1020