diff --git a/cfg/lockin830_cfg.py b/cfg/lockin830_cfg.py new file mode 100644 index 0000000..42f544a --- /dev/null +++ b/cfg/lockin830_cfg.py @@ -0,0 +1,16 @@ +Node('lockin830test.psi.ch', + 'lockin830 test', + 'tcp://5000', + ) + +Mod('io', + 'frappy_psi.SR830.SR830_IO', + 'lockin communication', + uri='tcp://linse-976d-ts:3002', + ) + +Mod('XY', + 'frappy_psi.SR830.XY', + 'XY channels', + io='io', + ) diff --git a/cfg/multimeter_cfg.py b/cfg/multimeter_cfg.py new file mode 100644 index 0000000..4296485 --- /dev/null +++ b/cfg/multimeter_cfg.py @@ -0,0 +1,34 @@ +Node('multimetertest.psi.ch', + 'multimeter test', + 'tcp://5000', + ) + +Mod('io', + 'frappy_psi.HP.HP_IO', + 'multimeter communication', + uri='/dev/cu.usbserial-21410', + ) + +Mod('Voltage', + 'frappy_psi.HP.Voltage', + 'voltage', + io='io', + ) + +Mod('Current', + 'frappy_psi.HP.Current', + 'current', + io='io', + ) + +Mod('Resistance', + 'frappy_psi.HP.Resistance', + 'resistivity', + io='io', + ) + +Mod('Frequency', + 'frappy_psi.HP.Frequency', + 'resistivity', + io='io', + ) diff --git a/frappy_psi/HP.py b/frappy_psi/HP.py new file mode 100644 index 0000000..2d27223 --- /dev/null +++ b/frappy_psi/HP.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# ***************************************************************************** +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Module authors: Oksana Shliakhtun +# ***************************************************************************** +import re +from frappy.core import HasIO, Readable, Parameter, FloatRange, EnumType, StatusType, Drivable + + +def string_to_value(value): + value_with_unit = re.compile(r'(\d+)([pnumkMG]?)') + value, pfx = value_with_unit.match(value).groups() + pfx_dict = {'p': 1e-12, 'n': 1e-9, 'u': 1e-6, 'm': 1e-3, 'k': 1e3, 'M': 1e6, 'G': 1e9} + if pfx in pfx_dict: + value = round(float(value) * pfx_dict[pfx], 12) + return float(value) + + +class HP_IO(HasIO): + end_of_line = b'\n' + identification = [('*IDN?', r'HEWLETT-PACKARD,34401A,0,.*')] + + +class HP34401A(HP_IO): + status = Parameter(datatype=StatusType(Readable, 'BUSY')) + autorange = Parameter('autorange_on', EnumType('autorange', off=0, on=1), readonly=False, default=0) + + def comm(self, cmd): # read until \n + string = f'{cmd}\n' + n_string = string.encode() + response = self.communicate(n_string) + + if response: + return response + + response = self.communicate(n_string) + return response if response else None + + def read_range(self, function): + return self.comm(f'{function}:range?') + + def write_range(self, function, range): + return self.comm(f'{function}:range {range}') + + def write_autorange(self, function): + cmd = f'{function}:range:auto {"on" if self.autorange == 0 else "off"}' + self.comm(cmd) + return self.comm(f'{function}:range:auto?') + + def read_resolution(self, function): + return self.comm(f'{function}:resolution?') + + def write_resolution(self, function, resolution): + self.comm(f'{function}:resolution {resolution}') + return self.comm(f'{function}:resolution?') + + +class Voltage(HP34401A, Readable): + value = Parameter('voltage', datatype=FloatRange(0.1, 1000), unit='V') + range = Parameter('voltage sensitivity value', FloatRange(), unit='V', default=1, readonly=False) + resolution = Parameter('resolution') + mode = Parameter('measurement mode: ac/dc', EnumType(AC=1, DC=2), readonly=False) + + ioClass = HP_IO + + VOLT_RANGE = ['100mV', '1V', '10V', '100V', '1000V'] + v_range = Parameter('voltage range', EnumType('voltage index range', + {name: idx for idx, name in enumerate(VOLT_RANGE)}), readonly=False) + + def write_mode(self, mode): + if mode == 1: + self.comm(f'configure:voltage:AC {self.range}, {self.resolution}') + else: + self.comm(f'configure:voltage:DC {self.range}, {self.resolution}') + return self.comm(f'function?') + + def read_value(self): + self.comm(f'measure:voltage:') + + def write_autorange_acdc(self, function): + mode_pr = {1: f'{function}:ac', 2: f'{function}:dc'} + full_function = mode_pr[self.mode] + return self.write_autorange(full_function) + + def read_range_voltage(self): + return self.read_range(f'voltage:{"ac" if self.mode == 1 else "dc"}') + + def write_range_voltage(self, range): + type = 'ac' if self.mode == 1 else 'dc' + return self.write_range(f'voltage:{type}', range) + + def write_autorange_voltage(self): + return self.write_autorange_acdc('voltage') + + def read_resolution_voltage(self): + type = 'ac' if self.mode == 1 else 'dc' + return self.read_resolution(f'voltage:{type}') + + def write_resolution_voltage(self, resolution): + return self.write_resolution(f'voltage:{"ac" if self.mode == 1 else "dc"}', resolution) + + +class Current(HP34401A, Readable, Voltage): + value = Parameter('current', FloatRange, unit='A') + range = Parameter('current range', FloatRange) + CURR_RANGE_AC = ['10mA', '100mA', '1A', '3A'] + CURR_RANGE_DC = ['1A', '3A'] + + def read_range_current(self): + return self.read_range(f'current:{"ac" if self.mode == 1 else "dc"}') + + def write_autrange_current(self): + return self.write_autorange_acdc('current') + + def write_range_current(self, range): + return self.write_range(f'current:{"ac" if self.mode == 1 else "dc"}', range) + + def read_resolution_current(self): + type = 'ac' if self.mode == 1 else 'dc' + return self.read_resolution(f'current:{type}') + + def write_resolution_current(self, resolution): + return self.write_resolution(f'current:{"ac" if self.mode == 1 else "dc"}', resolution) + + +class Resistance(HP34401A, Readable): + value = Parameter('resistance') + mode = Parameter('measurement mode: 2-/4-wire ohms', EnumType(two_wire=2, four_wire=4), readonly=False) + resolution = Parameter('resistance resolution') + RESIST_RANGE = ['100Om', '1kOm', '10kOm', '100kOm', '1MOm', '10MOm', '100MOm'] + FUNCTION_MAP = {2: 'resistance', 4: 'fresistance'} + + def write_mode(self, mode): + pass # configure:... + + def write_range_resistance(self, range): + return self.write_range(f'{self.FUNCTION_MAP[self.mode]}', range) + + def read_range_resistance(self): + return self.read_range(f'{self.FUNCTION_MAP[self.mode]}') + + def write_autorange_resistance(self): + return self.write_autorange(self.FUNCTION_MAP[self.mode]) + + def read_resolution_resistance(self): + return self.read_resolution(f'{self.FUNCTION_MAP[self.mode]}') + + def write_resolution_resistance(self, resolution): + return self.write_resolution(f'{self.FUNCTION_MAP[self.mode]}', resolution) + + +class Frequency(HP34401A, Readable): + value = Parameter('frequency', FloatRange(3, 300e3), unit='Hz') + + def write_autorange_frequency(self): + return self.write_autorange('frequency') + + def read_resolution_frequency(self): + return self.read_resolution('frequency') + + def write_resolution_frequency(self, resolution): + return self.write_resolution('frequency', resolution) +