Calibration Memory¶
The HP 3478A has a battery backed calibration memory. It is recommended to create a backup of that memory in case the battery fails. This library supports backing up the calibration memory and also modifying it. This allows the user to change the calibration constants while the library does all the checksum calculations in the background.
Backing up the calibration memory¶
To back up the calibration memory use the following python scrip also found in the examples folder. This script will copy the calibration memory to a file called calram.bin. Note: It will not overwrite the file and error out if it already exists.
#!/usr/bin/env python3
# pylint: disable=duplicate-code
# ##### BEGIN GPL LICENSE BLOCK #####
#
# Copyright (C) 2021 Patrick Baus
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# ##### END GPL LICENSE BLOCK #####
"""This example shows how to pull the calibration memory from the HP 3478A"""
import asyncio
import logging
import sys
import typing
import warnings
import aiofiles
# Devices
from hp3478a_async import HP_3478A
from hp3478a_async.hp_3478a_helper import decode_cal_data, format_cal_string
if typing.TYPE_CHECKING:
from async_gpib import AsyncGpib
from prologix_gpib_async import AsyncPrologixGpibEthernetController, EosMode
else:
# Uncomment if using a Prologix GPIB Ethernet adapter
from prologix_gpib_async import AsyncPrologixGpibEthernetController, EosMode
# Uncomment if using linux-gpib
# from async_gpib import AsyncGpib
# Create the gpib device. We need a timeout of > 10 PLC (20 ms), because the DMM might reply to a conversion request
# and unable to reply to a status request during conversion (maximum time 10 PLC)
if "prologix_gpib_async" in sys.modules:
IP_ADDRESS = "127.0.0.1"
# pylint: disable=used-before-assignment # false positive
gpib_device = AsyncPrologixGpibEthernetController(IP_ADDRESS, pad=27, timeout=1, eos_mode=EosMode.APPEND_NONE)
elif "async_gpib" in sys.modules:
# Set the timeout to 1 second (T1s=11)
# NI GPIB adapter
gpib_device = AsyncGpib(name=0, pad=27, timeout=11) # pylint: disable=used-before-assignment # false positive
else:
raise ImportWarning("No GPIB module loaded. Please check your imports")
# This example will read the calibration memory and write it to a file named 'calram.bin'
async def main():
"""Read the calibration memory from the DMM"""
async with HP_3478A(connection=gpib_device) as hp3478a:
await hp3478a.clear() # flush all buffers
logging.getLogger(__name__).info("Reading calibration memory. This will take about 10 seconds.")
result, filehandle = await asyncio.gather(hp3478a.get_cal_ram(), aiofiles.open("calram.bin", mode="x"))
is_cal_enabled, data = decode_cal_data(result) # decode to a tuple of dicts
logging.getLogger(__name__).info("Calibration switch is enabled: %(enabled)s", {"enabled": is_cal_enabled})
logging.getLogger(__name__).info("Calibration data: %(data)s", {"data": data})
await filehandle.write(format_cal_string(result))
await filehandle.close()
logging.getLogger(__name__).info("Calibration data written to calram.bin")
# Report all mistakes managing asynchronous resources.
warnings.simplefilter("always", ResourceWarning)
logging.basicConfig(
format="%(asctime)s.%(msecs)03d %(levelname)-8s %(message)s",
level=logging.INFO, # Enable logs from the ip connection. Set to debug for even more info
datefmt="%Y-%m-%d %H:%M:%S",
)
try:
asyncio.run(main(), debug=False)
except KeyboardInterrupt:
# The loop will be canceled on a KeyboardInterrupt by the run() method, we just want to suppress the exception
pass
Writing the backup back to the DMM¶
The backup can be written back to the dmm using another script. To do so, the CAL
switch on the front panel must be
set to enable. Otherwise the memory cannot be written to.
#!/usr/bin/env python3
# pylint: disable=duplicate-code
# ##### BEGIN GPL LICENSE BLOCK #####
#
# Copyright (C) 2021 Patrick Baus
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# ##### END GPL LICENSE BLOCK #####
"""This example shows how to write the calibration memory from a file to the HP 3478A"""
import asyncio
import logging
import sys
import typing
import warnings
import aiofiles
# Devices
from hp3478a_async import HP_3478A
from hp3478a_async.hp_3478a_helper import decode_cal_data, encode_cal_data
if typing.TYPE_CHECKING:
from async_gpib import AsyncGpib
from prologix_gpib_async import AsyncPrologixGpibEthernetController, EosMode
else:
# Uncomment if using a Prologix GPIB Ethernet adapter
from prologix_gpib_async import AsyncPrologixGpibEthernetController, EosMode
# Uncomment if using linux-gpib
# from async_gpib import AsyncGpib
# Create the gpib device. We need a timeout of > 10 PLC (20 ms), because the DMM might reply to a conversion request
# and unable to reply to a status request during conversion (maximum time 10 PLC)
if "prologix_gpib_async" in sys.modules:
IP_ADDRESS = "127.0.0.1"
# pylint: disable=used-before-assignment # false positive
gpib_device = AsyncPrologixGpibEthernetController(IP_ADDRESS, pad=27, timeout=1, eos_mode=EosMode.APPEND_NONE)
elif "async_gpib" in sys.modules:
# Set the timeout to 1 second (T1s=11)
# NI GPIB adapter
gpib_device = AsyncGpib(name=0, pad=27, timeout=11) # pylint: disable=used-before-assignment # false positive
else:
raise ImportWarning("No GPIB module loaded. Please check your imports")
async def main():
"""Read the calibration memory from a file and write it to the DMM"""
# Read the calibration memory file
async with aiofiles.open("calram.bin", mode="r") as filehandle:
result = (await filehandle.read()).replace("\n", "")
is_cal_enabled, data = decode_cal_data(result) # decode to dict
async with HP_3478A(connection=gpib_device) as hp3478a:
await hp3478a.clear() # flush all buffers
is_cal_enabled, data = decode_cal_data(result) # decode to dict
data[5]["gain"] = 1.0 # Modify entry 5 (Note: This entry is not used, adjust to your liking)
result = encode_cal_data(cal_enable=is_cal_enabled, data_blocks=data) # re-encode caldata
await hp3478a.set_cal_ram(result)
logging.getLogger(__name__).info("Calibration data written to DMM")
# Report all mistakes managing asynchronous resources.
warnings.simplefilter("always", ResourceWarning)
logging.basicConfig(
format="%(asctime)s.%(msecs)03d %(levelname)-8s %(message)s",
level=logging.INFO, # Enable logs from the ip connection. Set to debug for even more info
datefmt="%Y-%m-%d %H:%M:%S",
)
try:
asyncio.run(main(), debug=False)
except KeyboardInterrupt:
# The loop will be canceled on a KeyboardInterrupt by the run() method, we just want to suppress the exception
pass
Technical details¶
The calibration memory dump is a human-readable ASCII string, that contain only printable characters. This was done by
adding 0x40
to each byte. After subtracting 0x40
, the result is a mix of bytes and
nibbles (4 bit, half-bytes). The calibration data is stored as nibbles, while
the checksum and the status of the CAL
switch have 8 bit boundaries. The first byte of the memory dump contain said
CAL
switch position and
is not part of the checksum-protected calibration data. The calibration memory is stored as
nibbles which contain
Binary-coded decimal (BCD)
encoded digits. The encoding is similar to BCD 8421. At the end there are two bytes for the checksum. The memory layout
is:
Memory Layout |
|
---|---|
0 |
1:247 |
|
Calibration data |
13 bytes per entry |
The calibration data consists of 19 entries, that have 11 bytes (22 nibbles) of data and 2 bytes for checksum:
Calram entry |
||
---|---|---|
0:5 |
6:10 |
11:12 |
Offset |
Gain |
Checksum |
The offset is standard BCD 8421 encoded. The gain field is a little more complicated. The gain is stored as a 4-bit
two’s complement signed number. Once decoded it gives the gain deviation from 1 in units of ppm. Finally the checksum
is 0xFF
minus the sum over the 11 data bytes. For implementation details check the source code of the
hp3478a_async.hp_3478a_helper
.
The 19 calibration memory entries are the following functions:
Index |
Function |
---|---|
0 |
30 mV DC |
1 |
300 mV DC |
2 |
3 V DC |
3 |
30 V DC |
4 |
300 V DC |
5 |
Not used |
6 |
V AC |
7 |
30 Ω 2W/4W |
8 |
300 Ω 2W/4W |
9 |
3 kΩ 2W/4W |
10 |
30 kΩ 2W/4W |
11 |
300 kΩ 2W/4W |
12 |
3 MΩ 2W/4W |
13 |
30 MΩ 2W/4W |
14 |
300 mA DC |
15 |
3 A DC |
16 |
Not used |
17 |
300 mA/3 A AC |
18 |
Not used |
The device ignores the unused entries and does not complain about invalid data. Typically these entries are set to
offset=0
and gain=1.0
.