Source code for hardware_interfaces.thorlabs_mc2000

from typing import List, Tuple
import logging
import time

# Import essential modules
import serial

# Set up logger
logger = logging.getLogger(__name__)


[docs]class ChopperController: """ Initializes a serial connection with the chopper controller. For completeness, this class contains all commands which are provided in the MC2000B Manual. Args: port (str): COM port to which the controller is connected. Please check in the device manager of your operating system. name (str, optional): Name / Identifier to give to this controller. This is relevant for log statements, especially when there is more than one motor in the setup. Defaults to "Chopper". Attributes: name (str, optional): Name / Identifier to give to this controller. This is relevant for log statements, especially when there is more than one motor in the setup. Defaults to "Chopper". ser (serial.Serial): Pyserial object which initiates the communication with the Counter. blade_type (str): Identifier of the blade that the chopper thinks is installed frequency (str): Frequency of chopper in Hz harmonic_divider (str): Divides the incoming frequency by the which is set via the harmonic divider. phase (str): Chopper phase in degrees. References: | MC2000B-Manual.pdf | https://github.com/AKuederle/thorlabs_chopper_interface """ def __init__(self, port: str, name: str = "Chopper"): # Assigning attributes self.name = name # Connecting the controller with pyserial self.ser = serial.Serial( port, 115200, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, timeout=1, ) logger.info('Port is open."{}" is ready.'.format(self.name)) self.get_blade_type() logger.info("Blade type is {} for {}".format(self.blade_type, self.name)) # self.get_frequency() # logger.info("Frequency is {} Hz for {}".format(self.frequency, self.name)) self.get_harmonic_divider() logger.info( "Harmonic divider is {} for {}".format(self.harmonic_divider, self.name) ) self.get_phase() logger.info("Phase is {}° for {}".format(self.phase, self.name)) self.get_enable() if self.enabled == "1": logger.info("{} is running.".format(self.name)) else: logger.info("{} is not running.".format(self.name))
[docs] def read(self): """ Reads out the controllers answer to a sent command. The byte object is not transformed into a string because problems due to invisible characters arise. It is easy to understands what the controller returns by seeing the command and the value. Returns: bytes: Answer to sent command. Typically consists of command and value. """ readout = self.ser.readline() return readout
[docs] def parse_data(self, command: str, readout: bytes): """ Extracts the information from the bytes that is returned from the chopper. Args: command (str): Command that was sent to controller. readout (bytes): Bytecode that controller returned after sending command. Returns: str: Relevant information """ return readout.decode("utf-8").replace(command + "\r", "").replace("\r> ", "")
[docs] def send_command(self, input_command: str): """ Creates necessary ascii format of the command which will be send to the controller Args: input_command (str): Command which should be send to the controller Returns: str: Command which is send to card. In ascii format. """ # .encode is used to transform command into byte ascii_command = (str(input_command) + "\r").encode() self.ser.write(ascii_command)
[docs] def get_commands(self): """ List the available commands. Returns: bytes: Answer to sent command. Typically consists of command and value. """ command = "?" self.send_command(command) return self.read()
[docs] def get_identification(self): """ Returns the model number and firmware version. Returns: bytes: Answer to sent command. Typically consists of command and value. """ command = "id?" self.send_command(command) self.read()
[docs] def set_frequency(self, value: int): """ Set the desired internal reference frequency. Args: value (int): Frequency in Herz (Hz). Returns: bytes: Answer to sent command. Typically consists of command and value. """ command = "freq={}".format(value) self.send_command(command) output = self.read() self.get_frequency() return output
[docs] def get_frequency(self): """ Returns the internal reference frequency. Returns: bytes: Answer to sent command. Typically consists of command and value. """ command = "freq?" self.send_command(command) self.frequency = self.parse_data(command, self.read()) return self.frequency
[docs] def get_ref_out_frequency(self): """ Returns the reference output frequency. Returns: bytes: Answer to sent command. Typically consists of command and value. """ command = "refoutfreq?" self.send_command(command) return self.read()
[docs] def set_blade_type(self, value: int): """ Set the blade type (value is number indicated in section 8.2. For explanation look in section 6.4). * MC1F2 = 0 * MC1F10 = 1 * MC1F15 = 2 * MC1F30 = 3 * MC1F60 = 4 * MC1F100 = 5 * MC1F10HP = 6 * MC1F2P10 = 7 * MC1F6P10 = 8 * MC1F10A = 9 * MC2F330 = 10 * MC2F47 = 11 * MC2F57B = 12 * MC2F860 = 13 * MC2F5360 = 14 Args: value (int): Number which defines the blade type. Returns: bytes: Answer to sent command. Typically consists of command and value. """ if value > 14 or value < 0: raise ValueError( "Incorrect blade number. Please provide the right one, corresponding to your blade." ) command = "blade={}".format(value) self.send_command(command) output = self.read() self.get_blade_type() return output
[docs] def get_blade_type(self): """ Return the blade type (see section 6.4 and 8.2 for more details). Returns: bytes: Answer to sent command. Typically consists of command and value. """ command = "blade?" self.send_command(command) self.blade_type = self.parse_data(command, self.read()) return self.blade_type
[docs] def set_harmonic_multiplier(self, value: int): """ Set Harmonic Multiplier applied to external reference frequency (1-15). Args: value (int): 1-15. Returns: bytes: Answer to sent command. Typically consists of command and value. """ command = "nharmonic={}".format(value) self.send_command(command) return self.read()
[docs] def get_harmonic_multiplier(self): """ Returns the Harmonic Multiplier. Returns: bytes: Answer to sent command. Typically consists of command and value. """ command = "nharmonic?" self.send_command(command) return self.read()
[docs] def set_harmonic_divider(self, value: int): """ Set the Harmonic Divider applied to external reference frequency (1-15). Args: value (float): 1-15. Returns: bytes: Answer to sent command. Typically consists of command and value. """ command = "dharmonic={}".format(value) self.send_command(command) output = self.read() harmonic_divider = self.get_harmonic_divider() logger.info( "Chopper {} harmonic divider is {}.".format(self.name, harmonic_divider) ) return output
[docs] def get_harmonic_divider(self): """ Returns the Harmonic Divider. Returns: bytes: Answer to sent command. Typically consists of command and value. """ command = "dharmonic?" self.send_command(command) self.harmonic_divider = self.parse_data(command, self.read()) logger.info( "Chopper {} harmonic divider is {}.".format( self.name, self.harmonic_divider ) ) return self.harmonic_divider
[docs] def set_phase(self, value: int): """ Set the Phase adjust (0-360°). Args: value (int): Phase in degrees (0-360°). Returns: bytes: Answer to sent command. Typically consists of command and value. """ command = "phase={}".format(value) self.send_command(command) output = self.read() phase = self.get_phase() logger.info("Chopper {} phase is set to {}.".format(self.name, phase)) return output
[docs] def get_phase(self): """ Returns the Phase adjust. Returns: bytes: Answer to sent command. Typically consists of command and value. """ command = "phase?" self.send_command(command) self.phase = self.parse_data(command, self.read()) logger.info("Chopper {} phase is {}.".format(self.name, self.phase)) return self.phase
[docs] def set_enable(self, value: int): """ Set Enable (0=disabled, 1=enabled). Args: value (int): (0=disabled, 1=enabled). Returns: bytes: Answer to sent command. Typically consists of command and value. """ command = "enable={}".format(value) self.send_command(command) output = self.read() state = self.get_enable() logger.info("Chopper {} was set to {}.".format(self.name, state)) return output
[docs] def get_enable(self): """ Get Enable. Returns: bytes: Answer to sent command. Typically consists of command and value. """ command = "enable?" self.send_command(command) self.enabled = self.parse_data(command, self.read()) logger.info("Chopper {} enable state is {}.".format(self.name, self.enabled)) return self.enabled
[docs] def set_reference(self, value: int): """ Set the reference mode (See blade dependent reference input 8.3) Args: value (int): Blade Dependent Reference Input. Returns: bytes: Answer to sent command. Typically consists of command and value. """ command = "ref={}".format(value) self.send_command(command) return self.read()
[docs] def get_reference(self): """ Returns the reference mode. Returns: bytes: Answer to sent command. Typically consists of command and value. """ command = "ref?" self.send_command(command) return self.read()
[docs] def set_ref_output(self, value: int): """ Set the output reference mode (See blade dependent reference output 8.4) Args: value (int): Blade Dependent Reference Output. Returns: bytes: Answer to sent command. Typically consists of command and value. """ command = "output={}".format(value) self.send_command(command) return self.read()
[docs] def get_ref_output(self): """ Returns the output reference mode. Returns: bytes: Answer to sent command. Typically consists of command and value. """ command = "output?" self.send_command(command) return self.read()
[docs] def set_on_cycle(self, value: int): """ Set On Cycle (1-50%). #! might not work for MC-2000 Args: value (int): Duty cycle 1-50% Returns: bytes: Answer to sent command. Typically consists of command and value. """ command = "oncycle={}".format(value) self.send_command(command) return self.read()
[docs] def get_on_cycle(self): """ Get On Cycle. #! might not work for MC-2000 Returns: bytes: Answer to sent command. Typically consists of command and value. """ command = "oncycle?" self.send_command(command) return self.read()
[docs] def set_display_intensity(self, value: int): """ Set the Display Intensity (1-10). Args: value (int): Display intensity (1-10) Returns: bytes: Answer to sent command. Typically consists of command and value. """ command = "intensity={}".format(value) self.send_command(command) return self.read()
[docs] def get_display_intensity(self): """ Returns the Display Intensity. Returns: bytes: Answer to sent command. Typically consists of command and value. """ command = "intensity?" self.send_command(command) return self.read()
[docs] def get_reference_frequency(self): """ Returns the current supplied external reference frequency. Returns: bytes: Answer to sent command. Typically consists of command and value. """ command = "input?" self.send_command(command) return self.read()
[docs] def restore(self): """ Restore the factory default parameters. Returns: bytes: Answer to sent command. Typically consists of command and value. """ command = "restore" self.send_command(command) return self.read()
[docs] def get_verbose(self): """ Returns the verbose mode. Returns: bytes: Answer to sent command. Typically consists of command and value. """ command = "verbose?" self.send_command(command) return self.read()
[docs] def set_verbose(self, value: int): """ When verbose mode is set to 1, status messages are output on the USB. Args: value (int): Display intensity (1-10) Returns: bytes: Answer to sent command. Typically consists of command and value. """ command = "verbose={}".format(value) self.send_command(command) return self.read()
[docs] def end(self): self.set_enable(0) logger.info("Chopper {} was turned off.".format(self.name)) self.ser.close()
def __enter__(self): return self def __exit__(self, type, value, tb): self.end() # ? should work fine
if __name__ == "__main__": logging.basicConfig(level=logging.INFO) port = "COM6" chopper = ChopperController(port) a = chopper.set_enable(1) print(a) a = chopper.set_enable(0) print(chopper.get_enable()) b = chopper.set_harmonic_divider(1) print(b) c = chopper.set_phase(359) print(c) d = chopper.set_frequency(1000) print(type(d)) time.sleep(5) chopper.end()