Whitney Knitter
Published © GPL3+

How to Script Test Measurements in Linux Mode on the ADP3450

See how to script a series of test measurements for automated testing on the ADP3450.

IntermediateFull instructions provided1 hour1,110
How to Script Test Measurements in Linux Mode on the ADP3450

Things used in this project

Hardware components

Digilent Analog Discovery Pro ADP3450
×1

Software apps and online services

WaveForms
Digilent WaveForms

Story

Read more

Code

automated_test0.py

Python
"""
Automated test script for ADP3450
"""
import os
import sys
import math
import time
import numpy
import random
import ctypes

from os import sep
import termplotlib as tpl
from dwfconstants import * 
from sys import platform, path
import matplotlib.pyplot as plt

####################################################################################################
### Function declarations 
####################################################################################################

### Oscilloscope functions 

def scope_open(device_handle, sampling_frequency=20e06, buffer_size=8192, offset=0, amplitude_range=5):
    """
        initialize the oscilloscope

        parameters: - device handle
                    - sampling frequency in Hz, default is 20MHz
                    - buffer size, default is 8192
                    - offset voltage in Volts, default is 0V
                    - amplitude range in Volts, default is 5V
    """
    # enable all channels
    dwf.FDwfAnalogInChannelEnableSet(device_handle, ctypes.c_int(0), ctypes.c_bool(True))
    
    # set offset voltage (in Volts)
    dwf.FDwfAnalogInChannelOffsetSet(device_handle, ctypes.c_int(0), ctypes.c_double(offset))
    
    # set range (maximum signal amplitude in Volts)
    dwf.FDwfAnalogInChannelRangeSet(device_handle, ctypes.c_int(0), ctypes.c_double(amplitude_range))
    
    # set the buffer size (data point in a recording)
    dwf.FDwfAnalogInBufferSizeSet(device_handle, ctypes.c_int(buffer_size))
    
    # set the acquisition frequency (in Hz)
    dwf.FDwfAnalogInFrequencySet(device_handle, ctypes.c_double(sampling_frequency))
    
    # disable averaging (for more info check the documentation)
    dwf.FDwfAnalogInChannelFilterSet(device_handle, ctypes.c_int(-1), constants.filterDecimate)
    return

def scope_close(device_handle):
    """
        reset the scope
    """
    dwf.FDwfAnalogInReset(device_handle)
    return

def scope_measure(device_handle, channel):
    """
        measure a voltage

        parameters: - device handler
                    - the selected oscilloscope channel (1-2, or 1-4)
        
        returns:    - the measured voltage in Volts
    """
    # set up the instrument
    dwf.FDwfAnalogInConfigure(device_handle, ctypes.c_bool(False), ctypes.c_bool(False))
    
    # read data to an internal buffer
    dwf.FDwfAnalogInStatus(device_handle, ctypes.c_bool(False), ctypes.c_int(0))
    
    # extract data from that buffer
    voltage = ctypes.c_double()   # variable to store the measured voltage
    dwf.FDwfAnalogInStatusSample(device_handle, ctypes.c_int(channel - 1), ctypes.byref(voltage))
    
    # store the result as float
    voltage = voltage.value
    return voltage

def scope_record(device_handle, channel, sampling_frequency=20e06, buffer_size=8192):
    """
        record an analog signal

        parameters: - device handle
                    - the selected oscilloscope channel (1-2, or 1-4)
                    - sampling frequency in Hz, default is 20MHz
                    - buffer size, default is 8192

        returns:    - buffer - a list with the recorded voltages
                    - time - a list with the time moments for each voltage in seconds (with the same index as "buffer")
    """
    # set up the instrument
    dwf.FDwfAnalogInConfigure(device_handle, ctypes.c_bool(False), ctypes.c_bool(True))
    
    # read data to an internal buffer
    while True:
        status = ctypes.c_byte()    # variable to store buffer status
        dwf.FDwfAnalogInStatus(device_handle, ctypes.c_bool(True), ctypes.byref(status))
    
        # check internal buffer status
        if status.value == constants.DwfStateDone.value:
                # exit loop when ready
                break
    
    # copy buffer
    buffer = (ctypes.c_double * buffer_size)()   # create an empty buffer
    dwf.FDwfAnalogInStatusData(device_handle, ctypes.c_int(channel - 1), buffer, ctypes.c_int(buffer_size))
    
    # calculate aquisition time
    time = range(0, buffer_size)
    time = [moment / sampling_frequency for moment in time]
    
    # convert into list
    buffer = [float(element) for element in buffer]
    return buffer #, time

def scope_trigger(device_handle, enable, source=None, channel=1, timeout=0, edge_rising=True, level=0):
    """
        set up triggering

        parameters: - device handle
                    - enable / disable triggering with True/False
                    - trigger source - possible: none, analog, digital, external[1-4]
                    - trigger channel - possible options: 1-4 for analog, or 0-15 for digital
                    - auto trigger timeout in seconds, default is 0
                    - trigger edge rising - True means rising, False means falling, default is rising
                    - trigger level in Volts, default is 0V
    """
    if enable and source != None:
        # enable/disable auto triggering
        dwf.FDwfAnalogInTriggerAutoTimeoutSet(device_handle, ctypes.c_double(timeout))

        # set trigger source
        dwf.FDwfAnalogInTriggerSourceSet(device_handle, source)

        # set trigger channel
        if source == constants.trigsrcDetectorAnalogIn:
            channel -= 1    # decrement analog channel index
        dwf.FDwfAnalogInTriggerChannelSet(device_handle, ctypes.c_int(channel))

        # set trigger type
        dwf.FDwfAnalogInTriggerTypeSet(device_handle, constants.trigtypeEdge)

        # set trigger level
        dwf.FDwfAnalogInTriggerLevelSet(device_handle, ctypes.c_double(level))

        # set trigger edge
        if edge_rising:
            # rising edge
            dwf.FDwfAnalogInTriggerConditionSet(device_handle, constants.trigcondRisingPositive)
        else:
            # falling edge
            dwf.FDwfAnalogInTriggerConditionSet(device_handle, constants.trigcondFallingNegative)
    else:
        # turn off the trigger
        dwf.FDwfAnalogInTriggerSourceSet(device_handle, constants.trigsrcNone)
    return

### Waveform generator functions

def wave_generate(device_handle, channel, function, offset, frequency=1e03, amplitude=1, symmetry=50, wait=0, run_time=0, repeat=0, data=[]):
    """
        generate an analog signal

        parameters: - device handle
                    - the selected wavegen channel (1-2)
                    - function - possible: custom, sine, square, triangle, noise, ds, pulse, trapezium, sine_power, ramp_up, ramp_down
                    - offset voltage in Volts
                    - frequency in Hz, default is 1KHz
                    - amplitude in Volts, default is 1V
                    - signal symmetry in percentage, default is 50%
                    - wait time in seconds, default is 0s
                    - run time in seconds, default is infinite (0)
                    - repeat count, default is infinite (0)
                    - data - list of voltages, used only if function=custom, default is empty
    """
    # enable channel
    channel = ctypes.c_int(channel - 1)
    dwf.FDwfAnalogOutNodeEnableSet(device_handle, channel, constants.AnalogOutNodeCarrier, ctypes.c_bool(True))
    
    # set function type
    dwf.FDwfAnalogOutNodeFunctionSet(device_handle, channel, constants.AnalogOutNodeCarrier, function)
    
    # load data if the function type is custom
    if function == constants.funcCustom:
        data_length = len(data)
        buffer = (ctypes.c_double * data_length)()
        for index in range(0, len(buffer)):
            buffer[index] = ctypes.c_double(data[index])
        dwf.FDwfAnalogOutNodeDataSet(device_handle, channel, constants.AnalogOutNodeCarrier, buffer, ctypes.c_int(data_length))
    
    # set frequency
    dwf.FDwfAnalogOutNodeFrequencySet(device_handle, channel, constants.AnalogOutNodeCarrier, ctypes.c_double(frequency))
    
    # set amplitude or DC voltage
    dwf.FDwfAnalogOutNodeAmplitudeSet(device_handle, channel, constants.AnalogOutNodeCarrier, ctypes.c_double(amplitude))
    
    # set offset
    dwf.FDwfAnalogOutNodeOffsetSet(device_handle, channel, constants.AnalogOutNodeCarrier, ctypes.c_double(offset))
    
    # set symmetry
    dwf.FDwfAnalogOutNodeSymmetrySet(device_handle, channel, constants.AnalogOutNodeCarrier, ctypes.c_double(symmetry))
    
    # set running time limit
    dwf.FDwfAnalogOutRunSet(device_handle, channel, ctypes.c_double(run_time))
    
    # set wait time before start
    dwf.FDwfAnalogOutWaitSet(device_handle, channel, ctypes.c_double(wait))
    
    # set number of repeating cycles
    dwf.FDwfAnalogOutRepeatSet(device_handle, channel, ctypes.c_int(repeat))
    
    # start
    dwf.FDwfAnalogOutConfigure(device_handle, channel, ctypes.c_bool(True))
    return

def wave_close(device_handle):
    """
        reset the wavegen
    """
    dwf.FDwfAnalogOutReset(device_handle)
    return

####################################################################################################
### Main 
####################################################################################################

# initialize the platform by loading the WaveForms dynamic library and connect to the host PC
dwf = ctypes.cdll.LoadLibrary("libdwf.so")
constants_path = sep + "usr" + sep + "share" + sep + "digilent" + sep + "waveforms" + sep + "samples" + sep + "py"

# import constants
path.append(constants_path)
import dwfconstants as constants

hdwf = ctypes.c_int()
sts = ctypes.c_byte()
rgdSamples = (ctypes.c_double*8000)()

print("Opening first device found...")
dwf.FDwfDeviceConfigOpen(ctypes.c_int(-1), ctypes.c_int(0), ctypes.byref(hdwf)) 

if hdwf.value == 0:
    print("failed to open device")
    szerr = create_string_buffer(512)
    dwf.FDwfGetLastErrorMsg(szerr)
    print(str(szerr.value))
    quit()

print("ADP3450 device open!")
scope_samp_freq = 10e06
buffer_size = 8192

# add new here
print("Configuring the instrument buffer.")
cBufMax = ctypes.c_int()
dwf.FDwfAnalogInBufferSizeInfo(hdwf, 0, ctypes.byref(cBufMax))
print("Device buffer size: "+str(cBufMax.value)) 

print("Configuring oscilloscope channel 1...")
scope_open(hdwf, scope_samp_freq, 8192, 0, 1)

print("Wait at least 2 seconds for the offset to stabilize...")
time.sleep(2)

print("Configuring waveform generator channel 1...")
wave_generate(hdwf, 1, constants.funcSine, 0, 1e03, 1, 50, 0, 0, 0)

print("Record waveform from oscilloscope...")
rgdSamples = scope_record(hdwf, 1, scope_samp_freq, buffer_size)

while True:
    dwf.FDwfAnalogInStatus(hdwf, ctypes.c_int(1), ctypes.byref(sts))
    if sts.value == DwfStateDone.value :
        break
    time.sleep(0.1)

print("Acquisition done!")

print("Closing all devices...")
dwf.FDwfDeviceCloseAll()

print("Plot window...")
x = numpy.linspace(0, 2*numpy.pi, len(rgdSamples), dtype=numpy.float32)
y = numpy.array(rgdSamples, dtype=numpy.float32)
fig = tpl.figure()
fig.plot(x, y, width=100, height=50)
fig.show()

Credits

Whitney Knitter

Whitney Knitter

169 projects • 1700 followers
All thoughts/opinions are my own and do not reflect those of any company/entity I currently/previously associate with.

Comments