1 '''GPIBWrap.py - A Python Interface to the GPIBlib Instrument Library.
2 ===================================================================
3
4 The combination of Python, the instrument-object based C++ GPIBlib
5 instrument control library, NumPy and MatPlotLib provides a quite
6 useful way of building measurement applications for computer controlled
7 instruments.
8
9 Currently implemented instruments are:
10
11 HP_3325A Synthesiser/Function Generator
12 HP_3457A Multimeter
13 HP_3582A FFT Spectrum Analyser
14 HP_8903E Distortion Analyser
15 TEK_710A Transient Digitizer (partially)
16
17 Author: Nick Glazzard 2012'''
18
19 from ctypes import *
20 from numpy import *
21 import matplotlib.pyplot as plt
22
23 lib = cdll.LoadLibrary( './GPIBlib.so' )
24
26 raise RuntimeError( 'Invalid arguments.' )
27
29 raise RuntimeError( 'GPIBlib call failed.' )
30
32 exp = { 'n':1e-9, 'u':1e-6, 'm':1e-3, 'k':1e3, 'M':1e6, 'G':1e9 }
33 print u[0:1]
34 try:
35 mult = exp[u[0:1]]
36 except Exception, err:
37 mult = 1
38 return( v * mult )
39
41 funits = { 'Hz':1, 'kHz':2, 'MHz':3, 'GHz':4 }
42 return funits[fu]
43
45 aunits = { 'v':1, 'mv':2, 'v_pp':3, 'mv_pp':4, 'v_rms':5, 'mv_rms':6, 'dbm':7 }
46 return aunits[au]
47
49 ccodes = { 'a':1, 'cha':1, 'b':2, 'chb':2, 'ch1':1, 'ch2':2, 'both':3, 'chboth':3 }
50 return ccodes[ch]
51
53 cupcodes = { 'ac':1, 'dc':2, 'gnd':3, 'ac_lf_reject':4, 'ac_hf_reject':5 }
54 return cupcodes[cu]
55
57 '''Base class for GPIB instruments with functions common to all instruments.'''
60
63
65 '''Allow (b=1) or suppress (b=0) error messages.'''
66 return lib.GpibInstrument_set_print_error(self.obj,c_int(b))
67
69 '''Log (b=1) or not (b=0) all raw GPIB traffic.'''
70 return lib.GpibInstrument_set_print_gpib(self.obj,c_int(b))
71
73 '''Show status (b=1) or not (b=0) after each GPIB command.'''
74 return lib.GpibInstrument_set_display_status(self.obj,c_int(b))
75
77 '''Dump a representation (b=1) or not (b=0) of binary GPIB read data.'''
78 return lib.GpibInstrument_set_dump_gpib(self.obj,c_int(b))
79
81 '''Force a reset on the GPIB bus.'''
82 return lib.GpibInstrument_reset_bus(self.obj)
83
84
86 '''HP 3457A Multimeter Object.'''
91
93 '''Check the device is open. Return True if it is.'''
94 return lib.HP_3457A_check_open(self.obj)
95
97 '''Start using the HP 3457A for 3.5 digit measurements as fast as possible.
98 If b=1, apply fast mode to AC measurements also (Only recommended for signals >400Hz).'''
99 if( lib.HP_3457A_set_fast_mode(self.obj,c_int(b)) != 1 ):
100 callerr()
101 return True
102
104 '''End fast mode. Revert to high precision mode.'''
105 if( lib.HP_3457A_clear_fast_mode(self.obj) != 1 ):
106 callerr()
107 return True
108
110 '''Set Function to f and Range to v.
111 f may be: 'dcv', 'acv', 'acdcv', 'ohm', 'ohm4', 'dci', 'aci', 'acdci', 'freq', 'period'
112 v depends on f.
113 For volts functions: 'auto', '30mv', '300mv', '3v', '30v', '300v'
114 For ohm functions: 'auto', '30r', '300r', '3k', '30k', '300k', '3m', '30m', '3g'
115 For time functions: 'auto' '''
116 funcs = { 'dcv':1, 'acv':2, 'acdcv':3, 'ohm':4, 'ohm4':5,
117 'dci':6, 'aci':7, 'acdci':8, 'freq':9, 'period':10 }
118 vvals = { 'auto':-1, '30mv':0.03, '300mv':0.3, '3v':3, '30v':30, '300v':300 }
119 ovals = { 'auto':-1, '30r':30, '300r':300, '3k':3000, '30k':30000, '300k':300000,
120 '3m':3000000, '30m':30000000, '3g':3000000000 }
121 ivals = { 'auto':-1, '300u':0.0003, '3m':0.003, '30m':0.03,
122 '300m':0.3, '1a':1, '1a5':1.5 }
123 autos = { 'auto':-1 }
124 funcvals = { 1:vvals, 2:vvals, 3:vvals, 4:ovals, 5:ovals, 6:ivals,
125 7:ivals, 8:ivals, 9:autos, 10:autos }
126 try:
127 code = funcs[f]
128 valuetab = funcvals[code]
129 value = valuetab[v]
130 except Exception, err:
131 argerr()
132 if( lib.HP_3457A_set_function(self.obj,c_int(code),c_double(value)) != 1 ):
133 callerr()
134 return True
135
137 '''Read the last measurement from the HP 3457A.'''
138 lib.HP_3457A_read_simple.restype = c_double
139 return lib.HP_3457A_read_simple(self.obj)
140
141
143 '''HP 3325A Synthesiser/Function Generator Object.'''
148
150 '''Check the device is open. Return True if it is.'''
151 return lib.HP_3325A_check_open(self.obj)
152
154 '''Check the last HP_3325A specific command executed OK. Return True if so.'''
155 return lib.HP_3325A_check_error(self.obj)
156
158 '''Set the output waveform to sw.
159 sw may be: 'dc', 'sine', 'square', 'triangle', 'ramp_up', 'ramp_down' '''
160 funcs = { 'dc':0, 'sine':1, 'square':2, 'triangle':3, 'ramp_up': 4, 'ramp_down':5 }
161 try:
162 w = funcs[sw]
163 except Exception, err:
164 argerr()
165 return lib.HP_3325A_set_function(self.obj,c_int(w))
166
168 '''Set the output frequency to freq (in sunits).
169 sunits may be: 'Hz', 'kHz', 'MHz' '''
170 try:
171 units = frequnits(sunits)
172 except Exception, err:
173 argerr()
174 return lib.HP_3325A_set_frequency(self.obj,c_float(freq),c_int(units))
175
177 '''Set the output amplitude to amp (in sunits).
178 sunits may be: 'v', 'mv', 'v_pp', 'mv_pp', 'v_rms', 'mv_rms', 'dbm' '''
179 try:
180 units = ampunits(sunits)
181 except Exception, err:
182 argerr()
183 return lib.HP_3325A_set_amplitude(self.obj,c_float(amp),c_int(units))
184
186 '''Set the output DC offset to dc (in sunits).
187 sunits may be: 'v', 'mv' '''
188 try:
189 units = ampunits(sunits)
190 except Exception, err:
191 argerr()
192 return lib.HP_3325A_set_dc_offset(self.obj,c_float(dc),c_int(units))
193
195 '''Set the phase offset to degrees degrees.'''
196 return lib.HP_3325A_set_phase(self.obj,c_float(degrees))
197
199 '''Set the frequency at the start of a sweep (in sunits).
200 sunits may be: 'Hz', 'kHz', 'MHz' '''
201 try:
202 units = frequnits(sunits)
203 except Exception, err:
204 argerr()
205 return lib.HP_3325A_set_sweep_start_frequency(self.obj,c_float(freq),c_int(units))
206
208 '''Set the frequency at the end of a sweep (in sunits).
209 sunits may be: 'Hz', 'kHz', 'MHz' '''
210 try:
211 units = frequnits(sunits)
212 except Exception, err:
213 argerr()
214 return lib.HP_3325A_set_sweep_end_frequency(self.obj,c_float(freq),c_int(units))
215
217 '''Set the frequency at which to output the marker signal during a sweep (in sunits).
218 sunits may be: 'Hz', 'kHz', 'MHz' '''
219 try:
220 units = frequnits(sunits)
221 except Exception, err:
222 argerr()
223 return lib.HP_3325A_set_sweep_marker_frequency(self.obj,c_float(freq),c_int(units))
224
226 '''Set the sweep mode to 'lin' or 'log'. '''
227 smodes = { 'lin':1, 'log':2 }
228 try:
229 sweep_mode = smodes(ssweep_mode)
230 except Exception, err:
231 argerr()
232 return lib.HP_3325A_set_sweep_mode(self.obj,c_int(sweep_mode))
233
235 '''Set the time over which to do a sweep (in seconds).'''
236 return lib.HP_3325A_set_sweep_time(self.obj,c_float(secs))
237
239 '''Do a single sweep. If waitfin=1, wait for it to end.'''
240 return lib.HP_3325A_start_sweep_single(self.obj,c_int(waitfin))
241
243 '''Start sweeping continuously.'''
244 return lib.HP_3325A_start_sweep_continuous(self.obj)
245
247 '''Turn amplitude modulation on (1) or off (0).'''
248 return lib.HP_3325A_use_amplitude_modulation(self.obj,c_int(yes))
249
251 '''Turn phase modulation on (1) or off (0).'''
252 return lib.HP_3325A_use_phase_modulation(self.obj,c_int(yes))
253
254
256 '''HP 8903E Distortion Analyzer Object.'''
261
263 '''Check the device is open. Return True if it is.'''
264 return lib.HP_8903E_check_open(self.obj)
265
267 '''Measure the AC voltage present at the input.'''
268 lib.HP_8903E_get_ac_volts.restype = c_double
269 return lib.HP_8903E_get_ac_volts(self.obj)
270
272 '''Measure the distortion of the signal present at the input.'''
273 lib.HP_8903E_get_distortion.restype = c_double
274 return lib.HP_8903E_get_distortion(self.obj)
275
276
278 '''HP 3582A Spectrum Analyzer Object.'''
283
285 '''Check the device is open. Return True if it is.'''
286 return lib.HP_3582A_check_open(self.obj)
287
296
298 '''Set the coupling mode for channel c to mode m.
299 c may be: 'a', 'cha', 'b', 'chb', 'ch1', 'ch2', 'both', 'chboth'
300 m may be: 'ac':1, 'dc' '''
301 try:
302 ic = chancodes(c)
303 im = couplecodes(m)
304 except Exception, err:
305 argerr()
306 return lib.HP_3582A_coupling(self.obj,c_int(ic),c_int(im))
307
309 '''Set the sensitivity of channel c to v volts rms.
310 c may be: 'a', 'cha', 'b', 'chb', 'ch1', 'ch2', 'both', 'chboth'
311 v is a voltage in the range 0.0 (30mV lowest range) to 30.0. '''
312 try:
313 ic = chancodes(c)
314 except Exception, err:
315 argerr()
316 return lib.HP_3582A_set_sensitivity(self.obj,c_int(ic),c_double(v))
317
319 '''Set the sensitivity of channel c to v volts rms in dB wrt 1V.
320 c may be: 'a', 'cha', 'b', 'chb', 'ch1', 'ch2', 'both', 'chboth'
321 v is dBV in the range -50.0 to 30.0. '''
322 try:
323 ic = chancodes(c)
324 except Exception, err:
325 argerr()
326 return lib.HP_3582A_set_sensitivity_db(self.obj,c_int(ic),c_double(v))
327
329 '''Set the frequency axis of the spectrum. The use of span and freq depends on mode m.
330 'full': 0->25kHz [span] and [freq] ignored.
331 'zero_start: 0->[span], [freq] ignored.
332 'set_start': [freq]->[freq]+[span]
333 'set_center: [freq]-[span]/2 -> [freq]+[span]/2 '''
334 fmodes = { 'full':1, 'zero_start':2, 'set_start':4, 'set_center':3 }
335 try:
336 im = fmodes(m)
337 except Exception, err:
338 argerr()
339 return lib.HP_3582A_set_frequency(self.obj,c_int(im),c_double(span),c_double(freq))
340
342 '''Select which channel(s) to display (c) and the Y scaling mode (ym).
343 c may be: 'a', 'cha', 'b', 'chb', 'ch1', 'ch2', 'both', 'chboth'
344 ym may be: 'linear', '10dB', '2dB' (per division). '''
345 ymodes = { 'linear':1, '10dB':2, '10db':2, '2dB':3, '2db':3 }
346 try:
347 ic = chancodes(c)
348 iym = ymodes[ym]
349 except Exception, err:
350 argerr()
351 return lib.HP_3582A_display(self.obj,c_int(ic),c_int(iym))
352
354 '''Set the display full scale point to be r dB below input full scale.
355 r will be forced to be a multiple of 10dB. '''
356 ir = float( int(r/10.0) * 10 )
357 return lib.HP_3582A_set_amp_ref_level_db(self.obj,c_double(ir))
358
360 '''Get the display full scale point in dB.'''
361 lib.HP_3582A_get_amp_ref_level_db.restype = c_double
362 return lib.HP_3582A_get_amp_ref_level_db(self.obj)
363
365 '''Set the window function to w.
366 w may be: 'flattop', 'hanning', 'uniform'. '''
367 wmodes = { 'flattop':1, 'hanning':2, 'uniform':3 }
368 try:
369 iw = wmodes[w]
370 except Exception, err:
371 argerr()
372 return lib.HP_3582A_window(self.obj,c_int(iw))
373
375 '''Start a measurement. The averaging mode is a: 'off', 'rms'.
376 The number of spectra to average is c. This will be made a power of 2 internally.
377 The maximum time to wait for completion is m seconds (rounded up to a multiple of 5).
378 N.B. Call this BEFORE get_spectrum().'''
379 amodes = { 'off':1, 'rms':2 }
380 try:
381 ia = amodes[a]
382 except Exception, err:
383 argerr()
384 im = int(m)/5 + 1
385 return lib.HP_3582A_start_measurement(self.obj,c_int(ia),c_int(c),c_int(im))
386
388 '''Read the display annotation.'''
389 lib.HP_3582A_read_annotation.restype = c_int
390 arrmem = create_string_buffer("",200)
391 iret = lib.HP_3582A_read_annotation(self.obj,arrmem,200)
392 if( iret > 0 ):
393 return arrmem.value
394 else:
395 return "None."
396
398 '''Return the annotation for channel c.
399 ann is the result of read_annotation().
400 c may be: 'a', 'cha', 'b', 'chb', 'ch1', 'ch2' '''
401 try:
402 ic = chancodes(c)
403 except Exception, err:
404 argerr()
405 if( ic == 1 ):
406 annpart = ann[0:32]
407 elif( ic == 2 ):
408 annpart = ann[32:64]
409 else:
410 return( 'Bad Channel.' )
411 return( ' '.join(annpart.split()) )
412
414 '''Return information on averaging from annotation ann.'''
415 annpart = ann[96:118]
416 return( ' '.join(annpart.split()) )
417
419 '''Return bandwidth information from annotation ann.'''
420 annpart = ann[118:128]
421 return( ' '.join(annpart.split()) )
422
424 '''Read a spectrum. This returns a tuple: (frq,cha,chb,samps,aok,bok).
425 frq is a NumPy array of frequencies.
426 cha is a NumPy array of channel A amplitudes at the frq frequencies.
427 chb is a NumPy array of channel B amplitudes at the frq frequencies.
428 samps in the number of values in the arrays.
429 aok is True if channel A was measured.
430 bok is True if channel B was measured. '''
431 freqs = (c_double*512)()
432 chamem = (c_double*512)()
433 chbmem = (c_double*512)()
434 nsamp = c_int(0)
435 a_avail = c_int(0)
436 b_avail = c_int(0)
437 iret = lib.HP_3582A_get_spectrum(self.obj,byref(freqs),byref(chamem),byref(chbmem),
438 byref(nsamp),byref(a_avail),byref(b_avail))
439 cha_d = fromiter( chamem, dtype=float, count=512 )
440 chb_d = fromiter( chbmem, dtype=float, count=512 )
441 return (freqs, cha_d, chb_d, nsamp.value, a_avail.value, b_avail.value)
442
443
445 '''Sony/Tektronix RTD-710A Transient Digitizer Object.'''
450
452 '''Set the sensitivity of channel c to v volts.
453 c may be: 'a', 'cha', 'b', 'chb', 'ch1', 'ch2' '''
454 try:
455 ic = chancodes(c)
456 except Exception, err:
457 argerr()
458 return lib.TEK_RTD710A_range(self.obj, c_int(ic), c_double(v))
459
461 '''Set the offset of channel c to v volts.
462 c may be: 'a', 'cha', 'b', 'chb', 'ch1', 'ch2' '''
463 try:
464 ic = chancodes(c)
465 except Exception, err:
466 argerr()
467 return lib.TEK_RTD710A_offset(self.obj, c_int(ic), c_double(v))
468
470 '''Set the coupling mode for channel c.
471 c may be: 'a', 'cha', 'b', 'chb', 'ch1', 'ch2'
472 m may be: 'ac', 'dc', 'gnd', 'ac_lf_reject', 'ac_hf_reject' '''
473 try:
474 ic = chancodes(c)
475 im = couplecodes(m)
476 except Exception, err:
477 argerr()
478 return lib.TEK_RTD710A_coupling(self.obj, c_int(ic), c_int(im))
479
481 '''Turn on bandwidth limiting (b=1) or turn it off (b=0).'''
482 return lib.TEK_RTD710A_bw_limit(self.obj, c_int(b))
483
485 '''Turn on Channel 1 only mode (b=1) or turn it off (b=0).'''
486 return lib.TEK_RTD710A_ch1_only(self.obj, c_int(b))
487
489 '''Set the interval between samples to be t seconds.'''
490 return lib.TEK_RTD710A_sample_interval(self.obj, c_double(t))
491
493 '''Set the number of samples to take to n.'''
494 return lib.TEK_RTD710A_samples(self.obj, c_int(n))
495
497 '''Stop sampling (b=1) or start again (b=0).'''
498 return lib.TEK_RTD710A_sample_hold(self.obj, c_int(b))
499
501 '''Record a waveform. Allow waitms milliseconds for this to complete.'''
502 return lib.TEK_RTD710A_record_wave(self.obj, c_int(waitms))
503
505 '''Read a waveform back from a specified location number and channel, c.
506 chan may be: 'a', 'cha', 'b', 'chb', 'ch1', 'ch2' '''
507 try:
508 ic = chancodes(c)
509 except Exception, err:
510 argerr()
511 wave = (c_double*300000)()
512 npoints = c_int(0)
513 tstep = c_double(0.0)
514 iret = lib.TEK_RTD710A_read_wave(self.obj, c_int(location), c_int(ic), byref(wave),
515 byref(npoints), byref(tstep))
516 wave_d = fromiter( wave, dtype=float, count=npoints.value )
517 return (wave_d, npoints.value, tstep.value)
518
519
520 if __name__ == '__main__':
521 address = { 'HP_3582A':12, 'HP_3325A':6, 'HP_3457A':2, 'HP_8903E':14, 'TEK_710A':10 }
522
523 if( False ):
524 d = TEK_710A( address['TEK_710A'], 0 )
525 d.range( 'ch1', 2 )
526 d.sample_interval( eu(200,'ns') )
527 d.record_wave( 1000 )
528 (wave, nsamps, timestep) = d.read_wave( 1, 'ch1' )
529 print nsamps, timestep
530 plt.plot(wave[0:nsamps-1])
531 plt.show()
532
533 if( True ):
534 m = HP_3457A( address['HP_3457A'], 0 )
535 m.set_print_gpib(1)
536 m.set_dump_gpib(1)
537 m.set_function( 'freq', 'auto' )
538 print m.read()
539
540 if( False ):
541
542 f = HP_3325A( address['HP_3325A'], 0 )
543 f.set_print_gpib(0)
544 f.set_print_error(0)
545 f.set_function( 'sine' )
546 f.set_frequency( 1, 'kHz' )
547 f.set_amplitude( 1, 'v_rms' )
548
549
550 s = HP_3582A( address['HP_3582A'], 0 )
551 s.set_print_gpib(0)
552 s.set_print_error(0)
553 s.set_input_mode( 'cha' )
554 s.set_sensitivity( 'cha', 3 )
555 s.start_measurement( 'rms', 5, 1 )
556 (frq,cha,chb,samps,aok,bok) = s.get_spectrum()
557
558
559 ann = s.read_annotation()
560 ch1ann = s.parse_channel_annotation(ann,'cha')
561 ch2ann = s.parse_channel_annotation(ann,'chb')
562 avann = s.parse_average_annotation(ann)
563 bwann = s.parse_bandwidth_annotation(ann)
564 plt.plot(frq[0:samps-1],cha[0:samps-1])
565 plt.title( 'HP 3582A Spectrum Analyzer' )
566 plt.xlabel( 'Frequency (Hz)' )
567 plt.ylabel( 'Amplitude (dBV)' )
568 plt.grid( True )
569 ax = plt.gca()
570 plt.text( 0.6, 0.90, ch1ann, transform=ax.transAxes )
571 plt.text( 0.6, 0.85, ch2ann, transform=ax.transAxes )
572 plt.text( 0.6, 0.80, avann, transform=ax.transAxes )
573 plt.text( 0.6, 0.75, bwann, transform=ax.transAxes )
574 plt.show()
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613