Source code for lantz.drivers.coherent.innova

# -*- coding: utf-8 -*-
"""
    lantz.drivers.coherent.innova
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Implements the drivers for Innova 300 Series gas lasers.


    Implementation Notes
    --------------------

    There are currently 3 drivers implemented Innova300C, ArgonInnova300C
    and KryptonInnova300C. The last two only add to the first the
    corresponding wavelength selection.

    Sources::

        - Innova 300C Manual

    :copyright: 2015 by Lantz Authors, see AUTHORS for more details.
    :license: BSD, see LICENSE for more details.

"""

from pyvisa import constants

from lantz import Feat, DictFeat, Action
from lantz.errors import InvalidCommand

from lantz.messagebased import MessageBasedDriver


def make_feat(command, **kwargs):

    def get(self):
        return self.query('PRINT {}'.format(command))

    def set(self, value):
        return self.query('{}={}'.format(command, value))

    if kwargs.pop('readonly', None):
        return Feat(fget=get, **kwargs)
    elif kwargs.pop('writeonly', None):
        return Feat(fset=set, **kwargs)

    return Feat(get, set, **kwargs)


[docs]class Innova300C(MessageBasedDriver): """Innova300 C Series. """ DEFAULTS = {'ASRL': {'write_termination': '\r\n', 'read_termination': '\r\n', 'baud_rate': 1200, 'bytesize': 8, 'parity': constants.Parity.none, 'stop_bits': constants.StopBits.one, 'encoding': 'ascii', }}
[docs] def initialize(self): super().initialize() self.echo_enabled = False
[docs] def query(self, command, *, send_args=(None, None), recv_args=(None, None)): """Send query to the laser and return the answer, after handling possible errors. :param command: command to be sent to the instrument :type command: string """ ans = super().query(command, send_args=send_args, recv_args=recv_args) # TODO: Echo handling if ans == 'Out of Range': raise ValueError() elif ans.startswith('Syntax Error'): raise InvalidCommand() elif ans == 'Laser must be off': raise Exception('Laser must be off') return ans
# General information and communication idn = make_feat('ID', readonly=True, doc='Laser identification, should be I300.', read_once=True) software_rev = make_feat('SOFTWARE', readonly=True, doc='Software revision level in the power supply.', read_once=True) head_software_rev = make_feat('HEAD SOFTWARE', readonly=True, doc='Software revision level in the laser head board.', read_once=True) echo_enabled = make_feat('ECHO', writeonly=True, doc='Echo mode of the serial interface.', values={True: 1, False: 0}) baudrate = Feat(values={110, 300, 1200, 2400, 4800, 9600, 19200}) @baudrate.setter def baudrate(self, value): """RS-232/422 baud rate, the serial connection will be reset after. """ self.query('BAUDRATE={}'.format(value)) #TODO: RESET Connection # Interface analog_relative = make_feat('ANALOG MODE', doc='Analog Interface input mode.', values={True: 1, False: 0}) analog_enabled = make_feat('ANALOGINT', doc='Analog Interface input state.', values={True: 1, False: 0}) current_range = make_feat('CURRENT RANGE', doc='Current corresponding to 5 Volts at the input'\ ' or output lines of the Analog Interface.', units='A', limits=(10, 100, 1)) control_pin_high = make_feat('CONTROL', readonly=True, doc='State of the input pin 10 of the Analog Interface.', values={True: 1, False: 0}) output_pin_high = make_feat('STATUS', doc='State of the output pin 24 and 25 of the Analog Interface.', values={(False, False): 0, (True, False): 1, (False, True): 2, (True, True): 3}) # Diagnostics @Feat() def faults(self): """List of all active faults. """ return self.query('PRINT FAULT').split('&') autofill_delta = make_feat('AUTOFILL DELTA', readonly=True, doc='Tube voltage minus the autofill setting.', units='V') autofill_needed = make_feat('AUTOFILL STATUS', readonly=True, doc='Is the autofill needed (wheter fill is enabled or not)', values={True: 1, False: 0}) remaining_time = make_feat('HRSTILSHUTDOWN', readonly=True, doc='Number of hours remaining before the laser '\ 'will shut down automatically.', units='hour') cathode_current = make_feat('CATHODE CURRENT', readonly=True, doc='Laser cathode current (AC).', units='A') cathode_voltage = make_feat('CATHODE VOLTAGE', readonly=True, doc='Laser cathode voltage (AC).', units='V') time_to_start = make_feat('START', readonly=True, doc='Timer countdown during the start delay cycle.', units='second') @Feat() def is_in_start_delay(self): """Laser is in start delay (tube not ionized) """ return self.query('LASER') == '1' tube_time = make_feat('HOURS', readonly=True, doc='Number of operating hours on the plasma tube.', units='hour') tube_voltage = make_feat('TUBE VOLTAGE', readonly=True, doc='Laser tube voltage.', units='V') water_flow = make_feat('FLOW', readonly=True, doc='Water flow.', units='gallons/minute') water_resistivity = make_feat('WATER RESISTIVITY', readonly=True, doc='Resistivity of the incoming water to the power supply.', units='kohm*cm') water_temperature = make_feat('WATER TEMPERATURE', doc='Temperature of the incoming water to the power supply.') # Other autofill_mode = make_feat('AUTOFILL', doc='Autofill mode.', values={'disabled': 0, 'enabled': 1, 'enabled until next autofill': 2}) laser_enabled = make_feat('LASER', doc='Energize the power supply.', values={True: 2, False: 0}) magnet_current = make_feat('MAGNET CURRENT', readonly=True, doc='Laser magnet current.', units='A') operating_mode = make_feat('MODE', readonly=True, doc='Laser operating mode.', values={'current regulation': 0, 'reduced bandwidth light regulation': 1, 'standard light regulation': 2, 'current regulation, light regulation out of range': 3}) # Etalon etalon_mode = make_feat('EMODE', doc='Etalon mode.', values={'manual': 0, 'modetrack': 1, 'modetune': 2}) etalon_temperature = make_feat('ETALON', readonly=True, doc='Etalon temperature.', units='degC') @Feat(units='degC', limits=(51.5, 54, 0.001)) def etalon_temperature_setpoint(self): """Setpoint for the etalon temperature. """ return self.query('PRINT SET ETALON') @etalon_temperature_setpoint.setter def etalon_temperature_setpoint(self, value): self.query('ETALON={}'.format(value)) # Magnetic field magnetic_field_high = make_feat('FIELD', doc='Magnetic field.', values={True: 1, False: 0}) @Feat(values={True: 1, False: 0}) def magnetic_field_setpoint_high(self): """Setpoint for magnetic field setting. """ return self.query('PRINT SET FIELD') @magnetic_field_setpoint_high.setter def magnetic_field_setpoint_high(self, value): self.query('FIELD={}'.format(value)) # Light and current regulation powertrack_mode_enabled = make_feat('PT', doc='PowerTrack.', values={True: 1, False: 0}) @DictFeat(keys=('A', 'B'), limits=(0, 255)) def powertrack_position(self, key): """Relative position of the PowerTrack solenoids. """ return self.query('PRINT PTDAC{}'.format(key)) @powertrack_position.setter def powertrack_position(self, key, value): self.query('PTDAC{}={}'.format(key, value)) @Action() def recalibrate_powertrack(self): """Recalibrate PowerTrack. This will only execute if PowerTrack is on and light regulation is off """ self.query('PT=2') @Action() def center_powertrack(self): """Center PowerTrack and turn it off. """ self.query('PT=3') current = make_feat('CURRENT', readonly=True, doc='Current regulation mode.', units='A') @Feat(units='A', limits=(0, 50, 0.01)) def current_setpoint(self): """Current setpoint when using the current regulation mode. """ return self.query('PRINT SET CURRENT') @current_setpoint.setter def current_setpoint(self, value): self.query('CURRENT={}'.format(value)) power = make_feat('LIGHT 3', readonly=True, doc='Current power output.', units='A') @Feat(units='W', limits=(0, 50, 0.0001)) def power_setpoint(self): """Setpoint for the light regulation. """ return self.query('PRINT SET LIGHT') @power_setpoint.setter def power_setpoint(self, value): self.query('LIGHT={}'.format(value)) auto_light_cal_enabled = make_feat('AUTOLTCAL', doc='Automatic light regulation calibration flag.', values={True: 1, False: 0}) current_change_limit = make_feat('PCTCHGTILRECAL', doc='Percent tube change before an automatic '\ 'light regulation recalibration becomes '\ 'necessary.', units='', #TODO: % limits=(5, 100, 1))
[docs]class ArgonInnova300C(Innova300C): """Argon Innova 300C. """ wavelength = make_feat('WAVELENGTH', doc='Wavelength for the internal power meter calibration', values={351, 364, 454, 457, 465, 472, 476, 488, 496, 501, 514, 528, 1090, 'MLVS', 'MLUV', 'MLDUV'})
[docs]class KryptonInnova300C(Innova300C): """Krypton Innova 300C. """ wavelength = make_feat('WAVELENGTH', doc='Wavelength for the internal power meter calibration', values={476, 482, 520, 530, 568, 647, 676, 752, 'MLVS', 'MLUV', 'MLVI', 'MLBG', 'MLRD', 'MLIR'})
if __name__ == '__main__': import argparse import lantz.log parser = argparse.ArgumentParser(description='Test Kentech HRI') parser.add_argument('-i', '--interactive', action='store_true', default=False, help='Show interactive GUI') parser.add_argument('-p', '--port', type=str, default='17', help='Serial port to connect to') args = parser.parse_args() lantz.log.log_to_socket(lantz.log.DEBUG) with Innova300C.from_serial_port(args.port) as inst: if args.interactive: from lantz.ui.app import start_test_app start_test_app(inst) else: print(inst.idn) print(inst.software_rev) print(inst.head_software_rev)