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 be found 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.