Speed Issues with StreamingMode Using Picoscope 4824A with Python

Post general discussions on using our drivers to write your own software here
Post Reply
PhilippP
Newbie
Posts: 0
Joined: Wed Sep 28, 2022 3:14 pm

Speed Issues with StreamingMode Using Picoscope 4824A with Python

Post by PhilippP »

Hi everybody,

I am currently working with a Picoscope 4824A and would like to use streaming mode using Python. I have gone through the examples provided in the Git repository, but I am facing two problems at the moment.

1) When using streaming mode I encounter a lag that occurs when using the provided callback that causes an overwritten buffer after some time. I am using the provided example (https://github.com/picotech/picosdk-pyt ... Example.py) and added some lines of code in the callback for debugging. I also uncommented the code that copies the data from the driver buffers to the full buffer used in the Python script (so in the callback almost nothing happens)

I print:
- the elapsed time since starting streaming
- the current buffer index the buffer is read from
- the difference between the current buffer index and the elapsed time in seconds times the sampling rate to check if there is a noticable delay

With the time, passing the difference between the actual index and the expected index increases. See below for console output:

Code: Select all

 1.018571s elapsed
nextSample = 18546688
expectedSample = 20371419
difference =  1824731

 2.019739s elapsed
nextSample = 36864000
expectedSample = 40394780
difference =  3530780

 3.02129s elapsed
nextSample = 55738368
expectedSample = 60425800
difference =  4687432

 4.023915s elapsed
nextSample = 74547200
expectedSample = 80478300
difference =  5931100

 5.026161s elapsed
nextSample = 93847552
expectedSample = 100523220
difference =  6675668

 6.028836s elapsed
nextSample = 113246208
expectedSample = 120576720
difference =  7330512

 7.04084s elapsed
nextSample = 132382720
expectedSample = 140816800
difference =  8434080

 8.047408s elapsed
nextSample = 151584768
expectedSample = 160948160
difference =  9363392

 9.057014s elapsed
nextSample = 171048960
expectedSample = 181140280
difference =  10091320

 10.058046s elapsed
nextSample = 190349312
expectedSample = 201160919
difference =  10811607
Done grabbing values.
{'openunit': 0, 'setChA': 0, 'setChB': 0, 'setDataBuffersA': 0, 'setDataBuffersB': 0, 'runStreaming': 0, 'getStreamingLastestValues': 0, 'stop': 0, 'close': 0}

Process finished with exit code 0
So after some more seconds the difference will be larger than the buffer size and data in the buffer gets overwritten before I am able to fetch it. What am I missing here? Is there a way to enable streaming without the lag?

Please find my code (modified example) below:
- Sampling rate: 20MS/s = (50ns sampling interval), 2 channels (A and B) [see lines 121 to 123]
- Buffer size: 20MS, Buffers to capture: 10 (have a total of 10s recording) [see lines 74 to 76]

Code: Select all

#
# Copyright (C) 2018-2019 Pico Technology Ltd. See LICENSE file for terms.
#
# PS2000 Series (A API) STREAMING MODE EXAMPLE
# This example demonstrates how to call the ps4000A driver API functions in order to open a device, setup 2 channels and collects streamed data (1 buffer).
# This data is then plotted as mV against time in ns.

import ctypes
from datetime import datetime

import numpy as np
from picosdk.ps4000a import ps4000a as ps
import matplotlib.pyplot as plt
from picosdk.functions import adc2mV, assert_pico_ok
import time

# Create chandle and status ready for use
chandle = ctypes.c_int16()
status = {}

# Open PicoScope 2000 Series device
# Returns handle to chandle for use in future API functions
status["openunit"] = ps.ps4000aOpenUnit(ctypes.byref(chandle), None)

try:
    assert_pico_ok(status["openunit"])
except:

    powerStatus = status["openunit"]

    if powerStatus == 286:
        status["changePowerSource"] = ps.ps4000aChangePowerSource(chandle, powerStatus)
    else:
        raise

    assert_pico_ok(status["changePowerSource"])


enabled = 1
disabled = 0
analogue_offset = 0.0

# Set up channel A
# handle = chandle
# channel = PS4000A_CHANNEL_A = 0
# enabled = 1
# coupling type = PS4000A_DC = 1
# range = PS4000A_2V = 7
# analogue offset = 0 V
channel_range = 7
status["setChA"] = ps.ps4000aSetChannel(chandle,
                                        ps.PS4000A_CHANNEL['PS4000A_CHANNEL_A'],
                                        enabled,
                                        ps.PS4000A_COUPLING['PS4000A_DC'],
                                        channel_range,
                                        analogue_offset)
assert_pico_ok(status["setChA"])

# Set up channel B
# handle = chandle
# channel = PS4000A_CHANNEL_B = 1
# enabled = 1
# coupling type = PS4000A_DC = 1
# range = PS4000A_2V = 7
# analogue offset = 0 V
status["setChB"] = ps.ps4000aSetChannel(chandle,
                                        ps.PS4000A_CHANNEL['PS4000A_CHANNEL_B'],
                                        enabled,
                                        ps.PS4000A_COUPLING['PS4000A_DC'],
                                        channel_range,
                                        analogue_offset)
assert_pico_ok(status["setChB"])

# Size of capture
sizeOfOneBuffer = 20_000_000  # 20MS buffer
numBuffersToCapture = 10  # run for 10 seconds

totalSamples = sizeOfOneBuffer * numBuffersToCapture

# Create buffers ready for assigning pointers for data collection
bufferAMax = np.zeros(shape=sizeOfOneBuffer, dtype=np.int16)
bufferBMax = np.zeros(shape=sizeOfOneBuffer, dtype=np.int16)

memory_segment = 0

# Set data buffer location for data collection from channel A
# handle = chandle
# source = PS4000A_CHANNEL_A = 0
# pointer to buffer max = ctypes.byref(bufferAMax)
# pointer to buffer min = ctypes.byref(bufferAMin)
# buffer length = maxSamples
# segment index = 0
# ratio mode = PS4000A_RATIO_MODE_NONE = 0
status["setDataBuffersA"] = ps.ps4000aSetDataBuffers(chandle,
                                                     ps.PS4000A_CHANNEL['PS4000A_CHANNEL_A'],
                                                     bufferAMax.ctypes.data_as(ctypes.POINTER(ctypes.c_int16)),
                                                     None,
                                                     sizeOfOneBuffer,
                                                     memory_segment,
                                                     ps.PS4000A_RATIO_MODE['PS4000A_RATIO_MODE_NONE'])
assert_pico_ok(status["setDataBuffersA"])

# Set data buffer location for data collection from channel B
# handle = chandle
# source = PS4000A_CHANNEL_B = 1
# pointer to buffer max = ctypes.byref(bufferBMax)
# pointer to buffer min = ctypes.byref(bufferBMin)
# buffer length = maxSamples
# segment index = 0
# ratio mode = PS4000A_RATIO_MODE_NONE = 0
status["setDataBuffersB"] = ps.ps4000aSetDataBuffers(chandle,
                                                     ps.PS4000A_CHANNEL['PS4000A_CHANNEL_B'],
                                                     bufferBMax.ctypes.data_as(ctypes.POINTER(ctypes.c_int16)),
                                                     None,
                                                     sizeOfOneBuffer,
                                                     memory_segment,
                                                     ps.PS4000A_RATIO_MODE['PS4000A_RATIO_MODE_NONE'])
assert_pico_ok(status["setDataBuffersB"])

# Begin streaming mode:
sampling_rate = 20e6  # 20 MS/s
sampleInterval = ctypes.c_int32(int(1e9 / sampling_rate))  # 1e9ns = 1s
sampleUnits = ps.PS4000A_TIME_UNITS['PS4000A_NS']

# We are not triggering:
maxPreTriggerSamples = 0
autoStopOn = 0
# No downsampling:
downsampleRatio = 1
status["runStreaming"] = ps.ps4000aRunStreaming(chandle,
                                                ctypes.byref(sampleInterval),
                                                sampleUnits,
                                                maxPreTriggerSamples,
                                                totalSamples,
                                                autoStopOn,
                                                downsampleRatio,
                                                ps.PS4000A_RATIO_MODE['PS4000A_RATIO_MODE_NONE'],
                                                sizeOfOneBuffer)
assert_pico_ok(status["runStreaming"])

# We need a big buffer, not registered with the driver, to keep our complete capture in.
bufferCompleteA = np.zeros(shape=totalSamples, dtype=np.int16)
bufferCompleteB = np.zeros(shape=totalSamples, dtype=np.int16)
nextSample = 0
autoStopOuter = False
wasCalledBack = False


def streaming_callback(handle, noOfSamples, startIndex, overflow, triggerAt, triggered, autoStop, param):
    global nextSample, autoStopOuter, wasCalledBack, start_time, last_reported
    wasCalledBack = True
    destEnd = nextSample + noOfSamples
    sourceEnd = startIndex + noOfSamples

    # do nothing: no completion of buffer
    # bufferCompleteA[nextSample:destEnd] = bufferAMax[startIndex:sourceEnd]
    # bufferCompleteB[nextSample:destEnd] = bufferBMax[startIndex:sourceEnd]

    nextSample += noOfSamples
    if autoStop:
        autoStopOuter = True

    # check elapsed time and compare indices
    time_now = datetime.now()
    elapsed_time = (time_now - start_time).total_seconds()
    if (time_now - last_reported).total_seconds() > 1:
        last_reported = time_now
        expectedSample = int(elapsed_time * sampling_rate)
        print('\n', f'{elapsed_time}s elapsed')
        print('nextSample =', nextSample)  # the buffer index used for next callback
        print('expectedSample =', expectedSample)  # expected buffer index
        print('difference = ', expectedSample - nextSample)  # index delay




# Convert the python function into a C function pointer.
cFuncPtr = ps.StreamingReadyType(streaming_callback)

# Fetch data from the driver in a loop, copying it out of the registered buffers and into our complete one.
start_time = datetime.now()
last_reported = datetime.now()
while nextSample < totalSamples and not autoStopOuter:
    wasCalledBack = False
    status["getStreamingLastestValues"] = ps.ps4000aGetStreamingLatestValues(chandle, cFuncPtr, None)
    if not wasCalledBack:
        # If we weren't called back by the driver, this means no data is ready. Sleep for a short while before trying
        # again.
        time.sleep(0.01)

print("Done grabbing values.")

# Stop the scope
# handle = chandle
status["stop"] = ps.ps4000aStop(chandle)
assert_pico_ok(status["stop"])

# Disconnect the scope
# handle = chandle
status["close"] = ps.ps4000aCloseUnit(chandle)
assert_pico_ok(status["close"])

# Display status returns
print(status)

2) According to the manual and programmers guide I should be able to use a sampling rate higher than 20MS/s if not using all channels. However, when I try to use channels A and B with e.g. 40MS/s (sampling interval 25ns) I get the error picosdk.errors.PicoSDKCtypesError: PicoSDK returned 'PICO_INVALID_SAMPLE_INTERVAL'. How can I enable 40MS/s or even 80MS/s?

Looking forward to hearding from you. Kind regards,
Philipp

Post Reply