20"""CoolLED illumination systems.
33_logger = logging.getLogger(__name__)
37 """Connection to the CoolLED controller, wraps base commands."""
52 "Not a CoolLED device, unable to get CSS"
56 """Get the global channel status map."""
59 answer = self.
_serial.readline()
60 if not answer.startswith(b
"CSS"):
62 "answer to 'CSS?' should start with 'CSS'"
63 " but got '%s' instead" % answer.decode
68 """Set status for any number of channels."""
69 assert len(css) % 6 == 0,
"css must be multiple of 6 (6 per channel)"
71 self.
_serial.write(b
"CSS" + css + b
"\n")
72 answer = self.
_serial.readline()
73 if not answer.startswith(b
"CSS"):
75 "answer to 'CSS?' should start with 'CSS'"
76 " but got '%s' instead" % answer.decode
80 """Return list of channel names (names are one character string)."""
85 return list(self.
get_css()[::6].decode())
89 """Wraps the CoolLED connection to control a single channel."""
91 def __init__(self, connection: _CoolLEDConnection, name: str) ->
None:
93 raise ValueError(
"name should be a one character string")
94 self.
_conn = connection
97 def _get_css(self) -> bytes:
98 global_css = self.
_conn.get_css()
102 """Intensity in integer percent [0 100]"""
106 """Intensity in integer percent [0 100]"""
107 percent = str(intensity).zfill(3)
111 """N (On) or F (Off)"""
112 return self.
_get_css()[2:3].decode()
115 """N (On) or F (Off)"""
116 if state
not in [
"N",
"F"]:
117 raise ValueError(
"state needs to be N (on) or F (off)")
119 self.
_conn.set_css(css[0:2] + state.encode() + css[3:])
121 def get_selected_state(self) -> str:
122 "S (Selected) or X (Unselected)" ""
123 return self.
_get_css()[1:2].decode()
126 """X (Unselected) or S (Selected)"""
127 if state
not in [
"X",
"S"]:
128 raise ValueError(
"state must be X (Unselected) or S (Selected)")
130 self.
_conn.set_css(css[0:1] + state.encode() + css[2:])
134 """Individual light devices that compose a CoolLED controller."""
137 self, connection: _CoolLEDConnection, name: str, **kwargs
139 super().__init__(**kwargs)
157 microscope.TriggerType.SOFTWARE, microscope.TriggerMode.BULB
160 def _do_shutdown(self) -> None:
167 self.
_conn.set_selected_state(
"S")
170 self.
_conn.set_switch_state(
"N")
173 self.
_conn.set_switch_state(
"F")
176 self.
_conn.set_selected_state(
"X")
179 selected = self.
_conn.get_selected_state()
180 assert selected
in [
"S",
"X"]
181 return selected ==
"S"
183 def _do_get_power(self) -> float:
184 return self.
_conn.get_intensity() / 100.0
186 def _do_set_power(self, power: float) ->
None:
187 self.
_conn.set_intensity(int(power * 100.0))
190 def trigger_type(self) -> microscope.TriggerType:
191 if self.
_conn.get_selected_state() ==
"S":
194 if self.
_conn.get_switch_state() ==
"N":
195 return microscope.TriggerType.SOFTWARE
197 return microscope.TriggerType.HIGH
202 return microscope.TriggerType.SOFTWARE
204 return microscope.TriggerType.HIGH
207 def trigger_mode(self) -> microscope.TriggerMode:
208 return microscope.TriggerMode.BULB
213 if tmode
is not microscope.TriggerMode.BULB:
215 "the only trigger mode supported is 'bulb'"
217 if ttype
is microscope.TriggerType.SOFTWARE:
218 self.
_conn.set_switch_state(
"N")
220 elif ttype
is microscope.TriggerType.HIGH:
221 self.
_conn.set_switch_state(
"F")
225 "trigger type supported must be 'SOFTWARE' or 'HIGH'"
228 def _do_trigger(self) -> None:
230 "trigger does not make sense in trigger mode bulb, only enable"
235 """CoolLED controller for the individual light devices.
238 port: port name (Windows) or path to port (everything
else) to
239 connect to. For example, `/dev/ttyACM0`, `COM1`,
or
242 The individual channels are named A to H
and depend on the actual
243 device. The pE-300 have three channels named A, B,
and C by
244 increasing order of wavelength of their spectral region. The
245 pE-4000 have four selectable channels named A, B, C,
and D
with
246 channels E-H
for peripheral devices via a pE expansion box.
248 .. code-block:: python
251 controller =
CoolLED(
'/dev/ttyACM0')
252 violet = controller.devices[
'A']
253 blue = controller.devices[
'B']
254 red = controller.devices[
'C']
259 CoolLED controllers are often used
with a control pod which can
260 select/unselect
and turn on/off individual channels. The meaning
261 of these two states are:
263 *
"selected" and "on": channel
is always emitting light. This
is
264 equivalent to being enabled
with `SOFTWARE` trigger type.
266 *
"selected" and "off": channel will emit light
in receipt of a
267 TTL signal. This
is equivalent to being enabled
with `HIGH`
270 *
"unselected" and "off": channel nevers emit light. This
is
271 equivalent to being disabled.
273 *
"unselected" and "on": this
is not possible. If an
"unselected"
274 channel
is turned
"on" it reverts back to
"off".
278 If a channel
is set
with `TriggerType.SOFTWARE` (
"on") it will
279 start emitting light once enabled (
"selected"). Once enabled,
280 even though trigger type
is set to software
and not hardware,
281 if the channel receives a TTL signal it will switch to
282 `TriggerType.HIGH`
and continue to report being set to
283 software. This seems to be an issue
with the CoolLED
284 https://github.com/python-microscope/vendor-issues/issues/9
286 This was developed
with a CoolLED pE-300 ultra but should work
287 with the whole pE-300 series. It should also work
with the
288 pE-4000
and the pE expansion box
with the exception of loading
293 def __init__(self, port: str, **kwargs) ->
None:
294 super().__init__(**kwargs)
298 serial_conn = serial.Serial(
302 bytesize=serial.EIGHTBITS,
303 stopbits=serial.STOPBITS_ONE,
304 parity=serial.PARITY_NONE,
311 for name
in connection.get_channels():
315 def devices(self) -> typing.Dict[str, microscope.abc.Device]:
316 return self._channels
None set_trigger(self, microscope.TriggerType ttype, microscope.TriggerMode tmode)
None set_switch_state(self, str state)
None set_intensity(self, int intensity)
str get_switch_state(self)
None set_selected_state(self, str state)
typing.List[str] get_status(self)
None set_trigger(self, microscope.TriggerType ttype, microscope.TriggerMode tmode)
None set_css(self, bytes css)
typing.List[str] get_channels(self)