Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tests for Yokogawa 7651 and tests + BF for Yokogawa 6370 #251

Merged
merged 1 commit into from
Aug 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
232 changes: 232 additions & 0 deletions instruments/tests/test_yokogawa/test_yokogawa7651.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Unit tests for the Yokogawa 7651 power supply
"""

# IMPORTS #####################################################################


import pytest

import instruments as ik
import instruments.units as u
from instruments.tests import expected_protocol


# TESTS #######################################################################

# pylint: disable=protected-access

# TEST CHANNEL #


def test_channel_init():
"""Initialize of channel class."""
with expected_protocol(
ik.yokogawa.Yokogawa7651,
[
],
[
]
) as yok:
assert yok.channel[0]._parent is yok
assert yok.channel[0]._name == 0


def test_channel_mode():
"""Get / Set mode of the channel."""
with expected_protocol(
ik.yokogawa.Yokogawa7651,
[
"F5;",
"E;", # trigger
"F1;",
"E;" # trigger
],
[
]
) as yok:
# query
with pytest.raises(NotImplementedError) as exc_info:
print(f"Mode is: {yok.channel[0].mode}")
exc_msg = exc_info.value.args[0]
assert exc_msg == "This instrument does not support querying the " \
"operation mode."

# set first current, then voltage mode
yok.channel[0].mode = yok.Mode.current
yok.channel[0].mode = yok.Mode.voltage


def test_channel_invalid_mode_set():
"""Set mode to invalid value."""
with expected_protocol(
ik.yokogawa.Yokogawa7651,
[
],
[
]
) as yok:
wrong_mode = 42
with pytest.raises(TypeError) as exc_info:
yok.channel[0].mode = wrong_mode
exc_msg = exc_info.value.args[0]
assert exc_msg == "Mode setting must be a `Yokogawa7651.Mode` " \
"value, got {} instead.".format(type(wrong_mode))


def test_channel_voltage():
"""Get / Set voltage of channel."""

# values to set for test
value_unitless = 5.
value_unitful = u.Quantity(500, u.mV)

with expected_protocol(
ik.yokogawa.Yokogawa7651,
[
"F1;\nE;", # set voltage mode
f"SA{value_unitless};",
"E;", # trigger
"F1;\nE;", # set voltage mode
f"SA{value_unitful.rescale(u.volt).magnitude};",
"E;" # trigger
],
[
]
) as yok:
# query
with pytest.raises(NotImplementedError) as exc_info:
print(f"Voltage is: {yok.channel[0].voltage}")
exc_msg = exc_info.value.args[0]
assert exc_msg == "This instrument does not support querying the " \
"output voltage setting."

# set first current, then voltage mode
yok.channel[0].voltage = value_unitless
yok.channel[0].voltage = value_unitful


def test_channel_current():
"""Get / Set current of channel."""

# values to set for test
value_unitless = 0.8
value_unitful = u.Quantity(50, u.mA)

with expected_protocol(
ik.yokogawa.Yokogawa7651,
[
"F5;\nE;", # set voltage mode
f"SA{value_unitless};",
"E;", # trigger
"F5;\nE;", # set voltage mode
f"SA{value_unitful.rescale(u.A).magnitude};",
"E;" # trigger
],
[
]
) as yok:
# query
with pytest.raises(NotImplementedError) as exc_info:
print(f"Current is: {yok.channel[0].current}")
exc_msg = exc_info.value.args[0]
assert exc_msg == "This instrument does not support querying the " \
"output current setting."

# set first current, then current mode
yok.channel[0].current = value_unitless
yok.channel[0].current = value_unitful


def test_channel_output():
"""Get / Set output of channel."""
with expected_protocol(
ik.yokogawa.Yokogawa7651,
[
"O1;", # turn output on
"E;",
"O0;", # turn output off
"E;"
],
[
]
) as yok:
# query
with pytest.raises(NotImplementedError) as exc_info:
print(f"Output is: {yok.channel[0].output}")
exc_msg = exc_info.value.args[0]
assert exc_msg == "This instrument does not support querying the " \
"output status."

# set first current, then current mode
yok.channel[0].output = True
yok.channel[0].output = False


# CLASS PROPERTIES #


def test_voltage():
"""Get / Set voltage of instrument."""

# values to set for test
value_unitless = 5.
value_unitful = u.Quantity(500, u.mV)

with expected_protocol(
ik.yokogawa.Yokogawa7651,
[
"F1;\nE;", # set voltage mode
f"SA{value_unitless};",
"E;", # trigger
"F1;\nE;", # set voltage mode
f"SA{value_unitful.rescale(u.volt).magnitude};",
"E;" # trigger
],
[
]
) as yok:
# query
with pytest.raises(NotImplementedError) as exc_info:
print(f"Voltage is: {yok.voltage}")
exc_msg = exc_info.value.args[0]
assert exc_msg == "This instrument does not support querying the " \
"output voltage setting."

# set first current, then voltage mode
yok.voltage = value_unitless
yok.voltage = value_unitful


def test_current():
"""Get / Set current of instrument."""

# values to set for test
value_unitless = 0.8
value_unitful = u.Quantity(50, u.mA)

with expected_protocol(
ik.yokogawa.Yokogawa7651,
[
"F5;\nE;", # set current mode
f"SA{value_unitless};",
"E;", # trigger
"F5;\nE;", # set current mode
f"SA{value_unitful.rescale(u.A).magnitude};",
"E;" # trigger
],
[
]
) as yok:
# query
with pytest.raises(NotImplementedError) as exc_info:
print(f"current is: {yok.current}")
exc_msg = exc_info.value.args[0]
assert exc_msg == "This instrument does not support querying the " \
"output current setting."

# set first current, then current mode
yok.current = value_unitless
yok.current = value_unitful
61 changes: 61 additions & 0 deletions instruments/tests/test_yokogawa/test_yokogawa_6370.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,67 @@ def test_active_trace():
assert inst.active_trace == inst.Traces.G


# METHODS #


@given(values=st.lists(st.decimals(allow_infinity=False, allow_nan=False),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great use of hypothesis 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally stolen from further up in the test suite. But that's one way to start. More to come 😃

min_size=1))
def test_data_active_trace(values):
"""Get data from active trace - method."""
values_packed = b"".join(struct.pack("<d", value) for value in values)
values_len = str(len(values_packed)).encode()
values_len_of_len = str(len(values_len)).encode()
channel = "TRA" # active trace
with expected_protocol(
ik.yokogawa.Yokogawa6370,
[
":FORMat:DATA REAL,64",
":TRAC:Y? {}".format(channel),
":TRAC:ACTIVE?",
":TRAC:Y? {}".format(channel)
],
[
b"#" + values_len_of_len + values_len + values_packed,
channel,
b"#" + values_len_of_len + values_len + values_packed
]
) as inst:
# data by channel
data_call_by_trace = inst.channel[channel].data()
# call active trace data
data_active_trace = inst.data()
assert (data_call_by_trace == data_active_trace).all()


@given(values=st.lists(st.decimals(allow_infinity=False, allow_nan=False),
min_size=1))
def test_wavelength_active_trace(values):
"""Get wavelength from active trace - method."""
values_packed = b"".join(struct.pack("<d", value) for value in values)
values_len = str(len(values_packed)).encode()
values_len_of_len = str(len(values_len)).encode()
channel = "TRA" # active trace
with expected_protocol(
ik.yokogawa.Yokogawa6370,
[
":FORMat:DATA REAL,64",
":TRAC:X? {}".format(channel),
":TRAC:ACTIVE?",
":TRAC:X? {}".format(channel)
],
[
b"#" + values_len_of_len + values_len + values_packed,
channel,
b"#" + values_len_of_len + values_len + values_packed
]
) as inst:
# data by channel
data_call_by_trace = inst.channel[channel].wavelength()
# call active trace data
data_active_trace = inst.wavelength()
assert (data_call_by_trace == data_active_trace).all()


def test_start_sweep():
with expected_protocol(
ik.yokogawa.Yokogawa6370,
Expand Down
4 changes: 2 additions & 2 deletions instruments/yokogawa/yokogawa6370.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,13 @@ def data(self):
"""
Function to query the active Trace data of the OSA.
"""
return self.channel[self.active_trace].data
return self.channel[self.active_trace].data()

def wavelength(self):
"""
Query the wavelength axis of the active trace.
"""
return self.channel[self.active_trace].wavelength
return self.channel[self.active_trace].wavelength()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is a TODO brewing here. I'd say out of scope for this PR, but we should think about if we want to make wavelength a property

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two methods for data and wavelength are simply a shortcut to access the currently active channel. The channels themselves in fact also have the additional arguments for bin_format:

def data(self, bin_format=True):

For oscilloscopes for example, getting data is also always defined as methods to allow for a bin_format argument. If we want to turn it into a method there is the question on how to implement the bin_format, maybe with a separate property on the data transfer format? If it is left as a method, it would be good to pass the bin_format through in these methods. What do you think? That latter could easily be included here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is something we will have to think about by taking a look at the manual and seeing how we might expect the user to use the wavelength data here. I'm not super familiar with the instrument so I would need to check that first.

This is def a later-thing though, and I'm okay with how it is right now!


def start_sweep(self):
"""
Expand Down