#!/usr/bin/env python
#
# hp6632b.py: Python class for the HP6632b power supply
#
# © 2014 Willem Dijkstra (wpd@xs4all.nl).
#
# This file is a part of the InstrumentKit project.
# Licensed under the AGPL version 3.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
"""
Driver for the HP6632b DC power supply
Originally contributed and copyright held by Willem Dijkstra (wpd@xs4all.nl)
An unrestricted license has been provided to the maintainers of the Instrument
Kit project.
"""
# IMPORTS #####################################################################
from enum import Enum, IntEnum
from instruments.generic_scpi.scpi_instrument import SCPIInstrument
from instruments.hp.hp6652a import HP6652a
from instruments.units import ureg as u
from instruments.util_fns import (
unitful_property,
unitless_property,
bool_property,
enum_property,
int_property,
)
# CLASSES #####################################################################
[docs]
class HP6632b(SCPIInstrument, HP6652a):
"""
The HP6632b is a system dc power supply with an output rating of 0-20V/0-5A,
precision low current measurement and low output noise.
According to the manual this class MIGHT be usable for any HP power supply
with a model number
- HP663Xb with X in {1, 2, 3, 4},
- HP661Xc with X in {1,2, 3, 4} and
- HP663X2A for X in {1, 3}, without the additional measurement capabilities.
HOWEVER, it has only been tested by the author with HP6632b supplies.
Example usage:
>>> import instruments as ik
>>> psu = ik.hp.HP6632b.open_gpibusb('/dev/ttyUSB0', 6)
>>> psu.voltage = 10 # Sets voltage to 10V.
>>> psu.output = True # Enable output
>>> psu.voltage
array(10.0) * V
>>> psu.voltage_trigger = 20 # Set transient trigger voltage
>>> psu.init_output_trigger() # Prime instrument to initiated state, ready for trigger
>>> psu.trigger() # Send trigger
>>> psu.voltage
array(10.0) * V
"""
# ENUMS ##
[docs]
class ALCBandwidth(IntEnum):
"""
Enum containing valid ALC bandwidth modes for the hp6632b
"""
normal = 1.5e4
fast = 6e4
[docs]
class DigitalFunction(Enum):
"""
Enum containing valid digital function modes for the hp6632b
"""
remote_inhibit = "RIDF"
data = "DIG"
[docs]
class DFISource(Enum):
"""
Enum containing valid DFI sources for the hp6632b
"""
questionable = "QUES"
operation = "OPER"
event_status_bit = "ESB"
request_service_bit = "RQS"
off = "OFF"
[docs]
class ErrorCodes(IntEnum):
"""
Enum containing generic-SCPI error codes along with codes specific
to the HP6632b.
"""
no_error = 0
# -100 BLOCK: COMMAND ERRORS ##
command_error = -100
invalid_character = -101
syntax_error = -102
invalid_separator = -103
data_type_error = -104
get_not_allowed = -105
# -106 and -107 not specified.
parameter_not_allowed = -108
missing_parameter = -109
command_header_error = -110
header_separator_error = -111
program_mnemonic_too_long = -112
undefined_header = -113
header_suffix_out_of_range = -114
unexpected_number_of_parameters = -115
numeric_data_error = -120
invalid_character_in_number = -121
exponent_too_large = -123
too_many_digits = -124
numeric_data_not_allowed = -128
suffix_error = -130
invalid_suffix = -131
suffix_too_long = -134
suffix_not_allowed = -138
character_data_error = -140
invalid_character_data = -141
character_data_too_long = -144
character_data_not_allowed = -148
string_data_error = -150
invalid_string_data = -151
string_data_not_allowed = -158
block_data_error = -160
invalid_block_data = -161
block_data_not_allowed = -168
expression_error = -170
invalid_expression = -171
expression_not_allowed = -178
macro_error_180 = -180
invalid_outside_macro_definition = -181
invalid_inside_macro_definition = -183
macro_parameter_error = -184
# -200 BLOCK: EXECUTION ERRORS ##
# -300 BLOCK: DEVICE-SPECIFIC ERRORS ##
# Note that device-specific errors also include all positive numbers.
# -400 BLOCK: QUERY ERRORS ##
# OTHER ERRORS ##
#: Raised when the instrument detects that it has been turned from
#: off to on.
power_on = -500 # Yes, SCPI 1999 defines the instrument turning on as
# an error. Yes, this makes my brain hurt.
user_request_event = -600
request_control_event = -700
operation_complete = -800
# -200 BLOCK: EXECUTION ERRORS
execution_error = -200
data_out_of_range = -222
too_much_data = -223
illegal_parameter_value = -224
out_of_memory = -225
macro_error_270 = -270
macro_execution_error = -272
illegal_macro_label = -273
macro_recursion_error = -276
macro_redefinition_not_allowed = -277
# -300 BLOCK: DEVICE-SPECIFIC ERRORS
system_error = -310
too_many_errors = -350
# -400 BLOCK: QUERY ERRORS
query_error = -400
query_interrupted = -410
query_unterminated = -420
query_deadlocked = -430
query_unterminated_after_indefinite_response = -440
# DEVICE ERRORS
ram_rd0_checksum_failed = 1
ram_config_checksum_failed = 2
ram_cal_checksum_failed = 3
ram_state_checksum_failed = 4
ram_rst_checksum_failed = 5
ram_selftest = 10
vdac_idac_selftest1 = 11
vdac_idac_selftest2 = 12
vdac_idac_selftest3 = 13
vdac_idac_selftest4 = 14
ovdac_selftest = 15
digital_io_selftest = 80
ingrd_recv_buffer_overrun = 213
rs232_recv_framing_error = 216
rs232_recv_parity_error = 217
rs232_recv_overrun_error = 218
front_panel_uart_overrun = 220
front_panel_uart_framing = 221
front_panel_uart_parity = 222
front_panel_uart_buffer_overrun = 223
front_panel_uart_timeout = 224
cal_switch_prevents_cal = 401
cal_password_incorrect = 402
cal_not_enabled = 403
computed_readback_cal_const_incorrect = 404
computed_prog_cal_constants_incorrect = 405
incorrect_seq_cal_commands = 406
cv_or_cc_status_incorrect = 407
output_mode_must_be_normal = 408
too_many_sweep_points = 601
command_only_applic_rs232 = 602
curr_or_volt_fetch_incompat_with_last_acq = 603
measurement_overrange = 604
[docs]
class RemoteInhibit(Enum):
"""
Enum containing vlaid remote inhibit modes for the hp6632b.
"""
latching = "LATC"
live = "LIVE"
off = "OFF"
[docs]
class SenseWindow(Enum):
"""
Enum containing valid sense window modes for the hp6632b.
"""
hanning = "HANN"
rectangular = "RECT"
# PROPERTIES ##
voltage_alc_bandwidth = enum_property(
"VOLT:ALC:BAND",
ALCBandwidth,
input_decoration=lambda x: int(float(x)),
readonly=True,
doc="""
Get the "automatic level control bandwidth" which for the HP66332A and
HP6631-6634 determines if the output capacitor is in circuit. `Normal`
denotes that it is, and `Fast` denotes that it is not.
:type: `~HP6632b.ALCBandwidth`
""",
)
voltage_trigger = unitful_property(
"VOLT:TRIG",
u.volt,
doc="""
Gets/sets the pending triggered output voltage.
Note there is no bounds checking on the value specified.
:units: As specified, or assumed to be :math:`\\text{V}` otherwise.
:type: `float` or `~pint.Quantity`
""",
)
current_trigger = unitful_property(
"CURR:TRIG",
u.amp,
doc="""
Gets/sets the pending triggered output current.
Note there is no bounds checking on the value specified.
:units: As specified, or assumed to be :math:`\\text{A}` otherwise.
:type: `float` or `~pint.Quantity`
""",
)
init_output_continuous = bool_property(
"INIT:CONT:SEQ1",
"1",
"0",
doc="""
Get/set the continuous output trigger. In this state, the power supply
will remain in the initiated state, and respond continuously on new
incoming triggers by applying the set voltage and current trigger
levels.
:type: `bool`
""",
)
current_sense_range = unitful_property(
"SENS:CURR:RANGE",
u.ampere,
doc="""
Get/set the sense current range by the current max value.
A current of 20mA or less selects the low-current range, a current
value higher than that selects the high-current range. The low current
range increases the low current measurement sensitivity and accuracy.
:units: As specified, or assumed to be :math:`\\text{A}` otherwise.
:type: `float` or `~pint.Quantity`
""",
)
output_dfi = bool_property(
"OUTP:DFI",
"1",
"0",
doc="""
Get/set the discrete fault indicator (DFI) output from the dc
source. The DFI is an open-collector logic signal connected to the read
panel FLT connection, that can be used to signal external devices when
a fault is detected.
:type: `bool`
""",
)
output_dfi_source = enum_property(
"OUTP:DFI:SOUR",
DFISource,
doc="""
Get/set the source for discrete fault indicator (DFI) events.
:type: `~HP6632b.DFISource`
""",
)
output_remote_inhibit = enum_property(
"OUTP:RI:MODE",
RemoteInhibit,
doc="""
Get/set the remote inhibit signal. Remote inhibit is an external,
chassis-referenced logic signal routed through the rear panel INH
connection, which allows an external device to signal a fault.
:type: `~HP6632b.RemoteInhibit`
""",
)
digital_function = enum_property(
"DIG:FUNC",
DigitalFunction,
doc="""
Get/set the inhibit+fault port to digital in+out or vice-versa.
:type: `~HP6632b.DigitalFunction`
""",
)
digital_data = int_property(
"DIG:DATA",
valid_set=range(0, 8),
doc="""
Get/set digital in+out port to data. Data can be an integer from 0-7.
:type: `int`
""",
)
sense_sweep_points = unitless_property(
"SENS:SWE:POIN",
doc="""
Get/set the number of points in a measurement sweep.
:type: `int`
""",
)
sense_sweep_interval = unitful_property(
"SENS:SWE:TINT",
u.second,
doc="""
Get/set the digitizer sample spacing. Can be set from 15.6 us to 31200
seconds, the interval will be rounded to the nearest 15.6 us increment.
:units: As specified, or assumed to be :math:`\\text{s}` otherwise.
:type: `float` or `~pint.Quantity`
""",
)
sense_window = enum_property(
"SENS:WIND",
SenseWindow,
doc="""
Get/set the measurement window function.
:type: `~HP6632b.SenseWindow`
""",
)
output_protection_delay = unitful_property(
"OUTP:PROT:DEL",
u.second,
doc="""
Get/set the time between programming of an output change that produces
a constant current condition and the recording of that condigition in
the Operation Status Condition register. This command also delays over
current protection, but not overvoltage protection.
:units: As specified, or assumed to be :math:`\\text{s}` otherwise.
:type: `float` or `~pint.Quantity`
""",
)
# FUNCTIONS ##
[docs]
def init_output_trigger(self):
"""
Set the output trigger system to the initiated state. In this state,
the power supply will respond to the next output trigger command.
"""
self.sendcmd("INIT:NAME TRAN")
[docs]
def abort_output_trigger(self):
"""
Set the output trigger system to the idle state.
"""
self.sendcmd("ABORT")
# SCPIInstrument commands that need local overrides
@property
def line_frequency(self):
raise NotImplementedError
@line_frequency.setter
def line_frequency(self, newval):
raise NotImplementedError
@property
def display_brightness(self):
raise NotImplementedError
@display_brightness.setter
def display_brightness(self, newval):
raise NotImplementedError
@property
def display_contrast(self):
raise NotImplementedError
@display_contrast.setter
def display_contrast(self, newval):
raise NotImplementedError
[docs]
def check_error_queue(self):
"""
Checks and clears the error queue for this device, returning a list of
:class:`~SCPIInstrument.ErrorCodes` or `int` elements for each error
reported by the connected instrument.
"""
done = False
result = []
while not done:
err = int(self.query("SYST:ERR?").split(",")[0])
if err == self.ErrorCodes.no_error:
done = True
else:
result.append(
self.ErrorCodes(err)
if any(err == item.value for item in self.ErrorCodes)
else err
)
return result