mmcb-rs232-avt 1.0.13__py3-none-any.whl → 1.0.18__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
mmcb_rs232/lexicon.py ADDED
@@ -0,0 +1,580 @@
1
+ """
2
+ Common functions related to serial port commands for project devices.
3
+
4
+ The functions motion() and power() are relatively expensive function calls
5
+ as there is a fair amount of substitution to perform for each call. Calls for
6
+ write commands (with variable parameters) will cache poorly, e.g. setting
7
+ voltage values; constant commands that read voltage/current will cache well.
8
+
9
+ power() has an LRU cache applied as calls are made very frequently to read
10
+ power supply voltage/current/status. Calls to motion(), though similarly
11
+ expensive, are relatively sparse.
12
+
13
+ e.g. for command:
14
+
15
+ import statistics
16
+ import timeit
17
+ statistics.mean(timeit.Timer("lexicon.power('2410', 'read measured vi')",
18
+ setup='import lexicon').repeat(100,1))
19
+
20
+ timings are:
21
+
22
+ 2.043e-05s (un-cached)
23
+ 4.207e-07s (cached)
24
+
25
+ Cache performance for: ./iv.py -200 --hold 1
26
+
27
+ CacheInfo(hits=82, misses=56, maxsize=128, currsize=56)
28
+
29
+ callable functions from this file:
30
+
31
+ motion
32
+ power
33
+ """
34
+
35
+ import functools
36
+
37
+
38
+ ##############################################################################
39
+ # motion controller command string generation
40
+ ##############################################################################
41
+
42
+ def motion(controller, command, identifier=None, argument1=None, argument2=None):
43
+ """
44
+ generate the requested RS232 command sequence appropriate for the
45
+ motion controller specified
46
+
47
+ --------------------------------------------------------------------------
48
+ args
49
+ controller : string
50
+ name of the controller
51
+ command : string
52
+ name of the command to be fetched
53
+ identifier : int
54
+ this may be either the controller axis to which the stage is
55
+ connected (for esp30 or mm4006), or the controller address in the
56
+ case of the smc100
57
+ argument1 : numeric
58
+ a parameter to substitute into the command string
59
+ argument2 : numeric
60
+ a parameter to substitute into the command string
61
+ --------------------------------------------------------------------------
62
+ returns : string
63
+ a valid command that can be sent to the controller over RS232
64
+ --------------------------------------------------------------------------
65
+ """
66
+ terminate = '\r\n'
67
+ axis = address = identifier
68
+
69
+ ##########################################################################
70
+ # Language: proprietary
71
+ #
72
+ # "The maximum number of characters allowed on a command line is 80."
73
+ # ESP300.pdf, p. 3-8
74
+ esp30 = {
75
+ 'is stage moving': f'{axis}MD?',
76
+ 'motor on': f'{axis}MO',
77
+ # p.3.98, response: 1 (ON) 0 (OFF)
78
+ 'check motor on': f'{axis}MO?',
79
+ # argument1: decimal.Decimal, absolute position
80
+ 'move absolute': f'{axis}PA{argument1}',
81
+ # argument1: decimal.Decimal, relative distance to move
82
+ 'move relative': f'{axis}PR{argument1}',
83
+ 'move to home position': f'{axis}MO;{axis}OR1',
84
+ 'read controller version': 'VE',
85
+ 'read error': 'TE?',
86
+ 'read position': f'{axis}TP?',
87
+ 'read stage identifier': f'{axis}ID?',
88
+ 'set speed': (
89
+ # argument1: int, acceleration
90
+ # argument2: int, velocity
91
+ f'{axis}AC{argument1};'
92
+ f'{axis}AG{argument1};'
93
+ f'{axis}VU{argument2};'
94
+ f'{axis}VA{argument2};'
95
+ f'{axis}OL{argument2};'
96
+ f'{axis}OH{argument2}{terminate}'
97
+ f'{axis}AC?;'
98
+ f'{axis}AG?;'
99
+ f'{axis}VU?;'
100
+ f'{axis}VA?;'
101
+ f'{axis}OL?;'
102
+ f'{axis}OH?'),
103
+ 'software limits check': f'{axis}ZS?',
104
+ 'software limits disable': f'{axis}ZS4H',
105
+ 'stop motion': f'{axis}ST'}
106
+
107
+ ##########################################################################
108
+ # Language: proprietary
109
+ #
110
+ # "A single command line ... may not exceed 110 characters."
111
+ # MM4K6_701_E3.pdf, p. 3.16
112
+ mm4006 = {
113
+ 'is stage moving': f'{axis}TS',
114
+ 'motor on': f'{axis}MO',
115
+ # check motor status, MM4K6_701_E3.pdf, p.3.86
116
+ # bit 1 motor status: 0 (ON) 1 (OFF)
117
+ 'check motor on': f'{axis}MS',
118
+ # argument1: decimal.Decimal, absolute position
119
+ 'move absolute': f'{axis}PA{argument1}',
120
+ # argument1: decimal.Decimal, relative distance to move
121
+ 'move relative': f'{axis}PR{argument1}',
122
+ 'move to home position': f'{axis}MO;{axis}OR',
123
+ 'read controller version': 'VE',
124
+ 'read error': 'TE',
125
+ 'read position': f'{axis}TP',
126
+ 'read stage identifier': f'{axis}TS',
127
+ 'set speed': (
128
+ # argument1: int, acceleration
129
+ # argument2: int, velocity
130
+ f'{axis}AC{argument1};'
131
+ f'{axis}VU{argument2};'
132
+ f'{axis}VA{argument2};'
133
+ f'{axis}OL{argument2};'
134
+ f'{axis}OH{argument2}{terminate}'
135
+ f'{axis}AC?;'
136
+ f'{axis}VU?;'
137
+ f'{axis}VA?;'
138
+ f'{axis}OL?;'
139
+ f'{axis}OH?'),
140
+ 'stop motion': f'{axis}ST'}
141
+
142
+ ##########################################################################
143
+ # Language: proprietary
144
+ smc = {
145
+ 'is stage moving': f'{address}TS',
146
+ 'motor on': f'{address}MM1',
147
+ 'check motor on': f'{address}MM1?',
148
+ # argument1: decimal.Decimal, absolute position
149
+ 'move absolute': f'{address}PA{argument1}',
150
+ # argument1: decimal.Decimal, relative distance to move
151
+ 'move relative': f'{address}PR{argument1}',
152
+ 'move to home position': f'{address}TS{terminate}{address}OR',
153
+ 'move to home position 2': '',
154
+ 'read controller version': f'{address}VE',
155
+ 'read error': f'{address}TE',
156
+ 'read position': f'{address}TP',
157
+ 'read stage identifier': f'{address}ID?',
158
+ 'stop motion': f'{address}ST'}
159
+
160
+ instructions = {'esp300': esp30, 'mm4006': mm4006, 'smc': smc}
161
+
162
+ # sanity checks
163
+ first = instructions.get(controller)
164
+ assert first is not None, 'unrecognised controller name'
165
+ second = first.get(command)
166
+ assert second is not None, 'unrecognised command'
167
+ cmd_string = instructions[controller][command]
168
+ assert 'None' not in cmd_string, 'missing argument to function'
169
+
170
+ return bytes(cmd_string + terminate, 'utf-8')
171
+
172
+
173
+ ##############################################################################
174
+ # power supply command string generation
175
+ ##############################################################################
176
+
177
+ @functools.lru_cache(maxsize=64)
178
+ def power(model, command, argument1=None, argument2=None, channel=None):
179
+ """
180
+ generate the requested RS232 command sequence appropriate for the
181
+ power supply specified
182
+
183
+ Manufacturers' implementations of Standard Commands for Programmable
184
+ Instruments (SCPI) all seem to have their own quirks, which is why there
185
+ is no generic SCPI command sequence, and each power supply has its own
186
+ dictionary.
187
+
188
+ --------------------------------------------------------------------------
189
+ args
190
+ model : string
191
+ power supply model
192
+ command : string
193
+ name of the command to be fetched
194
+ argument1 : numeric/string
195
+ a parameter to substitute into the command string
196
+ argument2 : numeric/string
197
+ a parameter to substitute into the command string
198
+ channel : string
199
+ channel name, the caller is required to use this for
200
+ multichannel power supplies, but may omit it for single-channel
201
+ --------------------------------------------------------------------------
202
+ returns : string
203
+ a valid command that can be sent to the psu over RS232
204
+ --------------------------------------------------------------------------
205
+ """
206
+ terminate = '\n' if model == '2614b' else '\r\n'
207
+
208
+ ##########################################################################
209
+ # Language: Standard Commands for Programmable Instruments (SCPI)
210
+ keithley_2410 = {
211
+ # returns '0' (not in compliance) or '1' (in compliance) (p. 18-70)
212
+ 'check compliance': ':CURR:PROT:TRIP?',
213
+ # returns '0' (disabled - no restrictions) or '1' (enabled)
214
+ 'check interlock': ':OUTP:INT:STAT?',
215
+ # returns '0' (output off) or '1' (output on)
216
+ 'check output': ':OUTP?',
217
+ 'clear event registers': '*CLS',
218
+ # compliance definitions on p. 6-5
219
+ 'configure': (
220
+ # clear all event registers and error queue
221
+ f'*CLS;'
222
+ # when reading status registers, use decimal format
223
+ f':FORM:SREG ASC;'
224
+ # select voltage source
225
+ f':SOUR:FUNC VOLT;'
226
+ # set fixed voltage source mode
227
+ f':SOUR:VOLT:MODE FIXED;'
228
+ # set voltage range to 1000V
229
+ f':SOUR:VOLT:RANG 1000;'
230
+ # set psu to output given voltage
231
+ f':SOUR:VOLT:LEV {argument1};'
232
+ # set power supply's current limit
233
+ f':CURR:PROT {argument2};'
234
+ # set current measurement range to match compliance value
235
+ f':CURR:RANG:UPP {argument2};'
236
+ # and make sure the measurement range stays constant
237
+ f':CURR:RANG:AUTO OFF'),
238
+ 'configure range': (
239
+ # clear all event registers and error queue
240
+ '*CLS;'
241
+ # when reading status registers, use decimal format
242
+ ':FORM:SREG ASC;'
243
+ # select voltage source
244
+ ':SOUR:FUNC VOLT;'
245
+ # set fixed voltage source mode
246
+ ':SOUR:VOLT:MODE FIXED;'
247
+ # set voltage range to 1000V
248
+ ':SOUR:VOLT:RANG 1000'),
249
+ 'identify': '*CLS;*IDN?',
250
+ 'output off': ':OUTP OFF',
251
+ 'output on': ':OUTP ON',
252
+ 'read measured vi': (
253
+ # set concurrent measurement mode on
254
+ f':FUNC:CONC 1;'
255
+ # measure actual dc voltage and current
256
+ f':FUNC:ON "VOLT:DC","CURR:DC"{terminate}'
257
+ # read value string
258
+ f':READ?'),
259
+ 'read set voltage': (
260
+ # disable concurrent measurement mode
261
+ f':FUNC:CONC 0;'
262
+ # measure actual dc current (forces set voltage to appear in index 0)
263
+ f':FUNC:ON "CURR:DC"{terminate}'
264
+ # read value string
265
+ f':READ?'),
266
+ 'read voltage': '*CLS;:READ?',
267
+ 'reset': '*RST',
268
+ # see p. 18-69 (p. 449 in PDF)
269
+ 'get current limit': ':CURR:PROT:LEV?',
270
+ # compliance definitions on p. 6-5
271
+ 'set current limit': (
272
+ # set power supply's current limit
273
+ f':CURR:PROT {argument1};'
274
+ # set current measurement range to match compliance value
275
+ f':CURR:RANG:UPP {argument1};'),
276
+ # argument1: power supply output 'ON' or 'OFF'
277
+ 'set output': f':OUTP {argument1}',
278
+ # argument1: use power supply 'REAR' or 'FRON' output
279
+ 'set route': f':ROUT:TERM {argument1}',
280
+ 'set voltage': (
281
+ # clear all event registers and error queue
282
+ f'*CLS;'
283
+ # set psu voltage
284
+ # argument1: decimal.Decimal, voltage
285
+ f':SOUR:VOLT:LEV {argument1}'),
286
+ 'set voltage and range': (f':SOUR:FUNC VOLT;'
287
+ f':SOUR:VOLT:MODE FIXED;'
288
+ f':SOUR:VOLT:RANG 1000;'
289
+ # argument1: decimal.Decimal, voltage
290
+ f':SOUR:VOLT:LEV {argument1}')}
291
+
292
+ ##########################################################################
293
+ # Language: Lua
294
+ keithley_2614b = {
295
+ # example response: 'true' or 'false' (p. 11-238)
296
+ 'check compliance': f'print(smu{channel}.source.compliance)',
297
+ # Without hardware interlock: '0.00000e+00'
298
+ # with hardware interlock: '2.04800e+03'
299
+ 'check interlock': 'print(status.measurement.condition)',
300
+ # returns '0.00000e+00' (off) or 1.00000e+00' (on)
301
+ 'check output': f'print(smu{channel}.source.output)',
302
+ # 2614b also has separate commands for model and serial
303
+ # e.g. m=localnode.model;s=localnode.serialno;print(m,s)
304
+ 'clear event registers': 'errorqueue.clear()',
305
+ # argument1: decimal.Decimal, voltage
306
+ 'configure': (f'beeper.enable=beeper.OFF;'
307
+ f'smu{channel}.source.func=smu{channel}.OUTPUT_DCVOLTS;'
308
+ f'smu{channel}.source.rangev=200;'
309
+ f'smu{channel}.source.autorangev=smu{channel}.AUTORANGE_OFF;'
310
+ f'smu{channel}.source.limiti={argument2};'
311
+ f'smu{channel}.source.levelv={argument1};'
312
+ f'smu{channel}.measure.autorangei=smu{channel}.AUTORANGE_ON;'
313
+ f'smu{channel}.source.output=smu{channel}.OUTPUT_ON;'
314
+ f'display.smu{channel}.measure.func=display.MEASURE_DCAMPS'),
315
+ 'configure range': (f'beeper.enable=beeper.OFF;'
316
+ f'smu{channel}.source.func=smu{channel}.OUTPUT_DCVOLTS;'
317
+ f'smu{channel}.source.rangev=200;'
318
+ f'smu{channel}.source.autorangev=smu{channel}.AUTORANGE_OFF;'
319
+ f'smu{channel}.measure.autorangei=smu{channel}.AUTORANGE_ON;'
320
+ f'display.smu{channel}.measure.func=display.MEASURE_DCAMPS'),
321
+ # example response '1.00000e-01'
322
+ 'get current limit': f'print(smu{channel}.source.limiti)',
323
+ # argument1: decimal.Decimal, voltage
324
+ 'set current limit': f'smu{channel}.source.limiti={argument1}',
325
+ # example response: 'Keithley Instruments SMU 2614B - 4428182'
326
+ 'identify': ('errorqueue.clear();'
327
+ 'print(localnode.description)'),
328
+ # argument1: string, channel 'a' or 'b'
329
+ 'output off': f'smu{channel}.source.output=smu{channel}.OUTPUT_OFF',
330
+ # argument1: string, channel 'a' or 'b'
331
+ 'output on': f'smu{channel}.source.output=smu{channel}.OUTPUT_ON',
332
+ 'read measured vi': f'print(smu{channel}.measure.v(),'
333
+ f'smu{channel}.measure.i())',
334
+ # argument1: string, channel 'a' or 'b'
335
+ # example response: '6.43730e-13'
336
+ 'read current': f'print(smu{channel}.measure.i())',
337
+ # example response: '4.00000e+00'
338
+ 'read set voltage': f'print(smu{channel}.source.levelv)',
339
+ # argument1: string, channel 'a' or 'b'
340
+ # example response: '1.00011e+01'
341
+ 'read voltage': f'print(smu{channel}.measure.v())',
342
+ # reset channel defaults
343
+ 'reset': 'localnode.reset()',
344
+ # argument1: string, channel 'a' or 'b'
345
+ # argument1: decimal.Decimal, voltage value
346
+ 'set voltage': f'smu{channel}.source.levelv={argument1}',
347
+ # used for clearing anything in the PSU's command buffer
348
+ 'terminator only': ''}
349
+
350
+ ##########################################################################
351
+ # Language: proprietary
352
+ #
353
+ # Cannot chain multiple commands for ISEG SHQ in the same command
354
+ # string as can be done for Keithley - particularly this affects
355
+ # initial configuration - all commands must be called individually.
356
+ iseg_shq = {
357
+ 'check compliance': f'S{channel}',
358
+ 'check interlock': f'S{channel}',
359
+ 'check module status': f'T{channel}',
360
+ 'check output': f'S{channel}',
361
+ 'configure': '',
362
+ # e.g. returns '484216;3.09;4000V;3mA' for ISEG SHQ 224M (2x 4kV / 2mA)
363
+ 'identify': '#',
364
+ 'read current': f'I{channel}',
365
+ # read maximum voltage rate of change
366
+ 'read max rate of change': f'V{channel}',
367
+ 'read voltage': f'U{channel}',
368
+ # for 100V ('D1=100') returns 01000-01, for 1100V (D1='1100') 11000-01
369
+ 'read set voltage': f'D{channel}',
370
+ # part of 'configure' for ISEG SHQ
371
+ # set auto ramp
372
+ # (as soon as voltage is set with D command, move to that voltage)
373
+ 'set auto ramp': f'A{channel}=8',
374
+ # set inter-character delay to minimum (1ms)
375
+ 'set char delay': 'W=1',
376
+ # Unlike Keithley, ISEG sets output channel polarity with hardware
377
+ # screws on the rear panel.
378
+ # All voltages specified must be positive, with a maximum of 2
379
+ # decimal places.
380
+ # argument1: string, channel '1' or '2'
381
+ # argument1: decimal.Decimal, voltage value
382
+ 'set voltage': f'D{channel}={argument1}',
383
+ # set maximum voltage rate of change to the fastest available 255V/s
384
+ 'set voltage max rate of change': f'V{channel}={argument1}',
385
+ # ISEG SHQ requires an initial CRLF to synchronise
386
+ 'synchronise': ''}
387
+
388
+ ##########################################################################
389
+ # Language: Standard Commands for Programmable Instruments (SCPI)
390
+ #
391
+ # dual-output DC power supply 0-35V, 0.8A and 0-60V, 0.5A
392
+ # e3647a can accept as many queries as needed with only a single read
393
+ agilent_e3647a = {
394
+ 'check output': ':OUTP?',
395
+ 'clear event registers': '*CLS',
396
+ 'identify': '*IDN?',
397
+ 'output off': ':OUTP OFF',
398
+ 'output on': ':OUTP ON',
399
+ 'read current': f':INST:SEL OUT{channel};:MEAS:CURR?',
400
+ 'read measured vi': f':INST:SEL OUT{channel};:MEAS:VOLT?;:MEAS:CURR?',
401
+ 'read set voltage': ':VOLT?',
402
+ 'read voltage': f':INST:SEL OUT{channel};:MEAS:VOLT?',
403
+ # argument1: decimal.Decimal, voltage value
404
+ 'set voltage': (f':INST:SEL OUT{channel};'
405
+ f':VOLT:RANG LOW;'
406
+ f':SOUR:VOLT {argument1}')}
407
+
408
+ ##########################################################################
409
+ # Language: Standard Commands for Programmable Instruments (SCPI)
410
+ #
411
+ # single-output DC power supply
412
+ # e3634a expects response to be read back after each query and MUST be put
413
+ # into remote mode before MEAS can be used
414
+ agilent_e3634a = {
415
+ 'check output': ':OUTP?',
416
+ 'clear event registers': '*CLS',
417
+ 'identify': '*IDN?',
418
+ 'output off': 'OUTP OFF',
419
+ 'output on': 'OUTP ON',
420
+ 'read current': f'SYST:REM{terminate}MEAS:CURR?',
421
+ 'read set voltage': 'VOLT?',
422
+ 'read voltage': f':SYST:REM{terminate}:MEAS:VOLT?',
423
+ 'set remote': 'SYST:REM',
424
+ # argument1: decimal.Decimal, voltage value
425
+ 'set voltage': (f'VOLT:RANG LOW{terminate}'
426
+ f'SOUR:VOLT {argument1}')}
427
+
428
+ ##########################################################################
429
+ # Language: Standard Commands for Programmable Instruments (SCPI)
430
+ #
431
+ # These are branded as Rohde & Schwarz but identify as Hameg
432
+ # quad-output DC power supply, 0-32V, 10A
433
+ hameg_hmp4040 = {
434
+ 'check output': f'INST OUT{channel}{terminate}OUTP?',
435
+ 'clear event registers': '*CLS',
436
+ 'identify': '*IDN?',
437
+ 'output off': f'INST OUT{channel}{terminate}OUTP OFF',
438
+ 'output on': f'INST OUT{channel}{terminate}OUTP ON',
439
+ 'output off all': 'OUTP:GEN OFF',
440
+ 'output on all': 'OUTP:GEN ON',
441
+ 'read current': f'INST OUT{channel}{terminate}MEAS:CURR?',
442
+ 'read set voltage': f'INST OUT{channel}{terminate}VOLT?',
443
+ 'read voltage': f'INST OUT{channel}{terminate}MEAS:VOLT?',
444
+ 'reset': '*RST',
445
+ 'set remote': 'SYST:REM',
446
+ # argument1: decimal.Decimal, voltage value
447
+ # note that a negative sign supplied as part of the voltage value is
448
+ # discarded by the power supply, e.g. -4.1V will set a value of 4.1V
449
+ 'set voltage': f'INST OUT{channel}{terminate}SOUR:VOLT {argument1}',
450
+ #
451
+ # Additions for HMP4040 constant current mode.
452
+ #
453
+ # "The R&S HMP switches to the CC mode automatically, when the output
454
+ # current exceeds the maximum value Imax"
455
+ # HMPSeries_UserManual_en_02.pdf, p.31 (PDF p.46)
456
+ #
457
+ # channel button illuminated with colour:
458
+ # Green : constant voltage mode (CV)
459
+ # Red : constant current mode (CC)
460
+ #
461
+ # STAT:QUES:COND? returns a 16-bit register value
462
+ # Bit 0 is set while the instrument is in constant current mode.
463
+ # Bit 1 is set while the instrument is in constant voltage mode.
464
+ 'read channel mode': f'STAT:QUES:INST:ISUM{channel}:COND?',
465
+ 'get current limit': f'INST OUT{channel}{terminate}CURR?',
466
+ # argument1: float, amps (no scientific representation)
467
+ 'set current limit': f'INST OUT{channel}{terminate}CURR {argument1}',
468
+ # argument1: float, amps
469
+ 'set current step': f'INST OUT{channel}{terminate}CURR:STEP {argument1}',
470
+ 'read current step': f'INST OUT{channel}{terminate}CURR:STEP?',
471
+ 'current step down': f'INST OUT{channel}{terminate}CURR DOWN',
472
+ 'current step up': f'INST OUT{channel}{terminate}CURR UP'}
473
+
474
+ instructions = {
475
+ '2410': keithley_2410, '2614b': keithley_2614b,
476
+ 'shq': iseg_shq,
477
+ 'e3647a': agilent_e3647a, 'e3634a': agilent_e3634a,
478
+ 'hmp4040': hameg_hmp4040
479
+ }
480
+
481
+ # sanity checks
482
+ first = instructions.get(model)
483
+ assert first is not None, 'unrecognised model name'
484
+ second = first.get(command)
485
+ assert second is not None, 'unrecognised command'
486
+ cmd_string = instructions[model][command]
487
+ assert 'None' not in cmd_string, 'missing argument to function'
488
+
489
+ return bytes(cmd_string + terminate, 'utf-8')
490
+
491
+
492
+ ##############################################################################
493
+ # general instrumentation
494
+ ##############################################################################
495
+
496
+ @functools.lru_cache(maxsize=64)
497
+ def instrument(model, command, argument1=None, argument2=None):
498
+ """
499
+ generate the requested RS232 command sequence appropriate for the
500
+ power supply specified
501
+
502
+ Manufacturers' implementations of Standard Commands for Programmable
503
+ Instruments (SCPI) all seem to have their own quirks, which is why there
504
+ is no generic SCPI command sequence, and each power supply has its own
505
+ dictionary.
506
+
507
+ --------------------------------------------------------------------------
508
+ args
509
+ model : string
510
+ instrument model
511
+ command : string
512
+ name of the command to be fetched
513
+ argument1 : numeric/string
514
+ a parameter to substitute into the command string
515
+ argument2 : numeric/string
516
+ a parameter to substitute into the command string
517
+ --------------------------------------------------------------------------
518
+ returns : string
519
+ a valid command that can be sent to the psu over RS232
520
+ --------------------------------------------------------------------------
521
+ """
522
+ terminate = '\r\n'
523
+
524
+ ##########################################################################
525
+ # Language: Standard Commands for Programmable Instruments (SCPI)
526
+ #
527
+ # Note that front/rear terminal usage can only be set using the front
528
+ # panel button
529
+ dmm6500 = {
530
+ 'configure to read capacitance': (
531
+ 'dmm.measure.func=dmm.FUNC_CAPACITANCE;'
532
+ 'dmm.measure.autorange=dmm.ON'
533
+ ),
534
+ 'configure to read ac current': (
535
+ 'dmm.measure.func=dmm.FUNC_AC_CURRENT;'
536
+ 'dmm.measure.autorange=dmm.ON'
537
+ ),
538
+ 'configure to read dc current': (
539
+ 'dmm.measure.func=dmm.FUNC_DC_CURRENT;'
540
+ 'dmm.measure.autorange=dmm.ON'
541
+ ),
542
+ 'configure to read resistance': (
543
+ 'dmm.measure.func=dmm.FUNC_RESISTANCE;'
544
+ 'dmm.measure.autorange=dmm.ON'
545
+ ),
546
+ # Auto range not available for this function.
547
+ # This is just a placeholder, and will need proper configuration later.
548
+ # See reference manual p.14-68 (p.786 in PDF).
549
+ 'configure to read temperature': (
550
+ 'dmm.measure.func=dmm.FUNC_TEMPERATURE;'
551
+ 'dmm.measure.unit=dmm.UNIT_CELSIUS'
552
+ ),
553
+ 'configure to read ac voltage': (
554
+ 'dmm.measure.func=dmm.FUNC_AC_VOLTAGE;'
555
+ 'dmm.measure.unit=dmm.UNIT_VOLT;'
556
+ 'dmm.measure.autorange=dmm.ON'
557
+ ),
558
+ 'configure to read dc voltage': (
559
+ 'dmm.measure.func=dmm.FUNC_DC_VOLTAGE;'
560
+ 'dmm.measure.unit=dmm.UNIT_VOLT;'
561
+ 'dmm.measure.autorange=dmm.ON'
562
+ ),
563
+ 'identify': '*IDN?',
564
+ 'read value': 'print(dmm.measure.read())',
565
+ 'reset': 'dmm.reset()',
566
+ }
567
+
568
+ instructions = {
569
+ 'dmm6500': dmm6500,
570
+ }
571
+
572
+ # sanity checks
573
+ first = instructions.get(model)
574
+ assert first is not None, 'unrecognised model name'
575
+ second = first.get(command)
576
+ assert second is not None, 'unrecognised command'
577
+ cmd_string = instructions[model][command]
578
+ assert 'None' not in cmd_string, 'missing argument to function'
579
+
580
+ return bytes(cmd_string + terminate, 'utf-8')