Skip to content
26 changes: 26 additions & 0 deletions doc/examples/ex_owon_sds1104.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env python
"""
Minimal example for connecting to an OWON SDS1104-family oscilloscope.
"""

import instruments as ik


def main():
"""
Open the scope over raw USB, print a few stable values, and read the
current CH1 screen waveform.
"""
scope = ik.owon.OWONSDS1104.open_usb()

print(f"Identity: {scope.name}")
print(f"Timebase scale: {scope.timebase_scale}")
print(f"CH1 displayed: {scope.channel[0].display}")
print(f"CH1 coupling: {scope.channel[0].coupling}")
time_s, voltage_v = scope.channel[0].read_waveform()
print(f"CH1 waveform samples: {len(voltage_v)}")
print(f"First sample: t={time_s[0]!r}, v={voltage_v[0]!r}")


if __name__ == "__main__":
main()
14 changes: 14 additions & 0 deletions doc/examples/kiprim/ex_kiprim_dc310s.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env python

import instruments as ik

psu = ik.kiprim.DC310S.open_serial("COM8", baud=115200, timeout=0.5)

print(psu.name)
print(f"Setpoint: {psu.voltage}, {psu.current}, output={psu.output}")
print(f"Measured: {psu.voltage_sense}, {psu.current_sense}, {psu.power_sense}")

# Uncomment to configure the supply explicitly.
# psu.voltage = 5 * ik.units.volt
# psu.current = 0.25 * ik.units.ampere
# psu.output = True
2 changes: 2 additions & 0 deletions doc/source/apiref/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ Contents:
holzworth
hp
keithley
kiprim
lakeshore
minghe
mettler_toledo
newport
ondax
oxford
owon
pfeiffer
phasematrix
picowatt
Expand Down
12 changes: 12 additions & 0 deletions doc/source/apiref/kiprim.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.. currentmodule:: instruments.kiprim

======
Kiprim
======

:class:`DC310S` Power Supply
============================

.. autoclass:: DC310S
:members:
:undoc-members:
15 changes: 15 additions & 0 deletions doc/source/apiref/owon.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
..
TODO: put documentation license header here.

.. currentmodule:: instruments.owon

====
OWON
====

:class:`OWONSDS1104` Oscilloscope
=================================

.. autoclass:: OWONSDS1104
:members:
:undoc-members:
2 changes: 2 additions & 0 deletions src/instruments/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@
from . import holzworth
from . import hp
from . import keithley
from . import kiprim
from . import lakeshore
from . import mettler_toledo
from . import minghe
from . import newport
from . import oxford
from . import owon
from . import phasematrix
from . import pfeiffer
from . import picowatt
Expand Down
94 changes: 89 additions & 5 deletions src/instruments/abstract_instruments/comm/usb_communicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def timeout(self):
@timeout.setter
def timeout(self, newval):
newval = assume_units(newval, u.second).to(u.ms).magnitude
self._dev.default_timeout = newval
self._dev.default_timeout = int(round(newval))

# FILE-LIKE METHODS #

Expand All @@ -136,14 +136,83 @@ def read_raw(self, size=-1):
if size == -1:
size = self._max_packet_size
term = self._terminator.encode("utf-8")
read_val = bytes(self._ep_in.read(size))
read_val = self.read_packet(size)
if term not in read_val:
raise OSError(
f"Did not find the terminator in the returned string. "
f"Total size of {size} might not be enough."
)
return read_val.rstrip(term)

def read_packet(self, size=-1):
"""
Read a single raw USB packet without interpreting terminators.

:param int size: Number of bytes requested from the USB endpoint.
A value of ``-1`` reads one full endpoint packet.
:rtype: `bytes`
"""
if size == -1:
size = self._max_packet_size
return bytes(self._ep_in.read(size))

def read_exact(self, size, chunk_size=None):
"""
Read exactly ``size`` raw bytes from the USB endpoint.

:param int size: Total number of bytes to read.
:param int chunk_size: Optional packet request size to use for each
underlying endpoint read.
:rtype: `bytes`
"""
if size < 0:
raise ValueError("Size must be non-negative.")
if chunk_size is None:
chunk_size = self._max_packet_size
if chunk_size <= 0:
raise ValueError("Chunk size must be positive.")

result = bytearray()
while len(result) < size:
packet = self.read_packet(min(chunk_size, size - len(result)))
result.extend(packet)
return bytes(result)

def read_binary(self, size=-1):
"""
Read raw binary data without looking for a terminator.

If ``size`` is negative, this reads packets until a short packet or a
USB timeout indicates the transfer has completed. If ``size`` is
non-negative, this reads exactly that many bytes.

:param int size: Number of bytes to read, or ``-1`` to read until the
transfer completes.
:rtype: `bytes`
"""
if size >= 0:
return self.read_exact(size)

result = bytearray()
while True:
try:
packet = self.read_packet()
except usb.core.USBTimeoutError:
if result:
break
raise
except usb.core.USBError as exc:
if result and "timeout" in str(exc).lower():
break
raise

if not packet:
break
result.extend(packet)
if len(packet) < self._max_packet_size:
break
return bytes(result)

def write_raw(self, msg):
"""Write bytes to the raw usb connection object.

Expand All @@ -152,18 +221,33 @@ def write_raw(self, msg):
"""
self._ep_out.write(msg)

def seek(self, offset): # pylint: disable=unused-argument,no-self-use
def seek(self, offset): # pylint: disable=unused-argument
raise NotImplementedError

def tell(self): # pylint: disable=no-self-use
def tell(self):
raise NotImplementedError

def flush_input(self):
"""
Instruct the communicator to flush the input buffer, discarding the
entirety of its contents.
"""
self._ep_in.read(self._max_packet_size)
original_timeout = self._dev.default_timeout
self._dev.default_timeout = 50
try:
while True:
try:
packet = self.read_packet()
except usb.core.USBTimeoutError:
break
except usb.core.USBError as exc:
if "timeout" in str(exc).lower():
break
raise
if not packet:
break
finally:
self._dev.default_timeout = original_timeout

# METHODS #

Expand Down
6 changes: 6 additions & 0 deletions src/instruments/kiprim/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env python
"""
Module containing Kiprim instruments.
"""

from .dc310s import DC310S
Loading