"""Interface to the Oscilloscopes NI PXIe-51xx (e.q. 5122)
:Date: |today|
:Author: Semi-ATE <info@Semi-ATE.org>
"""
import os
from pylab_ml.collate_instrument import Interface
from pylab_ml.base_instrument import InvalidInstrumentConnection
from pylab_ml.baseclass.base_natinst import NatInst
from pylab_ml.base_instrument import logger
from matplotlib import pyplot as plt
import unittest
import hightime
[docs]
class PXIe51xx(NatInst):
"""
Interface to the Oscilloscopes NI PXIe-51xx (e.q. 5122)
Until now only initialisation on poor basic functions
For more function see: https://nimi-python.readthedocs.io/en/master/niscope.html
call with: scope.inst.thefunctionname
Initialization arguments:
addr
Name from the PXI-Slot e.q. PXI1Slot4
instName
Instance Name from top
Example: Initialization
>>> scope = PXIe4138('PXI1Slot4',instName='scope') # connect and initialize instrument
>>> scope.trig_channel = 1
>>> scope.trig_level = 1.5
>>> scope.trig_slope = "POS"
>>> scope.trig_mode = "norm"
>>> scope.tdiv = 1e-3
>>> scope.tdelay = -3e-3
>>> scope.channel = 1
>>> scope.trace = True
>>> scope.vdiv = 1.0
>>> scope.offs = 0
Methods:
reset() reset
identify() instrument message, reflect address & interfade
message("") instrument message ("string") or ()
close() terminate interface
missing: get_waveform(n) get waveform data from channel n
missing: trig_oneshot(t) single measurement trigger to waveform or timeout after t s
Properties:
missing: memsize
missing: tdiv
missing: tdelay
missing: trig_mode
missing: trig_channel
missing: channel
missing: trace
missing: vdiv
missing: offs
missing: waveform
For more properties or functios see: https://nimi-python.readthedocs.io/en/master/niscope.html
scope.inst.functionname
"""
try:
import niscope
has_scope = True
except ImportError:
has_scope = False
if os.sys.platform != "win32":
logger.error("import niscope not found")
else:
logger.info("no import niscope can be found on non Windows platforms")
# Create functions or property and wrap it to the inst.funcname:
# If state != necessary state -> switch to the necessary state
# Property/Function name -> (inst.funcname (get,set), Range, Call_Functions)
# inst.funcname (get,set): Available in 'https://nimi-python.readthedocs.io/en/master/niscope.html'
# Range: None, Enum or Range value
# Call_Functions: see help in attributes
_properties = {
# 'aperture_time_units': ('aperture_time_units', 'backend.ApertureTimeUnits', {'sac': 'checkstate(uncommitted)'}),
"probeAttenuation": ("probe_attenuation", [1, 100], None),
"couplings": ("vertical_coupling", "backend.VerticalCoupling", None),
"onoff": ("channel_enabled", {"on": True, "off": False, 1: True, 0: False, True: True, False: False}, None),
"sampleRate": ("min_sample_rate", [2_000, 2_000_000_000], None),
"numberOfRecords": ("horz_num_records", [1, 1_000_000_000], None),
"numberOfPoints": ("horz_min_num_pts", [1, 1_000_000_000], None),
"numberOfSamples": ("horz_sample_rate", None, None),
"referencePosition": ("horz_record_ref_position", [0, 100], None),
"enforceRealtime": ("horz_enforce_realtime", {"on": True, "off": False}, None),
"triggerType": ("trigger_type", "backend.TriggerType", None),
"triggerModifier": ("trigger_modifier", "backend.TriggerModifier", None),
"triggerSource": ("trigger_source", {0: "0", 1: "1", "TRIG": "TRIG"}, None),
"triggerHysteresis": ("trigger_hysteresis", [0, 100], None),
"triggerLevel": ("trigger_level", [-100, 100], None),
"triggerDelay": ("trigger_delay_time", [0.0, 171.8], None),
"triggerHoldoff": ("trigger_holdoff", [0.0, 171.8], None),
"triggerSlope": ("trigger_slope", "backend.TriggerSlope", None),
"triggerCoupling": ("trigger_coupling", "backend.TriggerCoupling", None),
"terminals": ("channel_terminal_configuration", "backend.TerminalConfiguration", None),
"impedance": ("trigger_impedance", [50, 1000000], None),
"bandwidth": ("max_input_frequency", {"Full": -1.0, "Default": 0.0, "20MHz": 20_000_000.0, "125MHz": 125_000_000.0}, None),
"range": ("vertical_range", {0.04: 0.04, 0.1: 0.1, 0.2: 0.2, 0.4: 0.4, 1.0: 1.0, 2.0: 2.0, 4.0: 4.0, 10.0: 10.0, 20.0: 20.0, 40.0: 40.0}, None),
"offset": ("vertical_offset", [-30, 30], None),
}
interchoices = [Interface.pxie]
[docs]
def __init__(self, addr=None, identify=False, instName=None):
"""
Initialize the instrument and connect to it.
Parameters
----------
addr : str
Name from the PXI-Slot e.q. PXI1Slot4
identify : bool
If True, the instrument will be identified during initialization. Default is False.
instName : str
Instance Name from top
"""
if not self.has_scope:
msg = "\nPXIe50xx not usable!! missing nidscope\n"
msg = msg + "for installing niscope:\n"
msg = msg + " Start anaconda prompt and write: python –m pip install niscope\n"
msg = msg + "For more infomation see: http://nimi-python.readthedocs.io\n"
raise InvalidInstrumentConnection(msg)
kwargs = {"addr": addr, "backend": self.niscope, "identify": identify, "instName": instName}
super().__init__(**kwargs)
logger.debug("Class {}".format(self.__class__.__name__))
self.msg_row_col = (1, 20)
self.setup_inst()
inputTimeOut = 10
# minTimeOut = (self.numberOfSamples * (self.numberOfRecords + 1)) / self.sampleRate
# self.timeout = max(inputTimeOut, minTimeOut)
[docs]
def setup_inst(self):
"""Setup instrument settings"""
super().setup_inst()
self.mqtt_all = [""]
channels = []
if self.channels is None:
self.channels = "0,1"
temp = self.channels.split(",")
for ch in temp:
channels.append(int(ch))
self.channels = channels
self._create_channelinst(channels)
self.channel = channels[0]
[docs]
def measure(self, channel=[0, 1], num_of_record=1, start_record=0, num_of_samples=None, offset=0):
"""
Start to fetch data from the Scope.
Parameters
----------
channel : TYPE, (list or int)
DESCRIPTION. The default is [0, 1] to measure from both the channels. Can also give '0' or '1' to measure from separate channel.
num_of_record : TYPE, int
DESCRIPTION. The default is 1. Number of records to fetch. Use -1 to fetch all configured records.
start_record : TYPE, int
DESCRIPTION. The default is 0. Zero-based index of the first record to fetch.
num_of_samples : TYPE, int
DESCRIPTION. The default is None. The maximum number of samples to fetch for each waveform.
If the acquisition finishes with fewer points than requested, some devices return partial data if the acquisition finished, was aborted.
If it fails to complete within the timeout period, the method raises.
offset : TYPE, int
DESCRIPTION. The default is 0. Offset in samples to start fetching data within each record. The offset can be positive or negative.
Returns
-------
x : list
Contains list of Time values.
y : list
Contains list of Voltage values.
"""
if isinstance(channel, list):
channel_map = map(str, channel)
temp = "-".join(list(channel_map))
else:
temp = str(channel)
waveforms = self.inst.channels[temp].fetch(
num_samples=num_of_samples, record_number=start_record, offset=offset, num_records=num_of_record, timeout=hightime.timedelta(seconds=20)
)
if isinstance(channel, int):
x = []
for i in range(0, len(waveforms[0].samples)):
temp = i * waveforms[0].x_increment
x.append(temp)
y = waveforms[0].samples.tolist()
elif len(channel) == 2:
x1 = []
x2 = []
x = []
y = []
for i in range(0, len(waveforms[0].samples)):
temp1 = i * waveforms[0].x_increment
x1.append(temp1)
y1 = waveforms[0].samples.tolist()
for i in range(0, len(waveforms[1].samples)):
temp2 = i * waveforms[1].x_increment
x2.append(temp2)
y2 = waveforms[1].samples.tolist()
x.extend([x1, x2])
y.extend([y1, y2])
return x, y
[docs]
def plot_scope_data(self, time, voltage):
"""
Plot graph between Time and Voltage.
Parameters
----------
time : list
Input a list of Time values for x-axis.
voltage : list
Input a list of Voltage values for y-axis.
Returns
-------
None.
"""
if any(isinstance(i, list) for i in voltage):
plt.figure(figsize=(6.4, 4.8), dpi=300)
plt.plot(time[0], voltage[0], "bo", markersize=0.1, alpha=0.1, label="Channel 0")
plt.plot(time[1], voltage[1], "ro", markersize=0.1, alpha=0.1, label="Channel 1")
plt.title("Scope Measurements")
plt.xlabel("Time (s)")
plt.ylabel("Voltage (V)")
plt.legend()
plt.grid(linestyle="--")
plt.show()
else:
plt.figure(figsize=(6.4, 4.8), dpi=300)
plt.plot(time, voltage, "bo", markersize=0.1, alpha=0.1)
plt.title("Scope Measurements")
plt.xlabel("Time (s)")
plt.ylabel("Voltage (V)")
plt.grid(linestyle="--")
plt.show()
[docs]
def reset(self):
""" Reset the instrument to its default state. This method will clear all settings and configurations, returning the instrument to its factory defaults."""
super().reset()
[docs]
class PXIe5114(PXIe51xx):
""" Interface to the Oscilloscopes NI PXIe-51xx (e.q. 5114) """
_properties = {
"sampleRate": ("min_sample_rate", [200_000_000, 200_000_000], None),
"bandwidth": ("max_input_frequency", {"Full": -1.0, "Default": 0.0, "20MHz": 20_000_000.0, "125MHz": 125_000_000.0}, None),
"range": ("vertical_range", {0.04: 0.04, 0.1: 0.1, 0.2: 0.2, 0.4: 0.4, 1.0: 1.0, 2.0: 2.0, 4.0: 4.0, 10.0: 10.0, 20.0: 20.0, 40.0: 40.0}, None),
"offset": ("vertical_offset", [-30, 30], None),
# Max Offset values for Range values : {0.04: 0.8, 0.1: 0.8, 0.2: 0.8, 0.4: 0.8, 1.0: 8.0, 2.0: 8.0, 4.0: 8.0, 10.0: 30.0, 20.0: 25.0, 40.0: 15.0}
"probeAttenuation": ("probe_attenuation", [1, 100], None),
"couplings": ("vertical_coupling", "backend.VerticalCoupling", None),
"output": ("channel_enabled", {"on": True, "off": False}, None),
"numberOfRecords": ("horz_num_records", [1, 100_000], None),
"numberOfPoints": ("horz_min_num_pts", [1, 100_000], None),
"numberOfSamples": ("horz_sample_rate", None, None),
"referencePosition": ("horz_record_ref_position", [0, 100], None),
"enforceRealtime": ("horz_enforce_realtime", {"on": True, "off": False}, None),
"triggerType": ("trigger_type", "backend.TriggerType", None),
"triggerSource": ("trigger_source", {"CH1": "0", "CH2": "1", "CH3": "2", "CH4": "3"}, None),
"triggerHysteresis": ("trigger_hysteresis", [0, 100], None),
"triggerLevel": ("trigger_level", [-100, 100], None),
"triggerDelay": ("trigger_delay_time", [0.0, 171.8], None),
"triggerHoldoff": ("trigger_holdoff", [0.0, 171.8], None),
"triggerSlope": ("trigger_slope", "backend.TriggerSlope", None),
"triggerCoupling": ("trigger_coupling", "backend.TriggerCoupling", None),
"terminals": ("channel_terminal_configuration", "backend.TerminalConfiguration", None),
"impedance": ("trigger_impedance", [50, 1000000], None),
}
[docs]
def __init__(self, addr=None, identify=False, instName=None):
"""
Initialize the instrument and connect to it.
Parameters
----------
addr : str
Name from the PXI-Slot e.q. PXI1Slot4
identify : bool
If True, the instrument will be identified during initialization. Default is False.
instName : str
Instance Name from top
"""
if not self.has_scope:
msg = "\nPXIe51xx not usable!! missing nidscope\n"
msg = msg + "for installing niscope:\n"
msg = msg + " Start anaconda prompt and write: python –m pip install niscope\n"
msg = msg + "For more infomation see: http://nimi-python.readthedocs.io\n"
raise InvalidInstrumentConnection(msg)
super().__init__(addr=addr, identify=identify, instName=instName)
logger.debug("Class {}".format(self.__class__.__name__))
self.msg_row_col = (1, 20)
self.sampleRate = 200_000_000
self.bandwidth = "125MHz"
[docs]
class PXIe5122(PXIe51xx):
""" Interface to the Oscilloscopes NI PXIe-51xx (e.q. 5122) """
_properties = {
"sampleRate": ("min_sample_rate", [100_000_000, 100_000_000], None),
"bandwidth": ("max_input_frequency", {"Full": -1.0, "Default": 0.0, "20MHz": 20_000_000.0, "100MHz": 100_000_000.0}, None),
"range": ("vertical_range", {0.2: 0.2, 0.4: 0.4, 1.0: 1.0, 2.0: 2.0, 4.0: 4.0, 10.0: 10.0, 20.0: 20.0}, None),
"offset": ("vertical_offset", [-5, 5], None),
# Max Offset values for Range values : {0.2: 0.1, 0.4: 0.2, 1.0: 0.5, 2.0: 1.0, 4.0: 2.0, 10.0: 5.0, 20.0: 0}
"probeAttenuation": ("probe_attenuation", [1, 100], None),
"couplings": ("vertical_coupling", "backend.VerticalCoupling", None),
"output": ("channel_enabled", {"on": True, "off": False}, None),
"numberOfRecords": ("horz_num_records", [1, 100_000], None),
"numberOfPoints": ("horz_min_num_pts", [1, 100_000], None),
"numberOfSamples": ("horz_sample_rate", None, None),
"referencePosition": ("horz_record_ref_position", [0, 100], None),
"enforceRealtime": ("horz_enforce_realtime", {"on": True, "off": False}, None),
"triggerType": ("trigger_type", "backend.TriggerType", None),
"triggerSource": ("trigger_source", {"CH1": "0", "CH2": "1", "CH3": "2", "CH4": "3"}, None),
"triggerHysteresis": ("trigger_hysteresis", [0, 100], None),
"triggerLevel": ("trigger_level", [-100, 100], None),
"triggerDelay": ("trigger_delay_time", [0.0, 171.8], None),
"triggerHoldoff": ("trigger_holdoff", [0.0, 171.8], None),
"triggerSlope": ("trigger_slope", "backend.TriggerSlope", None),
"triggerCoupling": ("trigger_coupling", "backend.TriggerCoupling", None),
"terminals": ("channel_terminal_configuration", "backend.TerminalConfiguration", None),
"impedance": ("trigger_impedance", [50, 1000000], None),
}
[docs]
def __init__(self, addr=None, identify=False, instName=None):
"""
Initialize the instrument and connect to it.
Parameters
----------
addr : str
Name from the PXI-Slot e.q. PXI1Slot4
identify : bool
If True, the instrument will be identified during initialization. Default is False.
instName : str
Instance Name from top
"""
if not self.has_scope:
msg = "\nPXIe51xx not usable!! missing nidscope\n"
msg = msg + "for installing niscope:\n"
msg = msg + " Start anaconda prompt and write: python –m pip install niscope\n"
msg = msg + "For more infomation see: http://nimi-python.readthedocs.io\n"
raise InvalidInstrumentConnection(msg)
super().__init__(addr=addr, identify=identify, instName=instName)
logger.debug("Class {}".format(self.__class__.__name__))
self.msg_row_col = (1, 20)
self.sampleRate = 100_000_000
self.bandwidth = "20MHz"
[docs]
class PXIe5172(PXIe51xx):
""" Interface to the Oscilloscopes NI PXIe-5172 """
_properties = {
"sampleRate": ("min_sample_rate", [250_000_000, 250_000_000], None),
"bandwidth": ("max_input_frequency", {"Full": -1.0, "Default": 0.0, "20MHz": 20_000_000.0, "100MHz": 100_000_000.0}, None),
"range": ("vertical_range", {0.2: 0.2, 0.4: 0.4, 1.0: 1.0, 2.0: 2.0, 4.0: 4.0, 10.0: 10.0, 20.0: 20.0, 40.0: 40.0}, None),
"offset": ("vertical_offset", [-20, 20], None),
# Max Offset values for Range values : {0.2: 0.5, 0.4: 0.5, 1.0: 0.5, 4.0: 4.5, 10.0: 4.5, 40.0: 20.0}
"probeAttenuation": ("probe_attenuation", [1, 100], None),
"couplings": ("vertical_coupling", "backend.VerticalCoupling", None),
"output": ("channel_enabled", {"on": True, "off": False}, None),
"numberOfRecords": ("horz_num_records", [1, 100_000], None),
"numberOfPoints": ("horz_min_num_pts", [1, 100_000], None),
"numberOfSamples": ("horz_sample_rate", None, None),
"referencePosition": ("horz_record_ref_position", [0, 100], None),
"enforceRealtime": ("horz_enforce_realtime", {"on": True, "off": False}, None),
"triggerType": ("trigger_type", "backend.TriggerType", None),
"triggerSource": ("trigger_source", {"CH1": "0", "CH2": "1", "CH3": "2", "CH4": "3"}, None),
"triggerHysteresis": ("trigger_hysteresis", [0, 100], None),
"triggerLevel": ("trigger_level", [-100, 100], None),
"triggerDelay": ("trigger_delay_time", [0.0, 171.8], None),
"triggerHoldoff": ("trigger_holdoff", [0.0, 171.8], None),
"triggerSlope": ("trigger_slope", "backend.TriggerSlope", None),
"triggerCoupling": ("trigger_coupling", "backend.TriggerCoupling", None),
"terminals": ("channel_terminal_configuration", "backend.TerminalConfiguration", None),
"impedance": ("trigger_impedance", [50, 1000000], None),
}
[docs]
def __init__(self, addr=None, identify=False, instName=None):
"""
Initialize the instrument and connect to it.
Parameters
----------
addr : str
Name from the PXI-Slot e.q. PXI1Slot4
identify : bool
If True, the instrument will be identified during initialization. Default is False.
instName : str
Instance Name from top
"""
if not self.has_scope:
msg = "\nPXIe51xx not usable!! missing nidscope\n"
msg = msg + "for installing niscope:\n"
msg = msg + " Start anaconda prompt and write: python –m pip install niscope\n"
msg = msg + "For more infomation see: http://nimi-python.readthedocs.io\n"
raise InvalidInstrumentConnection(msg)
super().__init__(addr=addr, identify=identify, instName=instName)
logger.debug("Class {}".format(self.__class__.__name__))
self.msg_row_col = (1, 20)
self.sampleRate = 250_000_000
self.bandwidth = "20MHz"
[docs]
class TestClass(unittest.TestCase):
""" Test Class for PXIe51xx Oscilloscope Interface """
[docs]
def test_probeAttenuation(self):
""" Test the probe attenuation property of the scope. """
scope.probeAttenuation = 10.0
self.assertEqual(scope.probeAttenuation, 10.0)
scope.probeAttenuation = 101.0
self.assertNotEqual(scope.probeAttenuation, 101.0)
[docs]
def test_couplings(self):
""" Test the coupling property of the scope. """
scope.couplings = "DC"
self.assertEqual(scope.couplings, scope.niscope.VerticalCoupling.DC)
[docs]
def test_onoff(self):
""" Test the on/off state of the scope channels. """
scope.onoff = "on"
self.assertTrue(scope.inst.channel_enabled)
scope[2].onoff = False
self.assertFalse(scope.inst.channels[1].channel_enabled)
[docs]
def test_sampleRate(self):
""" Test the sample rate property of the scope. """
scope.sampleRate = 1_000_000
self.assertEqual(scope.sampleRate, 1000000)
[docs]
def test_numberOfRecords(self):
""" Test the number of records property of the scope. """
scope.numberOfRecords = 10
self.assertEqual(scope.numberOfRecords, 10)
scope.numberOfRecords = 100000
self.assertEqual(scope.numberOfRecords, 100000)
[docs]
def test_numberOfPoints(self):
""" Test the number of points property of the scope. """
scope.numberOfPoints = 100_000
self.assertEqual(scope.numberOfPoints, 100000)
scope.numberOfPoints = 100_000_000
self.assertNotEqual(scope.numberOfPoints, 100000000)
[docs]
def test_referencePosition(self):
""" Test the reference position property of the scope. """
scope.referencePosition = 10
self.assertEqual(scope.referencePosition, 10)
scope.referencePosition = 101
self.assertNotEqual(scope.referencePosition, 101)
[docs]
def test_enforceRealtime(self):
""" Test the enforce realtime property of the scope. """
scope.enforceRealtime = "off"
self.assertFalse(scope.inst.horz_enforce_realtime)
[docs]
def test_triggerType(self):
""" Test the trigger type property of the scope. """
scope.triggerType = "DIGITAL"
self.assertEqual(scope.triggerType, scope.niscope.TriggerType.DIGITAL)
[docs]
def test_triggerSource(self):
""" Test the trigger source property of the scope. """
scope.triggerSource = "CH3"
self.assertEqual(scope.triggerSource, str("CH3"))
[docs]
def test_triggerHysteresis(self):
""" Test the trigger hysteresis property of the scope. """
scope.triggerHysteresis = 10
self.assertEqual(scope.triggerHysteresis, 10)
scope.triggerHysteresis = 101
self.assertNotEqual(scope.triggerHysteresis, 101)
[docs]
def test_triggerLevel(self):
""" Test the trigger level property of the scope. """
scope.triggerLevel = 10
self.assertEqual(scope.triggerLevel, 10)
scope.triggerLevel = 101
self.assertNotEqual(scope.triggerLevel, 101)
[docs]
def test_triggerDelay(self):
""" Test the trigger delay property of the scope. """
scope.triggerDelay = 50
self.assertEqual(scope.triggerDelay, 50)
scope.triggerDelay = 172
self.assertNotEqual(scope.triggerDelay, 172)
[docs]
def test_triggerHoldoff(self):
""" Test the trigger holdoff property of the scope. """
scope.triggerHoldoff = 75
self.assertEqual(scope.triggerHoldoff, 75)
scope.triggerHoldoff = 172
self.assertNotEqual(scope.triggerHoldoff, 172)
[docs]
def test_triggerSlope(self):
""" Test the trigger slope property of the scope. """
scope.triggerSlope = "FALLING"
self.assertEqual(scope.triggerSlope, scope.niscope.TriggerSlope.FALLING)
[docs]
def test_triggerCoupling(self):
""" Test the trigger coupling property of the scope. """
scope.triggerCoupling = "HF_REJECT"
self.assertEqual(scope.triggerCoupling, scope.niscope.TriggerCoupling.HF_REJECT)
[docs]
def test_terminals(self):
""" Test the terminals property of the scope. """
scope.terminals = "SINGLE_ENDED"
self.assertEqual(scope.terminals, scope.niscope.TerminalConfiguration.SINGLE_ENDED)
[docs]
def test_bandwidth(self):
""" Test the bandwidth property of the scope. """
scope.bandwidth = "20MHz"
self.assertEqual(scope.bandwidth, str("20MHz"))
[docs]
def test_range(self):
""" Test the range property of the scope. """
scope.range = 20
self.assertEqual(scope.range, 20)
scope.range = 25
self.assertNotEqual(scope.range, 25)
scope.range = 50
self.assertNotEqual(scope.range, 50)
[docs]
def test_offset(self):
""" Test the offset property of the scope. """
scope.offset = 15
self.assertEqual(scope.offset, 15)
scope.offset = 50
self.assertNotEqual(scope.offset, 50)
if __name__ == "__main__":
from pylab_ml.base_instrument import logsetup
# import scope
logsetup()
scope = PXIe51xx("PXI1Slot4", instName="scope")
scope.abort()
# scope_5114 = PXIe5114('PXI1Slot4', instName='scope_5114')
# scope_5122 = PXIe5122('PXI1Slot4', instName='scope_5122')
# scope_5172 = PXIe5172('PXI1Slot4', instName='scope_5172')
scope.onoff = "on"
scope[1].range = 4.0
scope[2].range = 20.0
# scope.triggerSource = 0
scope.bandwidth = "125MHz"
scope.triggerType = "IMMEDIATE"
scope.triggerSlope = "RISING"
scope.triggerLevel = 0.5
scope.triggerCoupling = "DC"
scope.sampleRate = 100_000
scope.numberOfPoints = 300_000
scope.numberOfRecords = 1
scope.initiate()
# time, voltage = scope.measure(1, 5000, num_of_samples=4, filter='mfilter')
time, voltage = scope.measure(0)
scope.abort()
scope.plot_scope_data(time, voltage)
# unittest.main()
scope.close()
# scope_5114.close()
# scope_5122.close()
# scope_5172.close()