Jump to content
  • 0

AD2: play wavegen, record scope, trigger


bharathb

Question

I'm not sure the project I'm trying to build is very unique, but I've been having trouble getting a python script I've written up using the sdk to function as desired. 

Here are my requirements: 

- Wavegen should play particular waveforms imported from csv files. Number of samples is ~80000, sample rate 12kHz

- scope should operate in record mode and save the scope data from both channels to a/separate csv files. Similarly collect 80000 samples at 12kHz. 

- all channels of scope and wavegen should be triggered simultaneously. 

- ideally would want this as a python script I can invoke from Matlab to integrate with the rest of my workflow, with the required args. (This has been working fine)

I've used bits and pieces from the different examples posted. When using a single device and triggering the scope channels with trigsrcAnalogOut1, with the existing logic in the examples for Record/play, I get arbitrary delays between the scope channel compared to the input waveform. 

with synchronization setup using the relevant examples, and using the second device purely to generate the external trigger signal for the main AD device, I see 4096 samples of delay. I'm guessing this is because of the different sizes of the buffers between scope(8k) and wavegen (4k) instruments. I could in theory account for this 4096 sample delay if it was consistent, but I can get up to 4200 samples of delay if I run the script from a slower computer. The application I'm using this for does not allow synchronization in post processing because there are inherently unknown delays between the input and output. What am I doing wrong here?

The files below are what I used:

data_acq2.py - single device, scope channels triggered from analog out


from ctypes import *
from dwfconstants import *
import math
import time
import matplotlib.pyplot as plt
import sys
import numpy as np
import csv

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

#declare ctype variables
hdwf = c_int()
sts = c_byte()
hzAcq_int = int(sys.argv[1])
hzAcq = c_double(hzAcq_int)
rate = hzAcq
nSamples = int(sys.argv[2])
sRun = 1.0*nSamples/hzAcq.value

# Load Play data
filename1 = sys.argv[3]
filename2 = sys.argv[4]
play1 = np.genfromtxt(filename1,dtype=float,delimiter='',skip_header=0)
play2 = np.genfromtxt(filename2,dtype=float,delimiter='',skip_header=0)

# AnalogOut expects double normalized to +/-1 value
play1_max = play1.max()
play1 /= play1_max
data1 = (c_double * nSamples)(*play1)

play2_max = play2.max()
play2 /= play2_max
data2 = (c_double * nSamples)(*play2)

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

#open device
# print("Opening first device")
if dwf.FDwfDeviceOpen(c_int(-1), byref(hdwf)) != 1 or hdwf.value == hdwfNone.value:
    szerr = create_string_buffer(512)
    dwf.FDwfGetLastErrorMsg(szerr)
    print(str(szerr.value))
    print("failed to open device")
    quit()

iRecord = 0
record1 = (c_double*nSamples)()
record2 = (c_double*nSamples)()

#set up acquisition

sRecord = nSamples/hzAcq.value

dwf.FDwfAnalogInChannelEnableSet(hdwf, c_int(0), c_int(1)) # channel 1
dwf.FDwfAnalogInChannelEnableSet(hdwf, c_int(1), c_int(1)) # channel 2
dwf.FDwfAnalogInChannelRangeSet(hdwf, c_int(-1), c_double(5.0))
dwf.FDwfAnalogInChannelOffsetSet(hdwf, c_int(-1), c_double(0))
dwf.FDwfAnalogInAcquisitionModeSet(hdwf, acqmodeRecord)
dwf.FDwfAnalogInFrequencySet(hdwf, c_double(hzAcq.value))
dwf.FDwfAnalogInRecordLengthSet(hdwf, c_double(sRecord))


#set up trigger
dwf.FDwfAnalogInTriggerAutoTimeoutSet(hdwf, c_double(0)) # 10 second auto trigger timeout

# dwf.FDwfAnalogInTriggerSourceSet(hdwf, trigsrcDetectorAnalogIn) # trigsrcDetectorAnalogIn
dwf.FDwfAnalogInTriggerSourceSet(hdwf, trigsrcAnalogOut1) # trigsrcDetectorAnalogIn

# dwf.FDwfAnalogInTriggerPositionSet(hdwf, c_double(nSamples/2/rate.value)) # -0.25 = trigger at 25%
dwf.FDwfAnalogInTriggerPositionSet(hdwf, c_double(0)) # -0.25 = trigger at 25%

dwf.FDwfAnalogInTriggerTypeSet(hdwf, c_int(0)) # trigtypeEdge

dwf.FDwfAnalogInTriggerChannelSet(hdwf, trigsrcDetectorAnalogIn) # AnalogOut1
# dwf.FDwfAnalogInTriggerChannelSet(hdwf, c_int(-1))

dwf.FDwfAnalogInTriggerLevelSet(hdwf, c_double(0)) # 0V
dwf.FDwfAnalogInTriggerHysteresisSet(hdwf, c_double(0.01)) # 0.001V
dwf.FDwfAnalogInTriggerConditionSet(hdwf, c_int(0)) # trigcondRisingPositive 

# #wait at least 2 seconds for the offset to stabilize
time.sleep(2)

# print("Starting oscilloscope")
dwf.FDwfAnalogInConfigure(hdwf, c_int(0), c_int(1))

# print("Playing file...")
iPlay = 0
channel = c_int(-1) # AWG 1 & 2
channel1 = c_int(0) # AWG 1
channel2 = c_int(1) # AWG 2
dwf.FDwfAnalogOutNodeEnableSet(hdwf, channel, 0, c_int(1))
dwf.FDwfAnalogOutNodeFunctionSet(hdwf, channel, 0, funcPlay)
dwf.FDwfAnalogOutRepeatSet(hdwf, channel, c_int(1))
dwf.FDwfAnalogOutRunSet(hdwf, channel, c_double(sRun))
dwf.FDwfAnalogOutNodeFrequencySet(hdwf, channel, 0, rate)
dwf.FDwfAnalogOutNodeAmplitudeSet(hdwf, channel1, 0, c_double(play1_max))
dwf.FDwfAnalogOutNodeAmplitudeSet(hdwf, channel2, 0, c_double(play2_max))

# Trigger channel2 Output trigger
dwf.FDwfAnalogOutTriggerSourceSet(hdwf, channel2, trigsrcAnalogOut1) 
dwf.FDwfAnalogOutTriggerSlopeSet(hdwf, channel, 2)

# prime the buffer with the first chunk of data
cBuffer = c_int(0)
dwf.FDwfAnalogOutNodeDataInfo(hdwf, channel, 0, 0, byref(cBuffer))
if cBuffer.value > nSamples : cBuffer.value = nSamples
dwf.FDwfAnalogOutNodeDataSet(hdwf, channel1, 0, data1, cBuffer)
dwf.FDwfAnalogOutNodeDataSet(hdwf, channel2, 0, data2, cBuffer)
iPlay += cBuffer.value
dwf.FDwfAnalogOutConfigure(hdwf, channel, c_int(1))

iSample = 0

dataLost = c_int(0)
dataFree = c_int(0)
dataAvailable = c_int(0)
dataCorrupted = c_int(0)
sts = c_ubyte(0)
totalLost = 0
totalCorrupted = 0

# loop to send out and read in data chunks
while iRecord < nSamples :
    if dwf.FDwfAnalogOutStatus(hdwf, channel, byref(sts)) != 1: # handle error
        print("Error")
        szerr = create_string_buffer(512)
        dwf.FDwfGetLastErrorMsg(szerr)
        print(szerr.value)
        break
    
    # play, analog out data chunk
    if sts.value == DwfStateRunning.value and iPlay < nSamples :  # running and more data to stream
        dwf.FDwfAnalogOutNodePlayStatus(hdwf, channel, 0, byref(dataFree), byref(dataLost), byref(dataCorrupted))
        totalLost += dataLost.value
        totalCorrupted += dataCorrupted.value
        if iPlay + dataFree.value > nSamples : # last chunk might be less than the free buffer size
            dataFree.value = nSamples - iPlay
        if dataFree.value > 0 : 
            if dwf.FDwfAnalogOutNodePlayData(hdwf, channel1, 0, byref(data1, iPlay*8), dataFree) != 1: # offset for double is *8 (bytes) 
                print("Error")
                break
            if dwf.FDwfAnalogOutNodePlayData(hdwf, channel2, 0, byref(data2, iPlay*8), dataFree) != 1: # offset for double is *8 (bytes) 
                print("Error")
                break
            iPlay += dataFree.value
    
    # handle error       
    if dwf.FDwfAnalogInStatus(hdwf, c_int(1), byref(sts)) != 1:
        print("FDwfAnalogInStatus Error")
        szerr = create_string_buffer(512)
        dwf.FDwfGetLastErrorMsg(szerr)
        print(szerr.value)
        break
    
    # record, analog in data chunk
    
    if sts.value == DwfStateRunning.value or sts.value == DwfStateDone.value : # recording or done
        dwf.FDwfAnalogInStatusRecord(hdwf, byref(dataAvailable), byref(dataLost), byref(dataCorrupted))
        iRecord += dataLost.value
        totalLost += dataLost.value
        totalCorrupted += dataCorrupted.value
        if dataAvailable.value > 0 :
            if iRecord+dataAvailable.value > nSamples :
                dataAvailable = c_int(nSamples-iRecord)
            dwf.FDwfAnalogInStatusData2(hdwf, c_int(0), byref(record1, sizeof(c_double)*iRecord), c_int(0), dataAvailable) # get channel 1 data chunk, offset for 16bit data is 2*
            dwf.FDwfAnalogInStatusData2(hdwf, c_int(1), byref(record2, sizeof(c_double)*iRecord), c_int(0), dataAvailable) # get channel 2 data chunk
            iRecord += dataAvailable.value
    
    
# print("Lost: "+str(totalLost))
# print("Corrupted: "+str(totalCorrupted))
print("done")
dwf.FDwfAnalogOutReset(hdwf, channel)
dwf.FDwfDeviceClose(hdwf)   

if iSample != 0 :
    record1 = record1[iSample:]+record1[:iSample]
    record2 = record2[iSample:]+record2[:iSample]

ch1_save = sys.argv[5]
ch2_save = sys.argv[6]


f = open(ch1_save, "w")
for v in record1:
    f.write("%s\n" % v)
f.close()

f = open(ch2_save, "w")
for v in record2:
    f.write("%s\n" % v)
f.close()

# plt.plot(np.fromiter(record1, dtype = float), color='orange')
# plt.plot(np.fromiter(record2, dtype = float), color='blue')
# plt.show()

data_acq_rev3.py - two devices, device 0 acts as the trigger source for device 1 which runs the scope and wavegen instruments all triggered by external trigger 1 (the T1 ports on the two devices are connected)


from ctypes import *
from dwfconstants import *
import math
import time
import matplotlib.pyplot as plt
import sys
import numpy as np
import csv

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

#declare ctype variables
hdwf = c_int()
rghdwf = []
cDevices = c_int()
sts = c_byte()
hzAcq_int = int(sys.argv[1])
hzAcq = c_double(hzAcq_int)
rate = hzAcq
nSamples = int(sys.argv[2])
sRun = 1.0*nSamples/hzAcq.value

# Load Play data
filename1 = sys.argv[3]
filename2 = sys.argv[4]
play1 = np.genfromtxt(filename1,dtype=float,delimiter='',skip_header=0)
play2 = np.genfromtxt(filename2,dtype=float,delimiter='',skip_header=0)

# AnalogOut expects double normalized to +/-1 value
play1_max = play1.max()
play1 /= play1_max
data1 = (c_double * nSamples)(*play1)

play2_max = play2.max()
play2 /= play2_max
data2 = (c_double * nSamples)(*play2)

iRecord = 0
record1 = (c_double*nSamples)()
record2 = (c_double*nSamples)()

#set up acquisition period

sRecord = nSamples/hzAcq.value

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

# declare string variables
devicename = create_string_buffer(64)
serialnum = create_string_buffer(16)

# enumerate connected devices
dwf.FDwfEnum(c_int(0), byref(cDevices))
if cDevices.value == 0:
    print("No device found")
    dwf.FDwfDeviceCloseAll()
    sys.exit(0)

# open devices
for iDevice in range(0, cDevices.value):
    dwf.FDwfEnumDeviceName(c_int(iDevice), devicename)
    dwf.FDwfEnumSN(c_int(iDevice), serialnum)
    print("------------------------------")
    print("Device "+str(iDevice+1)+" : \t" + str(devicename.value) + "\t" + str(serialnum.value))
    dwf.FDwfDeviceOpen(c_int(iDevice), byref(hdwf))
    if hdwf.value == 0:
        szerr = create_string_buffer(512)
        dwf.FDwfGetLastErrorMsg(szerr)
        print(str(szerr.value))
        dwf.FDwfDeviceCloseAll()
        sys.exit(0)
        
    rghdwf.append(hdwf.value)
        
     

    if iDevice == 0:
        
        dwf.FDwfAnalogInFrequencySet(hdwf, c_double(hzAcq.value))
        dwf.FDwfAnalogInChannelEnableSet(hdwf, c_int(0), c_int(1)) 
        dwf.FDwfAnalogInChannelOffsetSet(hdwf, c_int(0), c_double(0)) 
        dwf.FDwfAnalogInChannelRangeSet(hdwf, c_int(0), c_double(5))
        dwf.FDwfAnalogInAcquisitionModeSet(hdwf, acqmodeRecord)
        dwf.FDwfAnalogInRecordLengthSet(hdwf, c_double(sRecord))
        dwf.FDwfAnalogInTriggerPositionSet(hdwf, c_double(nSamples/2/rate.value)) # 0 is middle, 4/10 = 10%
        dwf.FDwfAnalogInTriggerSourceSet(hdwf, trigsrcPC) # 11 = trigsrcExternal1, T1
        dwf.FDwfAnalogInConfigure(hdwf, c_int(1), c_int(0))
        
        # trigger output on external trigger 2 of analog-out (AWG) 1
        # on first device drive External T1 (0) with trigsrcPC (1) 
        # expects T1 of each device to be connected together for synchronization
         
        dwf.FDwfDeviceTriggerSet(hdwf, c_int(0), c_byte(1)) # 0 = T1 , 1 = trigsrcPC, 7 = trigsrcAnalogOut1
        dwf.FDwfAnalogOutTriggerSourceSet(hdwf, c_int(0), trigsrcPC) # 11 = trigsrcExternal1
        dwf.FDwfAnalogOutNodeEnableSet(hdwf, c_int(0), c_int(0), c_int(1))
        dwf.FDwfAnalogOutNodeFunctionSet(hdwf, c_int(0), c_int(0), c_byte(1)) # funcSine
        dwf.FDwfAnalogOutNodeFrequencySet(hdwf, c_int(0), c_int(0), c_double(100))
        dwf.FDwfAnalogOutNodeAmplitudeSet(hdwf, c_int(0), c_int(0), c_double(1.5))
        dwf.FDwfAnalogOutNodeOffsetSet(hdwf, c_int(0), c_int(0), c_double(0))
    else:
        
        dwf.FDwfAnalogInFrequencySet(hdwf, c_double(hzAcq.value))
        dwf.FDwfAnalogInChannelEnableSet(hdwf, c_int(-1), c_int(1)) 
        dwf.FDwfAnalogInChannelOffsetSet(hdwf, c_int(-1), c_double(0)) 
        dwf.FDwfAnalogInChannelRangeSet(hdwf, c_int(-1), c_double(5))
        dwf.FDwfAnalogInAcquisitionModeSet(hdwf, acqmodeRecord)
        dwf.FDwfAnalogInRecordLengthSet(hdwf, c_double(sRecord))
        dwf.FDwfAnalogInTriggerPositionSet(hdwf, c_double(nSamples/2/rate.value)) # 0 is middle, 4/10 = 10%
        # dwf.FDwfAnalogInTriggerPositionSet(hdwf, c_double(0)) # 0 is middle, 4/10 = 10%
        # trigger source external trigger 1
        dwf.FDwfAnalogInTriggerSourceSet(hdwf, c_byte(11)) # 11 = trigsrcExternal1, T1
        dwf.FDwfAnalogInConfigure(hdwf, c_int(1), c_int(0))
        
        iPlay = 0
        channel = c_int(-1) # AWG 1 & 2
        channel1 = c_int(0) # AWG 1
        channel2 = c_int(1) # AWG 2
        dwf.FDwfAnalogOutNodeEnableSet(hdwf, channel, 0, c_int(1))
        dwf.FDwfAnalogOutNodeFunctionSet(hdwf, channel, 0, funcPlay)
        dwf.FDwfAnalogOutRepeatSet(hdwf, channel, c_int(1))
        dwf.FDwfAnalogOutRunSet(hdwf, channel, c_double(sRun))
        dwf.FDwfAnalogOutNodeFrequencySet(hdwf, channel, 0, rate)
        dwf.FDwfAnalogOutNodeAmplitudeSet(hdwf, channel1, 0, c_double(play1_max))
        dwf.FDwfAnalogOutNodeAmplitudeSet(hdwf, channel2, 0, c_double(play2_max))

        # Trigger channel Output trigger
        dwf.FDwfAnalogOutTriggerSourceSet(hdwf, channel, c_byte(11)) # 11 = trigsrcExternal1, T1

        # prime the buffer with the first chunk of data
        cBuffer = c_int(0)
        dwf.FDwfAnalogOutNodeDataInfo(hdwf, channel, 0, 0, byref(cBuffer))
        if cBuffer.value > nSamples : cBuffer.value = nSamples
        dwf.FDwfAnalogOutNodeDataSet(hdwf, channel1, 0, data1, cBuffer)
        dwf.FDwfAnalogOutNodeDataSet(hdwf, channel2, 0, data2, cBuffer)
        iPlay += cBuffer.value
        dwf.FDwfAnalogOutConfigure(hdwf, channel, c_int(1))
        

# #wait at least 2 seconds for the offset to stabilize
time.sleep(3)

# print("Starting oscilloscope")
# start analog-in (Scope)
for iDevice in range(len(rghdwf)):
    hdwf.value = rghdwf[iDevice]
    dwf.FDwfAnalogInConfigure(hdwf, c_int(0), c_int(1))
    
# wait for the last configured device to be armed too
hdwf.value = rghdwf[cDevices.value-1]
while True:
    dwf.FDwfAnalogInStatus(hdwf, c_int(0), byref(sts))
    if sts.value == 1 : # DwfStateArmed
        break

# print("Playing file...")


iSample = 0

dataLost = c_int(0)
dataFree = c_int(0)
dataAvailable = c_int(0)
dataCorrupted = c_int(0)
sts = c_ubyte(0)
totalLost = 0
totalCorrupted = 0

hdwf.value = rghdwf[0]
dwf.FDwfAnalogOutConfigure(hdwf, c_int(0), c_int(1)) # start analog-out (AWG) 1 of first device
# generate trigger signal
dwf.FDwfDeviceTriggerPC(hdwf)

# Select, play and colelct from main device

hdwf.value = rghdwf[1]

# loop to send out and read in data chunks
while iRecord < nSamples :
    if dwf.FDwfAnalogOutStatus(hdwf, channel, byref(sts)) != 1: # handle error
        print("Error")
        szerr = create_string_buffer(512)
        dwf.FDwfGetLastErrorMsg(szerr)
        print(szerr.value)
        break
    
    # play, analog out data chunk
    if sts.value == DwfStateRunning.value and iPlay < nSamples :  # running and more data to stream
        dwf.FDwfAnalogOutNodePlayStatus(hdwf, channel, 0, byref(dataFree), byref(dataLost), byref(dataCorrupted))
        totalLost += dataLost.value
        totalCorrupted += dataCorrupted.value
        if iPlay + dataFree.value > nSamples : # last chunk might be less than the free buffer size
            dataFree.value = nSamples - iPlay
        if dataFree.value > 0 : 
            if dwf.FDwfAnalogOutNodePlayData(hdwf, channel1, 0, byref(data1, iPlay*8), dataFree) != 1: # offset for double is *8 (bytes) 
                print("Error")
                break
            if dwf.FDwfAnalogOutNodePlayData(hdwf, channel2, 0, byref(data2, iPlay*8), dataFree) != 1: # offset for double is *8 (bytes) 
                print("Error")
                break
            iPlay += dataFree.value
    
    # handle error       
    if dwf.FDwfAnalogInStatus(hdwf, c_int(1), byref(sts)) != 1:
        print("FDwfAnalogInStatus Error")
        szerr = create_string_buffer(512)
        dwf.FDwfGetLastErrorMsg(szerr)
        print(szerr.value)
        break
    
    # record, analog in data chunk
    
    if sts.value == DwfStateRunning.value or sts.value == DwfStateDone.value : # recording or done
        dwf.FDwfAnalogInStatusRecord(hdwf, byref(dataAvailable), byref(dataLost), byref(dataCorrupted))
        iRecord += dataLost.value
        totalLost += dataLost.value
        totalCorrupted += dataCorrupted.value
        if dataAvailable.value > 0 :
            if iRecord+dataAvailable.value > nSamples :
                dataAvailable = c_int(nSamples-iRecord)
            dwf.FDwfAnalogInStatusData2(hdwf, c_int(0), byref(record1, sizeof(c_double)*iRecord), c_int(0), dataAvailable) # get channel 1 data chunk, offset for 16bit data is 2*
            dwf.FDwfAnalogInStatusData2(hdwf, c_int(1), byref(record2, sizeof(c_double)*iRecord), c_int(0), dataAvailable) # get channel 2 data chunk
            iRecord += dataAvailable.value
    
    
# print("Lost: "+str(totalLost))
# print("Corrupted: "+str(totalCorrupted))
print("done")
dwf.FDwfAnalogOutReset(hdwf, channel)
dwf.FDwfDeviceCloseAll()   

if iSample != 0 :
    record1 = record1[iSample:]+record1[:iSample]
    record2 = record2[iSample:]+record2[:iSample]

ch1_save = sys.argv[5]
ch2_save = sys.argv[6]


f = open(ch1_save, "w")
for v in record1:
    f.write("%s\n" % v)
f.close()

f = open(ch2_save, "w")
for v in record2:
    f.write("%s\n" % v)
f.close()

# plt.plot(np.fromiter(record1, dtype = float), color='orange')
# plt.plot(np.fromiter(record2, dtype = float), color='blue')
# plt.show()

I'd also like to say that I've implemented this very easily on the GUI itself, and don't have any such delay issues. But I can't use the GUI for now since I can't automate it with the rest of my workflow on Matlab. I understand there's the WaveForms scripter as well, which might be more appropriate for this implementation, but I haven't worked with JS before, so haven't explored that yet. If you think that's a better way to run this, please let me know. 

 

 

Edited by bharathb
Added inline code
Link to comment
Share on other sites

1 answer to this question

Recommended Posts

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
×
×
  • Create New...