230 lines
8.1 KiB
Python
230 lines
8.1 KiB
Python
#!/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 <oksana.shliakhtun@psi.ch>
|
|
# *****************************************************************************
|
|
"""Hewlett-Packard HP34401A Multimeter (not finished)"""
|
|
|
|
import re
|
|
from frappy.core import HasIO, Readable, Parameter, FloatRange, EnumType, StatusType, IDLE, ERROR, WARN
|
|
|
|
|
|
def string_to_value(value):
|
|
"""
|
|
Converting the value to float, removing the units, converting the prefix into the number.
|
|
:param value: value
|
|
:return: float value without units
|
|
"""
|
|
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?')
|
|
|
|
def read_status(self):
|
|
stb = int(self.comm('*STB?'))
|
|
esr = int(self.comm('*ESR?'))
|
|
|
|
if esr & (1 << 3):
|
|
return ERROR, 'self-test/calibration/reading failed'
|
|
if esr & (1 << 4):
|
|
return ERROR, 'execution error'
|
|
if esr & (1 << 5):
|
|
return ERROR, 'syntax error'
|
|
if esr & (1 << 2):
|
|
return ERROR, 'query error'
|
|
if stb & (1 << 3):
|
|
return WARN, 'questionable data'
|
|
if stb & (1 << 5):
|
|
return WARN, 'standard event register is not empty'
|
|
if stb & (1 << 6):
|
|
return WARN, 'requested service'
|
|
|
|
if any(stb & (1 << i) for i in range(3) or stb & (1 << 7)):
|
|
return IDLE, ''
|
|
if esr & (1 << 6):
|
|
return IDLE, ''
|
|
if esr & (1 << 7):
|
|
return IDLE, ''
|
|
if stb & (1 << 4):
|
|
return IDLE, 'message available'
|
|
if esr & (1 << 0):
|
|
return IDLE, 'operation complete'
|
|
if esr & (1 << 1):
|
|
return IDLE, 'not used'
|
|
|
|
|
|
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', readonly=False)
|
|
|
|
ioClass = HP_IO
|
|
|
|
MODE_NAMES = {0: 'dc', 1: 'ac'}
|
|
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)
|
|
|
|
acdc = None
|
|
|
|
def write_mode(self, mode):
|
|
"""
|
|
Set the mode - AC or DC
|
|
:param mode: AC/DC
|
|
:return:
|
|
"""
|
|
if mode == 1:
|
|
self.comm(f'configure:voltage:AC {self.range}, {self.resolution}')
|
|
else:
|
|
self.comm(f'configure:voltage:DC {self.range}, {self.resolution}')
|
|
self.acdc = self.MODE_NAMES[mode]
|
|
return self.comm(f'function?')
|
|
|
|
def read_value(self):
|
|
"""
|
|
Makes a AC/DC voltage measurement.
|
|
:return: AC/DC value
|
|
"""
|
|
return self.comm(f'measure:voltage:{self.acdc}?')
|
|
|
|
def write_autorange_acdc(self, function):
|
|
full_function = f'{function}:{self.acdc}'
|
|
return self.write_autorange(full_function)
|
|
|
|
def read_range_voltage(self):
|
|
return self.read_range(f'voltage:{self.acdc}')
|
|
|
|
def write_range_voltage(self, range):
|
|
return self.write_range(f'voltage:{self.acdc}', range)
|
|
|
|
def write_autorange_voltage(self):
|
|
return self.write_autorange_acdc('voltage')
|
|
|
|
def read_resolution_voltage(self):
|
|
return self.read_resolution(f'voltage:{self.acdc}')
|
|
|
|
def write_resolution_voltage(self, resolution):
|
|
return self.write_resolution(f'voltage:{self.acdc}', 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:{self.acdc}')
|
|
|
|
def write_autorange_current(self):
|
|
return self.write_autorange_acdc('current')
|
|
|
|
def write_range_current(self, range):
|
|
return self.write_range(f'current:{self.acdc}', range)
|
|
|
|
def read_resolution_current(self):
|
|
return self.read_resolution(f'current:{self.acdc}')
|
|
|
|
def write_resolution_current(self, resolution):
|
|
return self.write_resolution(f'current:{self.acdc}', 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 measurement resolution')
|
|
range = Parameter('resistance measurement range')
|
|
RESIST_RANGE = ['100Om', '1kOm', '10kOm', '100kOm', '1MOm', '10MOm', '100MOm']
|
|
FUNCTION_MAP = {2: 'resistance', 4: 'fresistance'}
|
|
|
|
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_mode(self, mode):
|
|
if mode == 2:
|
|
self.comm(f'configure:resistance {self.range},{self.resolution}')
|
|
elif mode == 4:
|
|
self.comm(f'configure:fresistance {self.range}, {self.resolution}')
|
|
return self.comm('configure?')
|
|
|
|
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)
|