forked from bsdphk/pylt
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhp3478a.py
235 lines (202 loc) · 10.5 KB
/
hp3478a.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
#!/usr/bin/env python
# -*- coding: utf8 -*-
# Python Imports
from __future__ import print_function
import sys
import math
from datetime import datetime, timedelta
import time
# Python Imports
import prologix_usb
from pylt import PyltError
class hp3478a(prologix_usb.gpib_dev):
'''Class for talking to HP 3478A bench multimeters'''
def __init__(self, name = "COM3", adr = 23):
prologix_usb.gpib_dev.__init__(self, name, adr)
self.id = "HP3478A"
self.attr("read_tmo_ms", 2000)
self.attr("rd_mode", "eoi")
self.attr("eos", 0)
self.attr("eoi", 1)
def _setup_dmm(self, command, delay=3.0):
'''Clear the adaptor, send the command, sleep for settling and throw
away the first 2 readings'''
self.clear()
self.wr(command)
# Sleep to allow autorange to happen and throw first readings away
time.sleep(delay)
self.rd()
self.rd()
return
def _take_samples(self, nsamples, dbg=False):
samples = 0
readings = []
count = max(nsamples/30,1)
while samples < nsamples:
t1 = time.time()
time_of_read = datetime.utcnow()
reading = self.rd()
t2 = time.time()
time_string = datetime.strftime(time_of_read, "%Y-%m-%d %H:%M:%S.%f")
if dbg: print("Reading took %5.3fs %f" % (t2-t1, float(reading)))
if (samples % count==0):
print("Taken %d samples" % samples)
readings.append((time_of_read, float(reading)))
samples += 1
print("Taken %d samples" % samples)
return readings
def read_dc_volts(self, ndig=5, nsamples=1, dbg=False):
'''Perform measurements of DC volts. Auto-ranging and auto-zero is turned on.
The number of digits [ndig, default=5] can be specified as 3, 4 or 5 (equivalent to 3 1/2, 4 1/2 or
5 1/2 digit display) and the number of samples [nsamples, default=1] can be specified.
A list of tuples of the UTC date/time and the readings is returned.'''
print("Measuring %d samples of DC volts at %d.5 digits" % (nsamples, ndig))
if ndig < 3 or ndig > 5:
raise PyltError(self.id, "Invalid number of digits (must be 3, 4 or 5")
# Set command string for DC volts (F1), auto-range (RA), number of digits (N<ndig>),
# single trigger (T1), autozero on (Z1)
command = "F1RAN%dT1Z1" % ndig
self.wr(command)
self._setup_dmm(command)
# Take readings and return them
readings = self._take_samples(nsamples, dbg)
return readings
def read_ac_volts(self, ndig=5, nsamples=1, dbg=False):
'''Perform measurements of AC volts. Auto-ranging and auto-zero is turned on.
The number of digits [ndig, default=5] can be specified as 3, 4 or 5 (equivalent to 3 1/2, 4 1/2 or
5 1/2 digit display) and the number of samples [nsamples, default=1] can be specified.
A list of tuples of the UTC date/time and the readings is returned.'''
print("Measuring %d samples of AC volts at %d.5 digits" % (nsamples, ndig))
if ndig < 3 or ndig > 5:
raise PyltError(self.id, "Invalid number of digits (must be 3, 4 or 5")
# Set command string for AC volts (F2), auto-range (RA), number of digits (N<ndig>),
# single trigger (T1), autozero on (Z1)
command = "F2RAN%dT1Z1" % ndig
self.wr(command)
self._setup_dmm(command)
# Take readings and return them
readings = self._take_samples(nsamples, dbg)
return readings
def read_2wire_ohms(self, ndig=5, nsamples=1, dbg=False):
'''Perform measurements of 2-wire resistance. Auto-ranging and auto-zero is turned on.
The number of digits [ndig, default=5] can be specified as 3, 4 or 5 (equivalent to 3 1/2, 4 1/2 or
5 1/2 digit display) and the number of samples [nsamples, default=1] can be specified.
A list of tuples of the UTC date/time and the readings is returned.'''
print("Measuring %d samples of 2-wire resistance at %d.5 digits" % (nsamples, ndig))
if ndig < 3 or ndig > 5:
raise PyltError(self.id, "Invalid number of digits (must be 3, 4 or 5")
# Set command string for 2-wire ohms (F3), auto-range (RA), number of digits (N<ndig>),
# single trigger (T1), autozero on (Z1)
command = "F3RAN%dT1Z1" % ndig
self._setup_dmm(command)
# Take readings and return them
readings = self._take_samples(nsamples, dbg)
return readings
def read_4wire_ohms(self, ndig=5, nsamples=1, dbg=False):
'''Perform measurements of 4-wire resistance. Auto-ranging and auto-zero is turned on.
The number of digits [ndig, default=5] can be specified as 3, 4 or 5 (equivalent to 3 1/2, 4 1/2 or
5 1/2 digit display) and the number of samples [nsamples, default=1] can be specified.
A list of tuples of the UTC date/time and the readings is returned.'''
print("Measuring %d samples of 4-wire resistance at %d.5 digits" % (nsamples, ndig))
if ndig < 3 or ndig > 5:
raise PyltError(self.id, "Invalid number of digits (must be 3, 4 or 5")
# Set command string for 4-wire resistance (F4), auto-range (RA), number of digits (N<ndig>),
# single trigger (T1), autozero on (Z1)
command = "F4RAN%dT1Z1" % ndig
self._setup_dmm(command)
# Take readings and return them
readings = self._take_samples(nsamples, dbg)
return readings
def read_dc_amps(self, ndig=5, nsamples=1, dbg=False):
'''Perform measurements of DC amps. Auto-ranging and auto-zero is turned on.
The number of digits [ndig, default=5] can be specified as 3, 4 or 5 (equivalent to 3 1/2, 4 1/2 or
5 1/2 digit display) and the number of samples [nsamples, default=1] can be specified.
A list of tuples of the UTC date/time and the readings is returned.'''
print("Measuring %d samples of DC amps at %d.5 digits" % (nsamples, ndig))
if ndig < 3 or ndig > 5:
raise PyltError(self.id, "Invalid number of digits (must be 3, 4 or 5")
# Set command string for DC amps (F5), auto-range (RA), number of digits (N<ndig>),
# single trigger (T1), autozero on (Z1)
command = "F5RAN%dT1Z1" % ndig
self._setup_dmm(command)
# Take readings and return them
readings = self._take_samples(nsamples, dbg)
return readings
def read_ac_amps(self, ndig=5, nsamples=1, dbg=False):
'''Perform measurements of AC amps. Auto-ranging and auto-zero is turned on.
The number of digits [ndig, default=5] can be specified as 3, 4 or 5 (equivalent to 3 1/2, 4 1/2 or
5 1/2 digit display) and the number of samples [nsamples, default=1] can be specified.
A list of tuples of the UTC date/time and the readings is returned.'''
print("Measuring %d samples of AC amps at %d.5 digits" % (nsamples, ndig))
if ndig < 3 or ndig > 5:
raise PyltError(self.id, "Invalid number of digits (must be 3, 4 or 5")
# Set command string for AC amps (F6), auto-range (RA), number of digits (N<ndig>),
# single trigger (T1), autozero on (Z1)
command = "F6RAN%dT1Z1" % ndig
self._setup_dmm(command, delay=3.0)
# Take readings and return them
readings = self._take_samples(nsamples, dbg)
return readings
def write_results(self, out_file, readings, units=None):
"""Write out the set of <readings> to <out_file>.
The format of the output file is:
reading #, datetime, time since first reading (secs), measurement
"""
lines_written = 0
with open(out_file, "w") as out_fh:
# Write header
if units:
unit = " (" + units + ")"
print("# reading #, datetime, time since first reading (secs), measurement"+unit,file=out_fh)
i = 0
t0 = readings[0][0]
t1 = readings[-1][0]
span = t1 - t0
dt_width = len(str(span.total_seconds()))
index_width = len(str(len(readings)))
for reading in readings:
time_string = reading[0].strftime("%Y-%m-%dT%H:%M:%S")
dt = reading[0] - t0
dt = dt.total_seconds()
line = "{0:0{index_width}d} {1} {2:0{dt_width}.1f} {3:+.5f}".format(i, time_string, dt, reading[1], index_width=index_width, dt_width=dt_width)
print(line, file=out_fh)
i+=1
lines_written = i
return lines_written
if __name__ == "__main__":
d=hp3478a()
d.wr("D2HI FROM PYLT")
print("Device has no ID function")
doc="""
Functions:
----------
H0 HOME Command. Place into DC volts, autorange, trigger hold 4.5 digits, autozero on.
H1 Measure DC Volts. As above but a single measure is triggered and reported.
H2 Measure AC Volts. As H1, but AC voltage measurement is taken.
H3 Measure 2-wire Ohms. As H1, but 2-wire resistance measurement is taken.
H4 Measure 4-wire Ohms. As H1, but 4-wire resistance measurement is taken.
H5 Measure DC Amps. As H1, but DC current measurement is taken.
H6 Measure AC Amps. As H1, but AC current measurement is taken.
H7 Measure Extended ohms. As H1, but extended resistance (>30 Mohms) measurement is taken.
N{3,4,5} Select number of digits on display.
T[1-5] Select trigger mode:
1: Internal trigger mode
2: External trigger mode
3: Single trigger mode
4: Trigger hold mode
5: Fast trigger (omit settling delay on AC volts and current and 2 highest ohm ranges)
Z{0,1} Autozero off/on
D{1,3} Display: 1: normal display, D2[text]: display [text] (12 chars max), 3: off
Functions and ranges:
Range Codes
Function Code R-2 R-1 R0 R1 R2 R3 R4 R5 R6 R7 RA
DC Volts F1 30mV 300mV 3V 30V 300V + + + + + Auto
AC Volts F1 * 300mV 3V 30V 300V + + + + + Auto
2-W Ohms F3 * * * 30Ω 300Ω 3kΩ 30kΩ 300kΩ 3MΩ 30MΩ Auto
4-W Ohms F4 * * * 30Ω 300Ω 3kΩ 30kΩ 300kΩ 3MΩ 30MΩ Auto
DC Amps F5 * 300mA 3A + + + + + + + Auto
AC Amps F5 * 300mA 3A + + + + + + + Auto
ExtendedΩ F7
* indicates code selects lowest (most sensitive) range
+ indicates code selects highest (least sensitive) range
"""