forked from M-Labs/artiq
162 lines
4.9 KiB
Python
162 lines
4.9 KiB
Python
import logging
|
|
import ctypes
|
|
import struct
|
|
|
|
|
|
logger = logging.getLogger("lda")
|
|
|
|
|
|
class HidError(Exception):
|
|
pass
|
|
|
|
|
|
class Ldasim:
|
|
"""Lab Brick Digital Attenuator simulation controller.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self._attenuation = None
|
|
|
|
def get_attenuation(self):
|
|
"""Reads last attenuation value set to the simulated device.
|
|
|
|
:return: Returns the attenuation value in dB, or None if it was
|
|
never set.
|
|
:rtype: float
|
|
"""
|
|
|
|
return self._attenuation
|
|
|
|
def set_attenuation(self, attenuation):
|
|
"""Stores the new attenuation value and prints it to console.
|
|
|
|
:param attenuation: The attenuation value in dB.
|
|
:type attenuation: int, float or Fraction
|
|
"""
|
|
|
|
attenuation = round(attenuation*4)/4.
|
|
print("[LDA-sim] setting attenuation to {}".format(attenuation))
|
|
self._attenuation = attenuation
|
|
|
|
|
|
class Lda:
|
|
"""Lab Brick Digital Attenuator controller.
|
|
|
|
This controller depends on the hidapi library.
|
|
|
|
On Linux you should install hidapi-libusb shared library in a directory
|
|
listed in your LD_LIBRARY_PATH or in the conventional places (/usr/lib,
|
|
/lib, /usr/local/lib). This can be done either from hidapi sources
|
|
or by installing the libhidapi-libusb0 binary package on Debian-like OS.
|
|
|
|
On Windows you should put hidapi.dll shared library in the same directory
|
|
as the controller.
|
|
|
|
"""
|
|
_vendor_id = 0x041f
|
|
_product_ids = {
|
|
"LDA-102": 0x1207,
|
|
"LDA-602": 0x1208,
|
|
"LDA-302P-1": 0x120E,
|
|
}
|
|
|
|
def __init__(self, serial=None, product="LDA-102"):
|
|
"""
|
|
:param serial: The serial number.
|
|
:param product: The product identifier string: LDA-102, LDA-602.
|
|
"""
|
|
|
|
from artiq.devices.lda.hidapi import hidapi
|
|
self.hidapi = hidapi
|
|
self.product = product
|
|
if serial is None:
|
|
serial = next(self.enumerate(product))
|
|
self._dev = self.hidapi.hid_open(self._vendor_id,
|
|
self._product_ids[product], serial)
|
|
assert self._dev
|
|
|
|
@classmethod
|
|
def enumerate(cls, product):
|
|
from artiq.devices.lda.hidapi import hidapi
|
|
devs = hidapi.hid_enumerate(cls._vendor_id,
|
|
cls._product_ids[product])
|
|
try:
|
|
dev = devs
|
|
while dev:
|
|
yield dev[0].serial
|
|
dev = dev[0].next
|
|
finally:
|
|
hidapi.hid_free_enumeration(devs)
|
|
|
|
def _check_error(self, ret):
|
|
if ret < 0:
|
|
err = self.hidapi.hid_error(self._dev)
|
|
raise HidError("{}: {}".format(ret, err))
|
|
return ret
|
|
|
|
def write(self, command, length, data=bytes()):
|
|
"""Writes a command to the Lab Brick device.
|
|
|
|
:param command: command ID.
|
|
:param length: number of meaningful bytes in the data array.
|
|
:param data: a byte array containing the payload of the command.
|
|
"""
|
|
|
|
# 0 is report id/padding
|
|
buf = struct.pack("BBB6s", 0, command, length, data)
|
|
res = self._check_error(self.hidapi.hid_write(self._dev, buf,
|
|
len(buf)))
|
|
assert res == len(buf), res
|
|
|
|
def set(self, command, data):
|
|
"""Sends a SET command to the Lab Brick device.
|
|
|
|
:param command: command ID, must have most significant bit set.
|
|
:param data: payload of the command.
|
|
"""
|
|
|
|
assert command & 0x80
|
|
assert data
|
|
self.write(command, len(data), data)
|
|
|
|
def get(self, command, length, timeout=1000):
|
|
"""Sends a GET command to read back some value of the Lab Brick device.
|
|
|
|
:param int command: Command ID, most significant bit must be cleared.
|
|
:param int length: Length of the command, "count" in the datasheet.
|
|
:param int timeout: Timeout of the HID read in ms.
|
|
:return: Returns the value read from the device.
|
|
:rtype: bytes
|
|
"""
|
|
|
|
assert not command & 0x80
|
|
status = None
|
|
self.write(command, length)
|
|
buf = ctypes.create_string_buffer(8)
|
|
while status != command:
|
|
res = self._check_error(self.hidapi.hid_read_timeout(self._dev,
|
|
buf, len(buf), timeout))
|
|
assert res == len(buf), res
|
|
status, length, data = struct.unpack("BB6s", buf.raw)
|
|
data = data[:length]
|
|
logger.info("%s %s %r", command, length, data)
|
|
return data
|
|
|
|
def get_attenuation(self):
|
|
"""Reads attenuation value from Lab Brick device.
|
|
|
|
:return: Returns the attenuation value in dB.
|
|
:rtype: float
|
|
"""
|
|
|
|
return ord(self.get(0x0d, 1))/4.
|
|
|
|
def set_attenuation(self, attenuation):
|
|
"""Sets attenuation value of the Lab Brick device.
|
|
|
|
:param attenuation: Attenuation value in dB.
|
|
:type attenuation: int, float or Fraction
|
|
"""
|
|
|
|
self.set(0x8d, bytes([int(round(attenuation*4))]))
|