Stream Process API#

class joulescope.v0.driver.StreamProcessApi[source]#

This API is used to chain multiple processing callbacks that are called when new data is received from the Joulescope.

The Joulescope driver will call notify() when new data is available. It will call close() when streaming is stopped.

close()[source]#

Close the processing instance. [optional]

This method may be called from the USB thread.

start(stream_buffer)[source]#

Start a new streaming session. [optional]

Parameters:

stream_buffer – The StreamBuffer instance which contains the new data from the Joulescope. This same instance will be passed to future stream_notify() calls, too.

This method will be called from the USB thread. The processing duration must be very short to prevent dropping samples. You should post any significant processing to a separate thread.

stop()[source]#

Stop the existing streaming session. [optional]

This method will be called from the USB thread.

stream_notify(stream_buffer)[source]#

Notify that new data is available from the Joulescope.

Parameters:

stream_buffer – The StreamBuffer instance which contains the new data from the Joulescope.

Returns:

False to continue streaming. True to stop streaming.

This method will be called from the USB thread. The processing duration must be very short to prevent dropping samples. You should post any significant processing to a separate thread.

Threads#

The stream process methods, such as stream_notify, the stop_fn given to Device.start, and statistics callbacks all run on the USB thread. Keep them fast, since slow callbacks drop samples, and do not call back into the device from them. Pass results to your thread with a queue.Queue or a threading.Event. For statistics, use Device.statistics_iter instead of a callback. See Statistics API.

Run length#

There are three ways to decide how long a capture runs:

  1. Sample count (recommended): device.start(stop_fn, duration) stops after duration seconds of samples, at the sample rate. The device stops itself, even if your thread is busy, and calls stop_fn(event, message) on the USB thread. stop_fn is also called when the device stops for any other reason, such as removal. Use contiguous_duration instead to require that the duration has no missing samples.

  2. Blocking read: device.read(duration) does the same as (1) and returns the samples, when the samples fit in the stream buffer.

  3. Wall-clock loop: device.start(), then loop until time.time() reaches the end time, then device.stop(). The capture length then includes the USB startup delay and your thread’s scheduling, so the sample count varies from run to run. The loop also does not notice when the device stops on an error, unless you also give stop_fn.

Use (1) or (2) for a fixed duration. Only use a loop for open-ended captures that end on Ctrl-C or on an external condition, and still give stop_fn. Here is the recommended pattern:

import joulescope
import threading

with joulescope.scan_require_one(config='auto') as device:
    done = threading.Event()

    def stop_fn(event, message):  # runs on the USB thread
        done.set()

    device.start(stop_fn=stop_fn, duration=10.0)
    while not done.wait(0.1):  # a timeout keeps Ctrl-C responsive
        pass

For statistics, the equivalent of (1) is a count: device.statistics_iter(count=duration * reduction_frequency).