Exemplo n.º 1
0
 def _dynamically_add_properties(self):
     '''Add all properties available on driver as Feats'''
     # What about units?
     props = self.properties.keys() if self.level == 'expert' else beginner_controls
     for p in props:
         feat = Feat(fget=create_getter(p),
                     fset=create_setter(p),
                     doc=self.cam.properties.get_description(p),
                     units=property_units.get(p, None),
                     )
         feat.name = p
         attach_dyn_propr(self, p, feat)
Exemplo n.º 2
0
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)
Exemplo n.º 3
0
    def _dynamically_add_properties(self):
        """Add all properties available on driver as Feats"""
        props = self.getFeatureNames(
        ) if self.level == 'expert' else beginner_controls
        for p in props:
            info = self.cam.getFeatureInfo(p)
            range_ = self.cam.getFeatureRange(p)
            limits = range_ if isinstance(tuple, range_) else None
            values = range_ if isinstance(list, range_) else None

            feat = Feat(
                fget=create_getter(p),
                fset=create_setter(p),
                doc=info.description,
                units=info.unit,
                limits=limits,
                values=values,
            )
            feat.name = p
            attach_dyn_propr(self, p, feat)
Exemplo n.º 4
0
def ofeat(command, doc, **kwargs):
    """Build Feat

    :param command: command root (without ?)
    :param doc: docstring to be applied to the feature
    """
    def _get(self):
        response = self.query(command + '?')
        return response

    def _set(self, value):
        self.query('{} {}'.format(command, value))

    return Feat(_get, _set, doc=doc, **kwargs)
Exemplo n.º 5
0
        class Spam(Driver):

            _eggs = None

            eggs = Feat()

            @eggs.setter
            def eggs(self_, value):
                self_._eggs = value

            _eggs2 = None

            @Feat(None)
            def eggs2(self_, value):
                self_._eggs2 = value
Exemplo n.º 6
0
    def deco(func):

        nonlocal command
        if command.endswith('!'):
            command = command[:-1]
            getter = None
        else:
            def getter(self):
                return self.query(command + '?')

        if command.endswith('?'):
            setter = None
        else:
            def setter(self, value):
                return self.send(command + str(value))

        return Feat(getter, setter, doc=func.__doc__, **kwargs)
Exemplo n.º 7
0
class IEEE4882Driver(Driver):
    """Implements mandatory functions for a IEE488.2 device.

    You can use it as a mixin class.
    """
    @Feat(read_once=True)
    def idn(self):
        """Instrument identification.
        """
        return self.parse_query(
            '*IDN?',
            format='{manufacturer:s},{model:s},{serialno:s},{softno:s}')

    @Feat(read_once=True)
    def fitted_options(self):
        """Fitted options.
        """
        return self.query('*OPT?').split(',')

    @Action()
    def reset(self):
        """Set the instrument functions to the factory default power up state.
        """
        self.send('*RST')

    @Action()
    def self_test(self):
        """Performs a complete instrument self-test.

        If test fails, one or more error messages will provide additional information.
        When available, Use SYSTem:ERRor? to read error queue.
        """
        return self.query('*TST?') == '0'

    @Action()
    def wait(self):
        """Inhibit execution of an overlapped command until the execution of
        the preceding operation has been completed.
        """
        self.send('*WAI')

    @Action()
    def trigger(self):
        """Equivalent to Group Execute Trigger.
        """
        self.send('*TRG')

    @Feat()
    def status_byte(self):
        """Status byte, a number between 0-255.

        Decimal sum of the bits in the register.
        Bit #6: Master Summary Status Bit (MSS)
                This bit is set, if one of the bits in STB becomes true
                and the corresponding bit in the SRE is enabled.
        Bit #5: Event Summary Bit (ESB)
                This bit is set, if one of the bits in ESR becomes true
                and the corresponding bit in the ESE is enabled.
        Bit #4: Message Available Bit (MAV)
                This bit is set, if there is a message in the output buffer available.
        """
        return int(self.query('*STB?'))

    @Feat()
    def service_request_enabled(self):
        """Service request enable register.

        Decimal sum of the bits in the register.
        """
        return int(self.query('*SRE?'))

    @service_request_enabled.setter
    def service_request_enabled(self, value):
        return self.query('*SRE {0:d}'.format(value))

    event_status_reg = Feat()

    @event_status_reg.setter
    def event_status_reg(self):
        """Queries the event register for the Standard Event Register group.
        Register is read-only; bits not cleared when read.
        """
        return int(self.query('*ESR?'))

    @Feat()
    def event_status_enabled(self):
        """Enables bits in the enable register for the Standard Event Register group.
        The selected bits are then reported to bit 5 of the Status Byte Register.

        Decimal sum of the bits in the register.
        Bit #7: Enable ESB when Power on or restart
        Bit #5: Enable ESB when a Command Error occur
        Bit #3: Enable ESB when a Device Dependent Error occur
        Bit #0: Enable ESB when Operation complete

        Others are not used.
        """
        return int(self.query('*ESE?'))

    @event_status_enabled.setter
    def event_status_enabled(self, value):
        self.query('*ESE {0:d}', value)

    @Action()
    def clear_status(self):
        """Clears the event registers in all register groups.
         Also clears the error queue.
        """
        self.send('*CLS')

    @Action()
    def wait_operation_complete_bit(self):
        """Returns 1 to the output buffer after all pending commands complete.

        Other commands cannot be executed until this command completes.
        """
        return self.query('*OPC?')

    @Action()
    def set_operation_complete_bit(self):
        """Sets "Operation Complete" (bit 0) in the Standard Event register at
        the completion of the current operation.

        The purpose of this command is to synchronize your application with the instrument.
        Other commands may be executed before Operation Complete bit is set.
        """
        return self.query('*OPC')

    @Feat(values={True: 0, False: 1})
    def poweron_status_clear_enabled(self):
        """Enables or disables clearing of two specific registers at power on:
        - Standard Event enable register
        - Status Byte condition register
        - Questionable Data Register
        - Standard Operation Register
        """
        return self.query('*PSC?')

    @poweron_status_clear_enabled.setter
    def poweron_status_clear_enabled(self, value):
        self.query('*PSC {}'.format(value))

    @Action()
    def recall_state(self, location):
        """Recalls (*RCL) instrument state in specified non-volatile location.

        :param location: non-volatile storage location.
        """
        self.send('*RCL {}'.format(location))

    @Action()
    def save_state(self, location):
        """Saves instrument state in specified non-volatile location.

        Previously stored state in location is overwritten (no error is generated).
        :param location: non-volatile storage location.
        """
        self.send('*SAV'.format(location))
Exemplo n.º 8
0
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',
                        }}


    def initialize(self):
        super().initialize()
        self.echo_enabled = False

    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))
Exemplo n.º 9
0
class SR830(MessageBasedDriver):

    DEFAULTS = {
        'COMMON': {
            'write_termination': '\n',
            'read_termination': '\n',
        }
    }

    @Feat(units='degrees', limits=(-360, 729.99, 0.01))
    def reference_phase_shift(self):
        """Phase shift of the reference.
        """
        return self.query('PHAS?')

    @reference_phase_shift.setter
    def reference_phase_shift(self, value):
        self.send('PHAS{:.2f}'.format(value))

    @Feat(values={True: 1, False: 0})
    def reference_internal(self):
        """Reference source.
        """
        return self.query('FMOD?')

    @reference_internal.setter
    def reference_internal(self, value):
        self.send('FMOD {}'.format(value))

    @Feat(units='Hz', limits=(0.001, 102000, 0.00001))
    def frequency(self):
        """Reference frequency.
        """
        return self.query('FREQ?')

    @frequency.setter
    def frequency(self, value):
        self.send('FREQ{:.5f}'.format(value))

    @Feat(values={'zero_crossing': 0, 'rising_edge': 1})
    def reference_trigger(self):
        """Reference trigger when using the external reference mode.
        """

    @reference_trigger.setter
    def reference_trigger(self, value):
        self.send('RSLP {}'.format(value))

    @Feat(limits=(1, 19999, 1))
    def harmonic(self):
        """Detection harmonic.
        """
        return self.query('HARM?')

    @harmonic.setter
    def harmonic(self, value):
        self.send('HARM {}'.format(value))

    @Feat(units='volt', limits=(0.004, 5., 0.002))
    def sine_output_amplitude(self):
        """Amplitude of the sine output.
        """
        return self.query('SLVL?')

    @sine_output_amplitude.setter
    def sine_output_amplitude(self, value):
        self.send('SLVL{:.2f}'.format(value))

    @Feat(values={'A': 0, 'A-B': 1, 'I1': 2, 'I100': 3})
    def input_configuration(self):
        """Configuration of the Input.
        """
        return self.query('ISRC?')

    @input_configuration.setter
    def input_configuration(self, value):
        self.send('ISRC {}'.format(value))

    @Feat(values={'float': 0, 'ground': 1})
    def input_shield(self):
        """Input shield grounding.
        """
        return self.query('IGND?')

    @input_shield.setter
    def input_shield(self, value):
        self.send('IGND {}'.format(value))

    @Feat(values={'AC': 0, 'DC': 1})
    def input_coupling(self):
        """Input coupling.
        """
        return self.query('ICPL?')

    @input_coupling.setter
    def input_coupling(self, value):
        self.send('ICPL {}'.format(value))

    @Feat(values={
        (False, False): 0,
        (True, False): 1,
        (False, True): 2,
        (True, True): 3
    })
    def input_filter(self):
        """Input line notch filters (1x, 2x).
        """
        return self.query('ILIN?')

    @input_filter.setter
    def input_filter(self, value):
        self.send('ILIN {}'.format(value))

    # GAIN and TIME CONSTANT COMMANDS.

    @Feat(values=SENS)
    def sensitivity(self):
        """Sensitivity.
        """
        return self.query('SENS?')

    @sensitivity.setter
    def sensitivity(self, value):
        self.send('SENS {}'.format(value))

    @Feat(values={'high': 0, 'normal': 1, 'low': 2})
    def reserve_mode(self):
        """Reserve mode.
        """
        return self.query('RMOD?')

    @reserve_mode.setter
    def reserve_mode(self, value):
        self.send('RMOD {}'.format(value))

    @Feat(values=TCONSTANTS)
    def time_constants(self):
        """Time constant.
        """
        return self.query('OFLT?')

    @time_constants.setter
    def time_constants(self, value):
        self.send('OFLT {}'.format(value))

    @Feat(values={6, 12, 18, 24})
    def filter_db_per_oct(self):
        """Time constant.
        """
        return self.query('OFSL?')

    @filter_db_per_oct.setter
    def filter_db_per_oct(self, value):
        self.send('OFSL {}'.format(value))

    @Feat(values={False: 0, True: 1})
    def sync_filter(self):
        """Synchronous filter status.
        """
        return self.query('SYNC?')

    @sync_filter.setter
    def sync_filter(self, value):
        self.send('SYNC {}'.format(value))

    ## DISPLAY and OUTPUT COMMANDS

    @DictFeat(keys={1, 2})
    def display(self, channel):
        """Front panel output source.
        """
        return self.query('DDEF? {}'.format(channel))

    @display.setter
    def display(self, channel, value):
        value, normalization = value
        self.send('DDEF {}, {}, {}'.format(channel, value, normalization))

    @DictFeat(keys={1, 2}, values={'display': 0, 'xy': 1})
    def front_output(self, channel):
        """Front panel output source.
        """
        return self.query('FPOP? {}'.format(channel))

    @front_output.setter
    def front_output(self, channel, value):
        self.send('FPOP {}, {}'.format(channel, value))

    # OEXP

    # AOFF is below.

    ## AUX INPUT and OUTPUT COMMANDS

    @DictFeat(keys={1, 2, 3, 4}, units='volt')
    def analog_input(self, key):
        """Input voltage in the auxiliary analog input.
        """
        self.query('AOUX? {}'.format(key))

    @DictFeat(None,
              keys={1, 2, 3, 4},
              units='volt',
              limits=(-10.5, 10.5, 0.001))
    def analog_output(self, key):
        """Ouput voltage in the auxiliary analog output.
        """
        self.query('AUXV? {}'.format(key))

    @analog_output.setter
    def analog_output(self, key, value):
        self.query('AUXV {}, {}'.format(key, value))

    ## SETUP COMMANDS

    remote = Feat(None, values={True: 0, False: 1})

    @remote.setter
    def remote(self, value):
        """Lock Front panel.
        """
        self.query('OVRM {}'.format(value))

    @Feat(values={True: 1, False: 0})
    def key_click_enabled(self):
        """Key click
        """
        return self.query('KCLK?')

    @key_click_enabled.setter
    def key_click_enabled(self, value):
        self.send('KCLK {}'.format(value))

    @Feat(values={True: 1, False: 0})
    def alarm_enabled(self):
        """Key click
        """
        return self.query('ALRM?')

    @alarm_enabled.setter
    def alarm_enabled(self, value):
        self.send('ALRM {}'.format(value))

    @Action(limits=(1, 9))
    def recall_state(self, location):
        """Recalls instrument state in specified non-volatile location.

        :param location: non-volatile storage location.
        """
        self.send('RSET {}'.format(location))

    @Action()
    def save_state(self, location):
        """Saves instrument state in specified non-volatile location.

        Previously stored state in location is overwritten (no error is generated).
        :param location: non-volatile storage location.
        """
        self.send('SSET'.format(location))

    ## AUTO FUNCTIONS

    def wait_bit1(self):
        pass

    @Action()
    def auto_gain_async(self):
        """Equivalent to press the Auto Gain key in the front panel.
        Might take some time if the time constant is long.
        Does nothing if the constant is greater than 1 second.
        """
        self.send('AGAN')

    @Action()
    def auto_gain(self):
        self.auto_gain_async()
        self.wait_bit1()

    @Action()
    def auto_reserve_async(self):
        """Equivalent to press the Auto Reserve key in the front panel.
        Might take some time if the time constant is long.
        """
        self.send('ARSV')

    @Action()
    def auto_reserve(self):
        self.auto_reserve_async()
        self.wait_bit1()

    @Action()
    def auto_phase_async(self):
        """Equivalent to press the Auto Phase key in the front panel.
        Might take some time if the time constant is long.
        Does nothing if the phase is unstable.
        """
        self.send('ARSV')

    @Action()
    def auto_phase(self):
        self.auto_phase_async()
        self.wait_bit1()

    @Action(values={'x': 1, 'y': 2, 'r': 3})
    def auto_offset_async(self, channel_name):
        """Automatically offset a given channel to zero.
        Is equivalent to press the Auto Offset Key in the front panel.

        :param channel_name: the name of the channel.
        """
        self.send('AOFF {}'.format(channel_name))

    @Action()
    def auto_offset(self):
        self.auto_offset_async()
        self.wait_bit1()

    ## DATA STORAGE COMMANDS

    @Feat(values=SAMPLE_RATES)
    def sample_rate(self):
        """Sample rate.
        """
        return self.query('SRAT?')

    @sample_rate.setter
    def sample_rate(self, value):
        self.send('SRAT {}'.format(value))

    @Feat(values={True: 0, False: 1})
    def single_shot(self):
        """End of buffer mode.

        If loop mode (single_shot = False), make sure to pause data storage
        before reading the data to avoid confusion about which point is the
        most recent.
        """
        return self.query('SEND?')

    @single_shot.setter
    def single_shot(self, value):
        self.send('SEND {}'.format(value))

    @Action()
    def trigger(self):
        """Software trigger.
        """
        self.send('TRIG')

    @Feat()
    def trigger_start_mode(self):
        self.query('TSTR?')

    @trigger_start_mode.setter
    def trigger_start_mode(self, value):
        self.send('TSTR {}'.format(value))

    @Action()
    def start_data_storage(self):
        """Start or resume data storage
        """
        self.send('STRT')

    @Action()
    def pause_data_storage(self):
        """Pause data storage
        """
        self.send('PAUS')

    @Action()
    def reset_data_storage(self):
        """Reset data buffers. The command can be sent at any time -
        any storage in progress, paused or not. will be reset. The command
        will erase the data buffer.
        """
        self.send('REST')

    ## DATA TRANSFER COMMANDS

    @DictFeat(keys={'x', 'y', 'r', 't', 1, 2}, units='volt')
    def analog_value(self, key):
        if key in 'xyrt':
            return self.query('OUTP? {}'.format(key))
        else:
            return self.query('OUTR? {}'.format(key))

    @Action()
    def measure(self, channels):
        d = {
            'x': '1',
            'y': '2',
            'r': '3',
            't': '4',
            '1': '5',
            '2': '6',
            '3': '7',
            '4': '8',
            'f': '9'
        }  #, '': 10, '': 11} TODO: how to deal with these?
        channels = ','.join(d[ch] for ch in channels)
        self.query('SNAP? {}'.format(channels))

    # OAUX See above

    @Feat()
    def buffer_length(self):
        return self.query('SPTS?')

    @Action()
    def read_buffer(self, channel, start=0, length=None, format='A'):
        """Queries points stored in the Channel buffer

        :param channel: Number of the channel (1, 2).
        :param start: Index of the buffer to start.
        :param length: Number of points to read.
                       Defaults to the number of points in the buffer.
        :param format: Transfer format
                      'a': ASCII (slow)
                      'b': IEEE Binary (fast) - NOT IMPLEMENTED
                      'c': Non-IEEE Binary (fastest) - NOT IMPLEMENTED
        """

        cmd = 'TRCA'
        if not length:
            length = self.buffer_length
        self.send('{}? {},{},{}'.format(cmd, channel, start, length))
        if cmd == 'TRCA':
            data = self.recv()
            return np.fromstring(data, sep=',') * ureg.volt
        else:
            raise ValueError(
                '{} transfer format is not implemented'.format(format))
Exemplo n.º 10
0
class _SR844(object):
    def __init__(self, port):
        super().__init__(port)
        super().initialize()  # Automatically open the port
        self.TIMEOUT = 20
        self.sens = np.array([
            1e-7, 3e-7, 1e-6, 3e-6, 1e-5, 3e-5, 1e-4, 3e-4, 1e-3, 3e-3, 1e-2,
            3e-2, 1e-1, 3e-1, 1
        ])  #Units in Volt
        self.timecnst = np.array([
            1e-4, 3e-4, 1e-3, 3e-3, 1e-2, 3e-2, 1e-1, 3e-1, 1e-0, 3e-0, 1, 3,
            1e2, 3e2, 1, 1e3, 3e3
        ])  #Units in seconds
        self.timecnstfilt = np.array([0, 6, 8, 12, 18, 24])  #Units in dB/oct
        self.sample_rates = np.array([
            62.5e-3, 125e-3, 250e-3, 500e-3, 1, 2, 4, 8, 16, 32, 64, 128, 256,
            512, 0
        ])  #Units in Hz

    @Feat()
    def idn(self):
        return self.query('*IDN?')

    @Feat(units='degrees', limits=(-360, 360, 0.01))
    def reference_phase_shift(self):
        """Phase shift of the reference.
        """
        return self.query('PHAS?')

    @reference_phase_shift.setter
    def reference_phase_shift(self, value):
        self.send('PHAS{:.2f}'.format(value))

    @Feat(values={True: 1, False: 0})
    def reference_internal(self):
        """Reference source.
        """
        return self.query('FMOD?')

    @reference_internal.setter
    def reference_internal(self, value):
        self.send('FMOD {}'.format(value))

    @Feat(units='Hz', limits=(5e4, 2e8))
    def frequency(self):
        """Reference frequency.
        """
        return self.query('FREQ?')

    @frequency.setter
    def frequency(self, value):
        self.send('FREQ{:.5f}'.format(value))

    @Feat(values={True: 0, False: 1})
    def harmonic(self):
        """Detection harmonic.
        (detect at F, i=0) or ON, (detect at 2F, i=1).
        """
        return self.query('HARM?')

    @harmonic.setter
    def harmonic(self, value):
        self.send('HARM {}'.format(value))

    # Signal input
    @Feat(values={'High': 0, 'Normal': 1, 'Low': 2})
    def wide_reserve_mode(self):
        return self.query('WRSV ?')

    @wide_reserve_mode.setter
    def wide_reserve_mode(self, value):
        self.send('WRSV {}'.format(value))

    # GAIN and TIME CONSTANT COMMANDS.

    @Feat(values={False: 0, True: 1})
    def rel_mode(self):
        """REL mode.
        """
        return self.query('RMOD?')

    @rel_mode.setter
    def rel_mode(self, value):
        self.send('RMOD {}'.format(value))

    @Feat(units='dB')
    def filter_db_per_oct(self):
        """Time constant.
        """
        return self.timecnstfilt[int(self.query('OFSL?'))]

    @filter_db_per_oct.setter
    def filter_db_per_oct(self, value):
        #if value=0 then no filter is set
        option = np.abs(self.timecnstfilt - value).argmin()
        if (self.timecnstfilt == value).any():
            print('Value %s is not a option pick nearest' % value)
            print('Nearest value is %s' % self.sens[option])
            #self.logger.warning('Value %s is not a option pick nearest' %value)
            #self.logger.warning('Nearest value is %s' %self.sens[option])
        self.send('OFSL %s' % option)

    @Feat(units='V')
    def sensitivity(self):
        return self.sens[int(self.query('SENS ?'))]

    @sensitivity.setter
    def sensitivity(self, value):
        print(value)
        option = np.abs(self.sens - value).argmin()
        if not (self.sens == value).any():
            print('Value %s is not a option pick nearest' % value)
            print('Nearest value is %s' % self.sens[option])
            #self.logger.warning('Value %s is not a option pick nearest' %value)
            #self.logger.warning('Nearest value is %s' %self.sens[option])
        self.send('SENS %s' % option)

    @Feat(units='s')
    def timeconstant(self):
        return self.timecnst[int(self.query('OFLT ?'))]

    @timeconstant.setter
    def timeconstant(self, value):
        option = np.abs(self.timecnst - value).argmin()
        if not (self.timecnst == value).any():
            print('Value %s is not a option pick nearest' % value)
            print('Nearest value is %s' % self.timecnst[option])
            #self.logger.warning('Value %s is not a option pick nearest' %value)
            #self.logger.warning('Nearest value is %s' %self.sens[option])
        self.send('OFLT %s' % option)

    @Feat(values={'High': 0, 'Normal': 1, 'Low': 2})
    def close_reserve_mode(self):
        return self.query('CRSV ?')

    @wide_reserve_mode.setter
    def close_reserve_mode(self, value):
        self.send('CRSV {}'.format(value))

    ## DISPLAY and OUTPUT COMMANDS

    @DictFeat(keys={1, 2}, values=display_output)
    def display(self, channel):
        """Front panel output source.
        """
        return self.query('DDEF? {}'.format(channel))

    @display.setter
    def display(self, channel, value):
        self.send('DDEF {}, {}'.format(channel, value))

    @DictFeat(keys={1, 2}, values={'Display': 0, 'X': 1, 'Y': 1})
    def front_output(self, channel):
        """Front panel output source.
        """
        return int(self.query('FPOP? {}'.format(channel)))

    @front_output.setter
    def front_output(self, channel, value):
        self.send('FPOP {}, {}'.format(channel, value))

    # OEXP

    # AOFF is below.

    ## AUX INPUT and OUTPUT COMMANDS

    @DictFeat(keys={1, 2}, units='volt')
    def analog_input(self, key):
        """Input voltage in the auxiliary analog input.
        """
        self.query('AOUX? {}'.format(key))

    @DictFeat(None, keys={1, 2}, units='volt', limits=(-10.5, 10.5))
    def analog_output(self, key):
        """Ouput voltage in the auxiliary analog output.
        """
        self.query('AUXV? {}'.format(key))

    @analog_output.setter
    def analog_output(self, key, value):
        self.query('AUXV {}, {}'.format(key, value))

    ## SETUP COMMANDS

    remote = Feat(None, values={True: 0, False: 1})

    @remote.setter
    def remote(self, value):
        """Lock Front panel.
        """
        self.query('OVRM {}'.format(value))

    @Feat(values={True: 1, False: 0})
    def key_click_enabled(self):
        """Key click
        """
        return self.query('KCLK?')

    @key_click_enabled.setter
    def key_click_enabled(self, value):
        return self.send('KCLK {}'.format(value))

    @Feat(values={True: 1, False: 0})
    def alarm_enabled(self):
        """Key click
        """
        return self.query('ALRM?')

    @alarm_enabled.setter
    def alarm_enabled(self, value):
        return self.send('ALRM {}'.format(value))

    @Action(limits=(1, 9))
    def recall_state(self, location):
        """Recalls instrument state in specified non-volatile location.

        :param location: non-volatile storage location.
        """
        self.send('RSET {}'.format(location))

    @Action(limits=(1, 9))
    def save_state(self, location):
        """Saves instrument state in specified non-volatile location.

        Previously stored state in location is overwritten (no error is generated).
        :param location: non-volatile storage location.
        """
        self.send('SSET {}'.format(location))

    ## AUTO FUNCTIONS

    def wait_bit1(self):
        pass

    @Action()
    def auto_gain_async(self):
        """Equivalent to press the Auto Gain key in the front panel.
        Might take some time if the time constant is long.
        Does nothing if the constant is greater than 1 second.
        """
        self.send('AGAN')

    @Action()
    def auto_gain(self):
        self.auto_gain_async()
        self.wait_bit1()

    @Action()
    def auto_reserve_async(self):
        """Equivalent to press the Auto Reserve key in the front panel.
        Might take some time if the time constant is long.
        """
        self.send('ARSV')

    @Action()
    def auto_reserve(self):
        self.auto_reserve_async()
        self.wait_bit1()

    @Action()
    def auto_phase_async(self):
        """Equivalent to press the Auto Phase key in the front panel.
        Might take some time if the time constant is long.
        Does nothing if the phase is unstable.
        """
        self.send('APHS')

    @Action()
    def auto_phase(self):
        self.auto_phase_async()
        self.wait_bit1()

    @DictFeat(keys={1, 2}, values={'X': 0, 'Rv': 1, 'RdB': 2, 'y': 0})
    def auto_offset_async(self, channel_name):
        """Automatically offset a given channel to zero.
        Is equivalent to press the Auto Offset Key in the front panel.

        :param channel_name: the name of the channel.
        """
        self.send('AOFF {}'.format(channel_name))

    @Action()
    def auto_offset(self):
        self.auto_offset_async()
        self.wait_bit1()

    ## DATA STORAGE COMMANDS

    @Feat(units='Hz')
    def sample_rate(self):
        """Sample rate.
        """
        return self.query('SRAT?')

    @sample_rate.setter
    def sample_rate(self, value):
        self.send('SRAT {}'.format(value))

    @Feat(values={True: 0, False: 1})
    def single_shot(self):
        """End of buffer mode.

        If loop mode (single_shot = False), make sure to pause data storage
        before reading the data to avoid confusion about which point is the
        most recent.
        """
        return self.query('SEND?')

    @single_shot.setter
    def single_shot(self, value):
        self.send('SEND {}'.format(value))

    @Action()
    def trigger(self):
        """Software trigger.
        """
        self.send('TRIG')

    @Feat(values={True: 0, False: 1})
    def trigger_start_mode(self):
        """The TSTR command sets or queries the Trigger Scan Mode to On (1) or Off (0).
        When Trigger Scan Mode is On (i=1), an external or software trigger starts the
        scan. This mode is only applicable for fixed data sample rates set by SRAT
        0-13."""
        self.query('TSTR?')

    @trigger_start_mode.setter
    def trigger_start_mode(self, value):
        self.send('TSTR {}'.format(value))

    @Action()
    def start_data_storage(self):
        """Start or resume data storage
        """
        self.send('STRT')

    @Action()
    def pause_data_storage(self):
        """Pause data storage
        """
        self.send('PAUS')

    @Action()
    def reset_data_storage(self):
        """Reset data buffers. The command can be sent at any time -
        any storage in progress, paused or not. will be reset. The command
        will erase the data buffer.
        """
        self.send('REST')

    ## DATA TRANSFER COMMANDS

    @DictFeat(keys={'X', 'Y', 'Rv', 'RdB', 'Theta', 1, 2})
    def analog_value(self, key):
        i = {'X': 1, 'Y': 2, 'Rv': 3, 'RdB': 4, 'Theta': 5}
        if key in i.keys():
            return self.query('OUTP? {}'.format(i[key]))
        else:
            return self.query('OUTR? {}'.format(key))

    @Action()
    def measure(self, channels):
        """ The SNAP? command returns the values of up to six parameters at a single
        instant. The SNAP? command requires at least two and at most six parameters
        """
        if 2 <= len(channels) <= 6:
            d = {
                'X': 1,
                'Y': 2,
                'Rv': 3,
                'RdB': 4,
                'Theta': 5,
                'AUX1': 6,
                'AUX2': 7,
                'RefFreq': 8,
                'Ch1': 9,
                'Ch2': 10
            }
            channels = ','.join(d[ch] for ch in channels)
            self.query('SNAP? {}'.format(channels))
        else:
            self.logger.error('Length {} is out of range(2,7)'.format(
                len(channels)))

    # OAUX See above

    @Feat()
    def buffer_length(self):
        return self.query('SPTS?')

    @Action()
    def read_buffer(self, channel, start=0, length=None, format='A'):
        """Queries points stored in the Channel buffer

        :param channel: Number of the channel (1, 2).
        :param start: Index of the buffer to start.
        :param length: Number of points to read.
                       Defaults to the number of points in the buffer.
        :param format: Transfer format
                      'a': ASCII (slow)
                      'b': IEEE Binary (fast) - NOT IMPLEMENTED
                      'c': Non-IEEE Binary (fastest) - NOT IMPLEMENTED
        """

        cmd = 'TRCA'
        if not length:
            length = self.buffer_length
        self.send('{}? {},{},{}'.format(cmd, channel, start, length))
        if cmd == 'TRCA':
            data = self.recv()
            return np.fromstring(data, sep=',') * ureg.volt
        else:
            raise ValueError(
                '{} transfer format is not implemented'.format(format))
Exemplo n.º 11
0
class USB4000(USBDriver):
    """Ocean Optics spectrometer
    """
    def __init__(self, serial_number=None, **kwargs):
        super().__init__(vendor=0x2457,
                         product=0x1022,
                         serial_number=serial_number,
                         **kwargs)

        self._spec_hi = usb_find_desc(self.usb_inf, bEndpointAddress=0x82)
        self._spec_lo = usb_find_desc(self.usb_inf, bEndpointAddress=0x86)

        # initialize spectrometer
        self.initialize()

    def initialize(self):
        """Ends the initialization command to the USB4000 spectrometer
            
        This command initializes certain parameters on the USB4000 and sets internal variables
        based on the USB communication speed. This command is called at object instantiation.
        """
        self.usb_send_ep.write(struct.pack('<B', 0x01))

    integration_time = Feat(units='microsecond', limits=(10, 65535000))

    @integration_time.setter
    def integration_time(self, dt):
        """Sets the integration time to use by the spectrometer in microseconds

        This command sets the integration time that the USB4000 uses when acquiring spectra. The
        value is passed as a 32 bit integer and has a valid range between 10 and 65,535,000 us. 
        """

        cmd = struct.pack('<BI', 0x02, dt)
        self.usb_send_ep.write(cmd)

    @DictFeat(keys=tuple(range(32)))
    def query_config(self, position):
        """returns the value stored in register `reg` of the spectrometer
        """
        cmd = struct.pack('<2B', 0x05, position)
        self.usb_send_ep.write(cmd)
        resp = bytearray(self.usb_recv_ep.read(64, timeout=1000))

        # Check that proper echo is returned
        assert resp[0:2] == bytearray([0x05, position])

        config.update({
            config_regs[x]:
            resp[2:].decode('ascii', errors='ignore').strip('\x00')
        })

        return config

    def reset(self):
        self.usb.reset()

    @Feat(units='degree_celsius')
    def pcb_temperature(self):
        cmd = struct.pack('<B', 0x6C)
        self.usb_send_ep.write(cmd)
        resp = bytearray(self.usb_recv_ep.read(3, timeout=1000))

        # Check that proper echo is returned
        assert resp[0:1] == bytearray([0x08])

        t = struct.unpack('<h', resp[1:3])[0]
        return t * 0.003906

    @Feat(read_once=True)
    def firmware_version(self):
        cmd = struct.pack('<2B', 0x6B, 0x04)
        self.usb_send_ep.write(cmd)

        resp = bytearray(self.usb_recv_ep.read(3, timeout=200))
        log.debug('got {:s}'.format(repr(resp)))

        assert resp[0:1] == bytearray([0x04
                                       ])  # Check that proper echo is returned

        vers = struct.unpack('>H', resp[1:3])[0]
        log.info('firmware is {:d}'.format(vers))
        return vers

    trigger = Feat(values={
        'Freerun': 0,
        'Software': 1,
        'ExternalSync': 2,
        'ExternalHard': 3
    })

    @trigger.setter
    def trigger(self, mode):
        cmd = struct.pack('<BH', 0x0A, mode)
        self.usb_send_ep.write(cmd)

    def request_spectra(self):
        cmd = struct.pack('<B', 0x09)
        self.log_debug('Requesting spectra')
        self.usb_send_ep.write(cmd)

        data = np.zeros(shape=(3840, ), dtype='<u2')

        try:
            data_lo = self._spec_lo.read(512 * 4, timeout=100)
            data_hi = self._spec_hi.read(512 * 11, timeout=100)

            data_sync = self._spec_hi.read(1, timeout=100)

            assert struct.unpack('<B', data_sync)[0] == 0x69

            data[:1024], data[1024:] = np.frombuffer(data_lo, dtype='<u2'), \
                                       np.frombuffer(data_hi, dtype='<u2')
        except AssertionError:
            self.log_error('Not synchronized')
        except VisaIOError:
            self.log_error('Timeout on usb')
        finally:
            self.log_debug('Obtained spectra')

        return data

    def get_status(self):
        cmd = struct.pack('<B', 0xFE)
        self.usb_send_ep.write(cmd)
        resp = bytearray(self.usb_recv_ep.read(16, timeout=1000))

        stat = {
            'num_pixels': struct.unpack('<H', resp[0:2])[0],
            'integration_time': struct.unpack('<I', resp[2:6])[0],
            'lamp_enable': bool(struct.unpack('<B', resp[6:7])[0]),
            'trigger_mode': struct.unpack('<B', resp[7:8])[0],
            'acq_status': struct.unpack('<B', resp[8:9])[0],
            'packets_in_spectra': struct.unpack('<B', resp[9:10])[0],
            'power_down': bool(struct.unpack('<B', resp[10:11])[0]),
            'packet_count': struct.unpack('<B', resp[11:12])[0],
            'usb_speed': struct.unpack('<B', resp[14:15])[0]
        }

        return stat