Over the last few years we were getting multiple requests about automating measurements and running experiments with precision multimeters and instruments, include remote control. There are multiple ways to achieve the task and get simple script to configure single DMM and datalog samples into text file or CSV file. And whole different thing is to have more or less versatile application, that can do essentially most of things than user can do in front the instruments.
Also different instruments have own language/command sets, even often when instrument made by same manufacturer. SCPI language is the step in right direction to unify command sets, but there are still differences.
One aspect that really can benefit from automating is calibration procedures. Most of modern multimeter have lot of functions and multiple ranges within each function. And calibration of each range usually consist of two points, near zero and near full-scale. As result even with all equipment available, calibration and adjustment of typical bench-top meter, such as 6½-digit Keithley 2000 or Agilent 34401A will take hours or more, and require careful entering of many numbers using often non-convinient front panel. Remote automated script to perform performance test and calibration adjustment can be a big time saver in such case.
Professional calibration labs are very commonly have one of the commercial calibration software systems integrated to their work-flow. Fluke MET/CAL service is perhaps one of the most known. However cost of this software (thousands USD/year) and it’s support license is out of reach of occasional enthusiast, who just need calibrate few meters for own use.
So we saw this as an opportunity to develop some piece of software, which will run on Linux machine, such as popular Raspberry Pi 3 + linux-gpib to perform the communication and remote control of calibrator source and DUT meter to do performance verification and then optional calibration adjustment. Software is written in python programming language, and supports only limited number of instruments which we own. But before we dig in into the thing, it’s important to refresh some terms used across this article:
Calibrator – Source that provide stable reference signal with known uncertainty, used as a reference for tests/adjustments
Calibration – Verification of unknown unit under test measurement reading against reference signal with known uncertainty. Calibration does NOT include adjustment or altering of stored calibration offset/gain correction constants in DUT.
Adjustment – Adjust UUT to reduce measurement errors, by running manufacturer recommended procedures to update DUT’s correction constants, often stored in instrument’s non-volatile RAM.
Calibrated range/function – Range/Function of the DUT that have known and verified error. True measurement can be mathematically determined by application of correction factor to account for error.
Transfer – Process of establishing error on DUT range/function by using known signal and known error in verified range/function. For example 3.0000 V measurement accuracy can be transferred to 10V range, if reference error is verified on calibrated 3V range.
Calkit minimum requirements
Software control system requirements are rather simple:
- Raspberry Pi 3 or another similar Linux-based SBC that have USB interface for linux-gpib.
- NI GPIB-USB-HS or Agilent 83527B dongle for interfacing GPIB instrumentation network
- Internet connection for data synchronization and procedures update
- Reference source / calibrator
- DUT device
Well, no intro required
You have calkit on your 192.168.100.151 pi in folder /root/xdevs_calkit
Calkit supported devices
Sources for signals
- Fluke 5700A multifunction calibrator
- Fluke 5700A/EP multifunction calibrator
- Fluke 5720A multifunction calibrator
- Fluke 5730A multifunction calibrator
- Datron 4808 multifunction calibrator
- Time Electronics 9823 multifunction calibrator
- Fluke 5450A resistance calibrator
- HP 3245A universal source
DUT devices and multimeters
- HP/Agilent/Keysight 34401A
- HP/Agilent/Keysight 34420A
- HP/Agilent/Keysight 3456A
- HP/Agilent/Keysight 3457A
- HP/Agilent/Keysight 3458A
- Keithley 2002
- Keithley 2001
- Keithley 2001/1801
- Keithley 2000
- Keithley 2182 and 2182A
- Keithley 182
- Datron/Wavetek 1281
- Datron/Wavetek 4920
- Datron/Wavetek 4920M
- Fluke 5790A
- Fluke 8508A
- Fluke 8588A
- Advantest R6581T
- NI USB-GPIB-HS or USB-GPIB-HS+ dongle
- Agilent 82357B dongle
- HP/Agilent/Keysight E5810A or E5810B
- VXI/LXI interfaces
- BME280 THP sensor
- Fluke 1620 THP logger
\.hg – Mercurial repository storage
\css – Styles for report HTML
\sensors – Module for sensors, currently just Adafruit BME280 I2C
\.hgignore – Repository file to ignore local temporary files
\main.py – This is main entry root programm. Contains menu prompts and help placeholders
\cal.py – This is main high level procedure to run on DUT and standards
\cal_ac.py – AC-related copy of cal.py, currently only for 5700+4920M. To be merged in future cal.py.
\dut.py – This is wrapper to specific low-level DUT functions
\dut_ac.py – This is wrapper to specific low-level AC DUT functions
\f5700a.py – Low-level support module for Fluke 5700A MFC
\f5720a.py – Low-level support module for Fluke 5720A MFC
\f5790a.py – Low-level support module for Fluke 5790A AVMS, broken
\hp3458a.py – Low-level support module for HP 3458
\if_bme280.py – Low-level support module for BME280 sensor
\if_debug.py – Low-level inteface simulate, to dump commands into debug log file
\if_gpib.py – Low-level inteface to linux-gpib
\k2001.py – Low-level support module for Keithley 2001
\k2002.py – Low-level support module for Keithley 2002
\markup.py – Markup module for HTML generator
\mfc.py – This is wrapper to specific low-level MFC source functions
\siggen.py – Low-level support module for signal generator
\t9823.py – Low-level support module for Time Electronics 9823 MFC
\w4920.py – Low-level support module for Wavetek Datron 4920 AVMS
\w4920m.py – Low-level support module for Wavetek Datron 4920M AVMS
CK depends on few packages in system:
- Python 2.7
- Working linux-gpib with python binding
- BME280 sensor at I2C port
Only GPIB instruments are supported now. Specific instruments GPIB addresses are hardcoded in low-level files.
For example we want to run HP3458A tests with Fluke 5700A MFC as source.
GPIB address 1 for calibrator is set on line 101.
intf = gpib(1,"F5700")
Similar setting for GPIB address 3 is for DMM DUT, line 103:
intf = gpib(3,"3458A") Now we can run program:
root@de1soclinux:/repo/calkit# python ./main.py cal.equipment calibration toolkit CLI Using NI GPIB adapter and linux-gpib library /!\ Do not swap terminals during calibration/tests, LETHAL voltage may be present ! ********************************************************************************  - Help guide  - Detect instruments  - Initialize instruments  - Select DUT  - Run performance verification procedure on DUT  - Run user calibration procedure on DUT  - Run manufacturer calibration procedure on DUT  - Generate HTML report  - Check on calibrator F5700  - Check on AVMS F5790  - Performance test W4920M  - User calibration W4920M  - Run calibration check procedure w/reports (CAL CHK) on F5700/5720  - Generate just calibration reports  - Low-level manufacturing calibration W4920M  - Program HP 3325 to output 3V 100kHz sine, GPIB 14 [X] - Quit
Item number 12 will talk with Fluke 5700A to collect all reports into local file:
\cal_cal_report_FLUKE_5700A_before.txt \cal_check_report_FLUKE_5700A_before.txt \cal_const_report_FLUKE_5700A_before.txt
Then it will run CAL ZERO and CAL CHK procedures which takes about an hour to complete. Program have timer to wait and will show you remaining time to finish.
After calibrator completes it’s test new set of reports is readback and appended to files:
\cal_cal_report_FLUKE_5700A_after.txt \cal_check_report_FLUKE_5700A_after.txt \cal_const_report_FLUKE_5700A_after.txt \cal_stcal_report_FLUKE_5700A_after.txt \cal_stcheck_report_FLUKE_5700A_after.txt
This allow to monitor all values before and after self-check and zero self-calibration.
If only report required without actual running CAL CHK for 1 hour, one can just run Item 13.
Now let’s preconfigure HP 3458A run item 3 – it resets meter and prepares it for use.
Programm will run commands and show them on screen with
Initialize HP 3458A -WRG- TARM HOLD -WRG- TRIG SGL -WRG- PRESET NORM -WRG- OFORMAT ASCII -WRG- FUNC DCV,AUTO -WRG- NPLC 100 -WRG- AZERO ON -WRG- NRDGS 1,AUTO -WRG- MEM OFF -WRG- END ALWAYS -WRG- NDIG 9
Main entry to run performance test procedure on the HP 3458A to execute Menu item 7.
This will collect status and information about MFC and DMM and proceed with testing.
To do so main.py will enter cal_report() function in cal.py file, which will in turn look onto mfc.py and dut.py to access lower-level device-specific code.
In current mfc.py we see binding to Fluke 5700A MFC:
from f5700a import * import time # Module-wide definitions mfc_mfg = f5700_mfg_id mfc_date = time.strftime("%d %B %Y") mfc_model = f5700_model_id mfc_ambient_t = "+27.3 °C" mfc_serno = "0" mfc_rh = "31 %" mfc_idno = "XC1" mfc_testmode = 0
Function names redefined accordingly here as well:
... def mfc_out_dci(): pass def mfc_dormant(): pass def mfc_selfcal(): pass def mfc_report_aftercal(): pass mfc_uncert = f5700_uncert mfc_measout = f5700_measout mfc_selfcal = f5700_selfcal mfc_report_aftercal = f5700_report_aftercal mfc_dormant = f5700_dormant ...
Similar is for UUT, which is wrapped in dut.py:
from hp3458a import * # Module-wide definitions dut_mfg = hp3458_mfg_id dut_date = "02 Jan 2017" dut_model = hp3458_model_id dut_ambient_t = "+25.5 °C" #dut_serno = "1167961" dut_serno = "B" dut_rh = "45.6 %" dut_idno = "B-test" dut_testmode = "PERFVAL" ... ... ... dut_acv_ranges = hp3458_acv_ranges dut_acv_unc = hp3458_acv_unc ...
Main code to execute specific performance tests is defined in low-level module of DUT – in our case – hp3458a.py.
Main function executed from cal.py is hp3458_perftest(). Let’s look closer on it:
dutresult =  print ("\033[1;31m HP 3458B verification \033[1;39m") hp3458_dutinit() hp3458_vfd_on("CALKIT START...") dutresult.append(hp3458_reztest()) #0 Here we execute resistance short test dutresult.append(hp3458_restest()) #1 Here we execute resistance 4W test ... dutresult.append([1000000000.0]) #2 This is dummy for 1Gohm resistance test (need manual connections of 1G STD) hp3458_dcv_conf(10,100) dutresult.append(hp3458_dcztest()) #3 Here we execute DCV Zero test and noise check dutresult.append(hp3458_dcvtest()) #4 Here we execute DCV positive and negative test dutresult.append(hp3458_acztest()) #5 Here we execute AC Zero test (currently dummy) dutresult.append(hp3458_ana_acvtest()) #6 Here we execute AC Analog quick check dutresult.append(hp3458_sync_acvtest()) #7 Here we execute main AC SYNC test, with high voltage/frequency limits dutresult.append(hp3458_dcitest()) #4 Here we execute current test, need swap terminals with open("bm2_result.log", 'a') as dile: for ival in xrange (0, len(dutresult)): dile.write ("%d %s" % (ival, dutresult[ival])) dile.write ("\r\n") # This is storage for raw test results data dile.close() return dutresult # Array of measured results returned to cal.py
Execution order of test is important, because cal.py data parser expects test data in specific array locations. Order and amount of tests defined in dut.py:
duttest_item_str= ["DCV","ACV","LOACV","DCI","RES","1G","DCZ","ACZ","REZ"] dut_testid_map = [ 4, 6, 7, 8, 1, 2, 3, 5, 0]
As tests proceed, programm will ask you for specific operations, such as change front/rear terminals or swap cables for current measurements.
Be sure to check that calibrator does not source dangerous voltages before
Current HP 3458A test procedure:
Test started by entering Menu item 7. Self-test (75 seconds) and ACAL ALL (835 seconds) procedures will be executed prior to actual performance verification.
First test, using MFC’s zero output. Each test point is measured multiple times and only last sample is taken. There are also delays to allow instruments to settle.
-WRG- OUT 0 OHM External sense on MFC is ON -WRG- EXTSENSE ON -WRG- OPER Select REAR SHORT 4W terminals and press ENTER
At this stage ohmic zero will be measured as reference point, using two ranges, 10 Ω and 10 KΩ
-WRG- OCOMP 1 -WRG- DELAY 0 3458A OCOMP ENABLED Configure OHMF range 10 with NPLC 0 OHMF 10.000000 -WRG- OHMF 10.000000 Set resistance to 1.00000000E+01 Ohm 9.011535822e-06 9.011535822e-06 4.12058298e-07 -1.19855218e-05
Next step is resistance test, to measure MFC outputs from 1 Ω to 100 MΩ. 100 MΩ is measured without OCOMP.
Configure OHMF range 10000 with NPLC 100 OHMF 10000.000000 NPLC 100.000000 Set resistance to 1.00000000E+04 Ohm 10000.05659 10000.05659 10000.05731 10000.05537 10000.05537 10000.05517
After resistance test uncertainty from calibrator using UNCERT? command is collected. No measurements by 3458A done at this step.
Next test is DCV Zero checks on all ranges and noise check. Noise check is simply collecting multiple samples to check min-max deviation.
Then zero offset is measured and nulled using 3458A’s MATH NULL command. After this actual measurement points on DCV are taken, using both positive and negative voltages from calibrator, from 0.1V to 1000V.
Next is AC voltage test, following standard HP 3458A calibration report. 100 VAC range limited by frequency at 100kHz and for 1000V range is only single frequency point, 1kHz due to safety limitations.
Final test is DC current, from 100nA to 1.0A, both sourcing and sinking with MFC. Before beginning of test cable connections at DMM must be swapped, so prompt to do so will be given and program will wait for user confirmation.
After successful data collection, which takes about 20 minutes, the measurement data will be processed into HTML report page, such as cal_report_3458b2.html
Modified: Jan. 2, 2023, 10:32 p.m.