Skip to content

USBTMC¤

Prerequisites¤

Before communicating with equipment that use the USB Test & Measurement Class (USBTMC) protocol, a libusb-compatible driver must be installed and the directory to the libusb library must be available on the PATH environment variable. See here for more details.

Capabilities ¤

Capabilities(data: array[int])

USBTMC device capabilities.

Note

Do not instantiate this class. The device capabilities are automatically determined when the connection is established. You can access these attributes via the capabilities property.

A manufacturer may not strictly follow the rules defined in the USBTMC standard when specifying the relationships between the capability bitmap values. You may change the value of an attribute if you get an error when calling one of the capability methods, such as trigger(), with an error message that states the capability is not supported. For example, if calling trigger() raises an error that states a trigger request is not supported (and you are certain that the device does support a trigger request) you may set tmc.capabilities.accepts_trigger = True, where tmc is the USBTMC instance, to bypass the error.

Attributes:

Name Type Description
data array[int]

The GET_CAPABILITIES response from the USBTMC device.

accepts_indicator_pulse bool

Whether the interface accepts the INDICATOR_PULSE request.

accepts_remote_local bool

Whether the interface accepts REN_CONTROL, GO_TO_LOCAL and LOCAL_LOCKOUT requests.

accepts_service_request bool

Whether the device accepts a service request.

accepts_term_char bool

Whether the device supports ending a Bulk-IN transfer when a byte matches the read_termination character.

accepts_trigger bool

Whether the device accepts the TRIGGER request.

is_488_interface bool

Whether the device understands all mandatory SCPI commands, accepts a service request and is a 488.2 interface. See Appendix 2: IEEE 488.2 compatibility in USBTMC_usb488_subclass_1_00.pdf for more details.

is_listen_only bool

Whether the interface is listen-only.

is_talk_only bool

Whether the interface is talk-only.

Source code in src/msl/equipment/interfaces/usbtmc.py
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
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
def __init__(self, data: array[int]) -> None:
    """USBTMC device capabilities.

    !!! note
        Do not instantiate this class. The device capabilities are automatically determined
        when the connection is established. You can access these attributes via the
        [capabilities][msl.equipment.interfaces.usbtmc.USBTMC.capabilities] property.

        A manufacturer may not strictly follow the rules defined in the USBTMC standard when
        specifying the relationships between the capability bitmap values. You may change
        the value of an attribute if you get an error when calling one of the *capability*
        methods, such as [trigger()][msl.equipment.interfaces.usbtmc.USBTMC.trigger], with
        an error message that states the capability is not supported. For example, if
        calling [trigger()][msl.equipment.interfaces.usbtmc.USBTMC.trigger] raises an error
        that states a trigger request is not supported (and you are certain that the device
        does support a trigger request) you may set `tmc.capabilities.accepts_trigger = True`,
        where `tmc` is the [USBTMC][msl.equipment.interfaces.usbtmc.USBTMC] instance, to bypass
        the error.

    Attributes:
        data (array[int]): The `GET_CAPABILITIES` response from the USBTMC device.
        accepts_indicator_pulse (bool): Whether the interface accepts the `INDICATOR_PULSE` request.
        accepts_remote_local (bool): Whether the interface accepts `REN_CONTROL`, `GO_TO_LOCAL`
            and `LOCAL_LOCKOUT` requests.
        accepts_service_request (bool): Whether the device accepts a service request.
        accepts_term_char (bool): Whether the device supports ending a Bulk-IN transfer when a byte matches the
            [read_termination][msl.equipment.interfaces.message_based.MessageBased.read_termination] character.
        accepts_trigger (bool): Whether the device accepts the `TRIGGER` request.
        is_488_interface (bool): Whether the device understands all mandatory SCPI commands, accepts a service
            request and is a 488.2 interface. See *Appendix 2: IEEE 488.2 compatibility* in
            [USBTMC_usb488_subclass_1_00.pdf](https://www.usb.org/document-library/test-measurement-class-specification){:target="_blank"}
            for more details.
        is_listen_only (bool): Whether the interface is listen-only.
        is_talk_only (bool): Whether the interface is talk-only.
    """
    # USBTMC_1_00.pdf: Section 4.2.1.8, Table 37
    # USBTMC_usb488_subclass_1_00.pdf: Section 4.2.2, Table 8
    self.data: array[int] = data
    status, iface, device, iface_488, device_488 = struct.unpack("B3x2B8x2B8x", data)
    if status != 1:  # unsuccessful response, silently ignore the data that came back
        iface = device = iface_488 = device_488 = 0

    # USBTMC Interface Capabilities
    self.accepts_indicator_pulse: bool = bool(iface & (1 << 2))
    self.is_talk_only: bool = bool(iface & (1 << 1))
    self.is_listen_only: bool = bool(iface & (1 << 0))

    # USBTMC Device Capabilities
    self.accepts_term_char: bool = bool(device & (1 << 0))

    # USB488 Interface Capabilities
    is_488_interface = bool(iface_488 & (1 << 2))
    accepts_remote_local = bool(iface_488 & (1 << 1))
    accepts_interface_trigger = bool(iface_488 & (1 << 0))

    # USB488 Device Capabilities
    understands_scpi = bool(device_488 & (1 << 3))
    is_sr_capable = bool(device_488 & (1 << 2))
    is_rl_capable = bool(device_488 & (1 << 1))
    is_dt_capable = bool(device_488 & (1 << 0))

    # Underneath Table 8 (USBTMC_usb488_subclass_1_00.pdf: Section 4.2.2)
    # there are additional rules, however, not all manufacturer's obey these rules.
    # If either of the bitmap values is True then the instance attribute is considered True.
    self.accepts_trigger: bool = is_dt_capable or accepts_interface_trigger  # rule 1
    self.accepts_remote_local: bool = is_rl_capable or accepts_remote_local  # rule 2
    self.accepts_service_request: bool = is_488_interface or is_sr_capable  # rule 3
    self.is_488_interface: bool = understands_scpi or (is_sr_capable and is_488_interface)  # rule 4

USBTMC (USB) ¤

USBTMC(equipment: Equipment)

Base class for the USBTMC communication protocol.

Parameters:

Name Type Description Default
equipment Equipment

An Equipment instance.

required

A Connection instance supports the same properties as those specified in USB.

Source code in src/msl/equipment/interfaces/usbtmc.py
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def __init__(self, equipment: Equipment) -> None:
    """Base class for the USBTMC communication protocol.

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

    A [Connection][msl.equipment.schema.Connection] instance supports the same _properties_
    as those specified in [USB][msl.equipment.interfaces.usb.USB].
    """
    super().__init__(equipment)
    self._tag_status: int = 1  # bTag for READ_STATUS_BYTE control transfer
    self._msg: _Message = _Message()

    # USBTMC_1_00.pdf: Section 4.2.1.8, Table 36
    self._capabilities: Capabilities = Capabilities(
        self.ctrl_transfer(
            request_type=0xA1,  # Dir=IN, Type=Class, Recipient=Interface
            request=7,  # GET_CAPABILITIES, Table 15
            value=0,
            index=self.bulk_in_endpoint.interface_number,
            data_or_length=0x0018,
        )
    )

bulk_in_endpoint property ¤

bulk_in_endpoint: Endpoint

Information about the Bulk-IN endpoint.

bulk_out_endpoint property ¤

bulk_out_endpoint: Endpoint

Information about the Bulk-OUT endpoint.

capabilities property ¤

capabilities: Capabilities

Returns the Capabilities of the USBTMC device.

device_version property ¤

device_version: int

Returns the device version (release) number.

Corresponds to the bcdDevice field in the Device Descriptor.

encoding property writable ¤

encoding: str

The encoding that is used for read and write operations.

equipment property ¤

equipment: Equipment

The Equipment associated with the interface.

intr_in_endpoint property ¤

intr_in_endpoint: Endpoint | None

Information about the Interrupt-IN endpoint.

intr_out_endpoint property ¤

intr_out_endpoint: Endpoint | None

Information about the Interrupt-OUT endpoint.

max_read_size property writable ¤

max_read_size: int

The maximum number of bytes that can be read.

read_termination property writable ¤

read_termination: bytes | None

The termination character sequence that is used for a read operation.

Reading stops when the equipment stops sending data or the read_termination character sequence is detected. If you set the read_termination to be equal to a variable of type str, it will be encoded as bytes.

rstrip property writable ¤

rstrip: bool

Whether to remove trailing whitespace from read messages.

timeout property writable ¤

timeout: float | None

The timeout, in seconds, for read and write operations.

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

write_termination property writable ¤

write_termination: bytes | None

The termination character sequence that is appended to write messages.

If you set the write_termination to be equal to a variable of type str, it will be encoded as bytes.

CtrlDirection (IntEnum) ¤

The direction of a control transfer.

Attributes:

Name Type Description
IN int

Transfer direction is from device to computer.

OUT int

Transfer direction is from computer to device.

CtrlRecipient (IntEnum) ¤

The recipient of a control transfer.

Attributes:

Name Type Description
DEVICE int

Transfer is for a Device descriptor.

INTERFACE int

Transfer is for an Interface descriptor.

ENDPOINT int

Transfer is for an Endpoint descriptor.

CtrlType (IntEnum) ¤

The type of a control transfer.

Attributes:

Name Type Description
STANDARD int

Standard type.

CLASS int

Class type.

VENDOR int

Vendor type.

build_request_type staticmethod ¤

build_request_type(
    direction: CtrlDirection, type: CtrlType, recipient: CtrlRecipient
) -> int

Build a bmRequestType field for a control request.

Parameters:

Name Type Description Default
direction CtrlDirection

Transfer direction.

required
type CtrlType

Transfer type.

required
recipient CtrlRecipient

Recipient of the transfer.

required

Returns:

Type Description
int

The request_type argument for a ctrl_transfer.

Source code in src/msl/equipment/interfaces/usb.py
471
472
473
474
475
476
477
478
479
480
481
482
483
484
@staticmethod
def build_request_type(direction: USB.CtrlDirection, type: USB.CtrlType, recipient: USB.CtrlRecipient) -> int:  # noqa: A002
    """Build a `bmRequestType` field for a control request.

    Args:
        direction: Transfer direction.
        type: Transfer type.
        recipient: Recipient of the transfer.

    Returns:
        The `request_type` argument for a [ctrl_transfer][msl.equipment.interfaces.usb.USB.ctrl_transfer].
    """
    request: int = usb.util.build_request_type(direction, type, recipient)
    return request

clear_device_buffers ¤

clear_device_buffers() -> None

Clear all input and output buffers associated with the USBTMC device.

Source code in src/msl/equipment/interfaces/usbtmc.py
359
360
361
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
395
396
397
398
def clear_device_buffers(self) -> None:
    """Clear all input and output buffers associated with the USBTMC device."""
    logger.debug("%s clearing USBTMC device buffers ...", self)
    self._byte_buffer.clear()

    # USBTMC_1_00.pdf, Section 4.2.1.6
    _ = self._check_ctrl_in_status(
        self.ctrl_transfer(
            request_type=0xA1,  # Dir=IN, Type=Class, Recipient=Interface
            request=5,  # INITIATE_CLEAR, Table 15
            value=0,
            index=self.bulk_in_endpoint.interface_number,  # Bulk-IN/OUT have the same interface number
            data_or_length=0x0001,
        )
    )

    # USBTMC_1_00.pdf, Section 4.2.1.7
    for i in range(1, 100):
        # Table 33 and 34
        status, clear = self.ctrl_transfer(
            request_type=0xA1,  # Dir=IN, Type=Class, Recipient=Interface
            request=6,  # CHECK_CLEAR_STATUS, Table 15
            value=0,
            index=self.bulk_in_endpoint.interface_number,  # Bulk-IN/OUT have the same interface number
            data_or_length=0x0002,
        )

        # Table 35 describes Host behaviour
        if status == 0x02:  # USBTMC_1_00.pdf, Table 16, STATUS_PENDING  # noqa: PLR2004
            logger.debug("%s clearing USBTMC device buffers PENDING [iteration=%d]", self, i)
            sleep(0.05)
            if clear:  # Table 34, bmClear.D0 = 1
                with contextlib.suppress(OSError):
                    self._device.read(self.bulk_in_endpoint.address, self.bulk_in_endpoint.max_packet_size, 1000)
            continue  # check again if STATUS_PENDING

        # Section 4.1.1 and Table 35, send CLEAR_FEATURE then done
        self.clear_halt(self.bulk_out_endpoint)
        logger.debug("%s clearing USBTMC device buffers done", self)
        return

clear_halt ¤

clear_halt(endpoint: Endpoint) -> None

Clear the halt/stall condition for an endpoint.

Parameters:

Name Type Description Default
endpoint Endpoint

The endpoint to clear.

required
Source code in src/msl/equipment/interfaces/usb.py
496
497
498
499
500
501
502
503
504
505
506
def clear_halt(self, endpoint: Endpoint) -> None:
    """Clear the halt/stall condition for an endpoint.

    Args:
        endpoint: The endpoint to clear.
    """
    logger.debug("%s.clear_halt(0x%02X)", self, endpoint.address)
    try:
        self._device.clear_halt(endpoint.address)
    except usb.core.USBError as e:
        raise MSLConnectionError(self, str(e)) from None

control_ren ¤

control_ren(mode: RENMode | str | int) -> None

Controls the state of the GPIB Remote Enable (REN) line.

Optionally the remote/local state of the device is also controlled.

Parameters:

Name Type Description Default
mode RENMode | str | int

The mode of the REN line and optionally the device remote/local state. Can be an enum member name (case insensitive) or value.

required
Source code in src/msl/equipment/interfaces/usbtmc.py
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
def control_ren(self, mode: RENMode | str | int) -> None:
    """Controls the state of the GPIB Remote Enable (REN) line.

    Optionally the remote/local state of the device is also controlled.

    Args:
        mode: The mode of the REN line and optionally the device remote/local state.
            Can be an enum member name (case insensitive) or value.
    """
    if not self._capabilities.accepts_remote_local:
        msg = "The USBTMC device does not accept a remote-local request"
        raise MSLConnectionError(self, msg)

    mode = to_enum(mode, RENMode, to_upper=True)

    # mimic pyvisa-py
    if mode in {RENMode.ASSERT, RENMode.ASSERT_ADDRESS, RENMode.ASSERT_ADDRESS_LLO}:
        # USBTMC_usb488_subclass_1_00.pdf: Section 4.3.2, Table 15
        _ = self._check_ctrl_in_status(
            self.ctrl_transfer(
                request_type=0xA1,  # Dir=IN, Type=Class, Recipient=Interface
                request=160,  # REN_CONTROL, Table 9
                value=1,  # assert
                index=self.bulk_in_endpoint.interface_number,
                data_or_length=0x0001,
            )
        )

    if mode in {RENMode.ASSERT_LLO, RENMode.ASSERT_ADDRESS_LLO}:
        # USBTMC_usb488_subclass_1_00.pdf: Section 4.3.4, Table 19
        _ = self._check_ctrl_in_status(
            self.ctrl_transfer(
                request_type=0xA1,  # Dir=IN, Type=Class, Recipient=Interface
                request=162,  # LOCAL_LOCKOUT, Table 9
                value=0,  # always 0
                index=self.bulk_in_endpoint.interface_number,
                data_or_length=0x0001,
            )
        )

    if mode in {RENMode.DEASSERT_GTL, RENMode.ADDRESS_GTL}:
        # USBTMC_usb488_subclass_1_00.pdf: Section 4.3.3, Table 17
        _ = self._check_ctrl_in_status(
            self.ctrl_transfer(
                request_type=0xA1,  # Dir=IN, Type=Class, Recipient=Interface
                request=161,  # GO_TO_LOCAL, Table 9
                value=0,  # always 0
                index=self.bulk_in_endpoint.interface_number,
                data_or_length=0x0001,
            )
        )

    if mode in {RENMode.DEASSERT, RENMode.DEASSERT_GTL}:
        # USBTMC_usb488_subclass_1_00.pdf: Section 4.3.2, Table 15
        _ = self._check_ctrl_in_status(
            self.ctrl_transfer(
                request_type=0xA1,  # Dir=IN, Type=Class, Recipient=Interface
                request=160,  # REN_CONTROL, Table 9
                value=0,  # deassert
                index=self.bulk_in_endpoint.interface_number,
                data_or_length=0x0001,
            )
        )

ctrl_transfer ¤

ctrl_transfer(
    request_type: int,
    request: int,
    value: int = ...,
    index: int = ...,
    data_or_length: None = None,
) -> int | array[int]
ctrl_transfer(
    request_type: int,
    request: int,
    value: int = ...,
    index: int = ...,
    data_or_length: array[int] | bytes | bytearray | str = ...,
) -> int
ctrl_transfer(
    request_type: int,
    request: int,
    value: int = ...,
    index: int = ...,
    data_or_length: int = ...,
) -> array[int]
ctrl_transfer(
    request_type: int,
    request: int,
    value: int = 0,
    index: int = 0,
    data_or_length: int | array[int] | bytes | bytearray | str | None = None,
) -> int | array[int]

Perform a control transfer on Endpoint 0.

Parameters:

Name Type Description Default
request_type int

The bmRequestType field of the request. The bitmap value defines the direction (OUT or IN) of the request, the type of request and the designated recipient. See build_request_type.

required
request int

The bRequest field of the request.

required
value int

The wValue field of the request.

0
index int

The wIndex field of the request.

0
data_or_length int | array[int] | bytes | bytearray | str | None

Either the data payload for an OUT request, an array buffer to receive data for an IN request, or the number of bytes to read for an IN request.

None

Returns:

Type Description
int | array[int]

For an OUT request or if data_or_length is an array, the returned value is the number of bytes transferred. For an IN request, the returned value is the data that was read (as an array).

Source code in src/msl/equipment/interfaces/usb.py
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
def ctrl_transfer(
    self,
    request_type: int,
    request: int,
    value: int = 0,
    index: int = 0,
    data_or_length: int | array[int] | bytes | bytearray | str | None = None,
) -> int | array[int]:
    """Perform a control transfer on Endpoint 0.

    Args:
        request_type: The *bmRequestType* field of the request. The bitmap value defines the
            direction (OUT or IN) of the request, the type of request and the designated recipient.
            See [build_request_type][msl.equipment.interfaces.usb.USB.build_request_type].
        request: The *bRequest* field of the request.
        value: The *wValue* field of the request.
        index: The *wIndex* field of the request.
        data_or_length: Either the data payload for an OUT request, an [array][array.array]
            buffer to receive data for an IN request, or the number of bytes to read for an
            IN request.

    Returns:
        For an OUT request or if `data_or_length` is an [array][array.array], the returned
            value is the number of bytes transferred. For an IN request, the returned
            value is the data that was read (as an [array][array.array]).
    """
    # fmt: off
    logger.debug(
        "%s.ctrl_transfer(0x%02X, 0x%02X, 0x%04X, 0x%04X, %s, %d)",
        self, request_type, request, value, index, data_or_length, self._timeout_ms
    )
    # fmt: on

    try:
        out: int | array[int] = self._device.ctrl_transfer(
            bmRequestType=request_type,
            bRequest=request,
            wValue=value,
            wIndex=index,
            data_or_wLength=data_or_length,
            timeout=self._timeout_ms,
        )
    except usb.core.USBTimeoutError:
        raise MSLTimeoutError(self) from None
    except usb.core.USBError as e:
        raise MSLConnectionError(self, str(e)) from None
    else:
        return out

disconnect ¤

disconnect() -> None

Disconnect from the USB device.

Source code in src/msl/equipment/interfaces/usb.py
600
601
602
603
604
605
606
607
608
609
610
611
612
613
def disconnect(self) -> None:  # pyright: ignore[reportImplicitOverride]
    """Disconnect from the USB device."""
    if not hasattr(self, "_device") or self._device is None:
        return None

    if self._detached:
        with contextlib.suppress(usb.core.USBError):
            self._device.attach_kernel_driver(self._interface_number)

    with contextlib.suppress(usb.core.USBError):
        usb.util.dispose_resources(self._device)

    self._device = None
    return super().disconnect()

indicator_pulse ¤

indicator_pulse() -> None

Request to turn on an activity indicator for identification purposes.

If the device supports the request, the device turns on an implementation-dependent activity indicator for a duration between 0.5 and 1 second. The activity indicator then automatically turns off.

Source code in src/msl/equipment/interfaces/usbtmc.py
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
def indicator_pulse(self) -> None:
    """Request to turn on an activity indicator for identification purposes.

    If the device supports the request, the device turns on an implementation-dependent
    activity indicator for a duration between 0.5 and 1 second. The activity indicator
    then automatically turns off.
    """
    if not self._capabilities.accepts_indicator_pulse:
        msg = "The USBTMC device does not accept an indicator-pulse request"
        raise MSLConnectionError(self, msg)

    # USBTMC_1_00.pdf: Section 4.2.1.9, Table 38
    _ = self._check_ctrl_in_status(
        self.ctrl_transfer(
            request_type=0xA1,  # Dir=IN, Type=Class, Recipient=Interface
            request=64,  # INDICATOR_PULSE, Table 15
            value=0,
            index=self.bulk_in_endpoint.interface_number,
            data_or_length=0x0001,
        )
    )

query ¤

query(
    message: bytes | str,
    *,
    delay: float = 0.0,
    decode: Literal[True] = True,
    dtype: None = None,
    fmt: MessageFormat = ...,
    size: int | None = ...
) -> str
query(
    message: bytes | str,
    *,
    delay: float = 0.0,
    decode: Literal[False] = False,
    dtype: None = None,
    fmt: MessageFormat = ...,
    size: int | None = ...
) -> bytes
query(
    message: bytes | str,
    *,
    delay: float = 0.0,
    decode: bool = ...,
    dtype: MessageDataType = ...,
    fmt: MessageFormat = ...,
    size: int | None = ...
) -> NumpyArray1D
query(
    message: bytes | str,
    *,
    delay: float = 0.0,
    decode: bool = True,
    dtype: MessageDataType | None = None,
    fmt: MessageFormat = None,
    size: int | None = None
) -> bytes | str | NumpyArray1D

Convenience method for performing a write followed by a read.

Parameters:

Name Type Description Default
message bytes | str

The message to write to the equipment.

required
delay float

Time delay, in seconds, to wait between the write and read operations.

0.0
decode bool

Whether to decode the returned message (i.e., convert the message to a str) or keep the message as bytes. Ignored if dtype is not None.

True
dtype MessageDataType | None

The data type of the elements in the returned message. Can be any object that numpy dtype supports. For messages that are of scalar type (i.e., a single number) it is more efficient to not specify dtype but to pass the returned message to the int or float class to convert the message to the appropriate numeric type. See MessageDataType for more details.

None
fmt MessageFormat

The format that the returned message data is in. Ignored if dtype is None. See MessageFormat for more details.

None
size int | None

The number of bytes to read. Ignored if the value is None.

None

Returns:

Type Description
bytes | str | NumpyArray1D

The message from the equipment. If dtype is specified, then the message is returned as a numpy ndarray, if decode is True then the message is returned as a str, otherwise the message is returned as bytes.

Source code in src/msl/equipment/interfaces/message_based.py
173
174
175
176
177
178
179
180
181
182
183
184
185
186
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 query(
    self,
    message: bytes | str,
    *,
    delay: float = 0.0,
    decode: bool = True,
    dtype: MessageDataType | None = None,
    fmt: MessageFormat = None,
    size: int | None = None,
) -> bytes | str | NumpyArray1D:
    """Convenience method for performing a [write][msl.equipment.interfaces.message_based.MessageBased.write]
    followed by a [read][msl.equipment.interfaces.message_based.MessageBased.read].

    Args:
        message: The message to write to the equipment.
        delay: Time delay, in seconds, to wait between the _write_ and _read_ operations.
        decode: Whether to decode the returned message (i.e., convert the message to a [str][])
            or keep the message as [bytes][]. Ignored if `dtype` is not `None`.
        dtype: The data type of the elements in the returned message. Can be any object that numpy
            [dtype][numpy.dtype] supports. For messages that are of scalar type (i.e., a single number)
            it is more efficient to not specify `dtype` but to pass the returned message to the
            [int][] or [float][] class to convert the message to the appropriate numeric type.
            See [MessageDataType][msl.equipment._types.MessageDataType] for more details.
        fmt: The format that the returned message data is in. Ignored if `dtype` is `None`.
            See [MessageFormat][msl.equipment._types.MessageFormat] for more details.
        size: The number of bytes to read. Ignored if the value is `None`.

    Returns:
        The message from the equipment. If `dtype` is specified, then the message is
            returned as a numpy [ndarray][numpy.ndarray], if `decode` is `True` then the message
            is returned as a [str][], otherwise the message is returned as [bytes][].
    """  # noqa: D205
    _ = self.write(message)
    if delay > 0:
        time.sleep(delay)
    if dtype:
        return self.read(dtype=dtype, fmt=fmt, size=size)
    return self.read(decode=decode, size=size)

read ¤

read(
    *,
    decode: Literal[True] = True,
    dtype: None = None,
    fmt: MessageFormat = ...,
    size: int | None = ...
) -> str
read(
    *,
    decode: Literal[False] = False,
    dtype: None = None,
    fmt: MessageFormat = ...,
    size: int | None = ...
) -> bytes
read(
    *,
    decode: bool = ...,
    dtype: MessageDataType = ...,
    fmt: MessageFormat = ...,
    size: int | None = ...
) -> NumpyArray1D
read(
    *,
    decode: bool = True,
    dtype: MessageDataType | None = None,
    fmt: MessageFormat = None,
    size: int | None = None
) -> bytes | str | NumpyArray1D

Read a message from the equipment.

This method will block until one of the following conditions is fulfilled:

  1. size bytes have been received — only if size is not None.
  2. the read_termination byte(s) is(are) received — only if read_termination is not None.
  3. a timeout occurs — only if timeout is not None. If a timeout occurs, an MSLTimeoutError is raised.
  4. max_read_size bytes have been received. If the maximum number of bytes have been read, an MSLConnectionError is raised.

Tip

You may also want to set the rstrip value for the class instance.

Parameters:

Name Type Description Default
decode bool

Whether to decode the message (i.e., convert the message to a str) or keep the message as bytes. Ignored if dtype is not None.

True
dtype MessageDataType | None

The data type of the elements in the returned message. Can be any object that numpy dtype supports. For messages that are of scalar type (i.e., a single number) it is more efficient to not specify dtype but to pass the returned message to the int or float class to convert the message to the appropriate numeric type. See MessageDataType for more details.

None
fmt MessageFormat

The format that the returned message data is in. Ignored if dtype is None. See MessageFormat for more details.

None
size int | None

The number of bytes to read. Ignored if the value is None.

None

Returns:

Type Description
bytes | str | NumpyArray1D

The message from the equipment. If dtype is specified, then the message is returned as a numpy ndarray, if decode is True then the message is returned as a str, otherwise the message is returned as bytes.

Source code in src/msl/equipment/interfaces/message_based.py
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
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
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
def read(
    self,
    *,
    decode: bool = True,
    dtype: MessageDataType | None = None,
    fmt: MessageFormat = None,
    size: int | None = None,
) -> bytes | str | NumpyArray1D:
    """Read a message from the equipment.

    This method will block until one of the following conditions is fulfilled:

    1. `size` bytes have been received &mdash; only if `size` is not `None`.
    2. the [read_termination][msl.equipment.interfaces.message_based.MessageBased.read_termination]
       byte(s) is(are) received &mdash; only if
       [read_termination][msl.equipment.interfaces.message_based.MessageBased.read_termination]
       is not `None`.
    3. a timeout occurs &mdash; only if [timeout][msl.equipment.interfaces.message_based.MessageBased.timeout]
       is not `None`. If a timeout occurs, an
       [MSLTimeoutError][msl.equipment.interfaces.message_based.MSLTimeoutError] is raised.
    4. [max_read_size][msl.equipment.interfaces.message_based.MessageBased.max_read_size]
       bytes have been received. If the maximum number of bytes have been read, an
       [MSLConnectionError][msl.equipment.interfaces.message_based.MSLConnectionError] is raised.

    !!! tip
        You may also want to set the [rstrip][msl.equipment.interfaces.message_based.MessageBased.rstrip]
        value for the class instance.

    Args:
        decode: Whether to decode the message (i.e., convert the message to a [str][])
            or keep the message as [bytes][]. Ignored if `dtype` is not `None`.
        dtype: The data type of the elements in the returned message. Can be any object that numpy
            [dtype][numpy.dtype] supports. For messages that are of scalar type (i.e., a single number)
            it is more efficient to not specify `dtype` but to pass the returned message to the
            [int][] or [float][] class to convert the message to the appropriate numeric type.
            See [MessageDataType][msl.equipment._types.MessageDataType] for more details.
        fmt: The format that the returned message data is in. Ignored if `dtype` is `None`.
            See [MessageFormat][msl.equipment._types.MessageFormat] for more details.
        size: The number of bytes to read. Ignored if the value is `None`.

    Returns:
        The message from the equipment. If `dtype` is specified, then the message is returned
            as a numpy [ndarray][numpy.ndarray], if `decode` is `True` then the message
            is returned as a [str][], otherwise the message is returned as [bytes][].
    """
    if size is not None and size > self._max_read_size:
        msg = f"max_read_size is {self._max_read_size} bytes, requesting {size} bytes"
        raise MSLConnectionError(self, msg)

    try:
        message = self._read(size)
    except (serial.SerialTimeoutException, socket.timeout, TimeoutError, USBTimeoutError):
        raise MSLTimeoutError(self) from None
    except Exception as e:  # noqa: BLE001
        msg = f"{e.__class__.__name__}: {e}"
        raise MSLConnectionError(self, msg) from None

    if size is None:
        if dtype:
            logger.debug("%s.read(dtype=%r, fmt=%r) -> %r", self, dtype, fmt, message)
        else:
            logger.debug("%s.read() -> %r", self, message)
    else:
        if len(message) != size:
            msg = f"received {len(message)} bytes, requested {size} bytes"
            raise MSLConnectionError(self, msg)
        logger.debug("%s.read(size=%s) -> %r", self, size, message)

    if self._rstrip:
        message = message.rstrip()

    if dtype:
        return from_bytes(message, fmt=fmt, dtype=dtype)

    if decode:
        return message.decode(encoding=self._encoding)

    return message

reset_device ¤

reset_device() -> None

Perform a USB port reset for the device.

If your program has to call this method, the reset will cause the device state to change (e.g., register values may be reset).

Source code in src/msl/equipment/interfaces/usb.py
625
626
627
628
629
630
631
632
def reset_device(self) -> None:
    """Perform a USB port reset for the device.

    If your program has to call this method, the reset will cause the
    device state to change (e.g., register values may be reset).
    """
    logger.debug("%s.reset_device()", self)
    self._device.reset()

serial_poll ¤

serial_poll() -> int

Read status byte / serial poll.

Returns:

Type Description
int

The status byte. See the table from here for the meaning of each bit in the status byte.

Source code in src/msl/equipment/interfaces/usbtmc.py
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
def serial_poll(self) -> int:
    """Read status byte / serial poll.

    Returns:
        The status byte. See the table from
            [here](https://linux-gpib.sourceforge.io/doc_html/reference-function-ibrsp.html){:target="_blank"}
            for the meaning of each bit in the status byte.
    """
    if not self._capabilities.is_488_interface:
        msg = "The USBTMC device does not accept a serial-poll request"
        raise MSLConnectionError(self, msg)

    self._tag_status += 1
    if self._tag_status > 127:  # noqa: PLR2004
        self._tag_status = 2

    # USBTMC_usb488_subclass_1_00.pdf: Section 4.3.1, Table 11
    _, tag, data = self._check_ctrl_in_status(
        self.ctrl_transfer(
            request_type=0xA1,  # Dir=IN, Type=Class, Recipient=Interface
            request=128,  # READ_STATUS_BYTE, Table 9
            value=self._tag_status,
            index=self.bulk_in_endpoint.interface_number,
            data_or_length=0x0003,
        )
    )

    if self._tag_status != tag:
        msg = f"sent bTag [{self._tag_status}] != received bTag [{tag}]"
        raise MSLConnectionError(self, msg)

    # USBTMC_usb488_subclass_1_00.pdf: Section 4.3.1.1, Table 12
    if self.intr_in_endpoint is None:
        return data

    # USBTMC_usb488_subclass_1_00.pdf: Section 3.4.2, Table 7
    status: int
    notify1, status = self._device.read(self.intr_in_endpoint.address, 2, self._timeout_ms)
    d7_not_1 = not (notify1 & 0x80)
    tag_mismatch = (notify1 & 0x7F) != self._tag_status
    if d7_not_1 or tag_mismatch:
        msg = "Invalid Interrupt-IN response packet"
        if d7_not_1:
            msg += ", bit 7 is not 1"
        if tag_mismatch:
            msg += f", sent bTag [{self._tag_status}] != received bTag [{notify1 & 0x7F}]"
        raise MSLConnectionError(self, msg)

    return status

trigger ¤

trigger() -> None

Trigger device.

Source code in src/msl/equipment/interfaces/usbtmc.py
541
542
543
544
545
546
547
def trigger(self) -> None:
    """Trigger device."""
    if not self._capabilities.accepts_trigger:
        msg = "The USBTMC device does not accept a trigger request"
        raise MSLConnectionError(self, msg)

    _ = super()._write(self._msg.trigger())

write ¤

write(
    message: bytes | str,
    *,
    data: Sequence1D | None = None,
    dtype: MessageDataType = "<f",
    fmt: MessageFormat = "ieee"
) -> int

Write a message to the equipment.

Parameters:

Name Type Description Default
message bytes | str

The message to write to the equipment.

required
data Sequence1D | None

The data to append to message.

None
dtype MessageDataType

The data type to use to convert each element in data to bytes. Ignored if data is None. See MessageDataType for more details.

'<f'
fmt MessageFormat

The format to use to convert data to bytes. Ignored if data is None. See MessageFormat for more details.

'ieee'

Returns:

Type Description
int

The number of bytes written.

Source code in src/msl/equipment/interfaces/message_based.py
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
395
396
397
398
399
400
401
402
403
def write(
    self,
    message: bytes | str,
    *,
    data: Sequence1D | None = None,
    dtype: MessageDataType = "<f",
    fmt: MessageFormat = "ieee",
) -> int:
    """Write a message to the equipment.

    Args:
        message: The message to write to the equipment.
        data: The data to append to `message`.
        dtype: The data type to use to convert each element in `data` to bytes. Ignored
            if `data` is `None`. See [MessageDataType][msl.equipment._types.MessageDataType]
            for more details.
        fmt: The format to use to convert `data` to bytes. Ignored if `data` is `None`.
            See [MessageFormat][msl.equipment._types.MessageFormat] for more details.

    Returns:
        The number of bytes written.
    """
    if not isinstance(message, bytes):
        message = message.encode(encoding=self._encoding)

    if data is not None:
        message += to_bytes(data, fmt=fmt, dtype=dtype)

    if self._write_termination and not message.endswith(self._write_termination):
        message += self._write_termination

    logger.debug("%s.write(%r)", self, message)

    try:
        return self._write(message)
    except (serial.SerialTimeoutException, socket.timeout, TimeoutError, USBTimeoutError):
        raise MSLTimeoutError(self) from None
    except Exception as e:  # noqa: BLE001
        raise MSLConnectionError(self, str(e)) from None