hitl_tester.modules.bms.dmm

Provides controls for the Rigol DM3068 DMM.

(c) 2020-2024 TurnAround Factor, Inc.

#

CUI DISTRIBUTION CONTROL

Controlled by: DLA J68 R&D SBIP

CUI Category: Small Business Research and Technology

Distribution/Dissemination Controls: PROTECTED BY SBIR DATA RIGHTS

POC: GOV SBIP Program Manager Denise Price, 571-767-0111

Distribution authorized to U.S. Government Agencies only, to protect information not owned by the

U.S. Government and protected by a contractor’s ‘limited rights’ statement, or received with the understanding that

it is not routinely transmitted outside the U.S. Government (determination made September 14, 2024). Other requests

for this document shall be referred to ACOR DLA Logistics Operations (J-68), 8725 John J. Kingman Rd., Suite 4317,

Fort Belvoir, VA 22060-6221

#

SBIR DATA RIGHTS

Contract No.:SP4701-23-C-0083

Contractor Name: TurnAround Factor, Inc.

Contractor Address: 10365 Wood Park Ct. Suite 313 / Ashland, VA 23005

Expiration of SBIR Data Rights Period: September 24, 2029

The Government's rights to use, modify, reproduce, release, perform, display, or disclose technical data or computer

software marked with this legend are restricted during the period shown as provided in paragraph (b)(4) of the Rights

in Noncommercial Technical Data and Computer Software--Small Business Innovative Research (SBIR) Program clause

contained in the above identified contract. No restrictions apply after the expiration date shown above. Any

reproduction of technical data, computer software, or portions thereof marked with this legend must also reproduce

the markings.

  1"""
  2Provides controls for the Rigol DM3068 DMM.
  3
  4# (c) 2020-2024 TurnAround Factor, Inc.
  5#
  6# CUI DISTRIBUTION CONTROL
  7# Controlled by: DLA J68 R&D SBIP
  8# CUI Category: Small Business Research and Technology
  9# Distribution/Dissemination Controls: PROTECTED BY SBIR DATA RIGHTS
 10# POC: GOV SBIP Program Manager Denise Price, 571-767-0111
 11# Distribution authorized to U.S. Government Agencies only, to protect information not owned by the
 12# U.S. Government and protected by a contractor’s ‘limited rights’ statement, or received with the understanding that
 13# it is not routinely transmitted outside the U.S. Government (determination made September 14, 2024). Other requests
 14# for this document shall be referred to ACOR DLA Logistics Operations (J-68), 8725 John J. Kingman Rd., Suite 4317,
 15# Fort Belvoir, VA 22060-6221
 16#
 17# SBIR DATA RIGHTS
 18# Contract No.:SP4701-23-C-0083
 19# Contractor Name: TurnAround Factor, Inc.
 20# Contractor Address: 10365 Wood Park Ct. Suite 313 / Ashland, VA 23005
 21# Expiration of SBIR Data Rights Period: September 24, 2029
 22# The Government's rights to use, modify, reproduce, release, perform, display, or disclose technical data or computer
 23# software marked with this legend are restricted during the period shown as provided in paragraph (b)(4) of the Rights
 24# in Noncommercial Technical Data and Computer Software--Small Business Innovative Research (SBIR) Program clause
 25# contained in the above identified contract. No restrictions apply after the expiration date shown above. Any
 26# reproduction of technical data, computer software, or portions thereof marked with this legend must also reproduce
 27# the markings.
 28"""
 29
 30from __future__ import annotations
 31
 32import time
 33from time import sleep
 34
 35from hitl_tester.modules.bms_types import SafeResource, DMMImpedance
 36
 37
 38class Dmm:
 39    """Rigol DM3068 DMM command wrapper."""
 40
 41    def __init__(self, resource: SafeResource):
 42        """
 43        Initialize the DM3068 wrapper with a specific PyVISA resource.
 44        This class does NOT open the resource, you have to open it for yourself!
 45        """
 46        self.resource = resource
 47        self.voltage_range = 20  # The maximum voltage that can be measured
 48        self.impedance_config = DMMImpedance(1, 1)
 49        self.reset()
 50
 51    def configure_voltage_normal(self):
 52        """The voltage settings for normal measurements."""
 53        self.resource.write("ZERO:AUTO ON")  # Only take the zero reading at the start. Speeds up measurement
 54        self.resource.write(f"CONF:VOLT:DC {self.voltage_range}")  # Range(V) = 0.2, 2, 20, 200, 1000, AUTO
 55        self.resource.write("VOLT:DC:NPLC 10")  # NPLC = 0.006, 0.02, 0.2, 1, 10, 100. 1PLC is equal to 0.02s
 56
 57    def configure_voltage_trigger(
 58        self, timestamps: bool = False, sample_count: int = 2000
 59    ):  # pylint: disable=unused-argument
 60        """Set up trigger parameters."""
 61        self.resource.write("ZERO:AUTO ONCE")  # Only take the zero reading at the start. Speeds up measurement
 62        self.resource.write(f"CONF:VOLT:DC {self.voltage_range},MAX")  # 20V range, with the fastest integration time
 63        self.resource.write("TRIG:SOUR IMM")  # Software trigger
 64        self.resource.write("TRIG:COUN 1")  # Only 1 group
 65        self.resource.write("TRIG:DEL 0")  # No delay in-between measurements
 66        self.resource.write(f"SAMP:COUN {sample_count}")  # Amount of samples
 67        self.resource.write("*ESE 1")  # Enable status bit (helps determine when operation completed)
 68
 69    def send_trigger(self):
 70        """Send a software trigger."""
 71
 72        self.resource.write("INIT")  # Run scans
 73        self.resource.write("*OPC")  # Set status bit to 1 when operation completes
 74
 75    def read_internal_memory(self) -> list[float]:
 76        """Return measured data from internal memory."""
 77
 78        # Wait until operation completes
 79        while int(self.resource.query("*ESR?")) & 1 != 1:
 80            time.sleep(0.1)
 81
 82        result = self.resource.query("FETC?")
 83        return list(map(float, result.split(",")))
 84
 85    @property
 86    def volts(self) -> float:
 87        """Measures DC voltage."""
 88        return float(self.resource.query("MEAS:VOLT:DC?"))
 89
 90    @property
 91    def amps_ac(self) -> float:
 92        """Measures AC current."""
 93        return float(self.resource.query("MEAS:CURR:AC?"))
 94
 95    def reset(self):
 96        """Resets the instrument."""
 97        self.resource.write("*CLS")  # Reset any errors
 98        self.resource.write("*RST")  # Put the system in a known state
 99        sleep(2)  # This instrument needs time to reset for some reason
100        self.resource.write("CMDSET AGILENT")
101        self.configure_voltage_normal()
class Dmm:
 39class Dmm:
 40    """Rigol DM3068 DMM command wrapper."""
 41
 42    def __init__(self, resource: SafeResource):
 43        """
 44        Initialize the DM3068 wrapper with a specific PyVISA resource.
 45        This class does NOT open the resource, you have to open it for yourself!
 46        """
 47        self.resource = resource
 48        self.voltage_range = 20  # The maximum voltage that can be measured
 49        self.impedance_config = DMMImpedance(1, 1)
 50        self.reset()
 51
 52    def configure_voltage_normal(self):
 53        """The voltage settings for normal measurements."""
 54        self.resource.write("ZERO:AUTO ON")  # Only take the zero reading at the start. Speeds up measurement
 55        self.resource.write(f"CONF:VOLT:DC {self.voltage_range}")  # Range(V) = 0.2, 2, 20, 200, 1000, AUTO
 56        self.resource.write("VOLT:DC:NPLC 10")  # NPLC = 0.006, 0.02, 0.2, 1, 10, 100. 1PLC is equal to 0.02s
 57
 58    def configure_voltage_trigger(
 59        self, timestamps: bool = False, sample_count: int = 2000
 60    ):  # pylint: disable=unused-argument
 61        """Set up trigger parameters."""
 62        self.resource.write("ZERO:AUTO ONCE")  # Only take the zero reading at the start. Speeds up measurement
 63        self.resource.write(f"CONF:VOLT:DC {self.voltage_range},MAX")  # 20V range, with the fastest integration time
 64        self.resource.write("TRIG:SOUR IMM")  # Software trigger
 65        self.resource.write("TRIG:COUN 1")  # Only 1 group
 66        self.resource.write("TRIG:DEL 0")  # No delay in-between measurements
 67        self.resource.write(f"SAMP:COUN {sample_count}")  # Amount of samples
 68        self.resource.write("*ESE 1")  # Enable status bit (helps determine when operation completed)
 69
 70    def send_trigger(self):
 71        """Send a software trigger."""
 72
 73        self.resource.write("INIT")  # Run scans
 74        self.resource.write("*OPC")  # Set status bit to 1 when operation completes
 75
 76    def read_internal_memory(self) -> list[float]:
 77        """Return measured data from internal memory."""
 78
 79        # Wait until operation completes
 80        while int(self.resource.query("*ESR?")) & 1 != 1:
 81            time.sleep(0.1)
 82
 83        result = self.resource.query("FETC?")
 84        return list(map(float, result.split(",")))
 85
 86    @property
 87    def volts(self) -> float:
 88        """Measures DC voltage."""
 89        return float(self.resource.query("MEAS:VOLT:DC?"))
 90
 91    @property
 92    def amps_ac(self) -> float:
 93        """Measures AC current."""
 94        return float(self.resource.query("MEAS:CURR:AC?"))
 95
 96    def reset(self):
 97        """Resets the instrument."""
 98        self.resource.write("*CLS")  # Reset any errors
 99        self.resource.write("*RST")  # Put the system in a known state
100        sleep(2)  # This instrument needs time to reset for some reason
101        self.resource.write("CMDSET AGILENT")
102        self.configure_voltage_normal()

Rigol DM3068 DMM command wrapper.

Dmm(resource: hitl_tester.modules.bms_types.SafeResource)
42    def __init__(self, resource: SafeResource):
43        """
44        Initialize the DM3068 wrapper with a specific PyVISA resource.
45        This class does NOT open the resource, you have to open it for yourself!
46        """
47        self.resource = resource
48        self.voltage_range = 20  # The maximum voltage that can be measured
49        self.impedance_config = DMMImpedance(1, 1)
50        self.reset()

Initialize the DM3068 wrapper with a specific PyVISA resource. This class does NOT open the resource, you have to open it for yourself!

resource
voltage_range
impedance_config
def configure_voltage_normal(self):
52    def configure_voltage_normal(self):
53        """The voltage settings for normal measurements."""
54        self.resource.write("ZERO:AUTO ON")  # Only take the zero reading at the start. Speeds up measurement
55        self.resource.write(f"CONF:VOLT:DC {self.voltage_range}")  # Range(V) = 0.2, 2, 20, 200, 1000, AUTO
56        self.resource.write("VOLT:DC:NPLC 10")  # NPLC = 0.006, 0.02, 0.2, 1, 10, 100. 1PLC is equal to 0.02s

The voltage settings for normal measurements.

def configure_voltage_trigger(self, timestamps: bool = False, sample_count: int = 2000):
58    def configure_voltage_trigger(
59        self, timestamps: bool = False, sample_count: int = 2000
60    ):  # pylint: disable=unused-argument
61        """Set up trigger parameters."""
62        self.resource.write("ZERO:AUTO ONCE")  # Only take the zero reading at the start. Speeds up measurement
63        self.resource.write(f"CONF:VOLT:DC {self.voltage_range},MAX")  # 20V range, with the fastest integration time
64        self.resource.write("TRIG:SOUR IMM")  # Software trigger
65        self.resource.write("TRIG:COUN 1")  # Only 1 group
66        self.resource.write("TRIG:DEL 0")  # No delay in-between measurements
67        self.resource.write(f"SAMP:COUN {sample_count}")  # Amount of samples
68        self.resource.write("*ESE 1")  # Enable status bit (helps determine when operation completed)

Set up trigger parameters.

def send_trigger(self):
70    def send_trigger(self):
71        """Send a software trigger."""
72
73        self.resource.write("INIT")  # Run scans
74        self.resource.write("*OPC")  # Set status bit to 1 when operation completes

Send a software trigger.

def read_internal_memory(self) -> list[float]:
76    def read_internal_memory(self) -> list[float]:
77        """Return measured data from internal memory."""
78
79        # Wait until operation completes
80        while int(self.resource.query("*ESR?")) & 1 != 1:
81            time.sleep(0.1)
82
83        result = self.resource.query("FETC?")
84        return list(map(float, result.split(",")))

Return measured data from internal memory.

volts: float
86    @property
87    def volts(self) -> float:
88        """Measures DC voltage."""
89        return float(self.resource.query("MEAS:VOLT:DC?"))

Measures DC voltage.

amps_ac: float
91    @property
92    def amps_ac(self) -> float:
93        """Measures AC current."""
94        return float(self.resource.query("MEAS:CURR:AC?"))

Measures AC current.

def reset(self):
 96    def reset(self):
 97        """Resets the instrument."""
 98        self.resource.write("*CLS")  # Reset any errors
 99        self.resource.write("*RST")  # Put the system in a known state
100        sleep(2)  # This instrument needs time to reset for some reason
101        self.resource.write("CMDSET AGILENT")
102        self.configure_voltage_normal()

Resets the instrument.