Quick'n dirty view / save utility to retrieve and store waveforms from SCPI compliant devices
Project description
OsziTrace -- EPICS IOC for Retrieving Oscilloscope Traces
[[TOC]]
Overview
Oszitrace exposes the data traces (i.e. digitized signals) of an attached oscilloscope. For a supported device access to the main data is offered for each channel, as well as a device-wide trigger arming functionality.
The IOC is deliberately kept simple and does not go into details about oscilloscope configuration. The generat strategy if you require specific setup and behavior control to pass an experiment-specific initialization talk routine to the IOC on startup.
We advise you to consider keeping a number of those in stock for your most common usage patterns.
Quick Start
Obtaining
There are three ways how you can get Oszitrace up & running at your site:
-
You can download it straigt off PyPI and run it from the command line:
$ pip install oszitrace-ioc $ oszitrace-ioc [...]
-
You can download it off Gitlab and install it locally:
$ git clone oszitrace $ pip install -e ./oszitrace $ oszitrace-ioc [...]
-
...or you can use the pre-built image off Gitlab with Docker or Podman:
$ podman run -ti --rm registry.gitlab.com/kmc3-xpp/oszitrace:latest [...]
Running & Customizing
Most of Oszitrace's behavior customized through environment variables. Currently there are no command line parameters.
Here is a list of variables that Oszitrace currently reacts to:
OSZI_EPICS_PREFIX
the EPICS prefix the IOC will useOSZI_NAME
the "name" of the oscilloscope device; Oszitrace is, in principle, designed to run many different instances within the same network, if you have the need for it. By default, the EPICS prefix of each instance will be of the form{prefix}:{name}
, with{prefix}
being used fromOSZI_EPICS_PREFIX
(see above), and{name}
being the last component of the module providing the dialogue object. Sometimes this is not enough, though -- e.g. when you have several devices of the same kind that you want to use.OSZI_NAME
allows you to override the{name}
component of the prefix.OSZI_INIT_TALK
this should contain the path of an init-talk file that will be fed to the device upon IOC startup.OSZI_DIALOGUE
this should contain a module / class specification of the form[oszitrace.]{module}[:{class}]
. Here, the{module}
part refers to a Python module available within the system for Oszitrace to use. If{module}
is a single-name (i.e. contains no dot.
), and is not found,oszitrace.{module}
is also tried (e.g.oszitrace.lecroy
instead of justlecroy
). The dialogue object within that module is expected to be calledDialogue
, and be compatible with theDialogue
device access API. This can be overridden by the{class}
part of the dialogue specification. For example:OSZI_DIALOGUE=oszitrace.lecroy:LecroyDialogue
.OSZI_LOG_LEVEL
one of"ERROR"
,"WARNING"
,"INFO"
or"DEBUG"
.
There are also variables intended for specific dialogue types (e.g. for the built-in simulator.
The EPICS IOC Interface
Oszitrace exports the following EPICS variables:
{prefix}:{name}:arm
if anything is written here, and the device is in any kind of triggered-mode, the trigger is armed and ready for a new measurement.{prefix}:{name}:{channel}:signal
the oscilloscope sampling data (a floating-point array){prefix}:{name}:{channel}:xaxis
the axis that corresponds to the dataw (also as a floating-point array){prefix}:{name}:{channel}:xoffset
(...also available:...:xdelta
and...:xreach
): these are the essential values that make up the X-axis data (xoffset
being the starting point,xdelta
the axis increment, andxreach
, as a courtesy, the last point on the axis i.e.xoffset+xdelta*<num_pts>
).
The {}
format variables above have the following meaning:
{prefix}
the IOC base prefix, specified asOSZI_EPICS_PREFIX
{name}
the IOC device name (see above){channel}
the name of the device channel; this is highly implementation specific, but we encourage, and indeed most built-in devices have, a scheme akin toch{N}
, where{N}
is a channel index. Note that some devices may (or not) export additional data, e.g. an FFT array, with a different designation (e.g.ch1
andch1fft
).
Diving Deeper
The Built-in Simulator
For testing and the-fun-of-things purposes, Oszitrace has
a built-in signal simulator. It will enable the IOC to be run
without an attached device, and will provide a number of
channels with the exact same functionality as a "real"
oscislloscipe, the only difference being that the signal will
be a simulated, running sin()
wave.
Specific environment variables for the built-in simulator:
OSZI_SIM_TRIGGERED
if set to"yes"
, the simulator will only deliver a signal after it has been triggered.
Initialization Talk
Generically, independently of the device type, an "initalization talk" object can be specified. This should be a YAML-compatible file, essentially like this:
type: oszitrace-ioc/ascii-talk
version: 0.1
init:
- q: "*IDN?"
r: "{}Agilent Version {version}{}"
- q: ":WAV:FORM ASC"
- q: ":WAV:STR ON"
In words: the object must be a list ([]
) which contains
dictionaries, and each dictionary MUST contain a q
element,
and MAY contain a r
element -- each as strings.
The q
element will be sent verbatim to the device over whichever
means of communication is employed (e.g. PyVISA or something different).
If an r
element exists, then an answer is expected, and the answer
is parsed against r
as a format string. The usual Python formats
({}
, {name}
, ...) are accepted. If the format parameter is named,
it will be stored in an internal dictionary for later reference
(see .parameters
in the device access API).
If the r
element does not exist, then the devices MUST NOT answer,
otherwise this will be treated as an error.
Device Access API
Device access is facilitated by a Python object (a "dialogue" object), which is expected to meet the following API; you can use this as a stub to get started from, if you intend to support your own hardware:
class Dialogue:
def __init__(self, env=None, ...):
...
async def init(self, init_chat=None):
...
async def retr_channel(self, channel, timeout=0.0):
...
async def arm_trigger(self):
...
@property
def channels(self):
...
@property
def parameters(self):
...
-
.__init__()
: initialization should work without parameters. This function must not block or hesitate, i.e. please move all device communication either in a working thread, or use the rest of the async API elements. However,.__init__()
will receive a list of environment variables to use via theenv={...}
parameter. The class is free to use other environment variable sources (e.g.os.environ
), but is discouraged from doing so. Theenv=...
parameter will actually contain the system env-vars fromos.environ
if the circumstances didn't explicitly ask for a different set. -
async def init(...)
: this is called once, after the asyncio loop has been etablished, to give the Dialogue class occasion to perform initial device communication and setup. Ifinit_chat
is specified, it's expected to be an init-talk list object. -
async def retr_channel(...)
: this waits until either data for the specified channel is available, ortimeout
seconds have elapsed. If there is no data for whatever reason (either because timeout has occurred, or because oscilloscopes are generally tricky and sometimes unreliable), then aDialogueRetry
exception must be raised -- either aDialogueTimeoutRetry
or aDialogueEmptyRetry
, depending on the reason. The rest of Oszitrace is designed to deal with that gracefully, without going into failure mode.If the intention is to fail (i.e. exit the IOC because a more serious reason is at play, e.g. loss of connectivity), then a different exception must be raised (e.g.
RuntimeError
, aDialogueError
, or whatever the underlying communication mechanism raises).The data must be returned as an
xarray.DataArray
, with built-in scaling, both axis and signal as floating-point data type. -
async def arm_trigger()
: this function is supposed to arm the trigger for a new signal acquisition if the device is any kind of triggered mode. -
property
channels
: this is a list of strings, each string representing the name of one device data channel. These are to be used as parameters, for instance, to.retr_channel()
calls. -
property
parameters
: this is provided as a courtesy to the user, and is expected to be a dictionary where the keys are named parse parameters from the init-talkr
sections (see above), and the data is the data that has been extracted.Oszitrace (currently) does not use any of this information, and there aren't any specific keys that are expected or treated in a particular way -- everything we care about in terms of signal information (scaling, offsets, axis, ...) is expected to already be baked into the
xarray
that.retr_channel()
is returning.But as Oszitrace API is still young, this may change in the future (though at this point it isn't clear how this would be beneficial, and how this could -- generically -- reach the IOC interface.)
Supported Devices
Here's a list of devices that Oszitrace supports our of the box
(you can use any of these as a value for the OSZI_DIALOGUE
env-var):
-
sim -- see above, a simulated device which delivers sine waves for testing.
-
lecroy -- tested on (?), but generally Lecroy devices have a fairly sophisticated discovery langauge built-in which describe essential parts of their internal workings and formats. Oszitrace goes a long way in automatically interpreting that language and adapting to the specific model, so chances are that a wide range of models are able to function out-of-the-box.
Binary readout is enabled, so Oszitrace is able to rapidly read large amounts of data (4 channels, > 1 mio data points each, readout time in the range of a fraction of a second).
-
agilent -- tested on (?). Only ASCII readout supported via the
:WAV:DATA?
SCPI command. 4-channel, 150k-ish points readout performed within ~1 second.
Adding support for additional devices should be fairly easy.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file oszitrace_ioc-0.2.0.tar.gz
.
File metadata
- Download URL: oszitrace_ioc-0.2.0.tar.gz
- Upload date:
- Size: 43.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.1 CPython/3.12.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 0169bbdf86a4c43b7f54be2e72f982b8e7336bf3096cf0e0c87a9d35a731ebe4 |
|
MD5 | 1826e90caf6ec09db2b6063e2ad287d3 |
|
BLAKE2b-256 | 53d42c68d1b9672d4ab9c527de03f86c19541622e96355d118e97e41647e7c4d |
File details
Details for the file oszitrace_ioc-0.2.0-py3-none-any.whl
.
File metadata
- Download URL: oszitrace_ioc-0.2.0-py3-none-any.whl
- Upload date:
- Size: 39.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.1 CPython/3.12.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | d653fdeee23db06a818b4bea3b348b2cdafbc3000a5aed372bd19332ff1d790b |
|
MD5 | b4d11412637dd923930f206c0e407e6f |
|
BLAKE2b-256 | ca341eca47a1f607a97dea850ad33c965ddfc8c415f3c252e6394d91d8652f4a |