Skip to content
Open
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
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()
1 change: 1 addition & 0 deletions doc/source/apiref/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Contents:
newport
ondax
oxford
owon
pfeiffer
phasematrix
picowatt
Expand Down
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:
1 change: 1 addition & 0 deletions src/instruments/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
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
10 changes: 10 additions & 0 deletions src/instruments/owon/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env python
"""
Module containing OWON instruments.
"""

from .sds1104 import (
OWONSDS1104,
SDS1104DeepMemoryCapture,
SDS1104SavedWaveformEntry,
)
Loading