summaryrefslogtreecommitdiffstats
path: root/lab_control
diff options
context:
space:
mode:
Diffstat (limited to 'lab_control')
-rw-r--r--lab_control/__init__.py3
-rw-r--r--lab_control/function_generator.py23
-rw-r--r--lab_control/jds6600.py65
-rw-r--r--lab_control/oscilloscope.py18
-rw-r--r--lab_control/sds1000xe.py26
-rw-r--r--lab_control/test/interfaces_test.py11
6 files changed, 87 insertions, 59 deletions
diff --git a/lab_control/__init__.py b/lab_control/__init__.py
index b8c59c6..e69de29 100644
--- a/lab_control/__init__.py
+++ b/lab_control/__init__.py
@@ -1,3 +0,0 @@
-#from os.path import dirname, abspath
-
-#ROOT_DIR = dirname(abspath(__file__))
diff --git a/lab_control/function_generator.py b/lab_control/function_generator.py
index be522d9..a403784 100644
--- a/lab_control/function_generator.py
+++ b/lab_control/function_generator.py
@@ -1,23 +1,24 @@
-class FunctionGenerator:
- SINE = 0
- SQUARE = 1
- TRIANGULAR = 3
+""" Interface definition for function generator devices. """
+class FunctionGenerator:
+ """
+ This interface specifies the common API for all
+ supported function generator devices.
+ """
def __init__(self):
- raise Exception("This class should not be instantiated directly, please extend it instead")
+ pass
def setOn(self, channel: int) -> None:
- pass
+ """ Enable channel. """
def setOff(self, channel: int) -> None:
- pass
+ """ Disable channel. """
def setFrequency(self, channel: int, frequency: float) -> None:
- pass
+ """ Set channel output frequency. """
def setAmplitude(self, channel: int, amplitude: float) -> None:
- pass
+ """ Set channel output amplitude. """
def setFunction(self, channel: int, function: int) -> None:
- pass
-
+ """ Set channel output waveform. """
diff --git a/lab_control/jds6600.py b/lab_control/jds6600.py
index 006a041..5ada6cf 100644
--- a/lab_control/jds6600.py
+++ b/lab_control/jds6600.py
@@ -1,25 +1,44 @@
+"""
+Implements partial support for Joy-IT JDS6600 function generator.
+"""
+
import serial
-import re
from lab_control.function_generator import FunctionGenerator
+def _checkChannel(channel: int):
+ assert channel in JDS6600.AVAILABLE_CHANNELS, f"JDS6600: Invalid channel {channel}"
+
+def _checkArg(arg: float, lowerBound: float, upperBound: float):
+ valid = arg is not None
+ if valid:
+ valid &= arg >= lowerBound
+ valid &= arg <= upperBound
+ assert valid, f"JDS6600: Invalid argument {arg}"
+
class JDS6600(FunctionGenerator):
+ """
+ Instances of this class bind to the JDS6600 serial port
+ and offer an API to control the device.
+ """
AVAILABLE_CHANNELS = [1, 2]
+ SINE = 0
+ SQUARE = 1
+ TRIANGULAR = 3
def __init__(self, portName):
+ super().__init__()
self._port = serial.Serial(portName)
self._port.baudrate = 115200
self._port.bytesize = serial.EIGHTBITS
self._port.stopbits = serial.STOPBITS_ONE
self._port.parity = serial.PARITY_NONE
- def _sendRequest(self, opcode: str, args: str="") -> str:
- request = f":{opcode}={args}.\r\n"
- self._port.write(request.encode())
- responseRaw = self._port.readline()
- return responseRaw.decode().strip()
-
def closePort(self) -> None:
+ """
+ Close the serial port. Instances of this class
+ are no longer usable after this is called.
+ """
self._port.close()
def _queryOnOff(self) -> list[str, str]:
@@ -27,39 +46,45 @@ class JDS6600(FunctionGenerator):
response = self._sendRequest("r20")
return [response[5], response[7]]
+ def _sendRequest(self, opcode: str, args: str="") -> str:
+ request = f":{opcode}={args}.\r\n"
+ self._port.write(request.encode())
+ responseRaw = self._port.readline()
+ return responseRaw.decode().strip()
+
def setOn(self, channel: int) -> None:
- assert channel in JDS6600.AVAILABLE_CHANNELS, f"JDS6600: Invalid channel {channel}"
+ _checkChannel(channel)
state = self._queryOnOff()
state[channel - 1] = "1"
- response = self._sendRequest("w20", f"{state[0]},{state[1]}")
+ self._sendRequest("w20", f"{state[0]},{state[1]}")
def setOff(self, channel: int) -> None:
- assert channel in JDS6600.AVAILABLE_CHANNELS, f"JDS6600: Invalid channel {channel}"
+ _checkChannel(channel)
state = self._queryOnOff()
state[channel - 1] = "0"
- response = self._sendRequest("w20", f"{state[0]},{state[1]}")
+ self._sendRequest("w20", f"{state[0]},{state[1]}")
def setFrequency(self, channel: int, frequency: float) -> None:
- assert channel in JDS6600.AVAILABLE_CHANNELS, f"JDS6600: Invalid channel {channel}"
- assert frequency is not None and frequency >= 0.0 and frequency <= 60e6, f"JDS6600: Invalid frequency {frequency}"
+ _checkChannel(channel)
+ _checkArg(frequency, 0.0, 60e6)
opcode = f"w{23 + channel - 1}"
arg = int(frequency * 100.0)
- response = self._sendRequest(opcode, str(arg))
+ self._sendRequest(opcode, str(arg))
def setAmplitude(self, channel: int, amplitude: float) -> None:
- assert channel in JDS6600.AVAILABLE_CHANNELS, f"JDS6600: Invalid channel {channel}"
- assert amplitude is not None and amplitude >= 0.0 and amplitude <= 20.0, f"JDS6600: Invalid amplitude {amplitude}"
+ _checkChannel(channel)
+ _checkArg(amplitude, 0.0, 20.0)
opcode = f"w{25 + channel - 1}"
arg = int(amplitude * 1000.0)
- response = self._sendRequest(opcode, str(arg))
+ self._sendRequest(opcode, str(arg))
def setFunction(self, channel: int, function: int) -> None:
- assert channel in JDS6600.AVAILABLE_CHANNELS, f"JDS6600: Invalid channel {channel}"
- assert function is not None and function >= 0 and function <= 16, f"JDS6600: Invalid function code {function}"
+ _checkChannel(channel)
+ _checkArg(function, 0, 16)
opcode = f"w{21 + channel - 1}"
- response = self._sendRequest(opcode, str(function))
+ self._sendRequest(opcode, str(function))
diff --git a/lab_control/oscilloscope.py b/lab_control/oscilloscope.py
index 9f2d02c..e4b5a89 100644
--- a/lab_control/oscilloscope.py
+++ b/lab_control/oscilloscope.py
@@ -1,17 +1,21 @@
+""" Interface definition for oscilloscope devices. """
+
class Oscilloscope:
+ """
+ This interface specifies the common API for all
+ supported oscilloscope devices.
+ """
def __init__(self):
- raise Exception("This class should not be instantiated directly, please extend it instead")
+ pass
def measureAmplitude(self, channel: int) -> float:
- pass
+ """ Return amplitude measurement on specific channel. """
def measurePeakToPeak(self, channel: int) -> float:
- pass
+ """ Return peak-to-peak measurement on specific channel. """
def measureRMS(self, channel: int) -> float:
- pass
+ """ Return RMS measurement on specific channel. """
def measureFrequency(self, channel: int) -> float:
- pass
-
-
+ """ Return frequency measurement on specific channel. """
diff --git a/lab_control/sds1000xe.py b/lab_control/sds1000xe.py
index 859b1bf..67fc29b 100644
--- a/lab_control/sds1000xe.py
+++ b/lab_control/sds1000xe.py
@@ -1,31 +1,44 @@
+"""
+Implements partial support for Siglent SDS1000X-E series oscilloscopes.
+"""
+
import socket
import re
from lab_control.oscilloscope import Oscilloscope
+def _checkChannel(channel):
+ assert channel in SDS1000XE.AVAILABLE_CHANNELS, "SDS1000X-E: Invalid channel {channel}"
+
class SDS1000XE(Oscilloscope):
+ """
+ Instances of this class connect to the SDS1000X-E IP
+ and port and offer an API to control the device.
+ """
PORT = 5025
TIMEOUT = 1.0
AVAILABLE_CHANNELS = range(1, 5)
def __init__(self, address):
+ super().__init__()
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._socket.connect((address, SDS1000XE.PORT))
self._socket.settimeout(SDS1000XE.TIMEOUT)
- def _measure(self, channel: int, code: str) -> float:
- assert channel in SDS1000XE.AVAILABLE_CHANNELS, "SDS1000X-E: Invalid channel {channel}"
+ def _measure(self, channel: int, code: str) -> float:
+ _checkChannel(channel)
+ pattern = r"C(?P<responseChannel>\d):PAVA .+,(?P<rawMeasurement>[\d.E+-]+)\w+"
query = f"C{channel}:PAVA? {code}\r\n"
self._socket.sendall(query.encode())
try:
# TODO add code to regex
response = self._socket.recv(4096).decode()
- m = re.search(r"C(?P<responseChannel>\d):PAVA .+,(?P<rawMeasurement>[\d.E+-]+)\w+", response)
- measurement = float(m.group("rawMeasurement"))
- except TimeoutError as e:
+ matches = re.search(pattern, response)
+ measurement = float(matches.group("rawMeasurement"))
+ except TimeoutError:
measurement = None
-
+
return measurement
def measureAmplitude(self, channel: int) -> float:
@@ -39,4 +52,3 @@ class SDS1000XE(Oscilloscope):
def measureFrequency(self, channel: int) -> float:
return self._measure(channel, "FREQ")
-
diff --git a/lab_control/test/interfaces_test.py b/lab_control/test/interfaces_test.py
deleted file mode 100644
index bf9006b..0000000
--- a/lab_control/test/interfaces_test.py
+++ /dev/null
@@ -1,11 +0,0 @@
-import pytest
-from lab_control.oscilloscope import Oscilloscope
-from lab_control.function_generator import FunctionGenerator
-
-def test_oscilloscopeInterface():
- with pytest.raises(Exception):
- uut = Oscilloscope()
-
-def test_functionGeneratorInterface():
- with pytest.raises(Exception):
- uut = FunctionGenerator()