• 0

AD2: Trigger of AnalogIn using DigitalOut-Trigger in SDK not well aligned


Posted (edited)

Hi. I have troubles using the trigger functions in the SDK to align the recording of a signal with the pattern generator (DigitalOut Instrument on AD2). 

What I want to do is essentially, using the SDK in a python script is:

- I record a signal from the AWG on the AD2 I am sending through an analog circuit using two multiplexers. 

- I control the multiplexers with the digitalOut instrument on the AD2.

- I want to align the starting point of the digitalOut-pattern and the starting point of the AnalogIn acquisition (record-mode, int16 data)

As far as I understand from the SDK reference for this purpose I can trigger the analogIn with the digitalOut (use trigsrcDigitalOut from the dwf constants). However, when I implemented this, I get an undefined timelag between the acquistion of data and the start of the digitalOut signal. To illustrate this, I recorded the signal of one of the digital pins I use to generate the pattern for the multiplexers (I record the bare 16int ADC conversions, to read faster from the AD2).

I sample at 1Mhz and the the pattern I output on this particular pin starts with (1,0,...) at 100Hz. As you can see, the timelag seems to be around ca. 1.5 ms.


I also did another experiment, trying out the same thing in the WaveForms Software. Instead of outputting a custom pattern, I just output a clock at 100Hz on the same digital pin and record this signal through channel 1 of the oscilloscope. 


From what I understand, the two experiments should be pretty much equal, so I am puzzled I get this delay of approximately 1.5ms when I implement it with SDK functions.


Does anyone have an explanation why the trigger is not correctly aligned in my python script, and why this timelag is not deterministic? Any help would be greatly appreciated!


(PS: I can provide a code snippet, but right now my code is shared from different fies & classes, so it is not straightforward to copy it to here...)

updated: I have a code snippet for illustration below:

import math
import sys
import time
from ctypes import *

import matplotlib.pyplot as plt
import numpy

from dwfconstants import *

if sys.platform.startswith("win"):
    dwf = cdll.dwf
elif sys.platform.startswith("darwin"):
    dwf = cdll.LoadLibrary("/Library/Frameworks/dwf.framework/dwf")
    dwf = cdll.LoadLibrary("libdwf.so")

# declare ctype variables
hdwf = c_int()
sts = c_byte()
hzAcq = c_double(1e6)
nSamples = 2 ** 17
rgSamples = (c_int16 * nSamples)()
cAvailable = c_int()
cLost = c_int()
cCorrupted = c_int()
fLost = 0
fCorrupted = 0

# print(DWF version
version = create_string_buffer(16)
print("DWF Version: " + str(version.value))

# open device
print("Opening first device")
dwf.FDwfDeviceOpen(c_int(-1), byref(hdwf))

if hdwf.value == hdwfNone.value:
    szerr = create_string_buffer(512)
    print("failed to open device")

# set up pattern generator

data = [1, 0, 1, 0, 0, 1, 0, 0, 1]
pin = 11

hzSys = c_double()

dwf.FDwfDigitalOutInternalClockInfo(hdwf, byref(hzSys))

rgbdata = (c_ubyte * ((len(data) + 7) >> 3))(0)
# array to bits in byte array
for j in range(len(data)):
    if data[j] != 0:
        rgbdata[j >> 3] |= 1 << (j & 7)  # var |= value is var = var | value
print("bytestream of data " + str(pin) + ": " + str(list(rgbdata)))

dwf.FDwfDigitalOutEnableSet(hdwf, c_int(pin), c_int(1))
dwf.FDwfDigitalOutTypeSet(hdwf, c_int(pin), DwfDigitalOutTypeCustom)
dwf.FDwfDigitalOutIdleSet(hdwf, c_int(pin), DwfDigitalOutIdleLow)
# set Divider to generate output at frequency f_scan.
dwf.FDwfDigitalOutDividerSet(hdwf, c_int(pin), c_int(int(hzSys.value / 100)))
dwf.FDwfDigitalOutDataSet(hdwf, c_int(pin), byref(rgbdata), c_int(len(data)))
dwf.FDwfDigitalOutTriggerSourceSet(hdwf, trigsrcNone)

# set up acquisition
dwf.FDwfAnalogInChannelEnableSet(hdwf, c_int(0), c_bool(True))
dwf.FDwfAnalogInChannelRangeSet(hdwf, c_int(0), c_double(5))
dwf.FDwfAnalogInAcquisitionModeSet(hdwf, acqmodeRecord)
dwf.FDwfAnalogInFrequencySet(hdwf, hzAcq)
    hdwf, c_double(nSamples / hzAcq.value)
)  # -1 infinite record length
dwf.FDwfAnalogInTriggerSourceSet(hdwf, trigsrcDigitalOut)

# wait at least 2 seconds for the offset to stabilize

print(f"Starting oscilloscope. Measure digitalout pin {pin}")
dwf.FDwfAnalogInConfigure(hdwf, c_int(0), c_int(1))
# configure digitalOut instrument, don't run yet (enough time to fill buffer with data)
dwf.FDwfDigitalOutConfigure(hdwf, c_int(0))

cSamples = 0
# start the digitalOut instrument
dwf.FDwfDigitalOutConfigure(hdwf, c_int(1))

while cSamples < nSamples:
    dwf.FDwfAnalogInStatus(hdwf, c_int(1), byref(sts))
    if cSamples == 0 and (
        sts == DwfStateConfig or sts == DwfStatePrefill or sts == DwfStateArmed
        # Acquisition not yet started.

        hdwf, byref(cAvailable), byref(cLost), byref(cCorrupted)

    cSamples += cLost.value

    if cLost.value:
        fLost = 1
    if cCorrupted.value:
        fCorrupted = 1

    if cAvailable.value == 0:

    if cSamples + cAvailable.value > nSamples:
        cAvailable = c_int(nSamples - cSamples)

        byref(rgSamples, sizeof(c_int16) * cSamples),
    )  # get channel 1 data
    # dwf.FDwfAnalogInStatusData16(hdwf, c_int(1), byref(rgSamples, sizeof(c_int16)*cSamples), c_int(c), Available) # get channel 2 data
    cSamples += cAvailable.value

dwf.FDwfAnalogOutReset(hdwf, c_int(0))

print("Recording done")
if fLost:
    print("Samples were lost! Reduce frequency")
if fCorrupted:
    print("Samples could be corrupted! Reduce frequency")

f = open("record.csv", "w")
for v in rgSamples:
    f.write("%s\n" % v)

plt.plot(numpy.fromiter(rgSamples, dtype=numpy.int16))



timelag of ca 1000 samples .png

Edited by marcelott
updated with code snippet

Share this post

Link to post
Share on other sites

2 answers to this question

Recommended Posts

  • 0
3 hours ago, attila said:

Hi @marcelott

See the following example: AnalogIn_Record_Trigger_int16.py

Hi @attila

Very impressed by your speedy response and the ready-to-use example! Thank you very much. I implemented it in my codestructure and it works perfectly well. 

So apparently the part I was missing is the "iSample" which tracks the overflow of the number of samples we actually want to capture. And to my understanding this overflow is random since the circular buffer is continuously filled and you don't know a priori at which position the buffer was being filled at the time the trigger occurs. By counting the number of samples you transfer from the buffer on the AD2 to the rgSamples1 array in python during the specified record time, you can then a posteriori backcalculate at which position in the buffer the trigger event occured and then you can discard the inital samples which were recorded before the trigger occured. 

The only change I made when implementing your solution is that I don't shift the acquired samples circularly but I just fill the last couple of array elements of "rgsamples1" with zeroes, as I am not recording a simple sinusoid and thus shifting the first few acquired samples to the end of the vector would be misleading.

Share this post

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now