Skip to content

SuperK¤

Communicate with a SuperK laser from NKT Photonics.

SuperK (NKT) ¤

SuperK(equipment: Equipment)

              flowchart LR
              msl.equipment_resources.nkt.superk.SuperK[SuperK]
              msl.equipment_resources.nkt.nkt.NKT[NKT]
              msl.equipment.schema.Interface[Interface]

                              msl.equipment_resources.nkt.nkt.NKT --> msl.equipment_resources.nkt.superk.SuperK
                                msl.equipment.schema.Interface --> msl.equipment_resources.nkt.nkt.NKT
                



              click msl.equipment_resources.nkt.superk.SuperK href "" "msl.equipment_resources.nkt.superk.SuperK"
              click msl.equipment_resources.nkt.nkt.NKT href "" "msl.equipment_resources.nkt.nkt.NKT"
              click msl.equipment.schema.Interface href "" "msl.equipment.schema.Interface"
            

Communicate with a SuperK laser from NKT Photonics.

SuperK EXTREME and SuperK FIANIUM are supported.

Regular-expression patterns that are used to select this Resource when connect() is called.

manufacturer=r"^NKT"
model=r"SuperK"

Parameters:

Name Type Description Default
equipment Equipment

An Equipment instance.

required

A Connection instance supports the following properties for the SuperK class plus all the properties of the NKT class.

Connection Properties:

Name Type Description
ensure_interlock_ok bool

Whether to make sure the interlock status is okay after connecting. Default: True

scan_timeout float

The maximum number of seconds to wait for a reply when scanning for the SuperK mainboard module. Default: 5.0

Source code in packages/resources/src/msl/equipment_resources/nkt/superk.py
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
def __init__(self, equipment: Equipment) -> None:
    """Communicate with a SuperK laser from NKT Photonics.

    *SuperK EXTREME* and *SuperK FIANIUM* are supported.

    Regular-expression patterns that are used to select this Resource when
    [connect()][msl.equipment.schema.Equipment.connect] is called.
    ```python
    manufacturer=r"^NKT"
    model=r"SuperK"
    ```

    Args:
        equipment: An [Equipment][] instance.

    A [Connection][msl.equipment.schema.Connection] instance supports the following _properties_
    for the `SuperK` class plus all the _properties_ of the [NKT][msl.equipment_resources.nkt.NKT] class.

    Attributes: Connection Properties:
        ensure_interlock_ok (bool): Whether to make sure the interlock status is okay after connecting.
            _Default: `True`_
        scan_timeout (float): The maximum number of seconds to wait for a reply when scanning for the
            SuperK mainboard module. _Default: `5.0`_
    """
    super().__init__(equipment)
    assert equipment.connection is not None  # noqa: S101

    superk_address = 15
    t = equipment.connection.properties.get("scan_timeout", 5.0)
    self._module: Module = self.scan_modules(start=superk_address, stop=superk_address, timeout=t)[0]
    self._is_fianium: bool = self._module.type == 0x88  # noqa: PLR2004

    settings = _modules.get(self._module.type)
    if settings is None:
        msg = f"SuperK module is not supported: {self._module}"
        raise MSLConnectionError(self, msg)

    self._settings: dict[_Key, _S] = settings

    if equipment.connection.properties.get("ensure_interlock_ok", True):
        self.ensure_interlock_ok()

emission property writable ¤

emission: bool

Turn the emission on (True) or off (False).

equipment property ¤

equipment: Equipment

The Equipment associated with the interface.

is_fianium property ¤

is_fianium: bool

Whether the SuperK laser system is FIANIUM.

lock_front_panel property writable ¤

lock_front_panel: bool

Lock the front panel so that the current or power level cannot be changed.

Not all SuperK lasers support front-panel locking. If the laser does not support this feature, setting this property does nothing.

nim_delay property writable ¤

nim_delay: int

Get/set the NIM trigger delay (in picoseconds).

The range is 0 - 9200 ps with an average step size of approximately 9 ps.

operating_mode property writable ¤

Get/set the operating mode.

output property writable ¤

output: float

Get/set the output level, or modulation setpoint, as a percentage.

The operating mode that the laser is currently in automatically handles whether the output level is for internal power/current or external feedback.

pulse_picker_ratio property writable ¤

pulse_picker_ratio: int

Get/set the pulse-picker ratio.

status property ¤

status: int

Get the mainboard status bytes.

temperature property ¤

temperature: float

Get the temperature of the laser.

timeout property writable ¤

timeout: float | None

The timeout, in seconds, for read_register and write_register operations.

A value <0 will set the timeout to be None (blocking mode).

user_setup property writable ¤

user_setup: UserSetup

Get/set the laser's user setup.

Only valid for the SuperK FIANIUM.

user_text property writable ¤

user_text: str

Get/set the user text.

The text to read/write from/to the laser's firmware. Only ASCII characters are allowed. The maximum number of characters is 20 for SuperK EXTREME (module type 60h) and 240 characters for SuperK FIANIUM (module type 0088h). SuperK EXTREME will also display the text on the front panel.

ExtremeOperatingMode (IntEnum) ¤


              flowchart LR
              msl.equipment_resources.nkt.superk.SuperK.ExtremeOperatingMode[ExtremeOperatingMode]

              

              click msl.equipment_resources.nkt.superk.SuperK.ExtremeOperatingMode href "" "msl.equipment_resources.nkt.superk.SuperK.ExtremeOperatingMode"
            

The operating mode of a SuperK EXTREME system.

Attributes:

Name Type Description
CONSTANT_CURRENT int

0

CONSTANT_POWER int

1

MODULATED_CURRENT int

2

MODULATED_POWER int

3

EXTERNAL_FEEDBACK int

4

FianiumOperatingMode (IntEnum) ¤


              flowchart LR
              msl.equipment_resources.nkt.superk.SuperK.FianiumOperatingMode[FianiumOperatingMode]

              

              click msl.equipment_resources.nkt.superk.SuperK.FianiumOperatingMode href "" "msl.equipment_resources.nkt.superk.SuperK.FianiumOperatingMode"
            

The operating mode of a SuperK FIANIUM system.

Attributes:

Name Type Description
INTERNAL_POWER int

0

EXTERNAL_FEEDBACK int

4

disconnect ¤

disconnect() -> None

Disconnect from the NKT equipment.

Source code in packages/resources/src/msl/equipment_resources/nkt/nkt.py
240
241
242
243
244
245
def disconnect(self) -> None:  # pyright: ignore[reportImplicitOverride]
    """Disconnect from the NKT equipment."""
    if self._connected:
        self._interface.disconnect()
        super().disconnect()
        self._connected = False

ensure_interlock_ok ¤

ensure_interlock_ok() -> None

Make sure that the interlock is okay.

Raises MSLConnectionError if it is not okay and it cannot be reset.

Source code in packages/resources/src/msl/equipment_resources/nkt/superk.py
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
def ensure_interlock_ok(self) -> None:
    """Make sure that the interlock is okay.

    Raises [MSLConnectionError][msl.equipment.interfaces.message_based.MSLConnectionError]
    if it is not okay and it cannot be reset.
    """
    register, dtype = self._settings[_Key.interlock]
    assert dtype is not None  # noqa: S101

    module = self._module.address
    status = self.read_register(module, register, dtype)
    if status & 0x0002:
        return

    if status & 0x0001:  # waiting for an interlock reset
        self.write_register(module, register, value=1, dtype=dtype)
        status = self.read_register(module, register, dtype)
        if status & 0x0002:
            return

    msg = f"Interlock not okay [status code={status}]"
    if status & 0x0100:
        msg += ", is the key in the off position?"
    raise MSLConnectionError(self, msg)

read_register ¤

read_register(module: int, register: int, dtype: None = None) -> bytes
read_register(module: int, register: int, dtype: Literal['string']) -> str
read_register(
    module: int,
    register: int,
    dtype: Literal["i8", "u8", "h8", "i16", "u16", "h16", "i32", "u32", "h32"],
) -> int
read_register(
    module: int, register: int, dtype: Literal["f32", "f64"]
) -> float
read_register(
    module: int,
    register: int,
    dtype: (
        Literal[
            "i8",
            "u8",
            "h8",
            "i16",
            "u16",
            "h16",
            "i32",
            "u32",
            "h32",
            "f32",
            "f64",
            "string",
        ]
        | None
    ) = None,
) -> bytes | str | int | float

Read the register value of the device.

Parameters:

Name Type Description Default
module int

Module address (device ID).

required
register int

Register address.

required
dtype Literal['i8', 'u8', 'h8', 'i16', 'u16', 'h16', 'i32', 'u32', 'h32', 'f32', 'f64', 'string'] | None

The data type to return the value in. If not specified, returns the value as bytes.

None

Returns:

Type Description
bytes | str | int | float

The register value.

Source code in packages/resources/src/msl/equipment_resources/nkt/nkt.py
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
def read_register(
    self,
    module: int,
    register: int,
    dtype: Literal["i8", "u8", "h8", "i16", "u16", "h16", "i32", "u32", "h32", "f32", "f64", "string"]
    | None = None,
) -> bytes | str | int | float:
    """Read the register value of the device.

    Args:
        module: Module address (device ID).
        register: Register address.
        dtype: The data type to return the value in. If not specified, returns the value as bytes.

    Returns:
        The register value.
    """
    r = self._send_telegram(_build_read_telegram(module, register))
    if r.type != _MessageType.DATAGRAM:
        msg = f"NKT device did not respond with a DATAGRAM message type, got {r.type!r}"
        raise MSLConnectionError(self, msg)

    if dtype is None:
        return r.data

    fmt = _data_types.get(dtype.lower())
    if fmt is None:
        dt = ", ".join(_data_types)
        msg = f"Invalid register data type {dtype!r}, must be one of: {dt}"
        raise ValueError(msg)

    if fmt == "string":
        return r.data.decode()

    value: int | float = struct.unpack(fmt, r.data)[0]
    return value

scan_modules ¤

scan_modules(
    *, start: int = 1, stop: int = 160, timeout: float = 0.05
) -> list[Module]

Scan for available modules that can be communicated with.

Parameters:

Name Type Description Default
start int

The address to start the scan at.

1
stop int

The address to stop the scan at.

160
timeout float

The maximum number of seconds to wait for a reply from a potential module.

0.05

Returns:

Type Description
list[Module]

The modules that are available.

Source code in packages/resources/src/msl/equipment_resources/nkt/nkt.py
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
def scan_modules(self, *, start: int = 1, stop: int = 160, timeout: float = 0.05) -> list[Module]:
    """Scan for available modules that can be communicated with.

    Args:
        start: The address to start the scan at.
        stop: The address to stop the scan at.
        timeout: The maximum number of seconds to wait for a reply from a potential module.

    Returns:
        The modules that are available.
    """
    original_timeout = self._interface.timeout
    self._interface.timeout = timeout

    modules: list[Module] = []
    for addr in range(start, stop + 1):
        with contextlib.suppress(MSLTimeoutError):
            r = self._send_telegram(_build_read_telegram(addr, 0x61), check_nack=False, check_busy=False)
            if r.type == _MessageType.DATAGRAM:
                # Section 6.2: Module type could either be 1 or 2 bytes
                if len(r.data) == 1:
                    mod_type, description = r.data[0], ""
                else:
                    # Don't use second byte for K81-1, K82-1 and K83-1 (module type 20h) or K80-1 (module type 21h)
                    mod_type = r.data[0] if r.data[0] in {0x20, 0x21} else int(struct.unpack("<H", r.data[:2])[0])
                    description = r.data[2:].rstrip(b"\x00").decode()

                # Serial number (65h)
                sn = self._send_telegram(_build_read_telegram(addr, 0x65))

                # Firmware version number (64h)
                # The firmware version register returns two parameters:
                #  1) 16-bit firmware version (major and minor version)
                #  2) Version ASCII text string, of up to 64 bytes length, zero terminated.
                fw = self._send_telegram(_build_read_telegram(addr, 0x64), check_nack=False)
                # only use ASCII text
                firmware = fw.data[2:].rstrip(b"\x00").decode() if fw.type == _MessageType.DATAGRAM else ""

                modules.append(
                    Module(
                        address=r.address,
                        type=mod_type,
                        description=description or _module_types.get(mod_type, ""),
                        firmware=firmware,
                        serial=sn.data.decode(),
                    )
                )

    self._interface.timeout = original_timeout
    return modules

write_register ¤

write_register(
    module: int,
    register: int,
    value: bytes | str | float,
    dtype: (
        Literal[
            "i8",
            "u8",
            "h8",
            "i16",
            "u16",
            "h16",
            "i32",
            "u32",
            "h32",
            "f32",
            "f64",
        ]
        | None
    ) = None,
) -> None

Write a value to a register of the device.

Parameters:

Name Type Description Default
module int

Module address (device ID).

required
register int

Register address.

required
value bytes | str | float

The value to write.

required
dtype Literal['i8', 'u8', 'h8', 'i16', 'u16', 'h16', 'i32', 'u32', 'h32', 'f32', 'f64'] | None

The data type to convert value to. Only required if value is not already in bytes or a string.

None
Source code in packages/resources/src/msl/equipment_resources/nkt/nkt.py
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
def write_register(
    self,
    module: int,
    register: int,
    value: bytes | str | float,
    dtype: Literal["i8", "u8", "h8", "i16", "u16", "h16", "i32", "u32", "h32", "f32", "f64"] | None = None,
) -> None:
    """Write a value to a register of the device.

    Args:
        module: Module address (device ID).
        register: Register address.
        value: The value to write.
        dtype: The data type to convert `value` to. Only required if `value` is not already in bytes or a string.
    """
    if isinstance(value, bytes):
        data = value
    elif isinstance(value, str):
        data = value.encode()
    else:
        if dtype is None:
            msg = "Must specify the data type of the register value since the value is not already in bytes"
            raise ValueError(msg)

        fmt = _data_types.get(dtype.lower())
        if fmt is None:
            dt = ", ".join(_data_types)
            msg = f"Invalid register data type {dtype!r}, must be one of: {dt}"
            raise ValueError(msg)

        data = struct.pack(fmt, value)

    _ = self._send_telegram(_build_telegram(module, 5, register, data))  # WRITE=5

UserSetup dataclass ¤

UserSetup(nim_output: bool)

SuperK user setup.

Attributes:

Name Type Description
nim_output bool

Whether the laser's pulse output is a NIM-style output (negative current) or a positive pulse output.