Using WaveForms SDK

Introduction

WaveForms SDK is a set of tools provided within the WaveForms installation that are used to develop custom software solutions that use Digilent Test and Measurement devices. The WaveForms SDK API is available in several programming languages, making it easy to to use across many different platforms.

Normally Test and Measurement devices are controlled and configured through the WaveForms application with a personal computer. Such a setup may be impossible in a given context, or an amount of automated signal measurement may be sought outside WaveForms' scripting environment. WaveForms SDK gives the necessary tools to help craft the perfect solution for any problem.

Sample Application

This guide walks through the implementation of a sample application to demonstrate a use-case for the WaveForms SDK as well as the proper workflow. The sample application, implemented in Python, will configure a Digilent Test and Measurement device to fill a data buffer with samples. These samples are then used to generate a graph image that is shared on a web page hosted locally.


Prerequisites

  • A Digilent Test & Measurement Device with Analog Input and Output Channels
  • A Computer with WaveForms Software Installed
    • WaveForms SDK is installed alongside the WaveForms application.

1. SDK Overview

WaveForms SDK is included with WaveForms and is installed alongside the application. The SDK is available to use with C/C++, C#, Python and Visual Basic through a dynamic library.

On Windows, the dynamic library can be found at C:\Windows\System32\dwf.dll and on Linux at /usr/lib/libdwf.so.x.x.x.

A static library on Windows is at C:\Program Files\Digilent\WaveFormsSDK\lib\x86 for 32-bit systems and for 64-bit systems at C:\Program Files (x86)\Digilent\WaveFormsSDK\lib\x64.

The C Header file is located at C:\Program Files\Digilent\WaveFormsSDK\inc for Windows 32-bit, C:\Program Files (x86)\Digilent\WaveFormsSDK\inc for Windows 64-bit and at /usr/local/include/digilent/waveforms for Linux.

Other working code examples for each described programming language are provided with the SDK and may befound at C:\Program Files\Digilent\WaveFormsSDK\samples for Windows 32-bit, C:\Program Files (x86)\Digilent\WaveFormsSDK\samples for Windows 64-bit and /usr/local/share/digilent/waveforms/samples on Linux.

2. Implementing the Sample Application

2.1 Setup

dwfconstants.py must be copied into project directory and its location differs by OS:

  • Win32 C:\Program Files\Digilent\WaveFormsSDK\samples\py
  • Win64 C:\Program Files (x86)\Digilent\WaveFormsSDK\samples\py
  • Linux /usr/local/share/digilent/waveforms/samples/py

Several Python packages are needed and are installed by invoking

pip install matplotlib, numpy, flask

2.2 Script Implementation

In the project directory, create a file called main.py and open in with a text editor. At the top of the file, declare the imports like so:

from ctypes import *
from dwfconstants import *
import math
import time
import matplotlib.pyplot as plt, mpld3
import sys
import numpy
from io import BytesIO, StringIO
from flask import Flask, Response

The dll must be loaded, and the method to do so depends on the operating system. Add the next lines of code to do so.

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

The next few lines of code declare some helper variables that are used to configure the Test and Measurement device. A sample buffer is also declared, which will soon be filled with data acquired from the device. Add the snippet to the project code:

#declare ctype variables
hdwf = c_int()
sts = c_byte()
hzAcq = c_double(100000) # 100 kHz
nSamples = 200000
rgdSamples = (c_double*nSamples)()
cAvailable = c_int()
cLost = c_int()
cCorrupted = c_int()
fLost = 0
fCorrupted = 0

Next, the first available device is opened. The API returns a device handle that will be used to configure the device. Add the code below:

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

The signal that is to be measures will come from the device itself. It is configured to output a sine wave on the device's wavegen channel 1. Add the following code:

# enable wavegen channel 1, set the waveform to sine, set the frequency to 1 Hz, the amplitude to 2v and start the wavegen
dwf.FDwfAnalogOutNodeEnableSet(hdwf, c_int(0), AnalogOutNodeCarrier, c_bool(True))
dwf.FDwfAnalogOutNodeFunctionSet(hdwf, c_int(0), AnalogOutNodeCarrier, funcSine)
dwf.FDwfAnalogOutNodeFrequencySet(hdwf, c_int(0), AnalogOutNodeCarrier, c_double(1))
dwf.FDwfAnalogOutNodeAmplitudeSet(hdwf, c_int(0), AnalogOutNodeCarrier, c_double(2))
dwf.FDwfAnalogOutConfigure(hdwf, c_int(0), c_bool(True))

The device's oscilloscope channel is then configured to take samples, and is started, with the addition of the following code:

# enable scope channel 1, set the input range to 5v, set acquisition mode to record, set the sample frequency to 100kHz and set the record length to 2 seconds
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)
dwf.FDwfAnalogInRecordLengthSet(hdwf, c_double(nSamples/hzAcq.value)) # -1 infinite record length
 
#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))

The next snippet then polls the status of the device, reads any available samples into the buffer. It continues to do so while the buffer isn't full.

cSamples = 0
 
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.
        continue
 
    # get the number of samples available, lost & corrupted
    dwf.FDwfAnalogInStatusRecord(hdwf, byref(cAvailable), byref(cLost), byref(cCorrupted))
 
    cSamples += cLost.value
 
    # set the lost & corrupted flags
    if cLost.value :
        fLost = 1
    if cCorrupted.value :
        fCorrupted = 
 
    # skip reading samples if there aren't any
    if cAvailable.value==0 :
        continue
 
    # cap the available samples if the buffer would overflow from what's really available
    if cSamples+cAvailable.value > nSamples :
        cAvailable = c_int(nSamples-cSamples)
 
    # Read channel 1's available samples into the buffer
    dwf.FDwfAnalogInStatusData(hdwf, c_int(0), byref(rgdSamples, sizeof(c_double)*cSamples), cAvailable) # get channel 1 data
    cSamples += cAvailable.value

After taking samples, it's good practice to cleanup by turning off the wavegen and closing the device.

# reset wavegen to stop it, close the device
dwf.FDwfAnalogOutReset(hdwf, c_int(0))
dwf.FDwfDeviceCloseAll()

A graph image is created from the sampled data, with the image being kept in its own buffer to be used by the web server.

# generate a graph image from the samples, and store it in a bytes buffer
plt.plot(numpy.fromiter(rgdSamples, dtype = numpy.float))
bio = BytesIO()
plt.savefig(bio, format="png")

Finally, a web server is setup to return the graph image whenever it gets a HTTP request.

# start web server, only if running as main
if __name__ == "__main__":
    app = Flask(__name__)
 
    @app.route('/')
    def root_handler():
        return Response(bio.getvalue(), mimetype="image/png") # return the graph image in response
 
    app.run()

A complete copy of the above code may be downloaded here


3. Running the Application

At this point, connect the Wavegen channel 1 and the Scope channel 1 pins of the Test and Measurement device together. Plug the device into the computer. In a console, call

python main.py

The console should then have output that is similar to the following:

DWF Version: b'3.10.9'
Opening first device
Generating sine wave...
Starting oscilloscope
Recording done
 * Serving Flask app "main" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Open the web browser and navigate to http://127.0.0.1:5000 to see the graph image of the sampled sine wave, similar to the below image:


Next Steps

For more guides on how to use the Digilent Test & Measurement Device, return to the device's Resource Center, linked from Instrumentation page of this wiki.

For more information on WaveForms visit the WaveForms Reference Manual.

For technical support, please visit the Scopes and Instruments section of the Digilent Forums.